Recipes
A recipe is a value that describes how to build an artifact. Recipes can span from “create a file containing this string” to “save the output from running these Bash commands”. Many recipes take other recipes as input, which can represent very complex build pipelines as a big tree of steps needed for the build.
To do anything useful, a recipe must be baked, which evaluates it and returns the resulting artifact.
Every artifact is itself a recipe— when baked, it just returns itself.
Recipes are immutable. There are several utilities that work with recipes, but these will return new, modified recipes, leaving the originals unchanged. For example, calling .remove()
on a recipe will return a new recipe that has the item removed, leaving the original recipe unchanged.
std.Recipe
In the Brioche standard library (the std
package), the type std.Recipe
represents a recipe. This type also takes an optional generic to specify what kind of artifact the recipe bakes into:
std.Recipe
: Bakes into a file, directory, or symlink artifactstd.Recipe<File>
: Bakes into a file artifactstd.Recipe<Directory>
: Bakes into a directory artifactstd.Recipe<Symlink>
: Bakes into a symlink artifactstd.Recipe<File | Directory>
: Bakes into either a file or directory artifact- …etc.
Recipes have some utility methods specific to the type of artifact they bake into. For example, .get()
only makes sense for directory recipes, so this method won’t be available for other types of recipes. If you want to cast a recipe to a more specific type, use a casting function such as std.castToFile
/ std.castToDirectory
/ std.castToSymlink
.
std.file
Takes a string or Uint8Array
and bakes to a file artifact containing the specified contents.
std.directory
Takes an object that associates filenames with recipes, and bakes to a directory artifact. Inner recipes are baked recursively and lazily.
std.symlink
Returns a symlink artifact with the specified target file.
std.merge
Bakes to a directory artifact that merges together two or more directory recipes. Directories are merged recursively. The rightmost directory take precedence.
std.glob
Takes a directory artifact and a list of glob patterns, and returns a new directory artifact only containing the files matching at least one glob pattern.
std.download
Bakes to a file containing the contents of the specified URL. A hash must be specified explicitly, and will be validated when the URL is downloaded.
To avoid manually specifying the download hash by hand, you can use the Brioche.download
static function instead.
std.process
A low-level utility that creates a recipe to run a process. Generally, you’ll call another function that wraps std.process
under the hood.
Takes an object with options for spawning the process:
command
(required): The command to runargs
: Command-line arguments to invoke the process withenv
: An object containing extra environment variables to pass to the process. A minimal set of environment variables is included by defaultdependencies
: An array of recipes to include in the process’s environment when run. Binaries and environment variables will be set based on all dependencies (see “Process Dependencies”)workDir
: The process starts by default in an empty working directory. Set this to a directory recipe to pre-populate the process’s working directory when it startsoutputScaffold
: The path$BRIOCHE_OUTPUT
initially doesn’t exist when the process starts, and must be written to before the process exits in order to succeed. SetoutputScaffold
to any recipe to initialize this output path with some sort of contents. This is useful to run a command likesed -i
that modifies its output contents in place, or to run a command likegcc
to let it output directly into abin/
directoryunsafe
: Opt-in to certain unsafe features (see “Unsafe processes”)
The values for command
, args
, and env
can be passed as plain strings or using process templates, created with std.tpl
. Recipes can be interpolated in the template, which will automatically bake the recipe before the process starts, then will resolve to a path containing the recipe’s output artifact.
See “Sandboxing” for more details about how processes are run and what they have access to.
The return value of std.process()
has additional utility methods to change the options for the process as it runs. Just like other recipe utilities, the original process is immutable and these methods return copies.
std.sync
Sync a recipe to the registry explicitly. This is a fairly low-level tool that is only used for optimizing the time to sync from the registry.
Normally, only the intermediate recipes of a build are synced to the registry, as doing so allows for better cache reuse for partial rebuilds and reduces the storage use for the registry.
You can wrap any recipe with std.sync
to sync it to the registry too (the intermediate recipes will still be synced). This will use more storage in the registry and will take longer to sync to the registry, but can speed up the time to sync complex recipes from the registry.
std.castToFile
/ std.castToDirectory
/ std.castToSymlink
Lazily cast a recipe from a more generic type to the more specific type. Returns an error when baked if the recipe doesn’t match the casted type. Effectively, these functions acts as a sort of assert that the recipe matches the expected type.
This is useful because some functions may return a generic type like std.Recipe
, and so some utility methods cannot be accessed without casting first.
For process recipes (created with std.process()
), consider using Process.toFile
/ Process.toDirectory
/ Process.toSymlink
instead
Recipe.bake
Eagerly bake a recipe, returning a Promise<Artifact>
.
Calling .bake()
manually should be a pretty rare occurrence. Brioche implicitly bakes the recipe returned when calling brioche build
, so this is only needed when an artifact needs to be interacted with directly within the build script.
File.withPermissions
Returns a new recipe that bakes to a file with the same contents, but with different permissions set.
File.unarchive
Unarchive a (possibly compressed) archive of a directory, such as a .tar.gz
file.
Calling .unarchive()
is pretty limited. If you need more advanced unarchiving options, consider using a process or Bash script to call a program such as tar
directly instead.
If the archive contains a single top-level directory, you can call .peel()
to remove this extra top layer.
File.readBytes
Read the contents of a file into a Uint8Array
. Implicitly calls .bake()
. See File.read
to read a string instead.
File.read
Read the contents of a file as a UTF-8 string. Implicitly calls .bake()
. Throws an exception if the file was not valid UTF-8.
Directory.get
Returns a recipe that retrieves the given path from the artifact. Fails if the path is not valid or descends into a non-directory artifact.
Directory.insert
Returns a new recipe with a new artifact inserted at the provided path. If the path already exists, it will be replaced. Fails if the path is not valid or descends into a non-directory artifact.
Note: Be sure you use the return value of the call to .insert()
instead of the original! .insert()
returns a new recipe, leaving the original unchanged.
Directory.remove
Returns a new recipe that removes the artifact from the provided path. Fails if the path is not valid or descends into a non-directory artifact. If the path doesn’t exist in the directory, then the newly-returned directory is unchanged.
Note: Be sure you use the return value of the call to .remove()
instead of the original! .remove()
returns a new recipe, leaving the original unchanged.
Directory.peel
If a directory only contains a single inner directory, return a recipe that just extracts this inner directory. Can also be called with an argument to specify the number of times to repeat this peeling process (the default value is 1
). Fails if a peeled directory does not contain exactly one directory artifact entry.
This is mostly useful for tarballs, which often contain a top-level directory named after the tarball itself.
Process.toFile
/ Process.toDirectory
/ Process.toSymlink
Lazily cast a process recipe from a more generic type to a more specific type. Returns an error if the process writes a non-matching artifact to $BRIOCHE_OUTPUT
(e.g. a process that returns a directory when .toFile()
was called).
These methods are convenience wrappers over the functions std.castToFile
/ std.castToDirectory
/ std.castToSymlink
, since process recipes frequently need to be cast to a more specific type.