# The Standard Library Build Tool

Building complex libraries and executables by invoking `gxc` quickly gets tedious. When you reach that point of complexity and you need a build tool, you can use the [`:std/make` library module](../reference/make.md) which provides a modest build tool that can handle reasonably complex project building.

## The project source code

For illustration purposes, we'll make a hello world library module and an executable that uses it.

``` scheme
$ cat gerbil.pkg
(package: example)

$ cat util.ss
(export #t)
(def (hello who)
  (displayln "hello, " who))

$ cat hello.ss
(import :example/util)
(export main)

(def (main . args)
  (for-each hello args))
```

## The standard build script template

The recommended way to write a build script is to use the template provided by the standard library.
You can do this by importing `:std/build-script` and using the `defbuild-script` macro.

The macro defines a main function suitable for building packages either directly or through gpxkg.
The syntax is
``` scheme
(defbuild-script build-spec . settings)
```

Using this, the build script for our project is the following:

``` scheme
$ cat build.ss
#!/usr/bin/env gxi
(import :std/build-script)
(defbuild-script
  '("util"
    (exe: "hello"))
  optimize: #t debug: 'src)
```

And we can build by invoking the script:
``` bash
$ chmod +x build.ss
$ ./build.ss
```

## Intermediate build scripts

Here is a full fledged build script that can handle building our library and executable.
It also supports dependency tracking for compiling in the correct order and incremental compilation that only compiles when relevant source modules are newer than compiled artifacts.
It is roughly equivalent to the script generated by `defbuild-script`, without actions specific to `gxpkg`.
``` scheme
$ cat build.ss
#!/usr/bin/env gxi

(import :std/make)

;; the build specification
(def build-spec
  '("util"
    (exe: "hello")))

;; the source directory anchor
(def srcdir
  (path-normalize (path-directory (this-source-file))))

;; the main function of the script
(def (main . args)
  (match args
    ;; this is the default (and, here, only) action, which builds the project
    ([]
     (make srcdir: srcdir          ; source anchor
           bindir: srcdir          ; where to place executables; default is GERBIL_PATH/bin
           optimize: #t            ; enable optimizations
           debug: 'src             ; enable debugger introspection
           static: #f              ; don't generate static compilation artifacts
           prefix: "example"       ; this matches your package prefix
           build-spec))))          ; the actual build specification
```

To build our project:

``` bash
$ chmod +x build.ss
$ ./build.ss
```

After the initial dependency graph generation, we can build during development by reusing the dependency graph and simply invoking ./build.ss. You only need to generate a new dependency graph if your import sets change.

## Building static executables

Static executables are simple to build:

- the executables are specified with the static-exe: build spec in place of exe:.
- the make invocation needs static: #t to be specified so that static compilation artifacts are built for modules.

However, there is a nuance: you usually don't want to build static executables with debug introspection as this will blow the executable size significantly.

Perhaps the simplest way to deal with the bloat issue is to have a separate step building the executables, while still compiling library modules with debug introspection for working in the repl.

The following build script breaks the build action into two steps, one for building library modules and another for building the executables:

``` scheme
#!/usr/bin/env gxi

(import :std/make)

;; the library module build specification
(def lib-build-spec
  '("util"))

(def bin-build-spec
  '((static-exe: "hello")))

;; the source directory anchor
(def srcdir
  (path-normalize (path-directory (this-source-file))))

;; the main function of the script
(def (main . args)
  (match args
    (["lib"]
     ;; this action builds the library modules -- with static compilation artifacts
     (make srcdir: srcdir
           bindir: srcdir
           optimize: #t
           debug: 'src             ; enable debugger introspection for library modules
           static: #t              ; generate static compilation artifacts; required!
           prefix: "example"
           ;; build-deps: "build-deps" ; this value is the default
           lib-build-spec))

    (["bin"]
     ;; this action builds the static executables -- no debug introspection
     (make srcdir: srcdir
           bindir: srcdir
           optimize: #t
           debug: #f               ; no debug bloat for executables
           static: #t              ; generate static compilation artifacts; required!
           prefix: "example"
           build-deps: "build-deps-bin" ; importantly, pick a file that differs from above
           bin-build-spec))

    ;; this is the default action, builds libraries and executables
    ([]
     (main "lib")
     (main "bin"))))
```

Note that the `build-deps:` file is a cache that stores your project dependencies.
In large project, an up-to-date cache can save many seconds in build times.
When multiple projects share a same directory, they must be made to use separate
`build-deps:` file, or the caches will clash and be ineffective.
All but one of the projects must explicitly specify the `build-deps:` argument
to point to its own distinct file.
