ninja.pl -- Ninja build system generator

This module contains helper dcg predicates to generate ninja build files akin to the ninja_syntax.py python module distributed by ninja. You can use these predicates if you want to generate your own build.ninja build file.

Example usage:

:- use_module(prolog/ninja).
:- initialization(write_build(build_graph), main).

build_graph -->
  rule(cp, "cp $in $out"),
  build("input.txt", cp, "output.txt").

Then build.ninja contains the following build specification:

rule generate
  command = swipl $in
  generator = 1
build build.ninja: generate /home/kwon-young/prog/ninja/build.pl | /home/kwon-young/prog/ninja/build.pl /home/kwon-young/prog/ninja/prolog/ninja.pl
rule cp
  command = cp $in $out
build input.txt: cp output.txt

See the ninja build format documentation for generating more complex build files.

author
- Kwon-Young Choi
license
- GPL
 variable(+Pair:pair)// is det
Generate a variable declaration from a pair Pair. The key can be an atom or a dcg, value should be a dcg. A variable definition always end with a new line.
?- phrase(variable(name-"Value"), L), format("~s", [L]).
name = Value
L = [110, 97, 109, 101, 32, 61, 32, 86, 97|...].
 variable(+Name:atom;dcg, +Value:dcg)// is det
Generate a variable declaration as a variable Name with value Value. The variable name can be an atom or a dcg, value should be a dcg. A variable definition always end with a new line.
?- phrase(variable(name, "Value"), L), format("~s", [L]).
name = Value
L = [110, 97, 109, 101, 32, 61, 32, 86, 97|...].
 rule(+Name:atom;dcg, +Command:dcg)// is det
Generate a rule declaration with no additional variables. The name can be an atom or a dcg, the command should be dcg.
?- phrase(rule(cp, "cp $in $out"), L), format("~s", [L]).
rule cp
  command = cp $in $out
L = [114, 117, 108, 101, 32, 99, 112, 10, 32|...].
 rule(+Name:atom;dcg, +Command:dcg, +Variables:list(pair))// is det
Generate a rule declaration with no additional variables. The name can be an atom or a dcg. The command should be dcg and describe the command to run. Variables is a list and will be generate using the variable//1 rule.
?- phrase(rule(cp, "cp $in $out"), L), format("~s", [L]).
rule cp
  command = cp $in $out
L = [114, 117, 108, 101, 32, 99, 112, 10, 32|...].
 build(+Outs:list(dcg), +Rule:atom;dcg, +Ins:list(dcg))// is det
Generate a build statement between input Ins and Output Outs with the rule Rule.
?- phrase(build(["input.txt"], cp, ["output.txt"]), L), format("~s", [L]).
build input.txt: cp output.txt
L = [98, 117, 105, 108, 100, 32, 105, 110, 112|...].
 build(+Outs:list(dcg);dcg, +Rule:atom;dcg, +Ins:list(dcg);dcg, +Options:list)// is det
Generate a build statement between input Ins and output Outs with the rule Rule. There should be at least one output. You can omit using a list of single element arguments. Optional arguments can be specified in the option list Options. Valid options are:
implicit_ins(ImplicitIns:list(dcg);dcg)
List of implicit dependencies, as list of dcgs.
implicit_outs(ImplicitOuts:list(dcg);dcg)
List of implicit outputs, as list of dcgs.
orderonly_ins(OrderonlyIns:list(dcg);dcg)
List of order only dependencies, as list of dcgs.
validations(Validations:list(dcg);dcg)
List of validation targets, as list of dcgs.
variables(Variables:list(pair);pair)
List of variables as pairs.
?- phrase(build(["input.txt"], cp, ["output.txt"],
     [implicit_outs(["implicit_out.txt"]),
      implicit_ins(["implicit_in.txt"]),
      orderonly_ins(["orderonly_in.txt"]),
      validations(["validation.txt"]),
      variables([name-"value"])]), L), format("~s", [L]).
build input.txt | implicit_out.txt: cp output.txt | implicit_in.txt || orderonly_in.txt |@ validation.txt
  name = value
L = `build input.txt | implici...alue\n`.
 deps(++Source:string)// is det
Generate a whitespace separated list of dependencies from prolog source file Source. It also include Source as a dependency.
?- phrase(deps("ninja.pl"), L), format("~s", [L]).
ninja.pl /usr/lib64/swipl-9.0.4/library/dcg/basics.pl /usr/lib64/swipl-9.0.4/library/dcg/high_order.pl /usr/lib64/swipl-9.0.4/library/option.pl /usr/lib64/swipl-9.0.4/library/error.pl
L = `ninja.pl /usr/lib64/swipl...or.pl`.
 generator(+Input:string)// is det
Generate a generator rule and build edge with Input as the prolog generator script. The build edge generated will also contains all prolog files that depends on Input. See the ninja documentation for such rule. The output filename will be named "build.ninja" as traditional.
?- phrase(generator("example.pl"), format("~s", [L]).
rule generate
  command = swipl $in
  generator = 1
build build.ninja: generate example.pl | example.pl /usr/lib64/swipl-9.0.4/library/dcg/basics.pl /usr/lib64/swipl-9.0.4/library/dcg/high_order.pl /home/kwon-young/prog/ninja/prolog/ninja.pl
 generator(+Output:string, +Input:string)// is det
Generate a generator rule and build edge with Input as the prolog generator script and Output as the ninja build file. The build edge generated will also contains all prolog files that depends on Input. See the ninja documentation for such rule.
?- phrase(generator("example.pl"), format("~s", [L]).
rule generate
  command = swipl $in
  generator = 1
build build.ninja: generate example.pl | example.pl /usr/lib64/swipl-9.0.4/library/dcg/basics.pl /usr/lib64/swipl-9.0.4/library/dcg/high_order.pl /home/kwon-young/prog/ninja/prolog/ninja.pl
 seq(+Goals:list(dcg))// is det
Experimental attempt to ease the chaining of sequential build edges. This predicate allows to specify a list a build edges without repeating the inputs of a build edge with the output of the previous build edge. However, the first build edge have to have its input specified.
?- phrase(seq([
  build("foo.jpg", convert, "foo.pdf"),
  build("foo.txt", ocr)]), L), format("~s", [L]).
build foo.jpg: convert foo.pdf
build foo.txt: ocr foo.jpg

Notice how "foo.jpg" is specified as input to the ocr build edge although we did not specified as input in the seq rule. You can still specify options after the rule name in the build rule.

 write_build(+Goal:dcg) is det
Generate a "build.ninja" file using the dcg Goal. This will also automatically add a generator rule as specified in generator/2 for the script containing Goal. You can use this goal as with initialization/2 directive to automatically generate your ninja build file when calling your script with swipl.

With the following prolog script "build.pl":

:- use_module(library(ninja)).
:- initialization(write_build(build_graph), main).

build_graph -->
  rule(cp, "cp $in $out"),
  build(["input.txt"], cp, ["output.txt"]).

When called on the command line like this:

$ swipl build.pl

Will generate the following "build.ninja" file:

rule generate
  command = swipl $in
  generator = 1
build build.ninja: generate /home/kwon-young/prog/ninja/build.pl | /home/kwon-young/prog/ninja/build.pl /home/kwon-young/prog/ninja/prolog/ninja.pl
rule cp
  command = cp $in $out
build input.txt: cp output.txt
 write_build(+Goal:dcg, Output) is det
See write_build/1. Output specify the name of the ninja build file.