Next: (maak git), Previous: (maak container), Up: API
Get a variable from the currently loaded maak file module.
Undocumented macro.
Undocumented macro.
Undocumented macro.
Undocumented macro.
Undocumented macro.
Undocumented macro.
Undocumented macro.
Undocumented macro.
Default value:
#t
Default value:
#f
Default value:
"sudo"
Default value:
()
Default value:
#f
Executes a shell command constructed from a list of strings.
($ '("echo -n" "hello-world"))
;; as root, in the background, quietly, and without caring whether it fails
($ '("systemctl daemon-reload")
#:sudo? #t #:async? #t #:dev-null? #t #:allow-failure? #t)
ARGUMENTS:
(("CI" . "1"))), as "KEY=VALUE" strings, or as one such
string. They are passed through env and reach the command
itself.
doas when it is
installed, sudo otherwise, or whatever MAAK_ELEVATE names.
See elevation-command.
/dev/null.
#f is returned instead, so a task can decide
what a failure means. The command line itself is left alone, which is
what lets the task be told the command failed.
run), instead
of a boolean.
RETURNS: The result of the syscall function: #t if
the command completes with an exit code of 0, #f if it failed and
#:allow-failure? was given, and a job record when #:async?
was given.
Prepend DIR to Guile’s load path and record it as a maak load path.
Returns the canonical name of the directory that was added, which makes it usable in a ‘let’ when a task wants to report what it did. This is a procedure rather than the ‘add-to-load-path’ syntax because a task computes the directory at run time.
Prepend every directory of DIRS to the load path, in order.
Returns the list of directories that were added.
Return #t when STR is empty or only whitespace.
Undocumented procedure.
Print FILE to standard output.
Return #t when COMMAND is an executable in one of the directories in PATH.
Copy a file or directory from X to Y.
ARGUMENTS: x: Source file or directory. y: Destination path.
recursive?: If #t, copy directories recursively (‘-r‘). Defaults to #t. force?: If #t, overwrite existing files without prompting (‘-f‘). Defaults to #f. verbose?: If #t, logs the final shell command before execution. Defaults to #f. verbose-copy?: If #t, enables verbose output from ‘cp‘ (‘-v‘). Defaults to #t. expand?: If #t, the shell expands X and Y, so a wildcard names the files it matches. Defaults to #f, which passes both as one argument each.
Constructs and executes a command of the form:
cp [OPTIONS] X Y
through ‘run‘, so X and Y reach ‘cp‘ as one argument each and a path with a space in it is copied rather than split.
RETURNS: #t if the copy succeeds.
ERRORS: Signals an error if the underlying ‘cp‘ command exits with a non-zero status.
Return CMD with the decorations a task asked for, applied in a fixed order.
The environment comes first and goes through ‘env’ rather than as a ‘KEY=VALUE’ prefix, because everything after a ‘guix shell –’ is executed directly: a prefix there would be looked up as a program name and fail with 127.
Whether a failure is allowed is not a decoration of the command line at all; ‘syscall’ reads the exit code, which a ‘|| true’ in the line would have hidden. Backgrounding is not one either: a trailing ‘&’ leaves nothing to wait on, so ‘#:async?’ forks instead and hands back a job (see ‘run’).
Remove the common leading indentation from a multiline STR.
Tabs are expanded to four spaces, leading and trailing blank lines are removed, and the smallest indentation shared by the remaining lines is stripped from all of them. Blank lines in the middle are preserved as empty lines.
(dedent-string " hello world") ⇒ "hello world"
Delete a directory and all its contents recursively. Runs ‘rm -rfv DIR‘ through ‘run‘, so DIR reaches ‘rm‘ as one argument however it is spelled. The deletion is recursive, forced and verbose.
With EXPAND? #t the shell expands DIR, so ‘build/*.o’ names every matching file; the same opt-in applies to the other file helpers via ‘run’.
ARGUMENTS:
RETURNS: #t if the command succeeded.
ERRORS: Signals an error if the underlying shell command exits with a non-zero status.
Return the command maak should use to run something as root.
PREFERRED, when non-empty, wins unchanged, which is how ‘MAAK_ELEVATE’ lets a user name an escalation program maak has never heard of. Otherwise ‘doas’ is chosen when it is on PATH, and ‘sudo’ when it is not: ‘doas’ is the smaller tool and the one a machine that has it usually means, while ‘sudo’ remains the fallback every other system understands.
PATH is the search path, ‘PATH’ itself by default, so the function can be tested without changing the process environment.
(detect-elevation-command #:path "/usr/bin:/bin") ;; => "sudo" (detect-elevation-command #:preferred "doas -n") ;; => "doas -n"
Return ENV as a list of ‘KEY=VALUE’ strings.
ENV may be #f, a single string, a list of strings, or an alist of pairs, so a caller can pass whatever shape it already has. An entry that is not a pair is used as it stands, which is what makes the plain string list useful when a value cannot be written as a literal.
Return the directories named by ‘MAAK_LOAD_PATH’.
‘GUILE_LOAD_PATH’ is already honoured by Guile itself and is deliberately not merged here; this is the maak-specific extra so a task can add paths without clobbering the user’s global setting.
Return the most descriptive string Guile attached to a caught error.
The shape of the arguments depends on who raised the error. A call to ‘error’ arrives as (#f MESSAGE () #f), where the message is the second element, while a primitive failure such as ‘(car 1)’ arrives as (#f MESSAGE (1) (1)) whose *first* string is only the name of the procedure – reporting that as the message would hide the one sentence the user needs. The second element is the message in both shapes, so it is preferred, and the remaining values are searched only when it is not a string, as when ‘throw’ is handed a key and a message but no irritants.
Return a one-line human message for a caught error.
It prefers an attached exception message, then the message among the raising values, and only then falls back to the raw key. Guile raises many errors with a format *template* (for example ‘no code for module ~S’) plus the arguments; those are filled in here so the user never sees a bare ‘~S’.
Return the ‘&maak-error’ among KEY and VALS, or ‘#f’.
Guile hands a ‘catch’ handler the raising key followed by its values; a ‘raise-exception’ of our condition type arrives as the first value, so both are searched. This is what lets ‘main’ tell our own messages apart from internal errors.
Run CMD-PARTS in a Guix container defined by MANIFEST.
A container isolates the process from the host: no network unless NETWORK? is #t, no access to the home directory unless it is SHARE’d. SHARE and EXPOSE are lists of ‘HOST=DEST’ (or ‘HOST’) strings passed through as ‘-S’ and ‘-E’.
(guix-container '("maak test")
#:manifest "manifest.scm"
#:share '("/home/joe"))
Build a ‘guix pack’ of the program in FILE.
FORMAT is any ‘guix pack –format’ value ("docker", "squashfs", "tarball"). IMAGE-TAG names a docker image, SYMLINKS are ‘TARGET=SOURCE’ entries for ‘-S’, and EXTRA-PACKAGES are appended to the pack.
(guix-pack '() #:format "docker" #:image-tag "maak"
#:symlinks '("/bin=bin") #:extra-packages '("bash"))
Run CMD-PARTS inside a Guix shell made of PACKAGES.
This is the package-list sibling of ‘manifest-shell’ and ‘program-shell’: the environment is described inline rather than in a manifest or a ‘guix.scm’. PACKAGES may be a list of strings ("guile", "coreutils") or a single string, which is used as-is.
(guix-shell '("guile -c '(+ 1 2)'") #:packages '("guile"))
Indent every line of STR by BY spaces.
When FIRST? is #f the first line is left flush, which is what aligning a continuation underneath an existing prefix needs.
Return #t when JOB has finished, without waiting for it.
A finished job is reaped here, so a background process that a task never waits for does not stay a zombie for the rest of the run.
Wait for JOB when needed and return its exit code.
Returns #f when the process was terminated by a signal, which is also what ‘status:exit-val’ says about such a status.
Return the number of leading space and tab characters in STR.
Undocumented procedure.
Undocumented procedure.
Undocumented procedure.
Undocumented procedure.
Undocumented procedure.
Undocumented procedure.
Undocumented procedure.
Predicate that indicates whether a procedure is a valid maak proc.
Predicate that indicates whether it is a valid maak task.
Undocumented procedure.
Execute a command inside a Guix shell defined by a manifest file.
ARGUMENTS:
cmd-parts: A list of strings representing the command to run inside the Guix shell environment.
#:verbose?: If #t, logs the fully constructed command before execution. Defaults to #f. #:pure?: If #t, it will run in a pure isolated shell (clean). Defaults to #f. #:join: The string separator used when joining command segments. Defaults to a single space. #:manifest: Path to the manifest file (in Scheme) describing the package environment. Defaults to "manifest.scm".
Constructs and executes a command of the form:
guix shell -m MANIFEST -- <cmd ...>
using the ‘$‘ wrapper to enforce proper execution and error handling.
RETURNS: #t if the command succeeds.
ERRORS: Signals an error if the underlying shell command exits with a non-zero status.
Create a directory and all necessary parent directories. Executes ‘mkdir -pv DIR‘ through ‘run‘. Parent directories are created as needed and each created directory is printed. A DIR that contains spaces is one directory, not several.
ARGUMENTS:
RETURNS: #t if the command completed successfully.
ERRORS: Signals an error if the underlying shell command exits with a non-zero code.
Move or rename a file or directory. Executes ‘mv -v X Y‘ through ‘run‘, so X and Y reach ‘mv‘ as one argument each. The ‘-v‘ flag prints each move operation performed.
ARGUMENTS:
RETURNS: #t if the command succeeds.
ERRORS: Signals an error if the underlying shell command returns a non-zero exit code.
Return #t when every stage of the pipeline that produced RESULT succeeded.
RESULT is what ‘run-pipeline’ returns when asked to capture the output, whether or not failures were allowed.
Execute a command inside a Guix shell defined by a program file.
ARGUMENTS: cmd-parts: A list of strings representing the command to run inside the Guix shell environment.
#:verbose?: If #t, logs the full constructed command before execution. Defaults to #f.
#:join: String used to join command segments. Defaults to a single space. #:file: Path to the Guix program or manifest file defining the shell environment. Defaults to "guix.scm".
Constructs and executes a command of the form:
guix shell -f FILE -- <cmd ...>
using the ‘$‘ wrapper to safely run the command and check for errors.
RETURNS: #t if the command succeeds.
ERRORS: Signals an error if the underlying shell command exits with a non-zero status.
Raise a ‘&maak-error’ with MESSAGE and an optional HINT.
MESSAGE is the single line the user reads; HINT, when given, is printed underneath as a suggestion for what to do next. Prefer this over ‘error’ in library code so the CLI can keep the report short.
Remove a directory if it exists, then recreate it. This results in an empty directory, regardless of its previous state. Calls ‘delete-file-recursively‘ then ‘mkdir-p‘ on DIR.
ARGUMENTS:
RETURNS: #t if the operations succeed.
ERRORS: Signals an error if any underlying shell command fails.
Delete PATH recursively, forcefully and verbosely.
A short spelling of delete-file-recursively, for the times a task
says rm -rfv build in the shell. It is an alias rather than a
second implementation, so it takes the same #:verbose? and
#:expand? keywords and behaves the same way.
(rm-rf "build") (rm-rf "build/*.o" #:expand? #t)
Run a program and its arguments without a shell.
ARGV is a list of strings: the program to run, followed by one string per argument. Nothing is split or interpolated, so a path with a space in it stays one argument. This is the default way to run a program; ‘$’ is for the lines that genuinely need a shell – pipes, redirections, globs, ‘$(...)’ and ‘VAR=value’ prefixes.
With #:expand? #t the arguments are joined with spaces and handed
to the shell, so wildcards, ‘~’ and ‘$VAR’ are expanded the way a shell
expands them. That is opt-in because the shell sees words rather than
arguments: a path with a space in it becomes several words again, which
is exactly what a caller asking for a pattern wants and exactly what the
default prevents.
(run (list "mkdir" "-p" build-dir))
(run (list "git" "commit" "-m" message))
;; start a server in the background and wait for it later
(let ((server (run (list "guix" "shell" "--" "run-server")
#:async? #t)))
(log-info "server started as ~a
" (job-pid server))
(wait-job server))
ARGUMENTS:
doas when it is
installed, sudo otherwise, or whatever MAAK_ELEVATE names.
See elevation-command.
/dev/null.
#f is returned instead.
RETURNS: #t if the program exits with code 0, #f if
it failed and failures were allowed, and a job record when
#:async? was given.
ERRORS: Signals a maak error when the program exits non-zero and failures are not allowed.
Run a pipeline of programs without a shell.
STAGES is a non-empty list of stages. A stage is an argument list
shaped like the ARGV of ‘run’ – the program first, then one string per
argument – or a stage form, which adds the options ‘run’ takes
for that stage alone. The standard output of each stage is the standard
input of the next, and the whole pipeline is done when every stage has
finished.
(run-pipeline (list (list "cat" file)
(list "grep" "-v" "^#")
(list "sort" "-u")))
;; a stage with an environment and a file of its own, and one that needs root
(run-pipeline (list (stage (list "sort" "-u")
#:env '("LC_ALL=C")
#:tee "sorted.txt")
(stage (list "tee" log) #:sudo? #t)))
;; keep what every stage wrote for the task to look at
(define result (run-pipeline (list (list "git" "diff" "--cached")
(list "grep" "^+"))
#:capture? #t
#:allow-failure? #t))
(pipeline-outputs result) ;; one string per stage, in order
(pipeline-ok? result) ;; #f when a stage failed
Unlike a shell’s ‘|’, which reports the exit status of the last stage
alone, a pipeline that fails anywhere reports the stages that failed and
raises; pass #:allow-failure? #t to get #f instead and
decide in the task. This is the pipefail behaviour, and it is the one
that does not let a failing producer hide behind a successful consumer.
A pipeline’s output can be looked at afterwards in two ways. A stage
with #:tee keeps a copy of what it wrote in a file the task
names, and the stage after it reads the same bytes while it runs:
nothing is held back until the pipeline ends. #:capture? #t
records every stage the same way, in temporary files, and hands the
bytes back in the result instead of printing them; the last stage’s
output is then the last entry of pipeline-outputs. Capturing is
the way to make a decision from what a pipeline produced – a count of
changed files, the list a ‘grep’ matched – without running the pipeline
twice.
ARGUMENTS:
stage.
#f is returned instead (or the result, when the
output was captured), so the task can decide what it means.
#:env is added to these, later names
winning.
<pipeline-result>, and the
pipeline prints nothing itself. The result’s pipeline-outputs
holds one string per stage, in stage order, and pipeline-ok? says
whether every stage succeeded. A stage that also has #:tee
records into that file, which is left in place.
#:allow-failure? said. An asynchronous pipeline cannot capture
its output, because the bytes would be read in the background process;
give the stage a #:tee file instead.
RETURNS: #t when every stage exits with code 0, #f
when a stage failed and failures were allowed, a
<pipeline-result> when #:capture? was given, and a job
record when #:async? was given.
ERRORS: Signals a maak error naming the failing stage or stages
when a pipeline fails and failures are not allowed, and when
#:capture? and #:async? are asked for together.
Return STR quoted so that a shell reads it as one literal word.
The POSIX recipe: wrap the value in single quotes and turn every embedded single quote into the four characters backslash-quote-quote-quote. Unlike ‘~s’, this is safe for spaces, tabs, ‘$’, backticks and newlines.
Split a ‘PATH’-style colon separated STR into a list of directories.
An empty entry is dropped, so a trailing or doubled colon does not add the current directory twice.
Describe one stage of a pipeline, with the options ‘run’ takes.
ARGV is a program and its arguments, in the shape ‘run’ takes them. ENV sets names for this stage alone, in the shapes ‘$’ accepts; they are added to the pipeline’s own environment, later names winning. With SUDO? the stage runs through the configured privilege escalation command (see ‘elevation-command’). TEE names a file that the stage’s standard output is also written to, the way ‘tee’ does it: the stage after this one reads the same bytes, and the file keeps them for the task to look at once the pipeline is done.
(run-pipeline (list (stage (list "grep" "-v" "^#")
#:env '("LC_ALL=C")
#:tee "clean.txt")
(stage (list "sort" "-u"))))
A stage is a description only; ‘run-pipeline’ starts it.
Split STR on newlines into a list of lines.
Executes a shell command and checks its exit code.
ARGUMENTS:
#f, the way ‘cmd || true’
would keep a shell going, except that the task also learns that the
command failed.
RETURNS: #t if the command completes with an exit code of 0,
#f if it failed and failures are allowed, and a job record when
#:async? was given.
ERRORS: Signals an error if the command returns a non-zero exit code, providing the command and the exit code in the error message.
Raise a user-facing error from a task.
This is what a task calls when it cannot do its job: the message is printed as one line, the hint (when given) underneath it, and no Guile backtrace follows. ARGS, when given, are handed to ‘format’ to build the message, so a task can report the value that made it give up.
(task-error "no compiled artefact for ~a" #:args (list host))
(task-error "the deploy key is missing"
#:hint "Set DEPLOY_KEY, or run maak deploy --dry-run.")
Execute a command using a pinned Guix revision via ‘guix time-machine‘.
ARGUMENTS: cmd-parts: A list of strings representing the command to run after the ‘guix time-machine‘ invocation.
#:verbose?: If #t, logs the final constructed command before running it. Defaults to #f. #:join: The string used to join command segments when constructing the final command. Defaults to a single space. #:channels: Path to the channels file used to pin the Guix revision. Defaults to "channels.scm".
Constructs and executes a command of the form:
guix time-machine --channels=CHANNELS -- <cmd ...>
using the ‘$‘ wrapper to run the resulting shell command safely.
RETURNS: #t if the command succeeds.
ERRORS: Signals an error if the underlying command exits with a non-zero status.
Execute a command inside a Guix shell defined by a manifest file, using a pinned Guix revision via ‘guix time-machine‘.
ARGUMENTS: cmd-parts: A list of strings representing the command to run inside the pinned Guix shell environment.
#:verbose?: If #t, logs the composed command before execution. Defaults to #f. #:pure?: If #t, it will run in a pure isolated shell (clean). Defaults to #f. join: String used to join command segments. Defaults to a single space. #:channels: Path to the channels file used to pin the Guix revision. Defaults to "channels.scm". #:manifest: Path to the manifest file describing the package environment. Defaults to "manifest.scm".
Constructs and executes a command of the form:
guix time-machine --channels=CHANNELS -- shell -m MANIFEST -- <cmd ...>
This provides a reproducible shell environment defined by the manifest file and pinned to the Guix revision specified in the channels file.
RETURNS: #t if the command succeeds.
ERRORS: Signals an error if any underlying shell command exits with a non-zero status.
Execute a command inside a Guix shell defined by a program file, using a pinned Guix revision via ‘guix time-machine‘.
ARGUMENTS: cmd-parts: A list of strings representing the command to run inside the pinned Guix shell environment.
#:verbose?: If #t, logs the composed command before execution. Defaults to #f. #:pure?: If #t, it will run in a pure isolated shell (clean). Defaults to #f. #:join: String used to join command segments. Defaults to a single space. #:channels: Path to the channels file used to pin the Guix revision. Defaults to "channels.scm". #:file: Path to the Guix program file (e.g. guix.scm) defining the environment. Defaults to "guix.scm".
Constructs and executes a command of the form:
guix time-machine --channels=CHANNELS -- shell -f FILE -- <cmd ...>
This provides a reproducible development environment defined by the program file and pinned to the Guix revision specified in the channels file.
RETURNS: #t if the command succeeds.
ERRORS: Signals an error if any underlying shell command exits with a non-zero status.
Join LINES with newlines, adding no trailing newline.
Join WORDS with single spaces.
Wait for JOB to finish and return #t when it exited with code 0.
Returns #f for a non-zero exit or a signal. Waiting twice is fine: the status is remembered after the first wait.
Call THUNK with DIRS added to the load path, restoring it afterwards.
DIRS is a list of directory names. The load path is saved and restored even when THUNK raises, so a failing task does not leave the process with a polluted search path.
(with-load-path '("./lib" "./src")
(lambda () (use-modules (my project))))
A utility function to format a string (interpolation). Alias for (apply format #f ARGS).
(~ "Hello world, my name is ~a and I am ~a years old"
"Joe"
30)
ARGUMENTS:
RETURNS: A newly allocated string resulting from the formatting operation.
This record type has the following fields:
messagehintNext: (maak git), Previous: (maak container), Up: API