Julia's Package Manager Pkg

November 11, 2021

Julia’s package manager is called Pkg. Pkg creates and manages environments for julia projects, much like npm maintains an environment for each of its packages. Understanding how Pkg works is essential for being effective in julia. So let’s dive in.

The Big Picture

Pkg prioritizes local project environments over global environments.

But how does Pkg identify a project environment? What constitutes a project? There are two files that Pkg checks for: Project.toml and Manifest.toml.

A julia project is defined as a Project.toml and Manifest.toml pair.

Given a Project.toml and a Manifest.toml pair, you can instantiate the exact same environment using Pkg.

Pkg uses the julia environment variable LOAD_PATH to search for project environments (Project.toml and Manifest.toml pairs) to work in. In most situations, LOAD_PATH has three entries like so:

The first is the current project’s environment (which needs to be activated for Pkg to see it). The next is the global julia environment thats installed on the machine, 1.6 for example. In fact, if you look in ~/.julia/environments/v1.6 you will find a Manifest.toml and Project.toml. And last are the standard libraries which come with julia when its installed.

Pkg starts with the first entry of LOAD_PATH, and will check subsequent entries in order until it finds what its looking for.

Project.toml and Manifest.toml

So how does Pkg interact with the .toml files individually?

You can think of Project.toml as the input for the Pkg interpreter, and Manifest.toml as Pkg’s output.

This is a typical julia project structure for your reference:

LICENSE
Manifest.toml
Project.toml
README.md
src/
    Project.jl
test/
    runtests.jl

Project.toml

Project.toml describes the project on a high level.

name = "test"
uuid = "3b32e9fd-c20c-4cfa-a9b2-bfc1ff37ba3c"
authors = ["KatharineME <katharine.me@icloud.com>"]
version = "0.1.0"

[deps]
Revise = "295af30f-e4ad-537b-8983-00126c2a3abe"
Example = "7876af07-990d-54b4-ab0e-23690620f79a"

[compat]
BenchmarkTools = "1.2.0"
Dates = "3.1.20"
julia = "1.6"

The name field is the name of the project, the uuid field sets a universally unique identifier (UUID) for the project, the author field is simply the name and email of the author, and the version of the project which follows semantic versioning.

The deps section lists the dependencies of the project. Notice the dependencies under deps dont include details like version number or the packages they depend on.

The compat section lists compatibility requirements: basically what has to be true for this package to run. For example julia = "1.6" is for the example project above to run. In the compat section you can also specifiy whether you will allow minor version updates or patch updates to the specificed packages. More documentation on the compat section is here.

Side note: A julia package is definded as a julia project with a uuid and name set in the Project.toml.

Manifest.toml

Manifest.toml is an “absolute record of the state of the packages in the environment”. It includes exact information about direct and recursive dependencies.

# This file is machine-generated - editing it directly is not advised

[[ArgTools]]
uuid = "0dad84c5-d112-42e6-8d28-ef12dabb789f"

[[Artifacts]]
uuid = "56f22d72-fd6d-98f1-02f0-08ddc0907c33"

[[Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"

[[BenchmarkTools]]
deps = ["JSON", "Logging", "Printf", "Profile", "Statistics", "UUIDs"]
git-tree-sha1 = "61adeb0823084487000600ef8b1c00cc2474cd47"
uuid = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
version = "1.2.0"

[[Dates]]
deps = ["Printf"]
uuid = "ade2ca70-3891-5945-98fb-dc099432e06a"

As you can see in the comment at the top, Manifest.toml is not intended to be manually edited, it is strictly Pkg’s output. It is continuously being regenerated and updated as the envrionment of the project changes.

Calling Pkg

Pkg has its own REPL that you can access from command line by starting julia and then hitting the ] key, which will move you from the julia REPL into the pkg REPL.

In the pkg REPL you can enter commands that manage package environments like add to add a dependency. More on that in the API section below.

You can also call Pkg from the julia REPL like so:

using Pkg;

Pkg.add("Example")

Pkg API

generate

Creates a boilerplate julia project in the current directory.

build

Runs the deps/build.jl script in the current package, then runs the build scripts for all the dependencies in depth-first recursive order.

develop

Make a package available for development.

activate

Activates a project’s environment. For example, activate . activates the current directory’s environment. This can also be done when starting julia in the project of interest like so: julia --project. This command needs to be run in a directory with Project.toml and Manifest.toml at the top level.

instantiate

If Manifest.toml exists in the active project, download all packages as described. If Manifest.toml doesn’t exist, get a set of feasible packages from Project.toml and install them, then create a manifest.toml.

st or status

Print out the status of the Project.toml or Manifest.toml. By default, the status of Project.toml will be printed. If --manifest is passed, the status of the Manifest.toml will be printed.

add

Add a package (a package that the current project will use and depend on). This command installs the package, updates the Project.toml deps section, and updates the Manifest.toml.

rm or remove

Remove a package. This command uninstalls the package from the project environment and removes it from the Project.toml. If no other packages depend on it, it will be removed from the Manifest.toml. If other packages this project depends on use it, it will remain in the Manifest.toml. You can run rm -m pkg_name to remove the package and all packages that depend on it from the Manifest.toml.

up or update

Update packages according to the deps and compat sections of Project.toml. Passing -m like in update -m will update packages according to the Manifest.toml.

resolve

Updates the Manifest.toml to the current needs of the project according to Project.toml.

test

Runs the test/runtest.jl in the package directory.

gc

Garbage collect packages not used for significant time to free disk space.