Builder combines a preprocessor with an expression language and advanced imports.
Directives start with the @
symbol.
@set <variable:identifier> <value:expression>
@set <variable:identifier> = <value:expression>
This directive assigns the value of an expression to a variable. Variables are defined in a global context.
Sets SOMEVAR to 1:
@set SOMEVAR min(1, 2, 3)
@macro <name>(<arguments>) <body> @endmacro
@endmacro
can be replaced with @end
.
This directive defines a code block that can take its own parameters. Macros are declared in a global scope. Macro parameters are only available within the macro scope and override global variables with the same name (but do not affect them). Macros can be used:
-
via the
@include
directive:@include macro(a, b, c)
-
inline:
@{macro(a, b, c)}
When macros are used inline:
- No line-control statements are generated for the output inside the macro scope.
- Trailing newlines are trimmed from the macro output.
@macro some_macro(a, b, c) Hello, @{a}! Roses are @{b}, And violets are @{defined(c) ? c : "of undefined color"}. @end
Then some_macro
can be used as:
@include some_macro("username", "red")
This will produce:
Hello, username!
Roses are red,
And violets are of undefined color.
Here is the same macro used inline:
[[[ @{some_macro("username", "red", "blue")} ]]]
This will output:
[[[ Hello, username!
Roses are red,
And violets are blue. ]]]
Use this directive to includes local files, external sources, or macros.
@include <source:expression>
@include some_macro("username", 123)
@include "somefile.ext"
@include "http://example.com/file.ext"
@include "https://example.com/file.ext"
Any text after any Builder expression statement, starting with //
and extending to the end of the line, will be ignored by Builder and will not appear in the result output.
@include "https://example.com/file.ext" // Need update to file2.ext
@include "github:<user>/<repo>/<path>[@<ref>]"
user
is the user/organization name.repo
is the repository name.ref
is the git reference (branch name or tag, defaults to master).
-
Head of the default branch
@include "github:electricimp/Promise/promise.class.nut"
-
Head of the develop branch
@include "github:electricimp/Promise/promise.class.nut@develop"
-
Tag v3.0.1:
@include "github:electricimp/Promise/[email protected]"
When using GitHub @includes
, authentication is optional. However, you should bear in mind that:
- If you use authentication, the GitHub API provides much higher rate limits.
- Authentication is required to access private repositories.
Apart from a GitHub username, you need to provide either a personal access token or password (which is less secure and not recommended). More information on how to provide those parameters is included in the usage section.
@include once <source:expression>
This acts the same as @include
but has no effect if source has already been included. Macros are always included.
@{<expression>}
@{macro(a, b, c)}
This directive inserts the value of the enclosed expression or executes a macro.
@set name "Someone" Hello, @{name}, the result is: @{123 * 456}.
This results in the following output:
Hello, Someone, the result is: 56088.
This invokes a while
loop. You can access the loop variable in @while
loops.
@while <test:expression> // 0-based iteration counter: @{loop.index} // 1-based iteration counter: @{loop.iteration} @endwhile
@endwhile
can be replaced with @end
.
This invokes a loop that repeats over a certain number of iterations. You can access the loop variable in @repeat
loops.
@repeat <times:expression> // 0-based iteration counter: @{loop.index} // 1-based iteration counter: @{loop.iteration} @endrepeat
@endrepeat
can be replaced with @end
.
@repeat 3 loop.iteration: @{loop.iteration} @end
This outputs:
loop.iteration: 1
loop.iteration: 2
loop.iteration: 3
This directive invokes conditional branching.
@if // Consequent code @elseif <test:expression> // else if #1 code // ...more elseifs... @else // Alternative code @endif
@endif
can be replaced with @end
.
@if __FILE__ == 'abc.ext' // include something @elseif __FILE__ == 'def.ext' // include something else @else // something completely different @endif
@error <message:expression>
Emits an error.
@if PLATFORM == "platform1" // platform 1 code @elseif PLATFORM == "platform2" // platform 2 code @elseif PLATFORM == "platform3" // platform 3 code @else @error "Platform is " + PLATFORM + " is unsupported" @endif
@warning <message:expression>
Emits a warning.
@if PLATFORM == "platform1" // platform 1 code @elseif PLATFORM == "platform2" // platform 2 code @elseif PLATFORM == "platform3" // platform 3 code @else @warning "Building for default platform" // default platform code @endif
The |
operator (filter) allows you to pass a value through any of the supported functions.
@{<expression> | <filter>}
This is equivalent to:
@{<filter>(<expression>)}
// Include external HTML to a string a = "@{include('index.html')|escape}" // Include external binary file to a base64-encoded string b = "@{include('file.bin')|base64}"
Directives that take parameters allow the usage of expression syntax. For example:
@include <source:expression>
@set <variable:identifier> <value:expression>
@if <condition:expression>
@elseif <condition:expression>
@{<expression>}
(inline expressions)
The following types are supported in expressions:
- numbers (eg.
1
,1E6
,1e-6
,1.567
) - strings (eg.
"abc"
,'abc'
) null
true
false
|| && == != < > <= >= + - * / %
+ - !
somevar.member
somevar["member"]
([1, 2, 3])[1]
test ? consequent : alternate
Variables can be used in Builder
expressions evaluation.
- Variables can be defined by
-D<variable name> <variable value>
command line parameter, read from the runtime environment, or defined by@set
statements. - Undefined variables are evaluated as
null
. - Variable names can contain
$
,_
, latin letters and digits. They must not start with a digit.
- When resolving a variable’s value, Builder first looks for its definition in the command line
-D
parameters (-D <variable name> <variable value>
) passed to the pleasebuild command. - If no such variable definition is found, Squirrel code is scanned for
@set
statements preceding the variable usage. - If no variable definitions are found in the previous steps, Builder looks for it in the host environment variables.
Line number (relative to the file in which this variable appears).
Example
Hi from line @{__LINE__}!
Name of the file in which this variable appears.
Example
Hi from file @{__FILE__}!
Absolute path (not including file name) to the file where this variable appears. Can contain a URL for remote includes.
Example
Hi from file @{__PATH__}!
Defined inside @while
and @repeat
loops. Contains information about the current loop:
loop.index
— 0-indexed iteration counterloop.iteration
— 1-indexed iteration counter
Example
@set myvar = 12 @while myvar > 9 @set myvar = myvar - 1 var: @{myvar} loop.index: @{loop.index} @end
This outputs:
myvar: 11
loop.index: 0
myvar: 10
loop.index: 1
myvar: 9
loop.index: 2
There is no special predicate required to make use of environment variables. Builder tries to resolve the macro from the context provided via the command line defines or from process environment variables. For example:
server.log("Host home path is @{HOME}");
will print the home directory path of the current user of the system where Builder was executed.
defined(<variable_name>)
— returnstrue
if a variable is defined,false
otherwise.include(<source>)
— includes external source.escape(<value>)
— escapes special characters in string (\b
,\f
,\n
,\r
,\t
,\
,'
,"
).base64(<value>)
— encodes value as base64.min(<numbers>)
max(<numbers>)
abs(<number>)
Lines starting with @
followed by space or a line break are treated as comments and not added to the output.
@ something about platform #1 @set PLATFORM "platform1"
Note Builder requires Node.js 4.0 and above.
-
It can be installed and used as an npm library:
npm i --save Builder
then
const builder = require('Builder'); // Provide GitHub credentials (optional) builder.machine.readers.github.username = "<username>"; builder.machine.readers.github.token = "<personal_access_token>"; // Set up cache params (optional) builder.machine.useCache = <boolean>; builder.machine.excludeList = "<path to exclude file>" // or "" for default name builder.machine.clearCache() // delete cache folder const output = builder.machine.execute(`@include "${inputFile}"`);
-
Or as a CLI:
Builder provides the
pleasebuild
command when installed globally. For example:npm i -g Builder pleasebuild [-D<variable> <value>...] [--github-user <username> --github-token <token>] [-l] [--cache] [--clear-cache] [--cache-exclude-list <path_to_file>] <input_file>
where:
-l
— generate line control statements.-D <variable> <value>
— define a variable.--github-user
— GitHub username.--github-token
— GitHub personal access token or password (not recommended).--cache
or-c
— enable cache for remote files.--clear-cache
— remove cache before builder starts running.--cache-exclude-list <path_to_file>
— path to exclude list file.
To reduce compilation time, Builder can optionally cache files included from a remote resource (GitHub or remote HTTP/HTTPs servers).
If this file cache is enabled, remote files are cached locally in the .builder-cache folder. Cached resources expire and are automatically invalidated 24 hours after their addition to the cache.
To turn the cache on, pass the --cache
or -c
option to Builder. If this option is not specified, Builder will not use the file cache even if the cached data exist and is valid — it will query remote resources on every execution.
To reset the cache use both the --cache
and the --clear-cache
options.
If a resource should never be cached, it needs to be added to the exclude-list.builder file. You can use wildcard characters to mask file names.
Pattern matching syntax is a similar to that of .gitignore. A string is a wildcard pattern if it contains '?
' or '*
' characters. Empty strings or strings that starts with '#
' are ignored.
A '?
' symbol matches any single character. For example, bo?t.js
matches boot.js
and boat.js
, but doesn't match bot.js
.
A '*
' matches any string, that is limited by slashes, including the empty string. For example, /foo/*ar
matches /foo/bar
, /foo/ar
and /foo/foo-bar
, but doesn't match /foo/get/bar
or /foo/bar/get
.
Two consecutive asterisks **
in patterns matched against full pathname may have special meaning:
-
A leading
**
followed by a slash means match in all directories. For example,**/foo
matches file or directoryfoo
anywhere, the same as patternfoo
.**/foo/bar
matches file or directorybar
anywhere that is directly under directoryfoo
. -
A trailing
/**
matches everything inside. For example,abc/**
matches all files inside directoryabc
. -
A slash followed by two consecutive asterisks then a slash matches zero or more directories. For example,
a/**/b
matchesa/b
,a/x/b
,a/x/y/b
and so on. -
Other consecutive asterisks are considered invalid.
# Avoid caching a specific file
github:electricimp/MessageManager/MessageManager.lib.nut
# Exclude all electricimp repos
github:electicimp/**
# Exclude all tagged files or files from the specific branches from the cache
github:*/**/*@*
Option Name | Short Version | Description |
---|---|---|
--cache |
-c |
Turns on file cache for all files included from remote resources |
--cache-exclude-list <path_to_file> |
Excludes the named file(s) from the cache | |
--clear-cache |
Clears the cache before Builder starts |
SPEC_LOGLEVEL=<debug|info|warning|error> \
SPEC_GITHUB_USERNAME=<GitHub username> \
SPEC_GITHUB_TOKEN=<GitHub password/access token> \
npm test
Builder is licensed under the MIT License.