Package management

A V module is a single folder with .v files inside. A V package can contain one or more V modules. A V package should have a v.mod file at its top folder, describing the contents of the package.

That v.mod file is also the package's relative module anchor. When V compiles a file beside or below it, imports are resolved relative to the folder containing v.mod.

V packages are installed normally in your ~/.vmodules folder. That location can be overridden by setting the env variable VMODULES.

v install --local installs into the project's own lookup root instead, i.e. the folder holding its v.mod, so the package lands beside the project's own modules and is imported by its name just like they are. That root is shared with the modules the project writes itself, so VPM keeps a record of what it installed there and only updates or removes those. A package an older V installed into the project's modules/ directory has no such record; after moving it up beside the v.mod, v install --local --adopt <module> tells VPM it is one of its own.

Package names and import paths

A package name can contain characters that are not valid in a V import path, for example - or uppercase letters. Such names are normalized when the package is installed: - becomes _ and the name is lowercased. A package named my-mod is therefore installed as ~/.vmodules/my_mod and imported with import my_mod. The same applies to the publisher part of a VPM package name, so Some-Publisher.repo is installed as ~/.vmodules/some_publisher/repo and imported with import some_publisher.repo.

v install prints a warning with the resulting import prefix whenever it has to normalize a name. A package may contain only nested modules, so append the nested module path when needed (for example, import my_mod.json). If you publish a package, prefer a name in v.mod that is already a valid import path.

v mod why

A V project declares its dependencies by hand in the dependencies field of its v.mod, and the compiler resolves imports against the module search path: the project folder, vlib, and the global module folders. Nothing records which of those a build actually reaches, so a v.mod quietly collects modules that no longer have anything to do with the code.

v mod why answers that question. It prints the shortest chain of imports that brings a module into the build, one module per line, starting at the project itself:

$ v mod why lib.http
app
app.net
lib
lib.http

Read from the bottom up, that chain says: the project imports app.net, which imports lib, which imports lib.http. So if you want to know what breaks when lib.http changes, you can see exactly what pulls it in.

The case worth acting on is a module that is installed but that nothing imports, which v mod why words differently, the same way go mod why does:

$ v mod why oldlib
(main module does not need module `oldlib`)

A module that cannot be found in the module search path at all is an error, and points at v install.

v mod why has to run inside a project folder, since it needs a v.mod in the current directory or one of its parents. For the whole compiler environment rather than just this project's modules, use v doctor. To install or remove a module, use v install and v remove.

One limitation worth stating: v mod why reads the imports from the source files of the project and of every module it reaches, rather than from the compiler's own resolved build list. That is what lets it work without a build, but it also means a file that a build constraint excludes can still contribute an import edge. In practice that can only add a path to a chain, never remove a real one.

Package commands

You can use the V frontend to do package operations, just like you can use it for compiling code, formatting code, vetting code etc.

v [package_command] [param]

where a package command can be one of:

   install           Install a package from VPM.
   remove            Remove a package that was installed from VPM.
   search            Search for a package from VPM.
   update            Update an installed package from VPM.
   upgrade           Upgrade all the outdated packages.
   list              List all installed packages.
   outdated          Show installed packages that need updates.

You can install packages already created by someone else with VPM:

v install [package]

Example:

v install ui

Packages can be installed directly from git or mercurial repositories.

v install [--once] [--git|--hg] [url]

Example:

v install --git https://github.com/vlang/markdown

Sometimes you may want to install the dependencies ONLY if those are not installed:

v install --once [package]

Removing a package with v:

v remove [package]

Example:

v remove ui

Updating an installed package from VPM:

v update [package]

Example:

v update ui

Or you can update all your packages:

v update

To see all the packages you have installed, you can use:

v list

Example:

> v list
Installed packages:
  markdown
  ui

To see all the packages that need updates:

v outdated

Example:

> v outdated
Package are up to date.

Resolving package versions

Dependencies can use semantic version ranges, for example vsl@^0.1.47 or nedpals.args@>=0.4.0 <0.6.0. VPM selects tagged releases satisfying all requirements for a repository and backtracks to older releases if their dependencies conflict. Unchanged projects prefer locked commits. v install --frozen uses the lock without writing it. Failed resolution reports the requirement chains before installing anything.

In projects using ranges, v update resolves within existing constraints. v update -p PACKAGE --precise REF chooses one exact ref while checking every constraint. v update --latest widens direct requirements to caret constraints for the newest resolvable stable releases and rewrites v.mod. Other locked packages are preferred during targeted updates. Bare-only projects retain default-branch installs and branch-based updates.

v why PACKAGE annotates each parent's version constraint; v mod graph prints versioned manifest dependency edges. Both work offline. v outdated reports Current, Upgradable, Resolvable and Latest for constrained projects: the installed version, the newest tag allowed by the installed graph, a fresh complete resolution, and the newest stable tag respectively. See the VPM guide for details.

Locking dependency revisions

When v install resolves the dependencies of a project, i.e. when it runs without packages in a folder holding a v.mod, or with --local, it records what it installed in a v.mod.lock file next to that v.mod. Commit that file, so that everyone working on the project, and its CI, builds against the same sources. A plain v install [package] installs globally, and records nothing. For each dependency, the lockfile records:

  • requested: the dependency string as written in v.mod, e.g. vsl@v0.1.50
  • resolved: the requested tag, or otherwise a pseudo-version made of the commit time and the short SHA, like v0.0.0-20240102150405-0123456789ab
  • revision: the full SHA of the installed commit
  • url: the source the package was cloned from

Without a lock entry, v install updates an already installed dependency to its latest revision. With one, it installs the locked revision instead, and puts an installed checkout that moved away from it back on the lock, fetching the revision first when needed. A dependency whose string in v.mod, or whose source, no longer matches its entry is resolved anew, and its entry is replaced. v install --locked fails instead of resolving anything anew, e.g. to check in CI that the lockfile is complete and up to date.

To move a locked dependency forward, run v update [package] or v upgrade inside the project: the installed checkout moves to the latest revision of the default branch of its source, and its lock entry is rewritten. Dependencies requested at a tag stay at that tag. v remove [package] drops the entry of the package from the lockfile.

Note that the global VMODULES folder holds a single checkout of each package, shared by all projects. v install in a project switches the checkouts of its dependencies to the revisions in its lockfile, and v install in another project that locks other revisions of the same packages switches them back. Use v install --local to give a project checkouts of its own.

Publish package

  1. Put a v.mod file inside the toplevel folder of your package (if you created your package with the command v new mypackage or v init you already have a v.mod file).

    v new mypackage
    Input your project description: My nice package.
    Input your project version: (0.0.0) 0.0.1
    Input your project license: (MIT)
    Initialising ...
    Complete!
    

    The prompts above appear only when running in a terminal; with a non-terminal stdin the defaults are used instead (see Getting started).

    Example v.mod:

    Module { name: 'mypackage' base_url: 'src' description: 'My nice package.' version: '0.0.1' license: 'MIT' dependencies: [] }

    base_url is optional. When set, V resolves the package sources relative to that folder, next to the v.mod file.

    Prefer a string list for dependencies, such as ['ui', 'nedpals.args']. Legacy entries such as [ui: 0.1], ['ui': '0.1'], and [ui] are also accepted for compatibility. Only the dependency names are retained; legacy version values are ignored.

    Minimal file structure:

    v.mod
    mypackage.v
    

    You can also add subdirs: ['internal'] to v.mod to compile files from selected subdirectories as part of the same module. These paths are relative to the module source root, and files there should declare the same module mypackage. v doc documents them as part of that module too. Tests in these subdirectories can use sources from the module root and its other declared subdirectories, even if their own directory contains only test files. Each test file runs independently; other test files are not included as support sources. v doc -m also discovers modules whose sources are all in external subdirs. HTML source links use the common root of the nearest manifest and its declared source directories, including external subdirs and the base_url source folder. An unrelated enclosing Git checkout does not override this root. Without a manifest, a discovered Git root is used instead.

    The name of your package should be used with the module directive at the top of all files in your package. For mypackage.v:

    module mypackage pub fn hello_world() { println('Hello World!') }
  2. Create a git repository in the folder with the v.mod file (this is not required if you used v new or v init):

    git init
    git add .
    git commit -m "INIT"
    
  3. Create a public repository on github.com.

  4. Connect your local repository to the remote repository and push the changes.

  5. Add your package to the public V package registry VPM: https://vpm.vlang.io/new

    You will have to login with your Github account to register the package. Warning: Currently it is not possible to edit your entry after submitting. Check your package name and github url twice as this cannot be changed by you later.

  6. The final package name is a combination of your github account and the package name you provided e.g. mygithubname.mypackage.

Optional: tag your V package with vlang and vlang-package on github.com to allow for a better search experience.

Advanced Topics