Build Tool
The :std/make
library provides support for building source Gerbil projects.
See also the guide.
usage
(import :std/make)
Overview
By convention, Gerbil projects are built by invoking a ./build.ss
script,
typically starting with #!/usr/bin/env gxi
that defines a function main
that will build the project when called with an empty list.
The libraries documented below are meant to be used in such a script,
to do most of the work in building your project.
Note that if your project has syntax dependencies on low-level Gambit macros
in gambit/lib/_gambit#.scm
, you should import :std/gambit-sharp
in your build script.
The :std/make
library exports a function make
that will build your project,
compiling files in automatically-detected dependency order.
It takes a build-spec argument, followed by keyword arguments.
The build-spec is a list of individual target specifications, described below.
The keyword arguments specify some configuration settings, also described below.
A trivial wrapper is available for simple projects, in the :std/build-script
library,
exporting a macro defbuild-script
.
The macro takes a build-spec first followed by keyword settings,
and defines a function main
in the context of the current file.
The function main
, when invoked with the argument "compile"
or with no arguments,
will compile the project in the current directory:
The Gerbil load-path
will be augmented with the current directory while compiling
(by passing a srcdir:
parameter to make
), while
the prefix from a gerbil.pkg
file in the current directory (if defined) will be used
(by passing a prefix:
parameter to make
).
Target specifications
The build-spec passed to make
or to defbuild-script
is
a list of individual target specifications.
Each target specification can be:
A string, which specifies a Scheme module. The
".ss"
extension is automatically added if no extension is specified. Dependencies will be automatically detected.A list of the form
(gxc: file (plist ...) opts ...)
or just(gxc: file opts ...)
, where (a) the file is a string as above, (b) the optional plist allows to specify extra build options, and (c) the remainder of the options are passed togsc
while compiling the file. The only supported extra build options so far are: (1)extra-inputs:
followed by a list of extra inputs, e.g. files that areinclude
'd or otherwiseread
at build-time, and (2)foreground:
followed by a boolean (default#f
, so only useful value#t
) that forces the Gerbil compilation to happen in the foreground, e.g. because it requires side-effects to the Gerbil compiler not available when invokinggxc
but already done before callingmake
, such as having loaded~~lib/_gambc#.scm
at syntax-expansion time.A list of the form
(gsc: file (plist ...) opts ...)
or just(gsc: file opts ...)
, which is similar togxc:
, except the file is a raw Gambit Scheme file directly compiled withgsc
and rather a Gerbil Scheme file first compiled to Gambit files bygxc
. As forgxc:
above, (a) the file is a string as above, (b) the optional plist allows to specify extra build options, and (c) the remainder of the options are passed togsc
while compiling the file. The only supported extra build option so far is:extra-inputs:
followed by a list of extra inputs, e.g. files that areinclude
'd or otherwiseread
at build-time. Such a target is useful for FFI and other low-level interactions with the Gambit runtime. To integrate the Gambit Scheme definitions from that file into Gerbil Scheme, there must be a subsequentssi:
target, before any other Gerbil-invoking target is specified (any target but one starting with thegsc:
orstatic-include:
keyword).A list of the form
(static-include: file)
where the file is a C file (or C++, if Gambit was compiled in C++) that will be compiled into the application. Once again, a subsequentssi:
target is required, with onlygsc:
orstatic-include:
targets in between.A list of the form
(ssi: file submodules ...)
where the file is a Scheme interface file. The".ssi"
extension is automatically added to the file if no extension is specified. The specified submodules may begsc:
orstatic-include:
target specification as above. Targets specified either immediately before thessi:
target or within as submodules are appended into a single list before processing. Dependencies specified in the.ssi
file will be compiled and loaded before any of the submodule targets are compiled.A list of the form
(exe: file opts ...)
where the file is a Gerbil Scheme module source file (with the".ss"
extension automatically added by default) that exports a functionmain
. The file will be compiled bygxc
with the provided options into an executable binary, that, when invoked, calls themain
function with a single argument, the list of command-line parameters (excluding theargv[0]
command location).A list of the form
(static-exe: file opts ...)
which is similar to theexe:
case above, except that the executable will be fully statically linked.A list of the form
(optimized-exe: file opts ..)
which is similar toexe:
above, but builds the executable with full program optimization. Note that you may have to pass library dependencies expclitly in this case, using"-ld-options" "-lyourlib" ...
in the options.A list of thhe form
(optimized-static-exe: file opts ...)
which is similar tooptimized-exe:
above, except that it use fully static linkage for the executable binary.A list of the form
(copy: file)
which specifies a file that will be copied to the Gerbil library path. It can be a precompiled C object, Scheme file, data file, etc., that your library uses and needs be present wherever your library is installed.
Build Settings
The make
function and the defbuild-script
macro after it accept
the following keyword arguments, that may configure how your project is built.
srcdir:
specifies the top directory under which to find the source code for your project, such that if you specify a target"foo/bar"
or"foo/bar.ss"
then it will refer to a file"foo/bar.ss"
under this directory. This argument is mandatory when callingmake
, and is the only mandatory settings argument. However, this argument is prohibited when using thedefbuild-script
macro, since the macro uses(path-normalize (path-directory (this-source-file)))
to automatically extract this directory from the current./build.ss
file containing it and pass it tomake
(and you cannot override it). You may use the same trick in your./build.ss
if for some reason you don't use that macro. (The trick is similar to$(realpath "$(dirname "${BASH_SOURCE[0]}")")
in bash or$(realpath "$(dirname "$0")")
in zsh).prefix:
specifies a prefix to the path of your Gerbil modules. This prefix will affect how your modules are named and where they will be installed within your libdir. Thus, a prefix oflegs
means that a file"foo/bar.ss"
will be compiled into a modulelegs/foo/bar
that you can import with(import :legs/foo/bar)
and that will be installed under your libdir as a bunch of files starting with the prefixlegs/foo/bar
. The default,#f
, means that a prefix will be extracted from the thepackage:
value in thegerbil.pkg
file in thesrcdir
, if it is defined and a symbol, and otherwise no prefix at all (all module names must then be fully-qualified).libdir:
specifies the directory under which compiled library files will be installed. If left unspecified, the default#f
designates the shell value of$GERBIL_PATH/lib
if the according environment variable is defined, or else your~/.gerbil/lib/
directory.bindir:
specifies the directory under which compiled executable files will be installed. If left unspecified, the default#f
designates the shell value of$GERBIL_PATH/bin/
if the according environment variable is defined, or else your~/.gerbil/bin/
directory.force:
specifies a boolean that if true forces the recompilation of every target in the current build. If for some reason a subtle change causes the build system to incorrectly believe some targets are up-to-date when they are not, you can useforce: #t
to force a complete rebuild. Note that usually, this means that you failed to specify a dependency (for instance, by using(include ...)
and not specifying the file in adep:
argument to a(gxc: ...)
target). It may also be a bug in Gerbil, in which case, please report it.optimize:
specifies a boolean that if true tells Gerbil to optimize the code some more, which should result in faster code, at the cost of compilation taking more time. The default is#t
.debug:
specifies one of#f
,env
orsrc
, and controls how much debugging information Gerbil will include in the compiled file. The default isenv
. With#f
Gerbil drops all debug information; withenv
it keeps a lot of information about the variables; withsrc
it additionally keeps around the expanded source code in the compiled output. Each level makes debugging easier at the cost of larger output, and larger compilation time. In production, you probably want#f
or maybeenv
if operators can sometimes debug there; for development, you probably wantsrc
for libraries you don't compile often, andenv
for code you compile over and over. Be sure to properly quote the symbol when passing it as argument to a function, as in(make build-spec debug: 'src)
.static-debug:
specifies the same thing asdebug:
but for the statically linked objects that will be used to build static executables. The default is#f
.verbose:
specifies a boolean or a real number. The default#f
should print a minimal amount of information, mostly telling you which module is bein compiled and that's all. If any value but#f
, some extra messages may be printed. If it is a number, even more messages may be printed (try up to 8 or 9).build-deps:
specifies the path to a file, by default under srcdir, that will store a cache of which files were built at what time from what dependencies, which can somewhat accelerate the build. If you have multiple projects in the same directory, i.e. invocations ofdefbuild-script
ormake
with differentbuild-spec
arguments, then you need to have these invocations specify distinctbuild-deps:
arguments to avoid clashes; otherwise projects will overwrite each other's cache.parallelize:
specifies the number of multiprocessor cores that Gerbil will attempt to use while compiling code. If it's a number n no less than 2, then Gerbil will spawn as many as n subprocesses to compile. If it's0
or less, then Gerbil will not spawn processes to compile files from Gerbil.ss
to Gambit.scm
but compile them in the current image. It will still spawn processes for Gambit compilation as such. If it's1
, then Gerbil will spawn a subprocess for the Gerbil compilation steps as well as for the Gambit compilation step, but only one process at a time. If it's#t
(the default), then the environment variableGERBIL_BUILD_CORES
is consulted, and used as above if it's an integer (represented as a decimal string). Otherwise (including if it's#f
), then no parallelism is used, same as0
. Note how the default behavior is to compile in parallel according to a specified number of cores, but that the default behavior's default number of cores is0
, which disables parallelism. An older release of Gerbil (v0.16) did use(##cpu-count)
by default, but this was disabled and the current behavior is to default to0
because parallelism has known failure modes with rough edges when you try to compile on a machine with a lot of cores but not a lot of memory, as seems to be frequent enough these days: Gerbil will spawn off a lot of GCC processes via Gambit, and these may try to allocate more memory than is available, and die in bad ways that Gambit and Gerbil don't handle in a very user-friendly way). If you have enough memory (e.g. I am fine with 16GB of memory on 4 cores), you could use:export GERBIL_BUILD_CORES="$(gsi -e '(display (##cpu-count)) (newline)')"
In the future, we may try to automatically estimate how many cores to use, and/or implement more robust handling of this failure mode. But for now, we disable parallelism. by default.
Interface
make
(make build-spec settings-keyword-arguments ...)
Compile all parts of the project that are not up-to-date.
shell-config
(shell-config ...)
Please document me!
env-cppflags
(env-cppflags ...)
Please document me!
env-ldflags
(env-ldflags ...)
Please document me!
include-gambit-sharp
(include-gambit-sharp ...)
Please document me!
pkg-config
(pkg-config ...)
Please document me!
pkg-config-libs
(pkg-config-libs ...)
Please document me!
pkg-config-cflags
(pkg-config-cflags ...)
Please document me!
ldflags
(ldflags ...)
Please document me!
cppflags
(cppflags ...)
Please document me!
Standard Package Build Script
usage
(import :std/build-script)
defbuild-script
(defbuild-script build-spec settings-keyword-arguments ...)
Define a main
function that will built the project.
See above.