mirror of https://github.com/natnat-mc/moonbuild
alfons-task
parent
ceada11b0b
commit
c8670ab903
@ -1,2 +1,2 @@ |
|||||||
/moonbuild/*.lua |
*.lua |
||||||
/bin/*.lua |
/out/* |
||||||
|
@ -1,11 +0,0 @@ |
|||||||
tasks: |
|
||||||
build: => |
|
||||||
sh "moon bin/moonbuild.moon compile" |
|
||||||
test: => |
|
||||||
sh "busted" |
|
||||||
install: => |
|
||||||
sh "moon bin/moonbuild.moon install" |
|
||||||
release: => |
|
||||||
error "no version provided" unless @v |
|
||||||
tasks.build! |
|
||||||
sh "rockbuild -m -t #{@v} upload" |
|
@ -1,34 +1,46 @@ |
|||||||
SOURCES_MOON = wildcard 'moonbuild/**.moon' |
public var 'MOONC', 'moonc' |
||||||
BINARY = 'bin/moonbuild.moon' |
public var 'AMALG', 'amalg.lua' |
||||||
OUT_LUA = patsubst SOURCES_MOON, '%.moon', '%.lua' |
public var 'RM', 'rm', '-f', '--' |
||||||
BINARY_LUA = patsubst BINARY, '%.moon', '%.lua' |
public var 'LUA', 'lua5.3' |
||||||
OUT_AMALG = 'moonbuild.lua' |
|
||||||
|
var 'LIB_SRC', _.wildcard 'moonbuild/**.moon' |
||||||
public target 'clean', fn: => |
var 'BIN_SRC', _.wildcard 'bin/*.moon' |
||||||
-rm '-f', OUT_LUA, BINARY_LUA |
|
||||||
|
var 'LIB_LUA', _.patsubst LIB_SRC, '%.moon', '%.lua' |
||||||
public target 'info', fn: => |
var 'BIN_LUA', _.patsubst BIN_SRC, '%.moon', '%.lua' |
||||||
#echo "Moonscript sources:", SOURCES_MOON |
var 'BIN', _.patsubst BIN_LUA, 'bin/%.lua', 'out/%' |
||||||
#echo "Compiled lua:", OUT_LUA |
|
||||||
|
var 'MODULES', _.foreach (_.patsubst LIB_LUA, '%.lua', '%'), => @gsub '/', '.' |
||||||
public target 'compile', deps: OUT_AMALG |
|
||||||
|
with public default target 'all' |
||||||
public target 'install', from: OUT_AMALG, out: '/usr/local/bin/moonbuild', fn: => |
\after 'bin' |
||||||
dfd, err = io.open @outfile, 'w' |
\after 'lib' |
||||||
error err unless dfd |
|
||||||
ifd, err = io.open @infile, 'r' |
with public target 'clean' |
||||||
error err unless ifd |
\fn => _.cmd RM, LIB_LUA |
||||||
dfd\write '#!/usr/bin/env lua5.3\n' |
\fn => _.cmd RM, BIN_LUA |
||||||
for line in ifd\lines! |
|
||||||
dfd\write line, '\n' |
with public target 'mrproper' |
||||||
ifd\close! |
\after 'clean' |
||||||
dfd\close! |
\fn => _.cmd RM, BIN |
||||||
-chmod '+x', @outfile |
|
||||||
#echo "Installed at:", @outfile |
with public target 'bin' |
||||||
|
\depends BIN |
||||||
default target OUT_AMALG, from: {BINARY_LUA, OUT_LUA}, out: OUT_AMALG, fn: => |
|
||||||
modules = foreach (patsubst OUT_LUA, '%.lua', '%'), => @gsub '/', '.' |
with public target 'lib' |
||||||
-Command 'amalg.lua', '-o', @outfile, '-s', 'bin/moonbuild.lua', modules |
\depends LIB_LUA |
||||||
|
|
||||||
target '%.lua', in: '%.moon', out: '%.lua', fn: => |
with target BIN, pattern: 'out/%' |
||||||
-moonc @infile |
\depends 'bin/%.lua' |
||||||
|
\depends LIB_LUA |
||||||
|
\produces 'out/%' |
||||||
|
\mkdirs! |
||||||
|
\fn => |
||||||
|
_.cmd AMALG, '-o', @outfile, '-s', @infile, MODULES |
||||||
|
_.writefile @outfile, "#!/usr/bin/env #{LUA}\n#{_.readfile @outfile}" |
||||||
|
_.cmd 'chmod', '+x', @outfile |
||||||
|
|
||||||
|
with target {LIB_LUA, BIN_LUA}, pattern: '%.lua' |
||||||
|
\depends '%.moon' |
||||||
|
\produces '%.lua' |
||||||
|
\fn => _.moonc @infile, @outfile |
||||||
|
@ -0,0 +1,39 @@ |
|||||||
|
.PHONY: all clean mrproper bin lib |
||||||
|
|
||||||
|
MOONC = moonc
|
||||||
|
AMALG = amalg.lua
|
||||||
|
RM = rm -f --
|
||||||
|
LUA = lua5.3
|
||||||
|
|
||||||
|
LIB_SRC = $(wildcard moonbuild/*.moon) $(wildcard moonbuild/*/*.moon) $(wildcard moonbuild/*/*/*.moon)
|
||||||
|
BIN_SRC = $(wildcard bin/*.moon)
|
||||||
|
|
||||||
|
LIB_LUA = $(foreach moon, $(LIB_SRC), $(patsubst %.moon, %.lua, $(moon)))
|
||||||
|
BIN_LUA = $(foreach moon, $(BIN_SRC), $(patsubst %.moon, %.lua, $(moon)))
|
||||||
|
BIN = $(foreach lua, $(BIN_LUA), $(patsubst bin/%.lua, out/%, $(lua)))
|
||||||
|
|
||||||
|
MODULES = $(shell echo $(foreach lib, $(LIB_LUA), $(patsubst %.lua, %, $(lib))) | sed 's|/|.|g')
|
||||||
|
|
||||||
|
all: bin lib |
||||||
|
|
||||||
|
clean: |
||||||
|
$(RM) $(LIB_LUA)
|
||||||
|
$(RM) $(BIN_LUA)
|
||||||
|
|
||||||
|
mrproper: clean |
||||||
|
$(RM) $(BIN)
|
||||||
|
|
||||||
|
bin: $(BIN) |
||||||
|
|
||||||
|
lib: $(LIB_LUA) |
||||||
|
|
||||||
|
out/%: bin/%.lua $(LIB_LUA) |
||||||
|
@mkdir -p `dirname $@`
|
||||||
|
$(AMALG) -o $@.body -s $< $(MODULES)
|
||||||
|
@printf '#!/usr/bin/env %s\n' $(LUA) > $@.headline
|
||||||
|
@cat $@.headline $@.body > $@
|
||||||
|
@rm $@.headline $@.body
|
||||||
|
chmod +x $@
|
||||||
|
|
||||||
|
%.lua: %.moon |
||||||
|
moonc $^
|
@ -1,118 +1,34 @@ |
|||||||
# Moonbuild |
# moonbuild |
||||||
Because `make` is painful to use, and build scripts are too slow. Moonbuild aims to be a good compromise. |
**Now in v2 - complete rework from v1** |
||||||
|
|
||||||
You should probably use [`tup`](http://gittup.org/tup/) instead if you want a good build system. |
Because `make` is painful to use, and build scripts are too slow. Moonbuild aims to be a good compromise. |
||||||
|
You should probably use [http://gittup.org/tup/](tup) instead if you want a good build system. |
||||||
|
|
||||||
## How does it work? |
## How does it work? |
||||||
Basically like `make`, but in [Moonscript](https://moonscript.org) and with explicit ordering. See its `Build.moon` for examples (and you can compare it with the `Makefile`, both do the same thing). |
`moonbuild` reads a build file, usually `Build.moon` that is written in a Moonscript DSL to define its targets and variables. |
||||||
|
It then builds a DAG for the dependancies of the targets you tell it to build. |
||||||
## Why moonscript? |
Then, it tries building every target in the graph while respecting dependancies, possibly on multiple processes. |
||||||
It's fast, based on lua, and it's easy to write DSLs with it, so the build instructions can be readable. Also, it's a full programming language, so there are no arbitrary restrictions. |
|
||||||
|
|
||||||
## How do I install it? |
|
||||||
Any of these will work |
|
||||||
- `luarocks install moonbuild` (probably as root) |
|
||||||
- `moon bin/moonbuild.moon install` (probably as root, in the cloned repo) |
|
||||||
- `alfons install` (probably as root, in the cloned repo, requires alfons) |
|
||||||
|
|
||||||
## Now, how do I use it? |
|
||||||
First, you'll need a `Build.moon`, `Buildfile.moon`, `Build` or `Buildfile` in the root of your project. |
|
||||||
Then, you'll need a few `target`s, and ideally a `default target` (or the default target will be `all`). `public target`s will be listed by `moonbuild -l`. |
|
||||||
To execute a command, you can use either `-cmd` or `#cmd` (the former will print it before executing it, the later won't). |
|
||||||
|
|
||||||
### `[default] [public] target <name> [deps: <deps>] [in: <inputs>] [out: <outputs>] [from: <from>] [fn: <code>]` |
|
||||||
Define a new target, and give it a list of depenancies, inputs, outputs and a function to run to build it. |
|
||||||
|
|
||||||
`deps`, `in` and `out` can be either strings or tables. `from` acts like both `in` and `deps`. `name` must be a string and `code` must be a function, that will be given a table with the following fields: |
|
||||||
- `name`: the name of the target |
|
||||||
- `ins`: the table of inputs |
|
||||||
- `infile`: the first input |
|
||||||
- `outs`: the table of outputs |
|
||||||
- `outfile`: the first output |
|
||||||
|
|
||||||
If `name` is a glob, the target becomes a glob target. |
|
||||||
Glob targets can be used with name that matches them (with a limit of one glob target per name, and no ordering is specified). |
|
||||||
Glob targets will have their name substituted for their inputs, outputs and dependancies. |
|
||||||
|
|
||||||
### `-cmd [<args>...]` |
|
||||||
Prints and executes the command `cmd` with the given args. See `run` for how `args` works. |
|
||||||
|
|
||||||
### `#cmd [<args>...]` |
|
||||||
Executes without printing the command `cmd` with the given args. See `run` for how `args` works. |
|
||||||
|
|
||||||
### `wildcard <wc>` |
|
||||||
Returns a table with all the matching files. Valid wildcards contain `**`, which can be expanded by any characters, including '/', and `*`, which cannot be expanded by `/`. |
|
||||||
|
|
||||||
`wc` must be a string |
|
||||||
|
|
||||||
### `exclude <list> [<exclusions>...]` |
|
||||||
Removes all exclusions from the given list, and returns it. |
|
||||||
|
|
||||||
`list` must be a table, and `exclusions` can be any type |
|
||||||
|
|
||||||
### `patsubst <str> <patt> <subst>` |
|
||||||
If the string matches `patt`, makes it match `subst` instead. If `str` is a table, it is recursively applied to all array values. |
|
||||||
|
|
||||||
Patterns are in the format `[prefix]%[suffix]`, with the `%` representing any sequence of characters, including `/`. |
|
||||||
|
|
||||||
`str`, `pat` and `subst` must be strings |
|
||||||
|
|
||||||
### `foreach <table> <code>` |
|
||||||
Applies `code` to every element of `table`, and returns the resulting table. |
|
||||||
|
|
||||||
`table` must be a table, and `code` a function. |
|
||||||
|
|
||||||
### `min|max <table>` |
|
||||||
Returns either the min or max value of the given table. |
|
||||||
|
|
||||||
`table` must be a table |
|
||||||
|
|
||||||
### `first <table> <code>` |
|
||||||
Returns the first value of the table that verifies the given condition. |
|
||||||
|
|
||||||
`table` must be a table, and `code` a function |
|
||||||
|
|
||||||
### `flatten <table>` |
|
||||||
Flattens a table so that it has exactly one dimension. |
|
||||||
|
|
||||||
`table` can be anything |
|
||||||
|
|
||||||
### `insert|unpack|concat` |
|
||||||
The functions, imported from the `table` library. |
|
||||||
|
|
||||||
### `mtime <file>` |
|
||||||
Returns the modification time of `file`, or `nil` if it doesn't exist. |
|
||||||
|
|
||||||
`file` must be a string |
|
||||||
|
|
||||||
### `exists <file>` |
|
||||||
Returns `true` if the file exists, `false` otherwise. |
|
||||||
|
|
||||||
`file` must be a string |
|
||||||
|
|
||||||
|
|
||||||
### `isdir <file>` |
|
||||||
Returns `true` if the file exists and is a directory, `false` otherwise. |
|
||||||
|
|
||||||
`file` must be a string |
|
||||||
|
|
||||||
### `run <cmd> [<args> [print: <print>] [error: <error>]]` |
|
||||||
Runs the given command with the given arguments. If `print` is truthy, prints the command before executing it, and if `error` is truthy, crashes if the command fails. Returns a boolean which is true if the command ended with success, and a number which is the return code. |
|
||||||
|
|
||||||
`cmd` must be a string, `args` must be a table, which can contain either strings or other tables. `raw: <val>` is a special kind of argument that will not be escaped. `print` and `error` can be anything, but booleans or nil are recommended |
Essentially, it works the same way as `make`, just with a different language, you can compare the `Build.moon` and `Makefile` in this repo to see for yourself. |
||||||
|
|
||||||
### `popen <cmd> [<args> [print: <print>]]` |
## Why Moonscript? |
||||||
Same as `run`, but returns a `io.popen` handle. |
Because it's fast, based on lua, and making DSLs with it is relatively easy. |
||||||
|
It's also a language I like a lot, so I might have been biased when choosing it. |
||||||
|
|
||||||
### `calccdeps <infile> [includesys]` |
## Installing |
||||||
Computes the dependancies required to compile a C source file. Optionally include system libraries as sources. |
It is available on luarocks with `luarocks install moonbuild`. |
||||||
|
It is also recommended to install `luaposix` if you can, as it speeds it up a lot, or `luafilesystem` in case it isn't available. |
||||||
|
|
||||||
`infile` must be a string and `includesys` must be a boolean or `nil` |
## Building from source |
||||||
|
You will need `argparse` and `moonscript` installed from luarocks, and `luaposix` or `luafilesystem` are recommended. |
||||||
|
|
||||||
### `findclib <lib> [what]` |
### Bootstrapping |
||||||
Finds the compiler or linker flags required to use a given C library. |
You can build moonbuild with itself: `moon bin/moonbuild.moon -qjy`. |
||||||
|
This will leave the binary ready to be used as `out/moonbuild`. |
||||||
|
|
||||||
`lib` must be a string and `what` must be either `nil`, `'all'`, `'cc'` or `'ld'` |
### Using make |
||||||
|
You can also build moonbuild with make: `make`. |
||||||
|
This will leave the binary ready to be used as `out/moonbuild`. |
||||||
|
|
||||||
## License |
## Docs |
||||||
MIT |
TODO |
||||||
|
@ -1,210 +1,88 @@ |
|||||||
#!/usr/bin/env moon |
-- load everything we need |
||||||
|
|
||||||
argparse = require 'argparse' |
|
||||||
|
|
||||||
require 'moonscript' |
|
||||||
import loadfile from require 'moonscript.base' |
import loadfile from require 'moonscript.base' |
||||||
import truncate_traceback, rewrite_traceback from require 'moonscript.errors' |
Context = require 'moonbuild.context' |
||||||
import trim from require 'moonscript.util' |
Variable = require 'moonbuild.core.Variable' |
||||||
|
DepGraph = require 'moonbuild.core.DAG' |
||||||
util = require 'moonbuild.util' |
import parseargs from require 'moonbuild._cmd.common' |
||||||
import freezecache, invalidatecache from require 'moonbuild.fsutil' |
import sort, concat from table |
||||||
import exists, mtime, run, min, max, first, flatten, match, patsubst, sortedpairs from util |
import exit from os |
||||||
|
|
||||||
import insert, concat from table |
-- parse the arguments |
||||||
|
argparse = require 'argparse' |
||||||
|
parser = with argparse "moonbuild", "A build system in moonscript" |
||||||
|
\option '-b --buildfile', "Build file to use", 'Build.moon' |
||||||
|
\option '-j --parallel', "Sets the number of parallel tasks, 'y' to run as many as we have cores", '1' |
||||||
|
\flag '-l --list', "List the targets", false |
||||||
|
\flag '-V --list-variables', "List the variables", false |
||||||
|
\flag '-q --quiet', "Don't print targets as they are being built", false |
||||||
|
\flag '-f --force', "Always rebuild every target", false |
||||||
|
\flag '-v --verbose', "Be verbose", false |
||||||
|
(\option '-u --unset', "Unsets a variable")\count '*' |
||||||
|
(\option '-s --set', "Sets a variable")\args(2)\count '*' |
||||||
|
(\option '-S --set-list', "Sets a variable to a list")\args(2)\count '*' |
||||||
|
(\argument 'targets', "Targets to build")\args '*' |
||||||
|
\add_complete! |
||||||
|
|
||||||
parser = argparse 'moonbuild' |
|
||||||
parser\argument('targets', "Targets to run")\args '*' |
|
||||||
parser\flag '-a --noskip', "Always run targets" |
|
||||||
parser\flag '-l --list', "List available targets" |
|
||||||
parser\flag '-d --deps', "List targets and their dependancies" |
|
||||||
args = parser\parse! |
args = parser\parse! |
||||||
|
|
||||||
-- util functions |
overrides = {} |
||||||
loadwithscope = (file, scope) -> |
for unset in *args.unset |
||||||
fn, err = loadfile file |
overrides[unset] = Variable.NIL |
||||||
error err or "failed to load code" unless fn |
for set in *args.set |
||||||
dumped, err = string.dump fn |
overrides[set[1]] = set[2] |
||||||
error err or "failed to dump function" unless dumped |
for set in *args.set_list |
||||||
load dumped, file, 'b', scope |
overrides[set[1]] = parseargs set[2] |
||||||
pcall = (fn, ...) -> |
|
||||||
rewrite = (err) -> |
|
||||||
trace = debug.traceback '', 2 |
|
||||||
trunc = truncate_traceback trim trace |
|
||||||
(rewrite_traceback trunc, err) or trace |
|
||||||
xpcall fn, rewrite, ... |
|
||||||
|
|
||||||
-- command object |
|
||||||
-- represents a command that can be called |
|
||||||
class Command |
|
||||||
new: (@cmd, ...) => |
|
||||||
@args = {...} |
|
||||||
|
|
||||||
__unm: => @run error: true, print: true |
|
||||||
__len: => @run error: true |
|
||||||
__tostring: => @cmd |
|
||||||
|
|
||||||
run: (params) => run @cmd, @args, params |
|
||||||
@run: (...) => -@ ... |
|
||||||
|
|
||||||
-- build object |
args.parallel = args.parallel == 'y' and 'y' or ((tonumber args.parallel) or error "Invalid argument for -j: #{args.parallel}") |
||||||
-- represents a target |
error "Invalid argument for -j: #{args.parallel}" if args.parallel != 'y' and (args.parallel<1 or args.parallel%1 != 0) |
||||||
class BuildObject |
print "Parsed CLI args" if args.verbose |
||||||
all = {} |
|
||||||
skip = {} |
|
||||||
|
|
||||||
@find: (name) => |
|
||||||
target = all[name] |
|
||||||
return target if target |
|
||||||
for glob, tgt in pairs all |
|
||||||
return tgt if match name, glob |
|
||||||
nil |
|
||||||
|
|
||||||
@list: => |
|
||||||
{target, {dep, @find dep for dep in *target.deps} for name, target in pairs all} |
|
||||||
|
|
||||||
@build: (name, upper) => |
|
||||||
target = (@find name) or error "No such target: #{name}" |
|
||||||
target\build name, upper |
|
||||||
|
|
||||||
__tostring: => |
|
||||||
"Target #{@name} (#{concat @deps, ', '})" |
|
||||||
|
|
||||||
new: (@name, @outs={}, @ins={}, @deps={}, @fn= =>) => |
|
||||||
@skip = false |
|
||||||
error "Duplicate build name #{@name}" if all[@name] |
|
||||||
all[@name] = @ |
|
||||||
|
|
||||||
build: (name, upper={}) => |
|
||||||
return if skip[name] |
|
||||||
error "Cycle detected on #{@name}" if upper[@] |
|
||||||
upper = setmetatable {[@]: true}, __index: upper |
|
||||||
if @name!=name |
|
||||||
@@build (patsubst name, @name, dep), upper for dep in *@deps |
|
||||||
else |
|
||||||
@@build dep, upper for dep in *@deps |
|
||||||
return unless @shouldbuild name |
|
||||||
|
|
||||||
ins = @ins |
|
||||||
outs = @outs |
|
||||||
if @name!=name |
|
||||||
ins = [patsubst name, @name, elem for elem in *@ins] |
|
||||||
outs = [patsubst name, @name, elem for elem in *@outs] |
|
||||||
print "Building #{@name} as #{name}" |
|
||||||
else |
|
||||||
print "Building #{name}" |
|
||||||
freezecache file for file in *outs |
|
||||||
ok, err = pcall -> |
|
||||||
@.fn |
|
||||||
ins: ins |
|
||||||
outs: outs |
|
||||||
infile: ins[1] |
|
||||||
outfile: outs[1] |
|
||||||
name: name |
|
||||||
invalidatecache file for file in *outs |
|
||||||
error "Can't build #{@name}: lua error\n#{err}" unless ok |
|
||||||
for f in *outs |
|
||||||
error "Can't build #{@name}: output file #{f} not created" unless exists f |
|
||||||
skip[name] = true |
|
||||||
|
|
||||||
shouldbuild: (name) => |
-- load the buildfile |
||||||
return true if args.noskip |
ctx = Context! |
||||||
return true if #@ins==0 or #@outs==0 |
ctx\load (loadfile args.buildfile), overrides |
||||||
|
print "Loaded buildfile" if args.verbose |
||||||
ins = if @name!=name |
|
||||||
[patsubst name, @name, elem for elem in *@ins] |
|
||||||
else |
|
||||||
@ins |
|
||||||
itimes = [mtime f for f in *ins] |
|
||||||
for i=1, #@ins |
|
||||||
error "Can't build #{@name}: missing inputs" unless itimes[i] |
|
||||||
|
|
||||||
outs = if @name!=name |
|
||||||
[patsubst name, @name, elem for elem in *@outs] |
|
||||||
else |
|
||||||
@outs |
|
||||||
otimes = [mtime f for f in *outs] |
|
||||||
for i=1, #@outs |
|
||||||
return true if not otimes[i] |
|
||||||
|
|
||||||
(max itimes)>(min otimes) |
|
||||||
|
|
||||||
error "Need Lua >=5.2" if setfenv |
|
||||||
|
|
||||||
local targets, defaulttarget |
|
||||||
|
|
||||||
buildscope = |
|
||||||
default: (target) -> |
|
||||||
defaulttarget=target.name |
|
||||||
target |
|
||||||
public: (target) -> |
|
||||||
insert targets, target.name |
|
||||||
target |
|
||||||
target: (name, params) -> |
|
||||||
tout = flatten params.out |
|
||||||
tin = flatten params.in |
|
||||||
tdeps = flatten params.deps |
|
||||||
for f in *flatten params.from |
|
||||||
insert tin, f |
|
||||||
insert tdeps, f |
|
||||||
BuildObject name, tout, tin, tdeps, params.fn |
|
||||||
:Command |
|
||||||
buildscope[k] = fn for k, fn in pairs util |
|
||||||
|
|
||||||
setmetatable buildscope, |
|
||||||
__index: (k) => |
|
||||||
global = rawget _G, k |
|
||||||
return global if global |
|
||||||
(...) -> Command k, ... |
|
||||||
|
|
||||||
loadtargets = -> |
|
||||||
targets = {} |
|
||||||
defaulttarget = 'all' |
|
||||||
file = first {'Build.moon', 'Buildfile.moon', 'Build', 'Buildfile'}, exists |
|
||||||
error "No Build.moon or Buildfile found" unless file |
|
||||||
buildfn = loadwithscope file, buildscope |
|
||||||
error "Failed to load build function" unless buildfn |
|
||||||
buildfn! |
|
||||||
|
|
||||||
buildtargets = -> |
|
||||||
if #args.targets==0 |
|
||||||
BuildObject\build defaulttarget |
|
||||||
for target in *args.targets |
|
||||||
BuildObject\build target |
|
||||||
|
|
||||||
ok, err = pcall loadtargets |
|
||||||
unless ok |
|
||||||
if err |
|
||||||
io.stderr\write "Error while loading build file: ", err, '\n' |
|
||||||
else |
|
||||||
io.stderr\write "Unknown error\n" |
|
||||||
os.exit 1 |
|
||||||
|
|
||||||
|
-- handle -l and -V |
||||||
if args.list |
if args.list |
||||||
io.write "Available targets:\n" |
print "Public targets" |
||||||
io.write "\t#{concat targets, ', '}\n" |
targets, n = {}, 1 |
||||||
os.exit 0 |
for t in *ctx.targets |
||||||
|
if t.public |
||||||
if args.deps |
targets[n], n = t.name, n+1 |
||||||
io.write "Targets:\n" |
sort targets |
||||||
for target, deps in sortedpairs BuildObject\list!, (a, b) -> a.name<b.name |
print concat targets, ", " |
||||||
io.write "\t#{target.name} " |
print! |
||||||
if #target.ins==0 |
exit 0 unless args.list_variables |
||||||
if #target.outs==0 |
if args.list_variables |
||||||
io.write "[no in/out]" |
print "Public variables" |
||||||
else |
vars, n = {}, 1 |
||||||
io.write "[spontaneous generation]" |
for k, v in pairs ctx.variables |
||||||
|
if v.public |
||||||
|
vars[n], n = k, n+1 |
||||||
|
sort vars |
||||||
|
print concat vars, ", " |
||||||
|
print! |
||||||
|
exit 0 |
||||||
|
|
||||||
|
-- initialize the buildfile further |
||||||
|
ctx\init! |
||||||
|
print "Initialized buildfile" if args.verbose |
||||||
|
|
||||||
|
-- create the DAG |
||||||
|
targets = #args.targets==0 and ctx.defaulttargets or args.targets |
||||||
|
dag = DepGraph ctx, targets |
||||||
|
print "Created dependancy graph" if args.verbose |
||||||
|
|
||||||
|
-- execute the build |
||||||
|
if args.parallel==1 |
||||||
|
Executor = require 'moonbuild.core.singleprocessexecutor' |
||||||
|
executor = Executor dag, args.parallel |
||||||
|
executor\execute args |
||||||
else |
else |
||||||
if #target.outs==0 |
ok, Executor = pcall -> require 'moonbuild.core.multiprocessexecutor' |
||||||
io.write "[consumer]" |
Executor = require 'moonbuild.core.singleprocessexecutor' unless ok |
||||||
else |
nparallel = args.parallel == 'y' and Executor\getmaxparallel! or args.parallel |
||||||
io.write "(#{concat target.ins, ', '} -> #{concat target.outs, ', '})" |
print "Building with #{nparallel} max parallel process#{nparallel>1 and "es" or ""}" if args.verbose |
||||||
io.write "\n" |
executor = Executor dag, nparallel |
||||||
for name, dep in sortedpairs deps |
executor\execute args |
||||||
io.write "\t\t#{name}" |
print "Finished" if args.verbose |
||||||
if name!=dep.name |
|
||||||
io.write " (#{dep.name})" |
|
||||||
io.write "\n" |
|
||||||
os.exit 0 |
|
||||||
|
|
||||||
buildtargets! |
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,15 @@ |
|||||||
|
import gmatch, match, gsub from string |
||||||
|
import insert, remove, concat, sub, sort from table |
||||||
|
|
||||||
|
_fs = require 'moonbuild._fs' |
||||||
|
_cmd = require 'moonbuild._cmd' |
||||||
|
_util = require 'moonbuild._util' |
||||||
|
_common = require 'moonbuild._common' |
||||||
|
|
||||||
|
_ = {} |
||||||
|
|
||||||
|
for k, lib in pairs {:_fs, :_cmd, :_util, :_common} |
||||||
|
_[k] = lib |
||||||
|
for n in *lib! |
||||||
|
_[n] = lib[n] |
||||||
|
_ |
@ -0,0 +1,21 @@ |
|||||||
|
import parseargs, escape from require 'moonbuild._cmd.common' |
||||||
|
|
||||||
|
ok, cmd, backend = false, nil, nil |
||||||
|
unless ok |
||||||
|
ok, cmd = pcall -> require 'moonbuild._cmd.posix' |
||||||
|
backend = 'posix' |
||||||
|
unless ok |
||||||
|
ok, cmd = pcall -> require 'moonbuild._cmd.lua' |
||||||
|
backend = 'lua' |
||||||
|
error "unable to load any cmd library, tried luaposix and posix commands" unless ok |
||||||
|
|
||||||
|
-- from the backend |
||||||
|
cmd = {k, v for k, v in pairs cmd} |
||||||
|
cmd.backend = backend |
||||||
|
|
||||||
|
-- common cmd function |
||||||
|
cmd.parseargs = parseargs |
||||||
|
cmd.escape = escape |
||||||
|
|
||||||
|
-- the library itself |
||||||
|
setmetatable cmd, __call: => {'cmd', 'cmdrst', 'sh'} |
@ -0,0 +1,121 @@ |
|||||||
|
import gsub, sub, match from string |
||||||
|
import concat from table |
||||||
|
|
||||||
|
specialchars = |
||||||
|
'\"': '\\\"' |
||||||
|
'\\': '\\\\' |
||||||
|
'\'': '\\\'' |
||||||
|
'\n': '\\n' |
||||||
|
'\r': '\\r' |
||||||
|
'\t': '\\t' |
||||||
|
|
||||||
|
replacespecialchar = (c) -> specialchars[c] or c |
||||||
|
|
||||||
|
escape = (arg) -> |
||||||
|
return arg if match arg, "^[a-zA-Z0-9_.-]+$" |
||||||
|
'"'..(gsub arg, "([\"\\\n\r\t])", replacespecialchar)..'"' |
||||||
|
|
||||||
|
parseargs = (argstr) -> |
||||||
|
state = 'normal' |
||||||
|
current, ci = {}, 1 |
||||||
|
args, ai = {}, 1 |
||||||
|
c = nil |
||||||
|
i = 0 |
||||||
|
running = true |
||||||
|
|
||||||
|
add = -> |
||||||
|
current[ci], ci = c, ci+1 |
||||||
|
push = -> |
||||||
|
args[ai], ai, current, ci = (concat current), ai+1, {}, 1 if ci!=1 |
||||||
|
addv = (v) -> |
||||||
|
current[ci], ci = v, ci+1 |
||||||
|
fail = (msg) -> |
||||||
|
error "failed to parse: #{msg} in state #{state} at pos #{i}", 2 |
||||||
|
finish = -> |
||||||
|
running = false |
||||||
|
EOF = '' |
||||||
|
|
||||||
|
while running |
||||||
|
i += 1 |
||||||
|
c = sub argstr, i, i |
||||||
|
|
||||||
|
switch state |
||||||
|
when 'normal' |
||||||
|
switch c |
||||||
|
when '\"' |
||||||
|
state = 'doublequote' |
||||||
|
when '\'' |
||||||
|
state = 'singlequote' |
||||||
|
when ' ' |
||||||
|
push! |
||||||
|
when '\n' |
||||||
|
push! |
||||||
|
when '\t' |
||||||
|
push! |
||||||
|
when '\\' |
||||||
|
state = 'backslashnormal' |
||||||
|
when EOF |
||||||
|
push! |
||||||
|
finish! |
||||||
|
else |
||||||
|
add! |
||||||
|
|
||||||
|
when 'doublequote' |
||||||
|
switch c |
||||||
|
when '\"' |
||||||
|
state = 'normal' |
||||||
|
when '\\' |
||||||
|
state = 'backslashdoublequote' |
||||||
|
when EOF |
||||||
|
fail "unexpected EOF" |
||||||
|
else |
||||||
|
add! |
||||||
|
|
||||||
|
when 'singlequote' |
||||||
|
switch c |
||||||
|
when '\'' |
||||||
|
state = 'normal' |
||||||
|
when EOF |
||||||
|
fail "unexpected EOF" |
||||||
|
else |
||||||
|
add! |
||||||
|
|
||||||
|
when 'backslashnormal' |
||||||
|
switch c |
||||||
|
when '\n' |
||||||
|
state = 'normal' |
||||||
|
when EOF |
||||||
|
fail "unexpected EOF" |
||||||
|
else |
||||||
|
add! |
||||||
|
state = 'normal' |
||||||
|
|
||||||
|
when 'backslashdoublequote' |
||||||
|
switch c |
||||||
|
when '$' |
||||||
|
add! |
||||||
|
state = 'doublequote' |
||||||
|
when '`' |
||||||
|
add! |
||||||
|
state = 'doublequote' |
||||||
|
when '\"' |
||||||
|
add! |
||||||
|
state = 'doublequote' |
||||||
|
when '\\' |
||||||
|
add! |
||||||
|
state = 'doublequote' |
||||||
|
when '\n' |
||||||
|
state = 'doublequote' |
||||||
|
when EOF |
||||||
|
fail "unexpected EOF" |
||||||
|
else |
||||||
|
addv '\\' |
||||||
|
add! |
||||||
|
state = 'doublequote' |
||||||
|
|
||||||
|
args |
||||||
|
|
||||||
|
{ |
||||||
|
:escape |
||||||
|
:parseargs |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
import escape from require 'moonbuild._cmd.common' |
||||||
|
import flatten from require 'moonbuild._common' |
||||||
|
import execute from require 'moonbuild.compat.execute' |
||||||
|
import popen from io |
||||||
|
import concat from table |
||||||
|
|
||||||
|
cmdline = (...) -> |
||||||
|
concat [escape arg for arg in *flatten ...], ' ' |
||||||
|
|
||||||
|
cmd = (...) -> |
||||||
|
ok, ret, code = execute cmdline ... |
||||||
|
error "command #{first ...} exited with #{code} (#{ret})" unless ok |
||||||
|
|
||||||
|
cmdrst = (...) -> |
||||||
|
fd, err = popen cmdline ... |
||||||
|
error err unless fd |
||||||
|
data = fd\read '*a' |
||||||
|
fd\close! |
||||||
|
data |
||||||
|
|
||||||
|
sh = (cli) -> |
||||||
|
ok, ret, code = execute cli |
||||||
|
error "command '#{cli}' exited with #{code} (#{ret})" unless ok |
||||||
|
|
||||||
|
{ |
||||||
|
:cmd |
||||||
|
:cmdrst |
||||||
|
:sh |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
import spawn from require 'posix' |
||||||
|
import fork, execp, pipe, dup2, _exit, close from require 'posix.unistd' |
||||||
|
import fdopen from require 'posix.stdio' |
||||||
|
import wait from require 'posix.sys.wait' |
||||||
|
import flatten, first from require 'moonbuild._common' |
||||||
|
import remove from table |
||||||
|
|
||||||
|
cmd = (...) -> |
||||||
|
code, ty = spawn flatten ... |
||||||
|
error "command #{first ...} #{ty} with code #{code}" if ty!='exited' or code!=0 |
||||||
|
|
||||||
|
cmdrst = (...) -> |
||||||
|
rd, wr = pipe! |
||||||
|
pid, err = fork! |
||||||
|
|
||||||
|
if pid == 0 |
||||||
|
dup2 wr, 1 |
||||||
|
close rd |
||||||
|
args = flatten ... |
||||||
|
c = remove args, 1 |
||||||
|
execp c, args |
||||||
|
return _exit 1 |
||||||
|
|
||||||
|
if pid == nil |
||||||
|
close rd |
||||||
|
close wr |
||||||
|
error "command #{first ...} failed to start: couldn't fork(): #{err}" |
||||||
|
|
||||||
|
close wr |
||||||
|
fd = fdopen rd, 'r' |
||||||
|
data = fd\read '*a' |
||||||
|
fd\close! |
||||||
|
close rd |
||||||
|
|
||||||
|
_, ty, code = wait pid |
||||||
|
error "command #{first ...} #{ty} with code #{code}" if ty!='exited' or code!=0 |
||||||
|
data |
||||||
|
|
||||||
|
sh = (cli) -> |
||||||
|
cmd 'sh', '-c', cli |
||||||
|
|
||||||
|
{ |
||||||
|
:cmd |
||||||
|
:cmdrst |
||||||
|
:sh |
||||||
|
} |
@ -0,0 +1,162 @@ |
|||||||
|
import sort, concat from table |
||||||
|
import huge from math |
||||||
|
import match, sub from string |
||||||
|
|
||||||
|
common = {} |
||||||
|
|
||||||
|
flatten = (list, ...) -> |
||||||
|
return flatten {list, ...} if (select '#', ...)!=0 |
||||||
|
t = type list |
||||||
|
switch t |
||||||
|
when 'nil' |
||||||
|
{} |
||||||
|
when 'string' |
||||||
|
{list} |
||||||
|
when 'number' |
||||||
|
{tostring list} |
||||||
|
when 'boolean' |
||||||
|
{list} |
||||||
|
when 'table' |
||||||
|
keys = [k for k in pairs list] |
||||||
|
sort keys |
||||||
|
elements, i = {}, 1 |
||||||
|
for k in *keys |
||||||
|
if (type k)=='number' |
||||||
|
for e in *(flatten list[k]) |
||||||
|
elements[i], i = e, i+1 |
||||||
|
else |
||||||
|
return {list} |
||||||
|
setmetatable elements, __tostring: => concat @, ' ' |
||||||
|
else |
||||||
|
error "can't flatten elements of type #{t}" |
||||||
|
|
||||||
|
first = (list, ...) -> |
||||||
|
t = type list |
||||||
|
switch t |
||||||
|
when 'nil' |
||||||
|
if (select '#', ...)==0 |
||||||
|
nil |
||||||
|
else |
||||||
|
first ... |
||||||
|
when 'string' |
||||||
|
list |
||||||
|
when 'number' |
||||||
|
tostring list |
||||||
|
when 'boolean' |
||||||
|
list |
||||||
|
when 'table' |
||||||
|
min = huge |
||||||
|
for k in pairs list |
||||||
|
if (type k) == 'number' |
||||||
|
min = k if k < min |
||||||
|
else |
||||||
|
return list |
||||||
|
first list[min] |
||||||
|
else |
||||||
|
error "can't find first of type #{t}" |
||||||
|
|
||||||
|
foreach = (list, fn) -> |
||||||
|
[fn v for v in *flatten list] |
||||||
|
|
||||||
|
filter = (list, fn) -> |
||||||
|
[v for v in *flatten list when fn v] |
||||||
|
|
||||||
|
includes = (list, v) -> |
||||||
|
return true if list==v |
||||||
|
if (type list) == 'table' |
||||||
|
for k, e in pairs list |
||||||
|
if (type k) == 'number' |
||||||
|
return true if includes e, v |
||||||
|
if (type list) == 'number' |
||||||
|
return (tostring list) == (tostring v) |
||||||
|
false |
||||||
|
|
||||||
|
patget = (s, pat) -> |
||||||
|
prefix, suffix = match pat, '^(.*)%%(.*)$' |
||||||
|
return s==pat and s or nil unless prefix |
||||||
|
if (sub s, 1, #prefix)==prefix and (suffix == '' or (sub s, -#suffix)==suffix) |
||||||
|
sub s, #prefix+1, -#suffix-1 |
||||||
|
else |
||||||
|
nil |
||||||
|
|
||||||
|
patset = (s, rep) -> |
||||||
|
prefix, suffix = match rep, '^(.*)%%(.*)$' |
||||||
|
if prefix |
||||||
|
prefix..s..suffix |
||||||
|
else |
||||||
|
rep |
||||||
|
|
||||||
|
patsubst = (s, pat, rep) -> |
||||||
|
prefix, suffix = match pat, '^(.*)%%(.*)$' |
||||||
|
rprefix, rsuffix = match rep, '^(.*)%%(.*)$' |
||||||
|
|
||||||
|
t = type s |
||||||
|
f = false |
||||||
|
if t=='nil' |
||||||
|
return nil |
||||||
|
if t=='number' |
||||||
|
t = 'string' |
||||||
|
s = tostring s |
||||||
|
if t=='string' |
||||||
|
t = 'table' |
||||||
|
s = {s} |
||||||
|
f = true |
||||||
|
if t!='table' |
||||||
|
error "can't substitute patterns on type #{t}" |
||||||
|
|
||||||
|
r, i = {}, 1 |
||||||
|
for s in *flatten s |
||||||
|
if not prefix |
||||||
|
if s==pat |
||||||
|
if rprefix |
||||||
|
r[i], i = rprefix..s..rsuffix, i+1 |
||||||
|
else |
||||||
|
r[i], i = rep, i+1 |
||||||
|
elseif (sub s, 1, #prefix)==prefix and (suffix == '' or (sub s, -#suffix)==suffix) |
||||||
|
if rprefix |
||||||
|
r[i], i = rprefix..(sub s, #prefix+1, -#suffix-1)..rsuffix, i+1 |
||||||
|
else |
||||||
|
r[i], i = rep, i+1 |
||||||
|
|
||||||
|
f and r[1] or r |
||||||
|
|
||||||
|
exclude = (list, ...) -> |
||||||
|
exclusions = flatten ... |
||||||
|
[v for v in *flatten list when not includes exclusions, v] |
||||||
|
|
||||||
|
min = (list) -> |
||||||
|
m = list[1] |
||||||
|
for i=2, #list |
||||||
|
e = list[i] |
||||||
|
m = e if e<m |
||||||
|
m |
||||||
|
|
||||||
|
max = (list) -> |
||||||
|
m = list[1] |
||||||
|
for i=2, #list |
||||||
|
e = list[i] |
||||||
|
m = e if e>m |
||||||
|
m |
||||||
|
|
||||||
|
minmax = (list) -> |
||||||
|
m = list[1] |
||||||
|
M = list[1] |
||||||
|
for i=2, #list |
||||||
|
e = list[i] |
||||||
|
m = e if e<m |
||||||
|
M = e if e>M |
||||||
|
m, M |
||||||
|
|
||||||
|
common.flatten = flatten |
||||||
|
common.first = first |
||||||
|
common.foreach = foreach |
||||||
|
common.filter = filter |
||||||
|
common.includes = includes |
||||||
|
common.patget = patget |
||||||
|
common.patset = patset |
||||||
|
common.patsubst = patsubst |
||||||
|
common.min = min |
||||||
|
common.max = max |
||||||
|
common.minmax = minmax |
||||||
|
|
||||||
|
setmetatable common, __call: => [k for k in pairs common] |
@ -0,0 +1,198 @@ |
|||||||
|
import remove, concat from table |
||||||
|
import gmatch, match, gsub, sub from string |
||||||
|
|
||||||
|
-- load backend |
||||||
|
ok, fs, backend = false, nil, nil |
||||||
|
unless ok |
||||||
|
ok, fs = pcall -> require 'moonbuild._fs.posix' |
||||||
|
backend = 'posix' |
||||||
|
unless ok |
||||||
|
ok, fs = pcall -> require 'moonbuild._fs.lfs' |
||||||
|
backend = 'lfs' |
||||||
|
unless ok |
||||||
|
ok, fs = pcall -> require 'moonbuild._fs.cmd' |
||||||
|
backend = 'cmd' |
||||||
|
error "unable to load any fs library, tried luaposix, luafilesystem and posix commands" unless ok |
||||||
|
|
||||||
|
-- caching mechanism |
||||||
|
DISABLED = ( -> DISABLED ) |
||||||
|
NIL = ( -> NIL ) |
||||||
|
|
||||||
|
cacheenabled = true |
||||||
|
caches = {} |
||||||
|
|
||||||
|
clearcache = -> |
||||||
|
v.clearcache! for k, v in pairs caches |
||||||
|
clearentry = (entry) -> |
||||||
|
v.clearentry entry for k, v in pairs caches |
||||||
|
disableentry = (entry) -> |
||||||
|
v.disableentry entry for k, v in pairs caches |
||||||
|
disablecache = -> |
||||||
|
cacheenabled = false |
||||||
|
enablecache = -> |
||||||
|
cacheenabled = true |
||||||
|
|
||||||
|
withcache = (fn) -> |
||||||
|
opts = {} |
||||||
|
opts.cache = {} |
||||||
|
opts.clearcache = -> |
||||||
|
opts.cache = {} |
||||||
|
opts.clearentry = (entry) -> |
||||||
|
opts.cache[entry] = nil |
||||||
|
opts.disableentry = (entry) -> |
||||||
|
opts.cache[entry] = DISABLED |
||||||
|
caches[fn] = opts |
||||||
|
|
||||||
|
setmetatable opts, |
||||||
|
__call: (arg) => |
||||||
|
return fn arg unless cacheenabled |
||||||
|
cached = opts.cache[arg] |
||||||
|
return fn arg if cached == DISABLED |
||||||
|
return nil if cached == NIL |
||||||
|
return cached if cached != nil |
||||||
|
cached = fn arg |
||||||
|
opts.cache[arg] = cached |
||||||
|
opts.cache[arg] = NIL if cached == nil |
||||||
|
return cached |
||||||
|
|
||||||
|
fs = { |
||||||
|
dir: withcache fs.dir |
||||||
|
attributes: withcache fs.attributes |
||||||
|
mkdir: fs.mkdir |
||||||
|
} |
||||||
|
import attributes, dir, mkdir from fs |
||||||
|
|
||||||
|
-- actual functions |
||||||
|
normalizepath = (file) -> |
||||||
|
parts = [part for part in gmatch file, '[^/]+'] |
||||||
|
absolute = (sub file, 1, 1)=='/' |
||||||
|
i = 1 |
||||||
|
while i<=#parts |
||||||
|
if parts[i]=='.' |
||||||
|
remove parts, i |
||||||
|
continue |
||||||
|
if parts[i]=='..' and i!=1 and parts[i-1]!='..' |
||||||
|
remove parts, i |
||||||
|
remove parts, i-1 |
||||||
|
i -= 1 |
||||||
|
continue |
||||||
|
i += 1 |
||||||
|
if #parts==0 |
||||||
|
absolute and '/' or '.' |
||||||
|
else |
||||||
|
(absolute and '/' or '') .. concat parts, '/' |
||||||
|
|
||||||
|
ls = (d) -> |
||||||
|
[f for f in *dir normalizepath d when f!='.' and f!='..'] |
||||||
|
|
||||||
|
lswithpath = (d) -> |
||||||
|
return ls '.' if d=='' |
||||||
|
[d..'/'..f for f in *dir normalizepath d when f!='.' and f!='..'] |
||||||
|
|
||||||
|
matchglob = (str, glob) -> |
||||||
|
glob = gsub glob, '[%[%]%%+.?-]', => '%'..@ |
||||||
|
patt = '^'..(gsub glob, '%*%*?', => @=='**' and '.*' or '[^/]*')..'$' |
||||||
|
rst = if (type str)=='table' |
||||||
|
results, i = {}, 1 |
||||||
|
for s in *str |
||||||
|
rst = (match s, patt) and s |
||||||
|
results[i], i = rst, i+1 if rst |
||||||
|
results |
||||||
|
else |
||||||
|
(match str, patt) and str |
||||||
|
rst |
||||||
|
|
||||||
|
exists = (f) -> |
||||||
|
(attributes normalizepath f) != nil |
||||||
|
|
||||||
|
isdir = (f) -> |
||||||
|
((attributes normalizepath f) or {}).mode == 'directory' |
||||||
|
|
||||||
|
wildcard = (glob) -> |
||||||
|
parts = [part for part in gmatch glob, '[^/]+'] |
||||||
|
absolute = (sub glob, 1, 1)=='/' |
||||||
|
|
||||||
|
for i, part in ipairs parts |
||||||
|
prevpath = (absolute and '/' or '') .. concat parts, '/', 1, i-1 |
||||||
|
currpath = (i==1 and '' or (prevpath .. '/')) .. part |
||||||
|
|
||||||
|
if match part, '%*%*.*%*%*' |
||||||
|
error "Two '**' in the same path component in a wildcard" |
||||||
|
|
||||||
|
if match part, '%*%*' |
||||||
|
prefix = match currpath, '^(.*)%*%*' |
||||||
|
suffix = (match part, '%*%*(.*)$') .. (i==#parts and '' or ('/'..concat parts, '/', i+1, #parts)) |
||||||
|
return {} unless exists prevpath |
||||||
|
files = lswithpath prevpath |
||||||
|
|
||||||
|
results, ri = {}, 1 |
||||||
|
for file in *files |
||||||
|
if matchglob file, currpath |
||||||
|
if i==#parts |
||||||
|
results[ri], ri = file, ri+1 |
||||||
|
elseif isdir file |
||||||
|
for result in *wildcard file .. '/' .. concat parts, '/', i+1, #parts |
||||||
|
results[ri], ri = result, ri+1 |
||||||
|
if (matchglob file, prefix..'**') and isdir file |
||||||
|
for result in *wildcard file .. '/**' .. suffix |
||||||
|
results[ri], ri = result, ri+1 |
||||||
|
return results |
||||||
|
|
||||||
|
if match part, '%*' |
||||||
|
return {} unless exists prevpath |
||||||
|
files = lswithpath prevpath |
||||||
|
|
||||||
|
if i==#parts |
||||||
|
return matchglob files, glob |
||||||
|
|
||||||
|
results, ri = {}, 1 |
||||||
|
for file in *files |
||||||
|
if (matchglob file, currpath) and isdir file |
||||||
|
for result in *wildcard file .. '/' .. concat parts, '/', i+1, #parts |
||||||
|
results[ri], ri = result, ri+1 |
||||||
|
return results |
||||||
|
|
||||||
|
if exists glob |
||||||
|
return {glob} |
||||||
|
else |
||||||
|
return {} |
||||||
|
|
||||||
|
parent = (file) -> |
||||||
|
normalizepath file..'/..' |
||||||
|
|
||||||
|
actualmkdir = mkdir |
||||||
|
mkdir = (dir) -> |
||||||
|
actualmkdir dir |
||||||
|
clearentry parent dir |
||||||
|
|
||||||
|
mkdirs = (dir) -> |
||||||
|
return if isdir dir |
||||||
|
error "Can't mkdirs #{dir}: file exists" if exists dir |
||||||
|
mkdirs parent dir |
||||||
|
mkdir dir |
||||||
|
|
||||||
|
-- from the backend |
||||||
|
fs = {k, withcache fn for k, fn in pairs fs} |
||||||
|
|
||||||
|
-- own functions |
||||||
|
fs.normalizepath = normalizepath |
||||||
|
fs.ls = ls |
||||||
|
fs.lswithpath = lswithpath |
||||||
|
fs.matchglob = matchglob |
||||||
|
fs.exists = exists |
||||||
|
fs.isdir = isdir |
||||||
|
fs.wildcard = wildcard |
||||||
|
fs.parent = parent |
||||||
|
fs.mkdir = mkdir |
||||||
|
fs.mkdirs = mkdirs |
||||||
|
|
||||||
|
-- cache and backend |
||||||
|
fs.clearcache = clearcache |
||||||
|
fs.clearentry = clearentry |
||||||
|
fs.disableentry = disableentry |
||||||
|
fs.disablecache = disablecache |
||||||
|
fs.enablecache = enablecache |
||||||
|
fs.backend = backend |
||||||
|
|
||||||
|
-- the library itself |
||||||
|
setmetatable fs, __call: => {'dir', 'ls', 'normalizepath', 'exists', 'isdir', 'wildcard', 'mkdir', 'mkdirs'} |
@ -0,0 +1,54 @@ |
|||||||
|
import escape from require 'moonbuild._cmd' |
||||||
|
import execute from require 'moonbuild.compat.execute' |
||||||
|
import popen from io |
||||||
|
import gmatch, match, sub from string |
||||||
|
|
||||||
|
error "commands ls and stat aren't available" unless (execute "which ls >/dev/null 2>&1") and (execute "which stat >/dev/null 2>&1") |
||||||
|
|
||||||
|
{ |
||||||
|
dir: (path) -> |
||||||
|
[file for file in (popen "ls -1 #{escape path}")\lines!] |
||||||
|
|
||||||
|
attributes: (path) -> |
||||||
|
fd = popen "stat -c '%d %i %A %h %u %g %s %b %t %T %X %Y %Z' #{escape path}" |
||||||
|
stat = [part for part in gmatch (fd\read '*a'), "%S+"] |
||||||
|
fd\close! |
||||||
|
|
||||||
|
fd = popen "stat -f -c '%S' #{escape path}" |
||||||
|
blksize = match (fd\read '*a'), '%S+' |
||||||
|
fd\close! |
||||||
|
|
||||||
|
{ |
||||||
|
dev: tonumber stat[1] |
||||||
|
ino: tonumber stat[2] |
||||||
|
nlink: tonumber stat[4] |
||||||
|
uid: tonumber stat[5] |
||||||
|
gid: tonumber stat[6] |
||||||
|
size: tonumber stat[7] |
||||||
|
blocks: tonumber stat[8] |
||||||
|
blksize: tonumber blksize |
||||||
|
access: tonumber stat[11] |
||||||
|
modification: tonumber stat[12] |
||||||
|
change: tonumber stat[13] |
||||||
|
|
||||||
|
permissions: do |
||||||
|
sub stat[3], 2 |
||||||
|
|
||||||
|
mode: do |
||||||
|
switch sub stat[3], 1, 1 |
||||||
|
when '-' then 'file' |
||||||
|
when 'd' then 'directory' |
||||||
|
when 'l' then 'link' |
||||||
|
when 's' then 'socket' |
||||||
|
when 'p' then 'named pipe' |
||||||
|
when 'c' then 'char device' |
||||||
|
when 'b' then 'block device' |
||||||
|
else 'other' |
||||||
|
|
||||||
|
rdev: do |
||||||
|
(tonumber stat[9]) * 256 + (tonumber stat[10]) |
||||||
|
} |
||||||
|
|
||||||
|
mkdir: (path) -> |
||||||
|
error "Mkdir #{path} failed" unless execute "mkdir #{escape path}" |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
import dir, attributes, mkdir from require 'lfs' |
||||||
|
|
||||||
|
{ |
||||||
|
dir: (path) -> |
||||||
|
[v for v in dir path] |
||||||
|
|
||||||
|
attributes: attributes |
||||||
|
|
||||||
|
mkdir: (path) -> |
||||||
|
ok, err = mkdir path |
||||||
|
error "Failed to mkdir #{path}: #{err}" unless ok |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
import dir from require 'posix.dirent' |
||||||
|
import stat, mkdir, S_IFMT, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO, S_IFLINK, S_IFREG, S_IFSOCK from require 'posix.sys.stat' |
||||||
|
import band, btest from require 'moonbuild.compat.bit' |
||||||
|
import concat from table |
||||||
|
|
||||||
|
{ |
||||||
|
dir: dir |
||||||
|
|
||||||
|
attributes: (path) -> |
||||||
|
st = stat path |
||||||
|
return nil unless st |
||||||
|
mode = st.st_mode |
||||||
|
{ |
||||||
|
mode: do |
||||||
|
ty = band mode, S_IFMT |
||||||
|
switch ty |
||||||
|
when S_IFREG then 'file' |
||||||
|
when S_IFDIR then 'directory' |
||||||
|
when S_IFLINK then 'link' |
||||||
|
when S_IFSOCK then 'socket' |
||||||
|
when S_IFIFO then 'named pipe' |
||||||
|
when S_IFCHR then 'char device' |
||||||
|
when S_IFBLK then 'block device' |
||||||
|
else 'other' |
||||||
|
|
||||||
|
permissions: do |
||||||
|
_suid = btest mode, 2048 |
||||||
|
_sgid = btest mode, 1024 |
||||||
|
_stic = btest mode, 512 |
||||||
|
_ur = btest mode, 256 |
||||||
|
_uw = btest mode, 128 |
||||||
|
_ux = btest mode, 64 |
||||||
|
_gr = btest mode, 32 |
||||||
|
_gw = btest mode, 16 |
||||||
|
_gx = btest mode, 8 |
||||||
|
_or = btest mode, 4 |
||||||
|
_ow = btest mode, 2 |
||||||
|
_ox = btest mode, 1 |
||||||
|
concat { |
||||||
|
_ur and 'r' or '-' |
||||||
|
_uw and 'w' or '-' |
||||||
|
_suid and 's' or (_ux and 'x' or '-') |
||||||
|
_gr and 'r' or '-' |
||||||
|
_gw and 'w' or '-' |
||||||
|
_sgid and 's' or (_gx and 'x' or '-') |
||||||
|
_or and 'r' or '-' |
||||||
|
_ow and 'w' or '-' |
||||||
|
_stic and 't' or (_ox and 'x' or '-') |
||||||
|
} |
||||||
|
|
||||||
|
dev: st.st_dev |
||||||
|
ino: st.st_ino |
||||||
|
nlink: st.st_nlink |
||||||
|
uid: st.st_uid |
||||||
|
gid: st.st_gid |
||||||
|
rdev: st.st_rdev |
||||||
|
access: st.st_atime |
||||||
|
modification: st.st_mtime |
||||||
|
change: st.st_ctime |
||||||
|
size: st.st_size |
||||||
|
blocks: st.st_blocks |
||||||
|
blksize: st.st_blksize |
||||||
|
} |
||||||
|
|
||||||
|
mkdir: (path) -> |
||||||
|
ok, err = mkdir path |
||||||
|
error "Failed to mkdir #{path}: #{err}" unless ok |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
import to_lua from require 'moonscript.base' |
||||||
|
import parseargs, cmdrst from require 'moonbuild._cmd' |
||||||
|
import gmatch, match, gsub from string |
||||||
|
import open from io |
||||||
|
|
||||||
|
util = {} |
||||||
|
|
||||||
|
_pkgconfig = (mode, ...) -> |
||||||
|
parseargs cmdrst 'pkg-config', "--#{mode}", ... |
||||||
|
pkgconfig = setmetatable {}, __index: (mode) => (...) -> _pkgconfig mode, ... |
||||||
|
|
||||||
|
_cdeps = (cc, cflags, path) -> |
||||||
|
raw = cmdrst cc, cflags, '-M', path |
||||||
|
rawlist = gsub (match raw, ':(.+)'), '\\\n', ' ' |
||||||
|
[v for v in gmatch rawlist, '%S+'] |
||||||
|
cdeps = setmetatable {}, |
||||||
|
__index: (cc) => (cflags, path) -> _cdeps cc, cflags, path |
||||||
|
__call: (cflags, path) => _cdeps 'cc', cflags, path |
||||||
|
|
||||||
|
readfile = (filename) -> |
||||||
|
fd, err = open filename, 'rb' |
||||||
|
error err unless fd |
||||||
|
data, err = fd\read '*a' |
||||||
|
error err unless data |
||||||
|
fd\close! |
||||||
|
data |
||||||
|
|
||||||
|
writefile = (filename, data) -> |
||||||
|
fd, err = open filename, 'wb' |
||||||
|
error err unless fd |
||||||
|
ok, err = fd\write data |
||||||
|
error err unless ok |
||||||
|
fd\close! |
||||||
|
nil |
||||||
|
|
||||||
|
moonc = (infile, outfile) -> |
||||||
|
code, err = to_lua readfile infile |
||||||
|
error "Failed to compile #{@infile}: #{err}" unless code |
||||||
|
writefile outfile, code |
||||||
|
|
||||||
|
util.pkgconfig = pkgconfig |
||||||
|
util.cdeps = cdeps |
||||||
|
util.readfile = readfile |
||||||
|
util.writefile = writefile |
||||||
|
util.moonc = moonc |
||||||
|
|
||||||
|
setmetatable util, __call: => [k for k in pairs util] |
@ -0,0 +1,91 @@ |
|||||||
|
loadstring = loadstring or load |
||||||
|
import floor, ceil, pow from math |
||||||
|
|
||||||
|
band = loadstring [[local a, b = ...; return a & b ]] |
||||||
|
bor = loadstring [[local a, b = ...; return a | b ]] |
||||||
|
bxor = loadstring [[local a, b = ...; return a ~ b ]] |
||||||
|
bnot = loadstring [[local a = ...; return ~a ]] |
||||||
|
shl = loadstring [[local a, b = ...; return a << b]] |
||||||
|
shr = loadstring [[local a, b = ...; return a >> b]] |
||||||
|
|
||||||
|
unless band |
||||||
|
_checkint = (n) -> |
||||||
|
if n%1 == 0 |
||||||
|
n |
||||||
|
else |
||||||
|
error "not an int" |
||||||
|
|
||||||
|
_shl = (a, b) -> |
||||||
|
a * pow(2, b) |
||||||
|
|
||||||
|
_shr = (a, b) -> |
||||||
|
v = a / pow(2, b) |
||||||
|
if v<0 |
||||||
|
ceil v |
||||||
|
else |
||||||
|
floor v |
||||||
|
|
||||||
|
_shr1 = (n) -> |
||||||
|
n /= 2 |
||||||
|
if n<0 |
||||||
|
ceil v |
||||||
|
else |
||||||
|
floor v |
||||||
|
|
||||||
|
_band = (a, b) -> |
||||||
|
v = 0 |
||||||
|
n = 1 |
||||||
|
for i=0, 63 |
||||||
|
if a%2 == 1 and b%2 == 1 |
||||||
|
v += n |
||||||
|
if i!=63 |
||||||
|
a = _shr1 a |
||||||
|
b = _shr1 b |
||||||
|
n *= 2 |
||||||
|
v |
||||||
|
|
||||||
|
_bor = (a, b) -> |
||||||
|
v = 0 |
||||||
|
n = 1 |
||||||
|
for i=0, 63 |
||||||
|
if a%2 == 1 or b%2 == 1 |
||||||
|
v += n |
||||||
|
if i!=63 |
||||||
|
a = _shr1 a |
||||||
|
b = _shr1 b |
||||||
|
n *= 2 |
||||||
|
v |
||||||
|
|
||||||
|
_bxor = (a, b) -> |
||||||
|
v = 0 |
||||||
|
n = 1 |
||||||
|
for i=0, 63 |
||||||
|
if a%2 != b%2 |
||||||
|
v += n |
||||||
|
if i!=63 |
||||||
|
a = _shr1 a |
||||||
|
b = _shr1 b |
||||||
|
n *= 2 |
||||||
|
v |
||||||
|
|
||||||
|
_bnot = (a) -> |
||||||
|
v = 0 |
||||||
|
n = 1 |
||||||
|
for i=0, 63 |
||||||
|
if a%2 == 0 |
||||||
|
v += n |
||||||
|
if i!=63 |
||||||
|
a = _shr1 a |
||||||
|
n *= 2 |
||||||
|
v |
||||||
|
|
||||||
|
band = (a, b) -> _band (_checkint a), (_checkint b) |
||||||
|
bor = (a, b) -> _bor (_checkint a), (_checkint b) |
||||||
|
bxor = (a, b) -> _bxor (_checkint a), (_checkint b) |
||||||
|
bnot = (a) -> _bnot (_checkint a) |
||||||
|
shl = (a, b) -> _shl (_checkint a), (_checkint b) |
||||||
|
shr = (a, b) -> _shr (_checkint a), (_checkint b) |
||||||
|
|
||||||
|
btest = (a, b) -> (band a, b) == b |
||||||
|
|
||||||
|
{ :band, :bor, :bxor, :bnot, :shl, :shr, :btest } |
@ -0,0 +1,29 @@ |
|||||||
|
pcall = require 'moonbuild.compat.pcall' |
||||||
|
|
||||||
|
runwithcontext = if setfenv |
||||||
|
(fn, ctx, ...) -> |
||||||
|
env = getfenv fn |
||||||
|
setfenv fn, ctx |
||||||
|
local data, ndata, ok |
||||||
|
acc = (succ, ...) -> |
||||||
|
ok = succ |
||||||
|
if succ |
||||||
|
data = {...} |
||||||
|
ndata = select '#', ... |
||||||
|
else |
||||||
|
data = ... |
||||||
|
acc pcall fn, ... |
||||||
|
setfenv fn, env |
||||||
|
if ok |
||||||
|
unpack data, 1, ndata |
||||||
|
else |
||||||
|
error data |
||||||
|
|
||||||
|
else |
||||||
|
import dump from string |
||||||
|
(fn, ctx, ...) -> |
||||||
|
code = dump fn, false |
||||||
|
fn = load code, 'runwithcontext', 'b', ctx |
||||||
|
fn ... |
||||||
|
|
||||||
|
{ :runwithcontext } |
@ -0,0 +1,10 @@ |
|||||||
|
import execute from os |
||||||
|
|
||||||
|
{ |
||||||
|
execute: (cmd) -> |
||||||
|
a, b, c = execute cmd |
||||||
|
if (type a) == 'boolean' |
||||||
|
a, b, c |
||||||
|
else |
||||||
|
a==0 or nil, 'exit', a |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
pcall = _G.pcall |
||||||
|
unpack = _G.unpack or table.unpack |
||||||
|
|
||||||
|
testfn = (a, b) -> a == b and a == 1 and true or error! |
||||||
|
testok, testrst = pcall testfn, 1, 1 |
||||||
|
unless testok and testrst |
||||||
|
realpcall = pcall |
||||||
|
pcall = (fn, ...) -> |
||||||
|
args = { n: (select '#', ...), ... } |
||||||
|
realpcall -> fn unpack args, 1, args.n |
||||||
|
|
||||||
|
pcall |
@ -0,0 +1,38 @@ |
|||||||
|
import runwithcontext from require 'moonbuild.compat.ctx' |
||||||
|
topenv = require 'moonbuild.env.top' |
||||||
|
initenv = require 'moonbuild.env.init' |
||||||
|
import includes from require 'moonbuild._common' |
||||||
|
import insert from table |
||||||
|
|
||||||
|
class Context |
||||||
|
new: => |
||||||
|
@targets = {} |
||||||
|
@defaulttargets = {} |
||||||
|
@variables = {} |
||||||
|
@inits = {} |
||||||
|
|
||||||
|
addvar: (var) => |
||||||
|
@variables[var.name] = var |
||||||
|
|
||||||
|
addinit: (fn) => |
||||||
|
insert @inits, fn |
||||||
|
|
||||||
|
addtarget: (target) => |
||||||
|
insert @targets, target |
||||||
|
|
||||||
|
resetexecuted: => |
||||||
|
@executedtargets = {} |
||||||
|
|
||||||
|
adddefault: (target) => |
||||||
|
error "not a target of the current context: #{target}" unless includes @targets, target |
||||||
|
error "not a named target" unless (type target.name) == 'string' |
||||||
|
insert @defaulttargets, target.name |
||||||
|
|
||||||
|
load: (code, overrides) => |
||||||
|
runwithcontext code, (topenv @, overrides) |
||||||
|
|
||||||
|
init: => |
||||||
|
if @inits[1] |
||||||
|
env = (initenv @) |
||||||
|
for init in *@inits |
||||||
|
runwithcontext code, env |
@ -0,0 +1,182 @@ |
|||||||
|
import filter, foreach, flatten, patsubst from require 'moonbuild._common' |
||||||
|
import runwithcontext from require 'moonbuild.compat.ctx' |
||||||
|
globalenv = require 'moonbuild.env.global' |
||||||
|
import exists, parent, mkdirs, clearentry, disableentry, attributes from require 'moonbuild._fs' |
||||||
|
import sort from table |
||||||
|
import huge from math |
||||||
|
|
||||||
|
local DepNode, FileTarget |
||||||
|
|
||||||
|
nodepriority = (a, b) -> |
||||||
|
ta = type a.name |
||||||
|
tb = type b.name |
||||||
|
da = #a.deps |
||||||
|
db = #b.deps |
||||||
|
if ta=='string' and tb!='string' |
||||||
|
return true |
||||||
|
elseif ta!='string' and tb=='string' |
||||||
|
return false |
||||||
|
elseif a.priority > b.priority |
||||||
|
return true |
||||||
|
elseif a.priority < b.priority |
||||||
|
return false |
||||||
|
else |
||||||
|
return da < db |
||||||
|
|
||||||
|
transclosure = (obj, prop) -> |
||||||
|
elems = {} |
||||||
|
i = 1 |
||||||
|
set = {} |
||||||
|
imp = (e) -> |
||||||
|
for v in *e[prop] |
||||||
|
if not set[v] |
||||||
|
elems[i], i = v, i+1 |
||||||
|
set[v] = i |
||||||
|
imp v |
||||||
|
imp obj |
||||||
|
elems |
||||||
|
|
||||||
|
mtime = (path) -> |
||||||
|
attr = attributes path |
||||||
|
attr and attr.modification |
||||||
|
|
||||||
|
class DepGraph |
||||||
|
new: (@ctx, names={}) => |
||||||
|
@nodes = {} |
||||||
|
@env = globalenv @ctx |
||||||
|
for name in *names |
||||||
|
@addnode name |
||||||
|
|
||||||
|
addnode: (name) => |
||||||
|
return if @nodes[name] |
||||||
|
elected = @resolvedeps name |
||||||
|
@nodes[name] = elected |
||||||
|
for dep in *(transclosure elected, 'deps') |
||||||
|
@nodes[dep.name] = dep |
||||||
|
dep.deps = nil |
||||||
|
elected.deps = nil |
||||||
|
|
||||||
|
resolvedeps: (name) => |
||||||
|
do |
||||||
|
node = @nodes[name] |
||||||
|
return node, {} if node |
||||||
|
candidates = filter {@ctx.targets, FileTarget!}, (target) -> target\matches name |
||||||
|
nodes = foreach candidates, (candidate) -> a: {pcall -> DepNode @, candidate, name} |
||||||
|
resolved = foreach (filter nodes, (node) -> node.a[1]), (node) -> node.a[2] |
||||||
|
sort resolved, nodepriority |
||||||
|
resolved[1] or error "Cannot resolve target #{name}" |
||||||
|
|
||||||
|
buildablenodes: => |
||||||
|
[v for k, v in pairs @nodes when v\canbuild! and not v.built] |
||||||
|
|
||||||
|
class DepNode |
||||||
|
new: (@dag, target, @name) => |
||||||
|
@priority = target.priority |
||||||
|
@buildfunctions = target.buildfunctions |
||||||
|
@mkdirs = target._mkdirs |
||||||
|
@sync = target._sync |
||||||
|
@type = target._type |
||||||
|
@outs = foreach target.outfiles, (name) -> patsubst @name, target.pattern, name |
||||||
|
@type = 'virtual' if #@outs == 0 |
||||||
|
@built = false |
||||||
|
|
||||||
|
resolve = (name) -> @dag\resolvedeps patsubst @name, target.pattern, name |
||||||
|
after = flatten foreach target.needtargets, resolve |
||||||
|
deps = flatten foreach target.infiles, resolve |
||||||
|
if #target.depfunctions!=0 |
||||||
|
ctx = setmetatable {}, |
||||||
|
__index: (_, k) -> |
||||||
|
switch k |
||||||
|
when 'infile' then first deps |
||||||
|
when 'infiles' then flatten deps |
||||||
|
when 'outfile' then first @outs |
||||||
|
when 'outfiles' then flatten @outs |
||||||
|
when 'name' then @name |
||||||
|
else error "No such field in TargetDepsContext: #{k}" |
||||||
|
__newindex: (k) => |
||||||
|
error "Attempt to set field #{k} of TargetDepsContext" |
||||||
|
for depfn in *target.depfunctions |
||||||
|
deps = flatten deps, foreach depfn, (fn) -> resolve runwithcontext fn, @dag.env, ctx |
||||||
|
@ins = foreach deps, (dep) -> dep.name |
||||||
|
@after = foreach after, (dep) -> dep.name |
||||||
|
@deps = flatten { deps, after } |
||||||
|
@built = true if #@deps == 0 and #@buildfunctions == 0 |
||||||
|
|
||||||
|
canbuild: => |
||||||
|
for node in *flatten { @ins, @after } |
||||||
|
if not @dag.nodes[node].built |
||||||
|
return false |
||||||
|
for file in *@ins |
||||||
|
if not exists file |
||||||
|
error "Node #{name} has ran all of its parents, but can't run since #{file} doesn't exist" |
||||||
|
return true |
||||||
|
|
||||||
|
build: (opts={}) => |
||||||
|
force = opts.force or false |
||||||
|
quiet = opts.quiet or false |
||||||
|
|
||||||
|
return if @built |
||||||
|
return unless force or @shouldbuild! |
||||||
|
print "#{@type == 'virtual' and "Running" or "Building"} #{@name}" unless quiet or #@buildfunctions == 0 |
||||||
|
@actuallybuild! |
||||||
|
|
||||||
|
|
||||||
|
shouldbuild: => |
||||||
|
-- targets with no outputs / inputs and virtual targets *NEED* to be built |
||||||
|
return true if #@outs == 0 or #@ins == 0 or @type == 'virtual' |
||||||
|
|
||||||
|
-- check min mtime for outputs |
||||||
|
minout = huge |
||||||
|
for file in *@outs |
||||||
|
time = mtime file |
||||||
|
-- if an output file is missing, we *NEED* to build it |
||||||
|
return true if not time |
||||||
|
minout = time if time < minout |
||||||
|
|
||||||
|
-- check max mtime for inputs |
||||||
|
maxin = 0 |
||||||
|
for file in *@ins |
||||||
|
time = mtime file |
||||||
|
maxin = time if time > maxin |
||||||
|
|
||||||
|
-- if any input file is more recent than any output file, we need to build |
||||||
|
maxin > minout |
||||||
|
|
||||||
|
actuallybuild: => |
||||||
|
if @mkdirs |
||||||
|
mkdirs parent file for file in *@outs |
||||||
|
disableentry file for file in *@outs |
||||||
|
ctx = setmetatable {}, |
||||||
|
__index: (_, k) -> |
||||||
|
switch k |
||||||
|
when 'infile' then @ins[1] |
||||||
|
when 'infiles' then @ins |
||||||
|
when 'outfile' then @outs[1] |
||||||
|
when 'outfiles' then @outs |
||||||
|
when 'name' then @name |
||||||
|
else error "No such field in TargetContext: #{k}" |
||||||
|
__newindex: (k) => |
||||||
|
error "Attempt to set field #{k} of TargetContext" |
||||||
|
for fn in *@buildfunctions |
||||||
|
runwithcontext fn, @dag.env, ctx |
||||||
|
|
||||||
|
updatecache: => |
||||||
|
clearentry file for file in *@outs |
||||||
|
|
||||||
|
class FileTarget |
||||||
|
new: => |
||||||
|
@priority = -huge |
||||||
|
@buildfunctions = {} |
||||||
|
@_mkdirs = false |
||||||
|
@_sync = false |
||||||
|
@_type = 'file' |
||||||
|
@needtargets = {} |
||||||
|
@infiles = {} |
||||||
|
@depfunctions = {} |
||||||
|
@outfiles = {'%'} |
||||||
|
@pattern = '%' |
||||||
|
|
||||||
|
matches: (name) => |
||||||
|
exists name |
||||||
|
|
||||||
|
DepGraph |
@ -0,0 +1,54 @@ |
|||||||
|
import flatten, includes, patget from require 'moonbuild._common' |
||||||
|
import insert from table |
||||||
|
|
||||||
|
class Target |
||||||
|
new: (@ctx, @name, opts={}) => |
||||||
|
@name = flatten @name if (type @name) != 'string' |
||||||
|
@pattern = opts.pattern or ((type @name) == 'string' and @name or '%') |
||||||
|
@priority = opts.priority or 0 |
||||||
|
error "pattern must be a string" unless (type @pattern) == 'string' |
||||||
|
error "priority must be an int" unless (type @priority) == 'number' and @priority%1 == 0 |
||||||
|
|
||||||
|
@outfiles = {} |
||||||
|
@infiles = {} |
||||||
|
@needtargets = {} |
||||||
|
@depfunctions = {} |
||||||
|
@buildfunctions = {} |
||||||
|
@_mkdirs = false |
||||||
|
@_sync = false |
||||||
|
@_type = 'normal' |
||||||
|
@public = false |
||||||
|
|
||||||
|
matches: (name) => |
||||||
|
if @name==name |
||||||
|
return true |
||||||
|
if (includes @name, name) and patget name, @pattern |
||||||
|
return true |
||||||
|
return false |
||||||
|
|
||||||
|
produces: (...) => |
||||||
|
n = #@outfiles+1 |
||||||
|
for obj in *flatten ... |
||||||
|
@outfiles[n], n = obj, n+1 |
||||||
|
|
||||||
|
depends: (...) => |
||||||
|
if (type ...) == 'function' |
||||||
|
insert @depfunctions, (...) |
||||||
|
else |
||||||
|
n = #@infiles+1 |
||||||
|
for obj in *flatten ... |
||||||
|
@infiles[n], n = obj, n+1 |
||||||
|
|
||||||
|
after: (...) => |
||||||
|
n = #@needtargets+1 |
||||||
|
for tgt in *flatten ... |
||||||
|
@needtargets[n], n = tgt, n+1 |
||||||
|
|
||||||
|
fn: (fn) => |
||||||
|
insert @buildfunctions, fn |
||||||
|
|
||||||
|
sync: => |
||||||
|
@_sync = true |
||||||
|
|
||||||
|
mkdirs: => |
||||||
|
@_mkdirs = true |
@ -0,0 +1,11 @@ |
|||||||
|
import flatten from require 'moonbuild._common' |
||||||
|
class Variable |
||||||
|
@NIL: -> |
||||||
|
|
||||||
|
new: (@name, ...) => |
||||||
|
@public = false |
||||||
|
if (select '#', ...) !=1 or (type ...) == 'table' |
||||||
|
@value = flatten ... |
||||||
|
else |
||||||
|
@value = ... |
||||||
|
|
@ -0,0 +1,61 @@ |
|||||||
|
import fork, _exit from require 'posix.unistd' |
||||||
|
import wait from require 'posix.sys.wait' |
||||||
|
import open, stderr from io |
||||||
|
import match from string |
||||||
|
|
||||||
|
class Executor |
||||||
|
@getmaxparallel: => |
||||||
|
fd = open '/proc/cpuinfo', 'r' |
||||||
|
return 1 unless fd |
||||||
|
ncpu = 0 |
||||||
|
for line in fd\lines! |
||||||
|
ncpu += 1 if match line, '^processor%s*:' |
||||||
|
fd\close! |
||||||
|
ncpu == 0 and 1 or ncpu |
||||||
|
|
||||||
|
new: (@dag, @nparallel) => |
||||||
|
@processes = {} |
||||||
|
@nprocesses = 0 |
||||||
|
@building = {} |
||||||
|
|
||||||
|
execute: (opts) => |
||||||
|
block = @dag\buildablenodes! |
||||||
|
while #block != 0 |
||||||
|
for node in *block |
||||||
|
@addprocess node, opts |
||||||
|
if @nprocesses == @nparallel |
||||||
|
@waitprocess! |
||||||
|
block = [node for node in *@dag\buildablenodes! when not @building[node]] |
||||||
|
while #block == 0 and @nprocesses != 0 |
||||||
|
@waitprocess! |
||||||
|
block = [node for node in *@dag\buildablenodes! when not @building[node]] |
||||||
|
|
||||||
|
while @nprocesses !=0 |
||||||
|
@waitprocess! |
||||||
|
|
||||||
|
for name, node in pairs @dag.nodes |
||||||
|
error "Node #{name} wasn't built" unless node.built |
||||||
|
|
||||||
|
addprocess: (node, opts) => |
||||||
|
pid = fork! |
||||||
|
error "Failed to fork" unless pid |
||||||
|
if pid!=0 |
||||||
|
@processes[pid] = node |
||||||
|
@nprocesses += 1 |
||||||
|
@building[node] = true |
||||||
|
else |
||||||
|
ok, err = pcall -> node\build opts |
||||||
|
if ok |
||||||
|
_exit 0 |
||||||
|
else |
||||||
|
stderr\write err |
||||||
|
_exit 1 |
||||||
|
|
||||||
|
waitprocess: => |
||||||
|
pid, ty, status = wait! |
||||||
|
error "Failed to wait" unless pid |
||||||
|
error "Failed to build #{@processes[pid].name}" if ty != 'exited' or status != 0 |
||||||
|
@processes[pid].built = true |
||||||
|
@processes[pid]\updatecache! |
||||||
|
@processes[pid] = nil |
||||||
|
@nprocesses -= 1 |
@ -0,0 +1,16 @@ |
|||||||
|
class Executor |
||||||
|
@getmaxparallel: => 1 |
||||||
|
|
||||||
|
new: (@dag, @nparallel) => |
||||||
|
|
||||||
|
execute: (opts) => |
||||||
|
block = @dag\buildablenodes! |
||||||
|
while #block != 0 |
||||||
|
for node in *block |
||||||
|
node\build opts |
||||||
|
node\updatecache! |
||||||
|
node.built = true |
||||||
|
block = @dag\buildablenodes! |
||||||
|
|
||||||
|
for name, node in pairs @dag.nodes |
||||||
|
error "Node #{name} wasn't built" unless node.built |
@ -0,0 +1,18 @@ |
|||||||
|
_ = require 'moonbuild._' |
||||||
|
|
||||||
|
(ctx) -> |
||||||
|
varlayer = setmetatable {}, |
||||||
|
__index: _G |
||||||
|
|
||||||
|
for name, var in pairs ctx.variables |
||||||
|
rawset varlayer, name, var.value |
||||||
|
|
||||||
|
env = setmetatable {}, |
||||||
|
__index: varlayer |
||||||
|
__newindex: (k) => error "attempt to assign to global variable '#{k}', which is disabled in the global env" |
||||||
|
|
||||||
|
rawset env, '_', _ |
||||||
|
rawset env, '_G', env |
||||||
|
rawset env, '_ENV', env |
||||||
|
|
||||||
|
env, varlayer |
@ -0,0 +1,32 @@ |
|||||||
|
Target = require 'moonbuild.core.Target' |
||||||
|
Variable = require 'moonbuild.core.Variable' |
||||||
|
_ = require 'moonbuild._' |
||||||
|
import flatten from _ |
||||||
|
|
||||||
|
(ctx) -> |
||||||
|
varlayer = setmetatable {}, |
||||||
|
__index: _G |
||||||
|
|
||||||
|
for name, var in pairs ctx.variables |
||||||
|
rawset varlayer, name, var.value |
||||||
|
|
||||||
|
env = setmetatable {}, |
||||||
|
__index: varlayer |
||||||
|
__newindex: (k) => error "attempt to assign to global variable '#{k}', use the function 'var' instead" |
||||||
|
|
||||||
|
rawset env, '_', _ |
||||||
|
rawset env, '_G', env |
||||||
|
rawset env, '_ENV', env |
||||||
|
|
||||||
|
rawset env, 'var', (name, ...) -> |
||||||
|
var = Variable name, ... |
||||||
|
ctx\addvar var |
||||||
|
rawset varlayer, name, var.value |
||||||
|
var |
||||||
|
|
||||||
|
rawset env, 'target', (name, opts) -> |
||||||
|
target = Target ctx, name, opts |
||||||
|
ctx\addtarget target |
||||||
|
target |
||||||
|
|
||||||
|
env, varlayer |
@ -0,0 +1,31 @@ |
|||||||
|
initenv = require 'moonbuild.env.init' |
||||||
|
Target = require 'moonbuild.core.Target' |
||||||
|
Variable = require 'moonbuild.core.Variable' |
||||||
|
|
||||||
|
(ctx, overrides) -> |
||||||
|
env, varlayer = initenv ctx |
||||||
|
|
||||||
|
rawset env, 'default', (target) -> |
||||||
|
ctx\adddefault target |
||||||
|
target |
||||||
|
|
||||||
|
rawset env, 'public', (e) -> |
||||||
|
clazz = ((getmetatable e) or {}).__class |
||||||
|
if clazz == Target |
||||||
|
e.public = true |
||||||
|
elseif clazz == Variable |
||||||
|
e.public = true |
||||||
|
override = overrides[e.name] |
||||||
|
if override |
||||||
|
override = nil if override == Variable.NIL |
||||||
|
e.value = override |
||||||
|
rawset varlayer, e.name, override |
||||||
|
else |
||||||
|
error "cannot set an object of type #{clazz and clazz.__name or type e} public" |
||||||
|
e |
||||||
|
|
||||||
|
rawset env, 'init', (fn) -> |
||||||
|
error "you can only add functions to init" unless (type fn) == 'function' |
||||||
|
ctx\addinit fn |
||||||
|
|
||||||
|
env, varlayer |
@ -1,52 +0,0 @@ |
|||||||
import attributes, dir from require 'lfs' |
|
||||||
unpack or= table.unpack |
|
||||||
|
|
||||||
FROZEN = -> |
|
||||||
|
|
||||||
makecached = (fn) -> |
|
||||||
cache = {} |
|
||||||
|
|
||||||
invalidate = (val) -> |
|
||||||
cache[val] = nil |
|
||||||
|
|
||||||
freeze = (val) -> |
|
||||||
cache[val] = FROZEN |
|
||||||
|
|
||||||
clear = -> |
|
||||||
cache = {} |
|
||||||
|
|
||||||
get = (val) -> |
|
||||||
if cache == FROZEN |
|
||||||
return fn val |
|
||||||
cached = cache[val] |
|
||||||
if cached!=FROZEN and cached!=nil |
|
||||||
return unpack cached |
|
||||||
ret = {fn val} |
|
||||||
if cached!=FROZEN |
|
||||||
cache[val] = ret |
|
||||||
unpack ret |
|
||||||
|
|
||||||
enable = -> |
|
||||||
cache = {} if cache==FROZEN |
|
||||||
|
|
||||||
disable = -> |
|
||||||
cache = FROZEN |
|
||||||
|
|
||||||
setmetatable { :get, :invalidate, :freeze, :clear, :enable, :disable }, |
|
||||||
__call: (val) => get val |
|
||||||
|
|
||||||
cached = { |
|
||||||
attributes: makecached attributes |
|
||||||
dir: makecached (file) -> [k for k in dir file] |
|
||||||
} |
|
||||||
|
|
||||||
enable = -> |
|
||||||
fn\enable! for _, fn in cached |
|
||||||
|
|
||||||
disable = -> |
|
||||||
fn\disable! for _, fn in cached |
|
||||||
|
|
||||||
clear = -> |
|
||||||
fn\clear! for _, fn in cached |
|
||||||
|
|
||||||
setmetatable { :enable, :disable, :clear }, __index: cached |
|
@ -1,124 +0,0 @@ |
|||||||
import dir, attributes, clear, enable, disable from require 'moonbuild.fscache' |
|
||||||
|
|
||||||
import gmatch, match, gsub, sub from string |
|
||||||
import insert, remove, concat from table |
|
||||||
|
|
||||||
normalizepath = (file) -> |
|
||||||
parts = [part for part in gmatch file, '[^/]+'] |
|
||||||
absolute = (sub file, 1, 1)=='/' |
|
||||||
i = 1 |
|
||||||
while i<=#parts |
|
||||||
if parts[i]=='.' |
|
||||||
remove parts, i |
|
||||||
continue |
|
||||||
if parts[i]=='..' and i!=1 and parts[i-1]!='..' |
|
||||||
remove parts, i |
|
||||||
remove parts, i-1 |
|
||||||
i -= 1 |
|
||||||
continue |
|
||||||
i += 1 |
|
||||||
if #parts==0 |
|
||||||
absolute and '/' or '.' |
|
||||||
else |
|
||||||
(absolute and '/' or '') .. concat parts, '/' |
|
||||||
|
|
||||||
ls = (d) -> |
|
||||||
[f for f in *dir normalizepath d when f!='.' and f!='..'] |
|
||||||
|
|
||||||
lswithpath = (d) -> |
|
||||||
return ls '.' if d=='' |
|
||||||
[d..'/'..f for f in *dir normalizepath d when f!='.' and f!='..'] |
|
||||||
|
|
||||||
exists = (f) -> |
|
||||||
(attributes normalizepath f) != nil |
|
||||||
|
|
||||||
isdir = (f) -> |
|
||||||
a = attributes normalizepath f |
|
||||||
a and a.mode == 'directory' or false |
|
||||||
|
|
||||||
mtime = (f) -> |
|
||||||
a = attributes normalizepath f |
|
||||||
a and a.modification |
|
||||||
|
|
||||||
matchglob = (str, glob) -> |
|
||||||
glob = gsub glob, '[%[%]%%+.?-]', => '%'..@ |
|
||||||
patt = '^'..(gsub glob, '%*%*?', => @=='**' and '.*' or '[^/]*')..'$' |
|
||||||
rst = if (type str)=='table' |
|
||||||
results, i = {}, 1 |
|
||||||
for s in *str |
|
||||||
rst = (match s, patt) and s |
|
||||||
results[i], i = rst, i+1 if rst |
|
||||||
results |
|
||||||
else |
|
||||||
(match str, patt) and str |
|
||||||
rst |
|
||||||
|
|
||||||
wildcard = (glob) -> |
|
||||||
parts = [part for part in gmatch glob, '[^/]+'] |
|
||||||
absolute = (sub glob, 1, 1)=='/' |
|
||||||
|
|
||||||
for i, part in ipairs parts |
|
||||||
prevpath = (absolute and '/' or '') .. concat parts, '/', 1, i-1 |
|
||||||
currpath = (i==1 and '' or (prevpath .. '/')) .. part |
|
||||||
|
|
||||||
if match part, '%*%*.*%*%*' |
|
||||||
error "Two '**' in the same path component in a wildcard" |
|
||||||
|
|
||||||
if match part, '%*%*' |
|
||||||
prefix = match currpath, '^(.*)%*%*' |
|
||||||
suffix = (match part, '%*%*(.*)$') .. (i==#parts and '' or ('/'..concat parts, '/', i+1, #parts)) |
|
||||||
files = lswithpath prevpath |
|
||||||
|
|
||||||
results = {} |
|
||||||
for file in *files |
|
||||||
if matchglob file, currpath |
|
||||||
if i==#parts |
|
||||||
insert results, file |
|
||||||
elseif isdir file |
|
||||||
for result in *wildcard file .. '/' .. concat parts, '/', i+1, #parts |
|
||||||
insert results, result |
|
||||||
if (matchglob file, prefix..'**') and isdir file |
|
||||||
for result in *wildcard file .. '/**' .. suffix |
|
||||||
insert results, result |
|
||||||
return results |
|
||||||
|
|
||||||
if match part, '%*' |
|
||||||
files = lswithpath prevpath |
|
||||||
|
|
||||||
if i==#parts |
|
||||||
return matchglob files, glob |
|
||||||
|
|
||||||
results = {} |
|
||||||
for file in *files |
|
||||||
if (matchglob file, currpath) and isdir file |
|
||||||
for result in *wildcard file .. '/' .. concat parts, '/', i+1, #parts |
|
||||||
insert results, result |
|
||||||
return results |
|
||||||
|
|
||||||
if exists glob |
|
||||||
return {glob} |
|
||||||
else |
|
||||||
return {} |
|
||||||
|
|
||||||
parentdir = (file) -> |
|
||||||
normalizepath file..'/..' |
|
||||||
|
|
||||||
freezecache = (file) -> |
|
||||||
dir.freeze file |
|
||||||
dir.freeze parentdir file |
|
||||||
attributes.invalidate file |
|
||||||
|
|
||||||
invalidatecache = (file) -> |
|
||||||
dir.invalidate file |
|
||||||
dir.invalidate parentdir file |
|
||||||
attributes.invalidate file |
|
||||||
|
|
||||||
{ |
|
||||||
:wildcard |
|
||||||
:exists, :isdir |
|
||||||
:mtime |
|
||||||
:normalizepath, :parentdir |
|
||||||
:matchglob |
|
||||||
:freezecache, :invalidatecache |
|
||||||
clearcache: clear, enablecache: enable, disablecache: disable |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
import match, gmatch, sub from string |
|
||||||
import upper, lower from string |
|
||||||
|
|
||||||
GLOB_PATT='^([^%%]*)%%([^%%]*)$' |
|
||||||
|
|
||||||
patsubst = (str, pattern, replacement) -> |
|
||||||
return [patsubst s, pattern, replacement for s in *str] if (type str)=='table' |
|
||||||
|
|
||||||
if str==pattern |
|
||||||
return replacement |
|
||||||
|
|
||||||
prefix, suffix = match pattern, GLOB_PATT |
|
||||||
if not (prefix or suffix) |
|
||||||
return str |
|
||||||
|
|
||||||
reprefix, resuffix = match replacement, GLOB_PATT |
|
||||||
if not (reprefix or resuffix) |
|
||||||
if (#prefix==0 or (sub str, 1, #prefix)==prefix) and (#suffix==0 or (sub str, -#suffix)==suffix) |
|
||||||
return replacement |
|
||||||
else |
|
||||||
return str |
|
||||||
|
|
||||||
if #prefix==0 or (sub str, 1, #prefix)==prefix |
|
||||||
str = reprefix..(sub str, #prefix+1) |
|
||||||
if #suffix==0 or (sub str, -#suffix)==suffix |
|
||||||
str = (sub str, 1, -#suffix-1)..resuffix |
|
||||||
str |
|
||||||
|
|
||||||
splitsp = (str) -> |
|
||||||
[elem for elem in gmatch str, '%S+'] |
|
||||||
|
|
||||||
{ |
|
||||||
:patsubst |
|
||||||
:splitsp |
|
||||||
|
|
||||||
:upper, :lower |
|
||||||
} |
|
@ -1,69 +0,0 @@ |
|||||||
import insert, remove, concat, sort from table |
|
||||||
unpack or= table.unpack |
|
||||||
|
|
||||||
sortedpairs = (table, cmp) -> |
|
||||||
keys = [k for k in pairs table] |
|
||||||
sort keys, cmp |
|
||||||
coroutine.wrap -> |
|
||||||
for key in *keys |
|
||||||
coroutine.yield key, table[key] |
|
||||||
|
|
||||||
min = (table, cmp=(a, b) -> a<b) -> |
|
||||||
val = table[1] |
|
||||||
for i=2, #table |
|
||||||
elem = table[i] |
|
||||||
if cmp elem, val |
|
||||||
val = elem |
|
||||||
val |
|
||||||
|
|
||||||
max = (table, cmp=(a, b) -> a<b) -> |
|
||||||
val = table[1] |
|
||||||
for i=2, #table |
|
||||||
elem = table[i] |
|
||||||
if not cmp elem, val |
|
||||||
val = elem |
|
||||||
val |
|
||||||
|
|
||||||
foreach = (tab, fn) -> |
|
||||||
[fn e for e in *tab] |
|
||||||
|
|
||||||
first = (tab, fn) -> |
|
||||||
for e in *tab |
|
||||||
return e if fn e |
|
||||||
|
|
||||||
exclude = (tab, ...) -> |
|
||||||
i=1 |
|
||||||
while i<=#tab |
|
||||||
removed=false |
|
||||||
for j=1, select '#', ... |
|
||||||
if tab[i]==select j, ... |
|
||||||
remove tab, i |
|
||||||
removed = true |
|
||||||
break |
|
||||||
i += 1 unless removed |
|
||||||
tab |
|
||||||
|
|
||||||
flatten = (tab) -> |
|
||||||
return {tab} if (type tab)!='table' |
|
||||||
out = {} |
|
||||||
for e in *tab |
|
||||||
if (type e)=='table' |
|
||||||
if e[1] == nil and (next e)!=nil |
|
||||||
insert out, e |
|
||||||
else |
|
||||||
insert out, v for v in *flatten e |
|
||||||
else |
|
||||||
insert out, e |
|
||||||
out |
|
||||||
|
|
||||||
{ |
|
||||||
:min, :max |
|
||||||
:foreach |
|
||||||
:first |
|
||||||
:exclude |
|
||||||
:flatten |
|
||||||
:sortedpairs |
|
||||||
|
|
||||||
:insert, :remove, :concat, :sort |
|
||||||
:unpack |
|
||||||
} |
|
@ -1,95 +0,0 @@ |
|||||||
import wildcard, exists, isdir, mtime from require 'moonbuild.fsutil' |
|
||||||
import foreach, first, flatten, exclude, sortedpairs, min, max from require 'moonbuild.tableutil' |
|
||||||
import patsubst, splitsp from require 'moonbuild.stringutil' |
|
||||||
|
|
||||||
import insert, concat, sort, pairs from require 'moonbuild.tableutil' |
|
||||||
import upper, lower from require 'moonbuild.stringutil' |
|
||||||
|
|
||||||
GLOB_PATT='^([^%%]*)%%([^%%]*)$' |
|
||||||
|
|
||||||
-- command functions |
|
||||||
escapecmdpart= (p) -> |
|
||||||
if (type p)=='table' |
|
||||||
return p.raw if p.raw |
|
||||||
return concat [escapecmdpart part for part in *p], ' ' |
|
||||||
return p if p\match '^[a-zA-Z0-9_./-]+$' |
|
||||||
'"'..p\gsub('\\', '\\\\')\gsub('"', '\\"')..'"' |
|
||||||
escapecmd= (c, args={}) -> |
|
||||||
c=escapecmdpart c |
|
||||||
for a in *flatten args |
|
||||||
c..=' '..escapecmdpart a if a |
|
||||||
c |
|
||||||
run= (c, args, params={}) -> |
|
||||||
escaped=escapecmd c, args |
|
||||||
print escaped if params.print |
|
||||||
ret, _, code=os.execute escaped |
|
||||||
ret, code=ret==0, ret if (type ret)=='number' |
|
||||||
error "#{c} failed with code #{code}" if params.error and not ret |
|
||||||
ret, code |
|
||||||
popen= (c, args, mode='r', params={}) -> |
|
||||||
escaped=escapecmd c, args |
|
||||||
print escaped if params.print |
|
||||||
io.popen escaped, mode |
|
||||||
|
|
||||||
calccdeps= (infile, includesys=false) -> |
|
||||||
data=(popen 'cc', {includesys and '-M' or '-MM', infile})\read '*a' |
|
||||||
rawdeps=data\gsub('\\\n', '')\match ':(.+)' |
|
||||||
[dep for dep in rawdeps\gmatch '%S+' when dep!=infile] |
|
||||||
|
|
||||||
findclib= (name, mode='all') -> |
|
||||||
args={name} |
|
||||||
insert args, '--cflags' if mode=='all' or mode=='cc' |
|
||||||
insert args, '--libs' if mode=='all' or mode=='ld' |
|
||||||
[arg for arg in (popen 'pkg-config', args)\read('*a')\gmatch '%S+'] |
|
||||||
|
|
||||||
-- glob match |
|
||||||
match= (str, glob) -> |
|
||||||
prefix, suffix=glob\match GLOB_PATT |
|
||||||
return str==glob unless prefix |
|
||||||
return str\sub #prefix+1, -#suffix-1 if (str\sub 1, #prefix)==prefix and (str\sub -#suffix)==suffix |
|
||||||
false |
|
||||||
|
|
||||||
-- is a valid glob |
|
||||||
isglob= (glob) -> |
|
||||||
return if glob\match GLOB_PATT |
|
||||||
true |
|
||||||
else |
|
||||||
false |
|
||||||
|
|
||||||
-- getenv |
|
||||||
env= (key, def) -> |
|
||||||
(os.getenv key) or def |
|
||||||
|
|
||||||
{ |
|
||||||
-- table function |
|
||||||
:min, :max |
|
||||||
:foreach |
|
||||||
:first |
|
||||||
:exclude |
|
||||||
:flatten |
|
||||||
:sortedpairs |
|
||||||
|
|
||||||
:insert, :remove, :concat, :sort |
|
||||||
:unpack |
|
||||||
|
|
||||||
-- file functions |
|
||||||
:wildcard |
|
||||||
:mtime |
|
||||||
:exists, :isdir |
|
||||||
|
|
||||||
-- command functions |
|
||||||
:run, :popen |
|
||||||
:calccdeps, :findclib |
|
||||||
|
|
||||||
-- string functions |
|
||||||
:patsubst |
|
||||||
:splitsp |
|
||||||
|
|
||||||
:upper, :lower |
|
||||||
|
|
||||||
-- glob functions |
|
||||||
:match, :isglob |
|
||||||
|
|
||||||
-- env functions |
|
||||||
:env |
|
||||||
} |
|
@ -1,23 +0,0 @@ |
|||||||
package: moonbuild |
|
||||||
source: |
|
||||||
url: git://github.com/natnat-mc/moonbuild |
|
||||||
description: |
|
||||||
summary: Small build system in between make and a build.sh |
|
||||||
detailed: > |
|
||||||
moonbuild is a small build system that simplifies your |
|
||||||
build definitions by allowing you to use declarative as |
|
||||||
well as imperative rules. |
|
||||||
It represents the build as a DAG with explicit ordering, |
|
||||||
and doesn't give you any default confusing rules (unlike |
|
||||||
make) |
|
||||||
homepage: https://github.com/natnat-mc/moonbuild |
|
||||||
dependencies: |
|
||||||
- lua >= 5.3 |
|
||||||
- luafilesystem >= 1.7.0 |
|
||||||
- argparse >= 0.7.1-1 |
|
||||||
- moonscript >= 0.5.0-1 |
|
||||||
build: |
|
||||||
type: builtin |
|
||||||
install: |
|
||||||
bin: |
|
||||||
moonbuild: moonbuild.lua |
|
@ -1,111 +0,0 @@ |
|||||||
describe 'fsutil', -> |
|
||||||
describe 'normalizepath', -> |
|
||||||
import normalizepath from require 'moonbuild.fsutil' |
|
||||||
|
|
||||||
test = (expected, source) -> |
|
||||||
it "normalizes #{source} correctly", -> |
|
||||||
assert.equal expected, normalizepath source |
|
||||||
|
|
||||||
testall = (tab) -> |
|
||||||
for a, b in pairs tab |
|
||||||
test b, a |
|
||||||
|
|
||||||
describe 'handles already normalized paths', -> |
|
||||||
testall { |
|
||||||
'.': '.' |
|
||||||
'..': '..' |
|
||||||
'../..': '../..' |
|
||||||
'/': '/' |
|
||||||
'/a': '/a' |
|
||||||
'/a/b': '/a/b' |
|
||||||
'a': 'a' |
|
||||||
'a/b': 'a/b' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'trims leading slashes', -> |
|
||||||
testall { |
|
||||||
'a/': 'a' |
|
||||||
'a/b/': 'a/b' |
|
||||||
'/a/': '/a' |
|
||||||
'/a/b/': '/a/b' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'normalizes absolute paths', -> |
|
||||||
testall { |
|
||||||
'/a/a/../b': '/a/b' |
|
||||||
'/a/./b': '/a/b' |
|
||||||
'/a/b/c/..': '/a/b' |
|
||||||
'/./a/./b/././.': '/a/b' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'normalizes relative paths', -> |
|
||||||
testall { |
|
||||||
'../x/../../a': '../../a' |
|
||||||
'../x/../a': '../a' |
|
||||||
'x/..': '.' |
|
||||||
'../.': '..' |
|
||||||
'./a': 'a' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'matchglob', -> |
|
||||||
import matchglob from require 'moonbuild.fsutil' |
|
||||||
|
|
||||||
test = (expected, source, glob) -> |
|
||||||
if expected |
|
||||||
it "matches #{glob} on #{source}", -> |
|
||||||
assert.equal source, matchglob source, glob |
|
||||||
else |
|
||||||
it "doesn't match #{glob} on #{source}", -> |
|
||||||
assert.equal nil, matchglob source, glob |
|
||||||
|
|
||||||
testall = (tab) -> |
|
||||||
for a, b in pairs tab |
|
||||||
test b, a[1], a[2] |
|
||||||
|
|
||||||
describe 'handles literal names', -> |
|
||||||
testall { |
|
||||||
[{'a', 'a'}]: true |
|
||||||
[{'a.b', 'a.b'}]: true |
|
||||||
[{'a/b', 'a/b'}]: true |
|
||||||
[{'..', '..'}]: true |
|
||||||
} |
|
||||||
|
|
||||||
describe 'doesn\'t treat things as special chars', -> |
|
||||||
testall { |
|
||||||
[{'a', '.'}]: false |
|
||||||
[{'a.b.c', '%S+'}]: false |
|
||||||
[{'%S+', '%S+'}]: true |
|
||||||
[{'%d', '%d'}]: true |
|
||||||
[{'a', '%S'}]: false |
|
||||||
[{'aaa', 'a+'}]: false |
|
||||||
} |
|
||||||
|
|
||||||
describe 'only matches fully', -> |
|
||||||
testall { |
|
||||||
[{'abcdef', 'bcde'}]: false |
|
||||||
[{'a/b/c', 'b/c'}]: false |
|
||||||
[{'a/b/c', 'a/b'}]: false |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles *', -> |
|
||||||
testall { |
|
||||||
[{'abcde', '*'}]: true |
|
||||||
[{'a/b/c/d', 'a/*/c/d'}]: true |
|
||||||
[{'a/b/c/d', 'a/*/d'}]: false |
|
||||||
[{'abcde', 'a*e'}]: true |
|
||||||
[{'abcde', 'a*f'}]: false |
|
||||||
[{'a/b/c/d/e', 'a/*/*/*/e'}]: true |
|
||||||
[{'a/b/c/d/e', 'a*/*/*e'}]: false |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles **', -> |
|
||||||
testall { |
|
||||||
[{'abcde', '**'}]: true |
|
||||||
[{'a/b/c/d', 'a/**/c/d'}]: true |
|
||||||
[{'abcde', 'a**e'}]: true |
|
||||||
[{'a/b/c/d/e', 'a/**/**/**/e'}]: true |
|
||||||
[{'a/b/c/d/e', 'a**e'}]: true |
|
||||||
[{'a/b/c/d/e', 'a/**/e'}]: true |
|
||||||
[{'a/b/c/d/e', 'a**f'}]: false |
|
||||||
[{'abcde', 'a**f'}]: false |
|
||||||
} |
|
@ -1,78 +0,0 @@ |
|||||||
describe 'stringutil', -> |
|
||||||
describe 'patsubst', -> |
|
||||||
import patsubst from require 'moonbuild.stringutil' |
|
||||||
|
|
||||||
test = (expected, source, patt, subst) -> |
|
||||||
it "substitutes #{source} into #{expected} with #{patt} and #{subst}", -> |
|
||||||
assert.equal expected, patsubst source, patt, subst |
|
||||||
|
|
||||||
testall = (tab) -> |
|
||||||
for a, b in pairs tab |
|
||||||
test b, a[1], a[2], a[3] |
|
||||||
|
|
||||||
describe 'handles just adding pre/suffix', -> |
|
||||||
testall { |
|
||||||
[{'a', '%', '_%'}]: '_a' |
|
||||||
[{'tx', '%', 'a_%'}]: 'a_tx' |
|
||||||
[{'a', '%', '%_'}]: 'a_' |
|
||||||
[{'tx', '%', '%_a'}]: 'tx_a' |
|
||||||
[{'a', '%', '_%_'}]: '_a_' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles doing nothing', -> |
|
||||||
for str in *({'a', 'aa', 'tx'}) |
|
||||||
test str, str, '%', '%' |
|
||||||
|
|
||||||
describe 'handles literal change', -> |
|
||||||
testall { |
|
||||||
[{'a', 'a', 'b'}]: 'b' |
|
||||||
[{'a', 'b', 'c'}]: 'a' |
|
||||||
[{'aa', 'a', 'b'}]: 'aa' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles match change', -> |
|
||||||
testall { |
|
||||||
[{'-a_', '-%_', 'b'}]: 'b' |
|
||||||
[{'-a_', '-%', 'b'}]: 'b' |
|
||||||
[{'-a_', '%_', 'b'}]: 'b' |
|
||||||
[{'-a_', '_%-', 'b'}]: '-a_' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles just removing pre/suffix', -> |
|
||||||
testall { |
|
||||||
[{'_a', '_%', '%'}]: 'a' |
|
||||||
[{'a_', '%_', '%'}]: 'a' |
|
||||||
[{'_a_', '_%_', '%'}]: 'a' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles not matching', -> |
|
||||||
testall { |
|
||||||
[{'a-', '%_', '%'}]: 'a-' |
|
||||||
[{'-a', '_%', '%'}]: '-a' |
|
||||||
[{'-a-', '_%_', '%'}]: '-a-' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'handles changing pre/suffix', -> |
|
||||||
testall { |
|
||||||
[{'a-', '%-', '%_'}]: 'a_' |
|
||||||
[{'-a', '-%', '_%'}]: '_a' |
|
||||||
[{'-a', '-%', '%_'}]: 'a_' |
|
||||||
[{'_a-', '_%-', '-%_'}]: '-a_' |
|
||||||
} |
|
||||||
|
|
||||||
describe 'splitsp', -> |
|
||||||
import splitsp from require 'moonbuild.stringutil' |
|
||||||
|
|
||||||
test = (expected, source) -> |
|
||||||
it "splits '#{source}' correctly", -> |
|
||||||
assert.same expected, splitsp source |
|
||||||
|
|
||||||
for source, expected in pairs { |
|
||||||
'a b c': {'a', 'b', 'c'} |
|
||||||
'abc': {'abc'} |
|
||||||
'': {} |
|
||||||
' a b c': {'a', 'b', 'c'} |
|
||||||
' ': {} |
|
||||||
' ab c': {'ab', 'c'} |
|
||||||
} |
|
||||||
test expected, source |
|
@ -1,82 +0,0 @@ |
|||||||
describe 'tableutil', -> |
|
||||||
describe 'sortedpairs', -> |
|
||||||
import sortedpairs from require 'moonbuild.tableutil' |
|
||||||
|
|
||||||
for src, dst in pairs { |
|
||||||
[{a: '1', c: 2, b: 3}]: {'a', '1', 'b', 3, 'c', 2} |
|
||||||
[{5, 4, 3}]: {1, 5, 2, 4, 3, 3} |
|
||||||
} |
|
||||||
it "works for #{src}", -> |
|
||||||
i = 1 |
|
||||||
for k, v in sortedpairs src |
|
||||||
assert.equal k, dst[i] |
|
||||||
assert.equal v, dst[i+1] |
|
||||||
i += 2 |
|
||||||
|
|
||||||
describe 'min and max', -> |
|
||||||
import min, max from require 'moonbuild.tableutil' |
|
||||||
|
|
||||||
for src, dst in pairs { |
|
||||||
[{1, 2, 3, 4, 5}]: {1, 5} |
|
||||||
[{5, 4, 3, 2, 1}]: {1, 5} |
|
||||||
[{2, 4, 5, 1, 3}]: {1, 5} |
|
||||||
[{1, 1, 1, 1, 1}]: {1, 1} |
|
||||||
[{1}]: {1, 1} |
|
||||||
} |
|
||||||
it "min of #{table.concat src, ','} is #{dst[1]}", -> |
|
||||||
assert.equal dst[1], min src |
|
||||||
it "max of #{table.concat src, ','} is #{dst[2]}", -> |
|
||||||
assert.equal dst[2], max src |
|
||||||
|
|
||||||
describe 'foreach', -> |
|
||||||
import foreach from require 'moonbuild.tableutil' |
|
||||||
|
|
||||||
src = {1, 2, 5, '79'} |
|
||||||
testall = (name, rst, fn) -> |
|
||||||
it name, -> |
|
||||||
assert.same rst, foreach src, fn |
|
||||||
|
|
||||||
|
|
||||||
testall 'works with tostring', {'1', '2', '5', '79'}, tostring |
|
||||||
testall 'works with tonumber', {1, 2, 5, 79}, tonumber |
|
||||||
testall 'works with some mix of tonumber and comparison', {false, false, true, true}, => 3<tonumber @ |
|
||||||
|
|
||||||
describe 'first', -> |
|
||||||
import first from require 'moonbuild.tableutil' |
|
||||||
|
|
||||||
test = (name, src, rst, fn) -> |
|
||||||
it name, -> |
|
||||||
assert.equal rst, (first src, fn) |
|
||||||
|
|
||||||
test 'works with == for first of list', {1, 3, 5}, 1, => @==1 |
|
||||||
test 'works with == for something else', {1, 3, 5}, 3, => @==3 |
|
||||||
test 'works with == for absent element', {1, 3, 5}, nil, => @==2 |
|
||||||
|
|
||||||
describe 'exclude', -> |
|
||||||
import exclude from require 'moonbuild.tableutil' |
|
||||||
unpack or= table.unpack |
|
||||||
|
|
||||||
test = (name, src, rst, ...) -> |
|
||||||
rest = {...} |
|
||||||
it name, -> |
|
||||||
assert.equal src, exclude src, unpack rest |
|
||||||
assert.same rst, src |
|
||||||
|
|
||||||
test 'works with nothing', {1, 2, 3}, {1, 2, 3} |
|
||||||
test 'works with absent elements', {1, 2, 3}, {1, 2, 3}, 4, 5, 6 |
|
||||||
test 'works with some elements', {1, 2, 3}, {1}, 2, 3 |
|
||||||
test 'works with all elements', {1, 2, 3}, {}, 1, 2, 3 |
|
||||||
test 'works with a mix', {1, 2, 3}, {1, 3}, 2, 4, 5 |
|
||||||
|
|
||||||
describe 'flatten', -> |
|
||||||
import flatten from require 'moonbuild.tableutil' |
|
||||||
|
|
||||||
test = (name, src, rst) -> |
|
||||||
it name, -> |
|
||||||
assert.same rst, flatten src |
|
||||||
|
|
||||||
test 'works with empty table', {}, {} |
|
||||||
test 'works with flat table', {1, 2, 3}, {1, 2, 3} |
|
||||||
test 'works with one level', {1, {2}, {3}}, {1, 2, 3} |
|
||||||
test 'works with multiple levels', {{{1, {{2}}}, 3}}, {1, 2, 3} |
|
||||||
test 'skips maps', {1, {a: 2}, 3}, {1, {a: 2}, 3} |
|
Loading…
Reference in new issue