Janet 1.39.1-e9c6678 Documentation
(Other Versions:
1.38.0
1.37.1
1.36.0
1.35.0
1.34.0
1.31.0
1.29.1
1.28.0
1.27.0
1.26.0
1.25.1
1.24.0
1.23.0
1.22.0
1.21.0
1.20.0
1.19.0
1.18.1
1.17.1
1.16.1
1.15.0
1.13.1
1.12.2
1.11.1
1.10.1
1.9.1
1.8.1
1.7.0
1.6.0
1.5.1
1.5.0
1.4.0
1.3.1
)
Project.janet
The project.janet file
To create your own software in Janet, it is a good idea to understand what the
project.janet file is and how it defines rules for building, testing, and
installing software. The code in project.janet is normal Janet source
code that is run in a special environment.
A project.janet file is loaded by jpm and evaluated to create
various recipes, or rules. For example, declare-project creates several
rules, including "install", "build", "clean", and
"test". These are a few of the rules that jpm expects
project.janet to create when executed.
Declaring a project
Use the declare-project as the first declare- macro towards the
beginning of your project.janet file. You can also pass in any metadata
about your project that you want, and add dependencies on other Janet projects
here.
(declare-project
:name "mylib" # required
:description "a library that does things" # some example metadata.
# Optional urls to git repositories that contain required artifacts.
:dependencies ["https://github.com/janet-lang/json.git"])Creating a module
A 100% Janet library is the easiest kind of software to distribute in Janet.
Since it does not need to be built and since installing it means simply moving
the files to a system directory, we only need to specify the files that comprise
the library in project.janet.
(declare-source
# :source is an array or tuple that can contain
# source files and directories that will be installed.
# Often will just be a single file or single directory.
:source ["mylib.janet"])For information on writing modules, see the modules docpage.
Creating a native module
Once you have written your C code that defines your native module (see the
embedding page on how to do this), you must declare
it in project.janet in order for jpm to build the native modules
for you.
(declare-native
:name "mynative"
:source ["mynative.c" "mysupport.c"]
:embedded ["extra-functions.janet"])This makes jpm create a native module called mynative when
jpm build is run, the arguments for which should be pretty
straightforward. The :embedded argument is Janet source code that will be
embedded as an array of bytes directly into the C source code. It is not
recommended to use the :embedded argument, as one can simply create
multiple artifacts, one for a pure C native module and one for Janet source
code.
Creating an executable
The declaration for an executable file is pretty simple.
(declare-executable
:name "myexec"
:entry "main.janet"
:install true)jpm is smart enough to figure out from the one entry file what
libraries and other code your executable depends on, and bundles them into
the final application for you. The final executable will be located at
build/myexec, or build\myexec.exe on Windows.
If the optional key-value pair :install true is specified in the
declare-executable form, by default, the appropriate jpm install
command will install the resulting executable to the JANET_BINPATH
(but see the jpm man page for further details).
Also note that the entry of an executable file should look different than a
normal Janet script. It should define a main function that can receive a
variable number of parameters, the command-line arguments. It will be called as
the entry point to your executable.
(import mylib1)
(import mylib2)
# This will be printed when you run `jpm build`
(print "build time!")
(defn main
[& args]
# You can also get command-line arguments through (dyn :args)
(print "args: " ;(interpose ", " args))
(mylib1/do-thing)
(mylib2/do-thing))It's important to remember that code at the top level will run when you invoke
jpm build, not at executable runtime. This is because in order to create
the executable, we marshal the main function of the app and write it to
an image. In order to create the main function, we need to actually compile and
run everything that it references, in the above case mylib1 and
mylib2.
This has a number of benefits, but the largest is that we only include bytecode
for the functions that our application uses. If we only use one function from a
library of 1000 functions, our final executable will not include the bytecode
for the other 999 functions (unless our one function references some of those
other functions, of course). This feature, called tree-shaking, only works
for Janet code. Native modules will be linked to the final executable statically
in full if they are used at all. A native module is considered "used" if is
imported at any time during jpm build. This may change, but it is
currently the most reliable way to check if a native modules needs to be linked
into the final executable.
There are some limitations to this approach. Any dynamically required modules
will not always be linked into the final executable. If require or
import is not called during jpm build, then the code will not be
linked into the executable. The module can still be required if it is available
at runtime, though.
For an example Janet executable built with jpm, see
https://github.com/bakpakin/littleserver.
Other declare- callables
Some additional declare- callables are:
declare-binDeclare a generic file to be installed as an executable. Specify file path via
:main.declare-binscriptDeclare a janet file to be installed as an executable script. Creates a shim on windows. If
:hardcode-syspathis true, will insert code into the script such that it will run correctly even whenJANET_PATHis changed. If:is-janetis truthy, will also automatically insert a correct shebang line if jpm's configuration is set with:auto-shebangas truthy.declare-headersDeclare headers for a library installation. Installed headers can be used by other native libraries. Specify paths via
:headersand prefix via:prefix.declare-manpageMark a manpage for installation.