Packages
Last updated on 2024-05-27 | Edit this page
While the module is an organizational unit within a project, packages are the distribution units of Julia.
Dependency Management
To install a package, we type ]
in the Julia REPL to
switch to package mode. The prompt changes from julia>
to (@v1.10) pkg>
. We can exit package mode by pressing
C-c C-c
or <Backspace>
.
Pkg.jl
Package mode is implemented by the Julia package Pkg.jl. You can find Pkg.jl’s documentation online.
The prompt in the package mode tells us which environment is
currently active (@v1.10
). The default environment is named
after the running Julia version. We can install packages into this
environment for general experimentation. Our projects will have their
own environments.
First, let us have a look at the status of the current environment:
(@v1.10) pkg> status
Status `~/.julia/environments/v1.10/Project.toml` (empty project)
This shows us the location of the Project.toml that describes the environment. It also lists explicitly installed packages, in our case currently none.
To install a package we run the command add
:
(@v1.10) pkg> add Memoize
Updating registry at `~/.julia/registries/General.toml`
Installed Memoize ---- v0.4.4
Updating `~/.julia/environments/v1.10/Project.toml`
[c03570c3] + Memoize v0.4.4
Updating `~/.julia/environments/v1.10/Manifest.toml`
[1914dd2f] + MacroTools v0.5.13
[c03570c3] + Memoize v0.4.4
[2a0f44e3] + Base64
[d6f4376e] + Markdown
[9a3f8284] + Random
[ea8e919c] + SHA v0.7.0
Precompiling project...
2 dependencies successfully precompiled in 2 seconds
(@v1.10) pkg> status
Status `~/.julia/environments/v1.10/Project.toml`
[c03570c3] Memoize v0.4.4
The command updates two files, Project.toml
and
Manifest.toml
, and seems to install six packages, five of
those dependencies of Memoize
. Now status
shows us that we have installed Memoize
(but tells us
nothing about its dependencies).
We can update a package using the update
command:
(@v1.10) pkg> update Memoize
n Updating registry at `~/.julia/registries/General.toml`
No Changes to `~/.julia/environments/v1.10/Project.toml`
No Changes to `~/.julia/environments/v1.10/Manifest.toml`
And we can remove installed packages again with rm
:
(@v1.10) pkg> rm Memoize
Updating `~/.julia/environments/v1.10/Project.toml`
[c03570c3] - Memoize v0.4.4
Updating `~/.julia/environments/v1.10/Manifest.toml`
[1914dd2f] - MacroTools v0.5.13
[c03570c3] - Memoize v0.4.4
[2a0f44e3] - Base64
[d6f4376e] - Markdown
[9a3f8284] - Random
[ea8e919c] - SHA v0.7.0
To import a package, we need to know what packages there are. There is a general package registry which can be searched through multiple front-ends, for example https://juliahub.com/ui/Packages .
If we want to install a package that is not in the general (or any other) registry, we can point to a git source instead:
(@v1.10) pkg> add https://github.com/JuliaLang/Example.jl#master
Cloning git-repo `https://github.com/JuliaLang/Example.jl`
Updating git-repo `https://github.com/JuliaLang/Example.jl`
Resolving package versions...
Updating `~/.julia/environments/v1.10/Project.toml`
[7876af07] + Example v0.5.4 `https://github.com/JuliaLang/Example.jl#master`
Updating `~/.julia/environments/v1.10/Manifest.toml`
[7876af07] + Example v0.5.4 `https://github.com/JuliaLang/Example.jl#master`
Precompiling project...
1 dependency successfully precompiled in 0 seconds
All commands discussed here have a lot more options. Particularly
important might be the ability to install specific versions packages or
branches of repositories containing packages. The shell provides
documentation for every command when running
help <command>
, for example:
(@v1.10) pkg> help update
[up|update] [-p|--project] [opts] pkg[=uuid] [@version] ...
[up|update] [-m|--manifest] [opts] pkg[=uuid] [@version] ...
opts: --major | --minor | --patch | --fixed
--preserve=<all/direct/none>
Update pkg within the constraints of the indicated version specifications. These
specifications are of the form @1, @1.2 or @1.2.3, allowing any version with a
prefix that matches, or ranges thereof, such as @1.2-3.4.5. In --project mode,
package specifications only match project packages, while in --manifest mode they
match any manifest package. Bound level options force the following packages to
be upgraded only within the current major, minor, patch version; if the --fixed
upgrade level is given, then the following packages will not be upgraded at all.
After any package updates the project will be precompiled. For more information
see pkg> ?precompile.
Creating Packages
After seeing how to use a package, we will have a look into creating one.
Using the shell in the Julia REPL, activated by pressing
;
, we navigate to the folder in which we want to create our
project. For example
shell> cd ~/temp/
/home/user/temp
We leave the shell again by pressing C-c
and check that
we are in the correct directory, by running pwd()
:
Now, we go into package mode (press ]
) and generate a
Julia package skeleton:
(@v1.10) pkg> generate OurVectors
Generating project OurVectors:
OurVectors/Project.toml
OurVectors/src/OurVectors.jl
The output tells us that two files were generated in a new directory
with the project name that we provided. We leave the package mode again,
to navigate Julia into the directory that was just created by pressing
C-c
:
We will have a look at what was created in the shell (press
;
):
shell> ls -RF
.:
Project.toml src/
./src:
OurVectors.jl
Julia generated two files for us. Project.toml
is the
project configuration file in TOML Format, while
OurVectors.jl
in the subdirectory src
is an
initial source file.
Let us first look at the configuration file:
shell> cat Project.toml
name = "OurVectors"
uuid = "53fd6d90-f326-4ab3-8600-de8f4fd33617"
authors = ["Some One <some.one@example.com>"]
version = "0.1.0"
It first states the name of the project (or package). A UUID randomly
generated for the project follows. The authors field is based on your
git configuration, falling back to a series
of environment variables. Finally the initial version is given as
0.1.0
.
The generated source file contains a module with an “Hello World” implementation:
shell> cat src/OurVectors.jl
module OurVectors
greet() = print("Hello World!")
end # module OurVectors
Tests
We edit the source file to contain our length
function:
Now, we want to write tests for our function. Idiomatically, tests
are run by evaluating the file test/runtests.jl
of a Julia
project. We create such a file and make use of the Test
package that comes bundled with Julia:
JULIA
using Test
using OurVectors
@test OurVectors.length([1]) == 1.0
@test OurVectors.length([3, 4]) == 5.0
@test_throws MethodError OurVectors.length([])
To run the script we can use the package mode again. But before
switching to it (by pressing ]
), we make sure that the
working directory of our REPL is the project directory:
This looks good. If we were in the wrong directory, we would use the
function cd
to change that.
In package mode, we first have to make our project the current environment. To do that, we run
(@v1.10) pkg> activate .
Activating project at `~/temp/OurVectors`
(OurVectors) pkg>
Note, how the prompt changes when switching environments.
To run tests, we can type test
in package mode:
(OurVectors) pkg> test
Testing OurVectors
Status `/tmp/jl_Bx3UOG/Project.toml`
[53fd6d90] OurVectors v0.1.0 `~/temp/OurVectors`
Status `/tmp/jl_Bx3UOG/Manifest.toml`
[53fd6d90] OurVectors v0.1.0 `~/temp/OurVectors`
Testing Running tests...
ERROR: LoadError: ArgumentError: Package Test not found in current path:
- Run `import Pkg; Pkg.add("Test")` to install the Test package.
Stacktrace:
[1] include(fname::String)
@ Base.MainInclude ./client.jl:489
[2] top-level scope
@ none:6
in expression starting at /home/user/temp/OurVectors/test/runtests.jl:1
ERROR: Package OurVectors errored during testing
This throws an error. Even though Test
comes with the
Julia installation, we still need to install it into our
environment:
(OurVectors) pkg> add Test
Resolving package versions...
Updating `~/temp/OurVectors/Project.toml`
[8dfed614] + Test
Updating `~/temp/OurVectors/Manifest.toml`
[2a0f44e3] + Base64
[b77e0a4c] + InteractiveUtils
[56ddb016] + Logging
[d6f4376e] + Markdown
[9a3f8284] + Random
[ea8e919c] + SHA v0.7.0
[9e88b42a] + Serialization
[8dfed614] + Test
This installs Test
and its dependencies. It also seems
to make changes to Project.toml
and a yet unknown file
called Manifest.toml
. We will have a look at them in a
minute. But first, we try to again run the tests:
(OurVectors) pkg> test
Testing OurVectors
Status `/tmp/jl_3CRrFm/Project.toml`
[53fd6d90] OurVectors v0.1.0 `~/temp/OurVectors`
[8dfed614] Test `@stdlib/Test`
Status `/tmp/jl_3CRrFm/Manifest.toml`
[53fd6d90] OurVectors v0.1.0 `~/temp/OurVectors`
[2a0f44e3] Base64 `@stdlib/Base64`
[b77e0a4c] InteractiveUtils `@stdlib/InteractiveUtils`
[56ddb016] Logging `@stdlib/Logging`
[d6f4376e] Markdown `@stdlib/Markdown`
[9a3f8284] Random `@stdlib/Random`
[ea8e919c] SHA v0.7.0 `@stdlib/SHA`
[9e88b42a] Serialization `@stdlib/Serialization`
[8dfed614] Test `@stdlib/Test`
Testing Running tests...
Testing OurVectors tests passed
Now the test suite runs and our tests passed.
Tracking Dependencies
Getting back to the file that changed, when we add Test
to the project, we first have look at Project.toml
:
The file got a new section, [deps]
, listing
dependencies. We added Test
, so it shows up in the list
with its UUID.
Now for the content of the new file, Manifest.toml
:
TOML
# This file is machine-generated - editing it directly is not advised
julia_version = "1.9.0"
manifest_format = "2.0"
project_hash = "71d91126b5a1fb1020e1098d9d492de2a4438fd2"
[[deps.Base64]]
uuid = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
[[deps.InteractiveUtils]]
deps = ["Markdown"]
uuid = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
[[deps.Logging]]
uuid = "56ddb016-857b-54e1-b83d-db4d58db5568"
[[deps.Markdown]]
deps = ["Base64"]
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"
[[deps.Random]]
deps = ["SHA", "Serialization"]
uuid = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
[[deps.SHA]]
uuid = "ea8e919c-243c-51af-8825-aaa63cd721ce"
version = "0.7.0"
[[deps.Serialization]]
uuid = "9e88b42a-f829-5b0c-bbe9-9e923198166b"
[[deps.Test]]
deps = ["InteractiveUtils", "Logging", "Random", "Serialization"]
uuid = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
This file lists not only the explicitly installed dependencies, but the whole dependency tree.
When we added the Test
package, we did not specify a
version. If we had done that, this would be recorded in
Manifest.toml
.