• Home
  • History
  • Annotate
Name Date Size #Lines LOC

..03-May-2022-

debian/H07-May-2022-4534

docker/H03-Aug-2021-3720

docs/H03-May-2022-2,0141,372

lib/H03-Aug-2021-826644

minilang/H03-May-2022-59,43748,893

radb/H03-May-2022-2,3951,970

src/H03-Aug-2021-5,4684,726

test/H03-Aug-2021-232186

.editorconfigH A D03-Aug-2021171 1210

.gitignoreH A D03-Aug-2021233 2524

.gitmodulesH A D03-Aug-2021165 76

LICENSEH A D03-Aug-20211 KiB2217

MakefileH A D03-May-20222.8 KiB127100

README.mdH A D03-Aug-202110.9 KiB302211

debian.shH A D03-Aug-202192 94

release.shH A D03-Aug-2021247 127

README.md

1# Rabs
2
3Rabs is an imperative build system, borrowing features from [http://omake.metaprl.org/index.html](Omake)
4but implemented in C instead of OCaml and supporting an imperative paradigm (with functional components) instead of a pure functional one,.
5
6## Introduction
7
8## History
9
10## Language
11
12The language in Rabs is called _Minilang_ since it is not meant to be very sophisticated.
13It is case sensitive with lower case keywords, ignores spaces and tabs but will treat an end-of-line
14marker as the end of a statement unless additional code is expected (e.g. after an infix operator or
15in a function call).
16
17### Sample Code
18
19```lua
20PLATFORM := defined("PLATFORM") or shell("uname"):trim
21OS := defined("OS")
22DEBUG := defined("DEBUG")
23
24CFLAGS := []
25LDFLAGS := []
26
27c_compile := fun(Object) do
28	var Source := Object % "c"
29	execute('gcc -c {CFLAGS} -o{Object} {Source}')
30end
31
32c_includes := fun(Target) do
33	var Files := []
34	var Lines := shell('gcc -c {CFLAGS} -M -MG {Target:source}')
35	var Files := Lines:trim:replace(r"\\\n ", "") / r"[^\\]( )"
36	Files:pop
37	for File in Files do
38		File := file(File:replace(r"\\ ", " "))
39	end
40	return Files
41end
42
43var SourceTypes := {
44	"c" is [c_includes, c_compile]
45}
46
47c_program := fun(Executable, Objects, Libraries) do
48	Objects := Objects or []
49	Libraries := Libraries or []
50	var Sources := []
51	for Object in Objects do
52		for Extension, Functions in SourceTypes do
53			var Source := Object % Extension
54			if Source:exists then
55				Sources:put(Source)
56				var Scan := Source:scan("INCLUDES", :true) => Functions[1]
57				Object[Source, Scan] => Functions[2]
58				exit
59			end
60		end
61	end
62	Executable[Objects, Libraries] => fun(Executable) do
63		execute('gcc', '-o', Executable, Objects, Libraries, LDFLAGS)
64		DEBUG or execute('strip', Executable)
65	end
66	DEFAULT[Executable]
67end
68```
69
70## Usage
71
72### Targets
73
74Everything in the Rabs build tree is considered a _target_.
75Every target has a unique id, and every unique id corresponds to a unique target.
76This means that when constructing a target anywhere in the build tree, if the construction results in the same id, then it will return the same target.
77
78Every target has a (possibly empty) set of dependencies, i.e. targets that must be built before this target is built.
79Cyclic dependencies are not allowed, and will trigger an error.
80
81Each run of Rabs is considered an iteration, and increments an internal iteration counter in the build database.
82In order to reduce unneccesary building for large project, at each iteration Rabs decides both whether a target needs to be built and whether, after building, it has actually changed.
83
84If a target is missing (e.g. for the first build, when a new target is added to the build or for a file that is missing from the file system), then it needs to be built.
85Once built, the build database records two iteration values for each target:
86
871. The last built iteration: when the target was last built.
882. The last changed iteration: when the target was last changed.
89
90Since a target can't change without being built, the last built iteration of a target is always greater or equal to the last changed iteration.
91The last built iteration of a target should be greater or equal to the last changed iteration of its dependencies.
92
93While building, if a target has a last built iteration less than the last changed iteration of any of its dependencies, then it is rebuilt, and its last built iteration updated to the current iteration.
94Then it is checked for changes (using hashing, depending on the target type), and the last changed iteration updated if it has indeed changed.
95This will trigger other target to be rebuilt as required.
96
97### Contexts
98
99Rabs executes minilang code within _contexts_. Each context maintains its own set of variables, and is associated with a directory in the file system.
100This directory is used to resolve relative file paths evaluated within the context.
101
102Contexts are hierarchical, each context has exactly one parent context (typically associated with the parent directory), and variables undefined in one context are searched for up the parent context chain.
103However, assigning a variable in a context only changes its value within that context and its children.
104This means the variables defined in a context are automatically available to child contexts and that parent values of variables can be extended or modified within a context and its children without affected its parents.
105
106There is exactly one **root** context, associated with the root directory of the project / build.
107
108New contexts are made when entering a child directory using [`subdir()`](#subdir) or by calling [`scope()`](#scope).
109
110### Symbols
111
112Although variables can be used in Minilang to store values during the build process, these variables are not visible to child contexts, and they do not play any part in dependency tracking.
113Instead, _symbols_ can be used for that purpose.
114These are created automatically whenever an identifier is referenced that has not been declared as a variable or builtin.
115
116For example, in the following code, `LOCAL_CFLAGS` will only accessible within the current Minilang scope, as per normal lexical scoping rules.
117However, `CFLAGS` will also be accessible from any child context of the current one, including child directories.
118
119```lua
120var LOCAL_CFLAGS := ["-O2"]
121CFLAGS := ["-O2"]
122```
123
124Ressigning a value to a symbol within a child context only affects that child context (and its children).
125For example, if `CFLAGS` is changed in a child context as follows:
126
127```lua
128CFLAGS := old + ["-march=native"]
129```
130
131Then `CFLAGS` will have the new value `["-O2", "-march=native"]` within the child context (and its children).
132`old` in this case will refer to the value of the symbol before the assignment, i.e. the value of `CFLAGS` in the parent context. This allows child contexts to extend the values of symbols from their parents.
133
134Symbols are targets.
135When used in a build function, an automatic dependency on that symbol is added to the target being built.
136Moreover, the value of the symbol in a build function is resolved within the context of the target being built.
137
138For example, if we have the following `_minibuild_` script in one directory:
139
140```lua
141CFLAGS := ["-O2"]
142
143compile_c := fun(Target) do
144	var Source := Target % "c"
145	execute("gcc", CFLAGS, "-c", Source, "-o", Target)
146end
147
148var Main := file("main.o") => compile_c
149
150DEFAULT[Main]
151
152subdir("test")
153```
154
155And if the following `_minibuild_` script is in the folder `test`:
156
157```lua
158CFLAGS := old + ["-march=native"]
159
160var Test := file("test.o") => compile_c
161
162DEFAULT[Test]
163```
164
165Then `main.o` will be compiled with `-O2`, but `test.o` will be compiled with `-O2 -march=native`.
166
167### Project Layout
168
169Rabs loads and executes code from build files named `_minibuild_`.
170When run, rabs first searches for the root `_minibuild_` file by ascending directories until it finds a `_minibuild_` file starting with `-- ROOT --`.
171
172`_minibuild_` files can be located in nested folders and loaded using [`subdir()`](#subdir).
173
174```
175<Project root>
176├── _minibuid_
177├── <Sub folder>
178|   ├── _minibuild_
179|   ├── <Sub folder>
180|   |   ├── _minibuild_
181|   |   ├── <Files>
182|   |   └── ...
183|   └── ...
184185├── <Sub folder>
186|   ├── _minibuild_
187|   └── ...
188└─── <Files>
189```
190
191
192
193### Built-in Functions
194
195* [`context()`](#context)
196* [`scope(Name, Callback)`](#scope)
197* [`subdir(TargetDir)`](#subdir)
198* [`vmount(TargetDir, SourceDir)`](#vmount)
199* [`file(FileName)`](#file)
200* [`meta(Name)`](#meta)
201* [`expr(Name)`](#expr)
202* [`include(File)`](#include)
203* [`execute(Command ...)`](#execute)
204* [`shell(Command ...)`](#shell)
205* ~[`mkdir(File)`](#mkdir)~
206* ~[`open(String)`](#open)~
207* [`print(Values ...)`](#print)
208* [`getenv(Key)`](#getenv)
209* [`setenv(Key, Value)`](#setenv)
210* [`defined(Key)`](#defined)
211
212#### `context()`
213
214Returns the name of the current context as a string.
215
216#### `scope(Name, Callback)`
217
218Creates a new child context (adding _"::Name"_ onto the end of the name of the current context).
219`Callback` is then executed within that context.
220This is useful for adjusting the build flags independantly for different targets within the same directory.
221
222#### `subdir(TargetDir)`
223
224Enters _`TargetDir`_ and loads the file `_minibuild_` within a new child context.
225
226#### `vmount(TargetDir, SourceDir)`
227
228Virtually mounts / overlays `SourceDir` over `TargetDir` during the build.
229This means that when a [file](#file-and-directory-targets) whose path contains `TargetDir` is referenced, the build system will look an existing file in two locations and return whichever exists, or return the unchanged path if neither exists.
230
231More specifically, in order to convert a file object with path _`TargetDir`/path/filename_ to a full file path, the build system will
232
2331.	return _`TargetDir`/path/filename_ if a file exists at this path
2342.	return _`SourceDir`/path/filename_ if a file exists at this path
2353.	return _`TargetDir`/path/filename_
236
237Multiple virtual mounts can be nested, and the build system will try each possible location for an existing file and return that path, returning the unchanged path if the file does not exist in any location.
238
239The typical use for this function is to overlay a source directory over the corresponding build output directory, so that build commands can be run in the output directory but reference source files as if they were in the same directory.
240
241#### `file(FileName)`
242
243Creates a [file target](#file-and-directory-targets).
244
245#### `meta(Name)`
246
247Creates a [meta target](#meta-targets).
248
249#### `expr(Name)`
250
251Creates an [expression target](#expression-targets).
252
253#### `include(File)`
254
255#### `execute(Command, ...)`
256
257#### `shell(Command, ...)`
258
259#### ~`mkdir(File | String)`~
260
261#### ~`open(File | String, String)`~
262
263#### Targets
264
265##### Methods
266
267* `Target[Dependencies...]`: adds dependcies to `Target`. `Dependencies` can be individual dependencies or lists of dependencies which are expanded recursively. Returns `Target`.
268* `Target => BuildFunction`: sets the build function for `Target`.
269* `Target:scan(Name)`: creates a [scan target](#scan-targets) for `Target`.
270
271#### File and Directory Targets
272
273##### Methods
274
275* `File:exists`: returns `File` if `File` exists, otherwise `nil`.
276* `File:open(Mode)`: opens `File` in read, write or append mode depending on `Mode` (`r`, `w` or `a`) and returns a [file handle](#file-handles).
277* `File:dir`: returns the directory containing `File`.
278* `File:basename`: returns the name of `File` without its path or extension.
279* `File % Extension`: returns a new file target by replacing the extension of `File` with `Extension`.
280* `File:copy(Dest)`: copies the contents of `File` to the file target at `Dest`.
281* `Dir:mkdir`: creates a directory (and all missing parent directories).
282* `Dir:ls([Filter])`: returns an iterator of files in `Dir`, filtered by an optional regular expression.
283* `Dir / FileName`: returns a new file target for the file called `FileName` in `Dir`.
284
285#### Scan Targets
286
287#### Meta Targets
288
289#### Expression Targets
290
291#### Symbol Targets
292
293### Other Builtin Features
294
295#### File Handles
296
297##### Methods
298
299* `Handle:read(...)`
300* `Handle:write(...)`
301* `Handle:close`
302