By: Great Lakes Consulting
Re-posted from: https://blog.glcs.io/package-creation
This post was written by Steven Whitaker.
The Julia programming languageis a high-level languagethat is known, at least in part,for its excellent package managerand outstanding composability.(See another blog post that illustrates this composability.)
Julia makes it super easyfor anybody to create their own package.Julia’s package manager enables easy development and testing of packages.The ease of package developmentencourages developers to split reusable chunks of codeinto individual packages,further enhancing Julia’s composability.
In this post,we will learn what comprises a Julia package.We will also discuss toolsthat automate the creation of packages.Finally,we will talk about the basics of package developmentand walk through how to publish (register) a packagefor others to use.
This post assumes you are comfortable navigating the Julia REPL.If you need a refresher,check out our post on the Julia REPL.
Components of a Package
Packages are easy enough to use:just install them with add PkgName
in the package promptand then run using PkgName
in the julia prompt.But what actually goes into a package?
Packages must follow a specific directory structureand include certain informationto be recognized as a package by Julia.
Suppose we are creating a package called PracticePackage.jl.First, we create a directory called PracticePackage
.This directory is the package root.Within the root directory we need a file called Project.toml
and another directory called src
.
The Project.toml
requires the following information:
name = "PracticePackage"uuid = "11111111-2222-3333-aaaa-bbbbbbbbbbbb"authors = ["Your Name <youremail@email.com>"]version = "0.1.0"
uuid
stands for universally unique identifier,and can be generated in Julia withusing UUIDs; uuid4()
.The purpose of a UUID is to allow different packages of the same name to coexist.
version
should be set to whatever version is appropriate for your package,typically "0.1.0"
or "1.0.0"
for an initial release.The versioning of Julia packages follows SemVer.
- The
Project.toml
will also include informationabout package dependencies,but more on that later.
The src
directory requires one Julia filenamed PracticePackage.jl
that defines a module named PracticePackage
:
module PracticePackageend
So, the directory structure of the packagelooks like the following:
PracticePackage Project.toml src PracticePackage.jl
And that’s all there is to a package!(Well, at least minimally.)
Some Technicalities
Feel free to skip this section,but if you are curious about some technicalitiesfor what comprises a valid package,read on.
- The
Project.toml
only needs the name
and uuid
fieldsfor Julia to recognize the package.Without the version
field,Julia treats the version as v0.0.0
.
- However, the
version
and authors
fields are neededto register the package.
- The name of the package root directory doesn’t matter,meaning it doesn’t have to match the package name.However, the
name
field in Project.toml
does have to match the name of the moduledefined in src/PracticePackage.jl
,and the file name of src/PracticePackage.jl
also has to match.
- For example,we could change the name of the packageby setting
name = "Oops"
in Project.toml
,renaming src/PracticePackage.jl
to src/Oops.jl
,and defining module Oops
in that file.We would not have to rename the package root directoryfrom PracticePackage
to Oops
(though that would be a good idea to avoid confusion).
Automatically Generating Packages
The basic structure of a package is pretty simple,so there ought to be a way to automate it, right?(I mean, who wants to manually generate a UUID?)Good news: package creation can be automated!
Package generate
Command
Julia comes with a generate
package command built-in.First, change directoriesto where the package root directory should live,then run generate
in the Julia package prompt:
pkg> generate PracticePackage
This command creates the package root directory PracticePackage
and the Project.toml
and src/PracticePackage.jl
files.Some notes:
- The
Project.toml
is pre-filled with the correct fields and values,including an automatically generated UUID.When I ran generate
on my computer,it also pre-filled the authors
fieldwith my name and email from my ~/.gitconfig
file.
src/PracticePackage.jl
is pre-filledwith a definition for the module PracticePackage
.It also defines a function greet
in the module,but typically you will replace that with your own code.
PkgTemplates.jl
The generate
command works fine,but it’s barebones.For example,if you are planning on hosting your package on GitHub,you might want to include a GitHub Actionfor continuous integration (CI),so it would be niceto automate the creation of the appropriate .yml
file.This is where PkgTemplates.jl comes in.
PkgTemplates.jl is a normal Julia package,so install it as usual and run using PkgTemplates
.Then we can create our PracticePackage.jl:
t = Template(; dir = ".")t("PracticePackage")
Running this code creates the packagewith the following directory structure:
PracticePackage .git .github dependabot.yml workflows CI.yml CompatHelper.yml TagBot.yml .gitignore LICENSE Manifest.toml Project.toml README.md src PracticePackage.jl test runtests.jl
As you can see,PkgTemplates.jl automatically generates a lot of filesthat aid in following package development best practices,like adding CI and tests.
Note that many optionscan be supplied to Template
to customize what files are generated.See the PkgTemplates.jl docs for all the options.
Basic Package Development
Once your package is set up,the next step is to actually add code.Add the functions, types, constants, etc.that your package needsdirectly in the PracticePackage
module in src/PracticePackage.jl
,or add additional files in the src
directoryand include
them in the module.(See a previous blog post for more information about modules,though note that using modules directly works slightly differentlythan using packages.)
To add dependencies for your package to use,you will need to activate your project’s package environmentand then add packages.For example,if you want your package to use the DataFrames.jl package,start Julia and navigate to your package root directory.Then, activate the package environment and add the package:
(@v1.X) pkg> activate .(PracticePackage) pkg> add DataFrames
After this,you will be able to include using DataFrames
in your package codeto enable the functionality provided by DataFrames.jl.
Adding packages after activating the package environmentedits the package’s Project.toml
file.It adds a [deps]
sectionthat lists the added packages and their UUIDs.In the example above,adding DataFrames.jladds the following lines to the Project.toml
file:
[deps]DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
(And (PracticePackage) pkg> rm DataFrames
would remove the DataFrames = ...
line,so it is best not to edit the [deps]
section manually.)
Finally,to try out your package,activate your package environment (as above)and then load your package as usual:
julia> using PracticePackage
Note that by default Julia will have to be restartedto reload any changes you make to your package code.If you want to avoid restarting Juliawhenever you make changes,check out Revise.jl.
Publishing/Registering a Package
Once your package is in working order,it is natural to want to publish the packagefor others to use.
A package can be publishedby registering it in a package registry,which basically is a map that tells the Julia package managerwhere to find a packageso it can be downloaded.
The General registry is the largest registryas well as the default registry used by Julia;most, if not all, of the most popular open-source packages(DataFrames.jl, Plots.jl, StaticArrays.jl, ModelingToolkit.jl, etc.)exist in General.Once a package is registered in General,it can be installed with pkg> add PracticePackage
.
(Note that if registering a package is not desired for some reason,a package can be added via URL, e.g.,pkg> add https://github.com/username/PracticePackage.jl
,assuming the package is in a public git repository.However,the package manager has limited abilityto manage packages added in this way;in particular,managing package versions must be done manually.)
The most common wayto register a package in Generalis to use Registrator.jl as a GitHub App.See the README for detailed instructions,but the process basically boils down to:
- Write/test package code.
- Update the
version
field in the Project.toml
(e.g., to "0.1.0"
or "1.0.0"
for the first registered version).
- Add a comment with
@JuliaRegistrator register
to the latest commit that should be includedin the registered version of the package.
Note that there are additional steps for preparing a package for publishingthat we did not discuss in this post(such as specifying compatible versionsof Julia and package dependencies).Refer to the General registry’s documentation and links therein for details.
Summary
In this post,we discussed creating Julia packages.We learned what comprises a package,how to automate package creation,and how to register a package in Julia’s General registry.
What package development tips do you have?Let us know in the comments below!
Additional Links
Cover image background provided by www.proflowers.com athttps://www.flickr.com/photos/127365614@N08/16011252136.
Treasure map image source: https://openclipart.org/detail/299283/x-marks-the-spot
]]>