👨💻 about me home CV/Resume News 🖊️ Contact Github LinkedIn I’m a Haskeller 🏆 Best of LuaX hey bang ypp panda lsvg Fizzbuzz Calculadoira TPG todo pwd rrpi
Bang is a Ninja file generator scriptable in LuaX.
Bang is written in LuaX. It can be compiled with Ninja and LuaX.
$ git clone https://github.com/CDSoft/luax
$ ninja -C luax install # this should install LuaX to ~/.local/bin
$ git clone https://github.com/CDSoft/bang
$ ninja -C bang install # this should compile bang with Ninja and install it to ~/.local/bin
or set $PREFIX
to install bang
to a custom
directory ($PREFIX/bin
):
$ PREFIX=/path ninja install # installs bang to /path/bin
Bang also comes with a pure Lua implementation for environments where
LuaX can not be executed. In this case $PREFIX/bin/bang.lua
can be executed with any standard Lua 5.4 interpreter.
Warning: bang.lua
is insanely slower
than bang
, especially when dealing with a large amount of
source files.
In case precompiled binaries are needed (GNU/Linux, MacOS, Windows), some can be found at cdelord.fr/hey. These archives contain bang as well as some other softwares more or less related to LuaX.
Warning: There are Linux binaries linked with musl and glibc. The musl binaries are platform independent but can not load shared libraries. The glibc binaries can load shared libraries but may depend on some specific glibc versions on the host.
$ bang -h
Usage: bang [-h] [-v] [-q] [-o output] [<input>]
Ninja file generator
Arguments after "--" are given to the input script
Arguments:
input Lua script (default: build.lua)
Options:
-h, --help Show this help message and exit.
-v Print Bang version
-q Quiet mode (no output on stdout)
-o output Output file (default: build.ninja)
For more information, see https://github.com/CDSoft/bang
bang
reads build.lua
and produces
build.ninja
.bang input.lua -o output.ninja
reads
input.lua
and produces output.ninja
.bang
can add comments to the Ninja file:
"This is a comment added to the Ninja file" comment
section
adds comments separated by horizontal lines:
[[
section A large title
that can run on several lines
]]
var
adds a new variable definition:
"varname" "string value"
var "varname" (number)
var "varname" {"word_1", "word_2", ...} -- will produce `varname = word_1 word_2 ...` var
The global variable vars
is a table containing a copy of
all the Ninja variables defined by the var
function.
var
returns the name of the variable (prefixed with
"$"
).
The special variable ninja_required_version
shall be set
by the ninja_required_version
function.
ninja_required_version
will change the default required
version only if the script requires a higher version.
"1.42" ninja_required_version
rule
adds a new rule definition:
"rule_name" {
rule description = "...",
command = "...",
-- ...
}
Variable values can be strings or lists of strings. Lists of strings are flattened and concatenated (separated with spaces).
Rules can defined variables (see Rule variables).
Bang allows some build statement variables to be defined at the rule level:
implicit_in
: list of implicit inputs common to all
build statementsimplicit_out
: list of implicit outputs common to all
build statementsorder_only_deps
: list of order-only dependencies common
to all build statementsThese variables are added at the beginning of the corresponding variables in the build statements that use this rule.
The rule
function returns the name of the rule
("rule_name"
).
build
adds a new build statement:
"outputs" { "rule_name", "inputs" } build
generates the build statement
build outputs: rule_name inputs
. The first word of the
input list (rule_name
) shall be the rule name applied by
the build statement.
The build statement can be added some variable definitions in the
inputs
table:
"outputs" { "rule_name", "inputs",
build varname = "value",
-- ...
}
There are reserved variable names for bang to specify implicit inputs and outputs, dependency orders and validation statements:
"outputs" { "rule_name", "inputs",
build implicit_out = "implicit outputs",
implicit_in = "implicit inputs",
order_only_deps = "order-only dependencies",
validations = "build statements used as validations",
-- ...
}
The build
function returns the outputs
("outputs"
), as a string if outputs
contains a
single output or a list of string otherwise.
Some rules are specific to a single output and are used once. This leads to write pairs of rules and build statements.
Bang can merge rules and build statements into a single build statement containing the definition of the associated rule.
A build statement with a command
variable is split into
two parts:
In this case, the build statement definition does not contain any rule name.
E.g.:
"output" { "inputs",
build command = "...",
}
is internally translated into:
"output" {
rule command = "...",
}
"output" { "output", "inputs" } build
Note: the rule name is the output name where special characters are replaced with underscores.
pool
adds a pool definition:
"name" {
pool depth = pool_depth
}
The pool
function returns the name of the pool
("pool_name"
).
default
adds targets to the default target:
"target1"
default {"target2", "target3"} default
phony
is a shortcut to build
that uses the
phony
rule:
"all" {"target1", "target2"}
phony -- same as
"all" {"phony", "target1", "target2"} build
The command line arguments of bang are stored in a global table named
bang
. This table contains:
bang.input
: name of the Lua input scriptbang.output
: name of the output Ninja fileArguments after “–” are given to the input script in the global
arg
table.
Bang can accumulate names (rules, targets, …) in a list that can later be used to define other rules or build statements.
A standard way to do this in Lua would use a Lua table and
table.concat
or the list[#list+1]
pattern.
Bang provides a simple function to simplify this usage:
my_list = {}
-- ...
(my_list) "item1"
acc(my_list) {"item2", "item3"}
acc--...
my_list -- contains {"item1", "item2", "item3"}
The case
function provides a switch-like structure to
simplify conditional expressions. case
is a curried
function that takes a value (generally a string) and a table. It
searches for the value in the keys of the table and returns the
associated value. If the key is not found it returns the value
associated to the F.Nil
key or nil
.
E.g.:
local cflags = {
(mode) {
casedebug = "-g -Og",
release = "-s -O3",
[F.Nil] = {},
}
}
The ls
function lists files in a directory. It returns a
list of filenames, with the metatable of LuaX F
lists.
ls "path"
: list of file names in path
ls "path/*.c"
: list of file names matching the
“*.c
” pattern in path
ls "path/**"
: recursive list of file names in
path
ls "path/**.c"
: recursive list of file names matching
the “*.c
” pattern in path
E.g.:
"doc/*.md"
ls : foreach(function(doc)
(doc:splitext()..".pdf") { "md_to_pdf", doc }
build end)
-- where md_to_pdf is a rule to convert Markdown files to PDF
The file
function creates new files. It returns a
callable object to add text to a file (note that the write
method is deprecated). The file is actually written when bang exits
successfully.
"name" "content" file
The file can be generated incrementally by calling the file object several times:
f = file "name"
-- ...
"Line 1\n"
f -- ...
"Line 2\n"
f -- ...
It is common in Makefiles to write commands with pipes. But pipes can be error prone since only the failure of the last process is captured by default. A simple solution (for Makefiles or Ninja files) is to chain several rules.
The pipe
function takes a list of rules and returns a
function that applies all the rules, in the order of the list. This
function takes two parameters: the output and the inputs of the
pipe.
Intermediate outputs are stored in $builddir/pipe
. If a
rule name contains a dot, its « extension » is used to name intermediate
outputs.
E.g.:
"ypp.md" { command = "ypp $in -o $out" }
rule "panda.html" { command = "panda $in -o $out", implicit_in = "foo.css" }
rule
local ypp_then_panda = pipe { "ypp.md", "panda.md" }
"$builddir/doc/mydoc.html" "doc/mydoc.md" ypp_then_panda
is equivalent to:
"$builddir/pipe/doc/mydoc.md" { "ypp.md", "doc/mydoc.md" }
build "$builddir/doc/mydoc.html" { "panda.html", "$builddir/pipe/doc/mydoc.md" } build
Since rule
returns the name of the rule, this can also
be written as:
local ypp_then_panda = pipe {
"ypp.md" { command = "ypp $in -o $out" },
rule "panda.html" { command = "panda $in -o $out", implicit_in = "foo.css" },
rule }
The input list can contain variable definitions. These variables are
added to all build statements, except for
implicit_in
and implicit_out
that are added
respectively to the first and last build statements only.
e.g.:
"out.html" { "in.md",
ypp_then_panda implicit_in = "foo.in",
implicit_out = "foo.out",
other_var = "42",
}
is equivalent to:
"$builddir/pipe/doc/out-1.md" { "ypp.md", "doc/in.md",
build implicit_in = "foo.in",
other_var = "42",
}
"out.html" { "panda.html", "$builddir/pipe/doc/out-1.md",
build implicit_out = "foo.out",
other_var = "42",
}
Bang can generate targets to clean the generated files. The
clean
function takes a directory name that shall be deleted
by ninja clean
.
"$builddir" -- `ninja clean` cleans $builddir
clean "tmp/foo" -- `ninja clean` cleans /tmp/foo clean
clean
defines the target clean
(run by
ninja clean
) and a line in the help message (see
ninja help
).
In the same vein, clean.mrproper
takes directories to
clean with ninja mrproper
.
Bang can generate targets to install files outside the build
directories. The install
function adds targets to be
installed with ninja install
The default installation prefix can be set by
install.prefix
:
install.prefix "$$HOME/foo/bar" -- `ninja install` installs to ~/foo/bar
The default prefix in ~/.local
.
It can be overridden by the PREFIX
environment variable
when calling Ninja. E.g.:
$ PREFIX=~/bar/foo ninja install
Artifacts are added to the list of files to be installed by the
function install
. This function takes the name of the
destination directory, relative to the prefix and the file to be
installed.
"bin" "$builddir/bang" -- installs bang to $prefix/bin/ install
install
defines the target install
(run by
ninja install
) and a line in the help message (see
ninja help
).
Bang can generate an help message (stored in a file next to the Ninja
file) displayed by ninja help
.
The help message is composed of three parts:
The description and epilog are defined by the
help.description
and help.epilog
functions.
Targets can be added by the help
function. It takes the
name of a target and its description.
help.description "A super useful Ninja file"
help.epilog "See https://cdelord.fr/bang"
-- ...
"compile" "Compile every thing"
help -- ...
Note: the clean
and install
target are
automatically documented by the clean
and
install
functions.
Bang generates a generator rule to update the Ninja file when the
build description changes. This behaviour can be customized or disabled
with the generator
function:
generator(true)
:
bang adds a generator rule at the end of the ninja file (default
behaviour)generator(false)
:
bang does not add a generator rulegenerator(t)
:
if t
is a table, bang adds a generator rule with the
additional variables defined in t
The generator rule runs bang with the same options than the initial bang command.
E.g.:
{
generator implicit_in = { "foo", "bar" },
}
In this example, the generator statement will be executed if the Lua
script has changed as well as foo
and bar
.
The Ninja file of bang (build.ninja
)
is generated by bang
from build.lua
.
The example
directory contains a larger example:
This file is part of bang.
bang is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
bang is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with bang. If not, see <https://www.gnu.org/licenses/>.
For further information about bang you can visit
https://cdelord.fr/bang