Suggested terminology for concepts related to Janet’s bundles.
Bundles in Janet
I’m currently working on Jeep, a bundle manager for the Janet programming language. As you might expect, I’ve had to learn quite a bit about how bundles work in Janet. One of the things that I’ve struggled with is what to call various aspects. In this post, I want to describe some of the concepts and suggest some terminology for the Janet community to consider using.
Bundle
A bundle is Janet’s term for what some languages call a package. At its most basic, a bundle:
- is a directory of files that can contain Janet code, documentation and other resources; and
- is installable.
Making a directory isn’t that difficult but how do you make a directory ‘installable’?
Bundle Types
Unfortunately, for historical reasons, Janet has two types of bundles.
First, there are legacy bundles.1 A legacy bundle is made installable by
the presence of a file titled project.janet
in the bundle root. Legacy bundles
were developed to work with the Janet Project Manager (commonly referred to as
jpm
). A legacy bundle cannot be installed by the janet
binary alone.
Second, there are modern bundles.2 A modern bundle is made installable
by the presence of an info file and a bundle script. Once you get a bundle
on the local file system, it can be installed by the janet
binary alone.
It is possible for a bundle to be both a legacy bundle and a modern bundle if
the bundle includes all the necessary files (a project.janet
file, an info
file and a bundle script).
Targets
Janet has built-in support for installing and uninstalling modern bundles using
bundle/install
and bundle/uninstall
respectively. As noted above, the
janet
binary cannot install legacy bundles without an additional tool.
Installing a bundle means copying one or more of the following targets3 into a target path:4
- a module file written in Janet
- a native module file compiled to machine code
- an executable file written in Janet
- a native executable file compiled to machine code
- a man page file
Uninstalling means removing the files that were copied. Janet’s bundle/install
function creates a manifest that lists the files that were installed. As a
result, bundle/uninstall
can make sure those files are removed when the bundle
is uninstalled.
Target Paths
In theory, Janet should have three target paths:
- the syspath
- the binpath
- the manpath
At the moment, the manpath is not yet a target path. Instead, there’s the
syspath and the binpath. The binpath is created by appending /bin
to the
systpath.
On POSIX systems, the default target paths are:
/usr/local/lib/janet
/usr/local/lib/janet/bin
On Windows, the default target paths are:
%APPDATA%/Janet/Library
%APPDATA%/Janet/Library/bin
The syspath is the most important as it is the path which Janet searches when importing modules and the path from which the binpath is created.
Bundle Hooks
Janet’s bundle/install
function deliberately leaves the specifics of
installation to the bundle author. It does this by using bundle hooks. A
bundle author can think of a bundle hook as a call to an identically named
function in the bundle’s bundle script.
The bundle/install
function calls five hooks:
dependencies
clean
build
install
check
The bundle/uninstall
function calls one hook:
uninstall
Bundle Script
The bundle script is a text file containing Janet code. The bundle script can be located in one of two places relative to the bundle root:
./bundle/info.janet
./bundle.janet
As noted above, each bundle hook calls an identically named function in the
bundle script. If a bundle author doesn’t have a function with that name,
nothing happens and bundle/install
moves onto the next hook. Some bundles
don’t need a build
step, for example, and so can simply not define that
function. Others may not need a dependencies
or check
function.
A bundle script can import other modules but it should not do it relative to
directories in the bundle root. This is because the ./bundle
directory5 is
installed separately to other files by bundle/install
. If a user’s bundle
script has dependencies, they should either be imported from the syspath:
(import foo)
or they should be imported from inside the ./bundle
directory:
(import ./foo)
Info File
The info file is a text file written in JDN (Janet data notation). The info file can be located in one of two places relative to the bundle root:
./bundle/info.jdn
./info.jdn
.
The file must describe a single data structure, either a struct or table, that
maps the key :name
to a string value that represents the name of the bundle.
Note that bundle names are used as the name of subdirectories within the
<syspath>/bundle
directory where information on installed bundles is kept. For
this reason, two bundles cannot be installed with the same name.
The info file should also list the external dependencies of the bundle (if any).
This information is stored as an array/tuple that is associated with the key
:dependencies
at the top-level of the data structure. Janet’s bundle/install
function will confirm that the dependencies listed are installed before
proceeding with installation.
The info file may further contain other information that is used by the bundle script. Here’s a simple example:
{:name "foo"
:version "1.0.0"
:description "Just adds all the foo"
:author "A Programmer"
:license "MIT"
:url "https://example.org/foo"
:repo "git+https://example.org/foo"
:dependencies [{:name "bar" :url "https://example.org/bar"}]
:executable ["bin/foo"]
:manpage ["man/man1/foo.1"]
:source {:prefix "foo"
:files ["init.janet" "lib"]}}
Conclusion
Janet bundles are still a bit of a work in progress and the suggested terms here are just that: suggestions. If you have questions, the GitHub repository is a good place to ask. ✺
-
This is my term of art. Use it at parties to sound cool. ↩
-
Again, sound cool at parties. ↩
-
Techically speaking, it is possible to copy other types of files. In practice, most bundles will only copy one or more of the files listed here. ↩
-
I don’t actually want to coin all these terms. The problem is that there don’t seem to be any. Alternate suggestions welcome. ↩
-
This article uses the term ‘bundle root’ to refer to the top-level of the bundle itself. However, a bundle can also put its info file and bundle script inside a directory
bundle
that is located in the bundle root, i.e../bundle
. Names are hard. ↩