diff --git a/Alfons.moon b/Alfons.moon index c271a7c..3bd9faf 100644 --- a/Alfons.moon +++ b/Alfons.moon @@ -1,7 +1,13 @@ tasks: - build: => - sh "moon bin/moonbuild.moon -jy" + bootstrap: => sh "moon bin/moonbuild.moon -jy" + bootstrapinstall: => sh "moon bin/moonbuild.moon install -jy" + release: => error "no version provided" unless @v tasks.build! sh "rockbuild -m -t #{@v} upload" + + build: => (require 'moonbuild') j: true + install: => (require 'moonbuild') 'install', j: true + clean: => (require 'moonbuild') 'clean' + mrproper: => (require 'moonbuild') 'mrproper' diff --git a/Build.moon b/Build.moon index 8f35782..729d809 100644 --- a/Build.moon +++ b/Build.moon @@ -17,9 +17,20 @@ with public default target 'all' \after 'lib' with public target 'install' + \after 'install-bin' + \after 'install-lib' + +with public target 'install-bin' \depends 'out/moonbuild' \produces '/usr/local/bin/moonbuild' \fn => _.cmd 'sudo', 'cp', @infile, @outfile + \sync! + +with public target 'install-lib' + \depends 'out/moonbuild.lua' + \produces "/usr/local/share/lua/#{LUA\gsub 'lua', ''}/moonbuild.lua" + \fn => _.cmd 'sudo', 'cp', @infile, @outfile + \sync! with public target 'clean' \fn => _.cmd RM, LIB_LUA @@ -45,6 +56,13 @@ with target BIN, pattern: 'out/%' _.writefile @outfile, "#!/usr/bin/env #{LUA}\n#{_.readfile @outfile}" _.cmd 'chmod', '+x', @outfile +with target 'out/moonbuild.lua' + \depends 'moonbuild/init.lua' + \depends LIB_LUA + \produces '%' + \fn => + _.cmd AMALG, '-o', @outfile, '-s', @infile, _.exclude MODULES, 'moonbuild.init' + with target {LIB_LUA, BIN_LUA}, pattern: '%.lua' \depends '%.moon' \produces '%.lua' diff --git a/README.md b/README.md index 250b8fa..11b9b4e 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,8 @@ You will need `argparse` and `moonscript` installed from luarocks, and `luaposix ### Bootstrapping You can build moonbuild with itself: `moon bin/moonbuild.moon -qjy`. -This will leave the binary ready to be used as `out/moonbuild`. - -### Using make -You can also build moonbuild with make: `make`. -This will leave the binary ready to be used as `out/moonbuild`. +This will leave the binary ready to be used as `out/moonbuild`. +The binary and library can then be installed with `bin/moonbuild install`. ## Docs TODO diff --git a/bin/moonbuild.moon b/bin/moonbuild.moon index 3fd336b..3bc6aba 100644 --- a/bin/moonbuild.moon +++ b/bin/moonbuild.moon @@ -3,6 +3,7 @@ import loadfile from require 'moonscript.base' Context = require 'moonbuild.context' Variable = require 'moonbuild.core.Variable' DepGraph = require 'moonbuild.core.DAG' +Executor = require 'moonbuild.core.executor' import parseargs from require 'moonbuild._cmd.common' import sort, concat from table import exit from os @@ -74,15 +75,8 @@ 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 - ok, Executor = pcall -> require 'moonbuild.core.multiprocessexecutor' - Executor = require 'moonbuild.core.singleprocessexecutor' unless ok - nparallel = args.parallel == 'y' and Executor\getmaxparallel! or args.parallel - print "Building with #{nparallel} max parallel process#{nparallel>1 and "es" or ""}" if args.verbose - executor = Executor dag, nparallel - executor\execute args +nparallel = args.parallel == 'y' and Executor\getmaxparallel! or args.parallel +print "Building with #{nparallel} max parallel process#{nparallel>1 and "es" or ""}" if args.verbose +executor = Executor dag, nparallel +executor\execute args print "Finished" if args.verbose diff --git a/moonbuild/_common.moon b/moonbuild/_common.moon index 57c9123..d36a365 100644 --- a/moonbuild/_common.moon +++ b/moonbuild/_common.moon @@ -155,6 +155,7 @@ common.includes = includes common.patget = patget common.patset = patset common.patsubst = patsubst +common.exclude = exclude common.min = min common.max = max common.minmax = minmax diff --git a/moonbuild/core/DAG.moon b/moonbuild/core/DAG.moon index bc36f44..d536801 100644 --- a/moonbuild/core/DAG.moon +++ b/moonbuild/core/DAG.moon @@ -1,8 +1,8 @@ -import first, filter, foreach, flatten, patsubst from require 'moonbuild._common' +import first, filter, foreach, flatten, patsubst, includes 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 sort, insert, remove from table import huge from math local DepNode, FileTarget @@ -76,6 +76,20 @@ class DepGraph buildablenodes: => [v for k, v in pairs @nodes when v\canbuild! and not v.built] + reset: => + n.built = false for k, n in pairs @nodes + + resetchildren: (names) => + done = {} + stack = [v for v in *names] + while #stack != 0 + name = remove stack + continue if done[name] + done[name] = true + node = @nodes[name] + node.built = false + insert stack, n for n in *(node\children!) + class DepNode new: (@dag, target, @name) => @priority = target.priority @@ -117,6 +131,9 @@ class DepNode @deps = flatten { deps, after } @built = true if #@deps == 0 and #@buildfunctions == 0 + children: => + [k for k, n in pairs @dag.nodes when (includes n.ins, @name) or (includes n.after, @name)] + canbuild: => for node in *flatten { @ins, @after } if not @dag.nodes[node].built @@ -130,10 +147,11 @@ class DepNode 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 + return false if @built or #@buildfunctions == 0 + return false unless force or @shouldbuild! + print "#{@type == 'virtual' and "Running" or "Building"} #{@name}" unless quiet @actuallybuild! + true shouldbuild: => diff --git a/moonbuild/core/executor.moon b/moonbuild/core/executor.moon new file mode 100644 index 0000000..f09b2cb --- /dev/null +++ b/moonbuild/core/executor.moon @@ -0,0 +1,2 @@ +ok, MultiProcessExecutor = pcall -> require 'moonbuild.core.multiprocessexecutor' +ok and MultiProcessExecutor or require 'moonbuild.core.singleprocessexecutor' diff --git a/moonbuild/core/multiprocessexecutor.moon b/moonbuild/core/multiprocessexecutor.moon index 2d81b19..022c38a 100644 --- a/moonbuild/core/multiprocessexecutor.moon +++ b/moonbuild/core/multiprocessexecutor.moon @@ -1,3 +1,4 @@ +SingleProcessExecutor = require 'moonbuild.core.singleprocessexecutor' import fork, _exit from require 'posix.unistd' import wait from require 'posix.sys.wait' import open, stderr from io @@ -17,8 +18,12 @@ class Executor @processes = {} @nprocesses = 0 @building = {} + @nbuilt = 0 execute: (opts) => + if @nparallel == 1 + return (SingleProcessExecutor @dag, 1)\execute opts + block = @dag\buildablenodes! while #block != 0 for node in *block @@ -36,6 +41,12 @@ class Executor for name, node in pairs @dag.nodes error "Node #{name} wasn't built" unless node.built + unless opts.quiet + if @nbuilt == 0 + print "Nothing to be done" + else + print "Built #{@nbuilt} targets" + addprocess: (node, opts) => if node.sync while @nprocesses != 0 @@ -52,18 +63,20 @@ class Executor @nprocesses += 1 @building[node] = true else - ok, err = pcall -> node\build opts + ok, status = pcall -> node\build opts if ok + _exit status and 0 or 2 _exit 0 else - stderr\write err + stderr\write status _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 + error "Failed to build #{@processes[pid].name}" if ty != 'exited' or status != 0 and status != 2 @processes[pid].built = true @processes[pid]\updatecache! @processes[pid] = nil @nprocesses -= 1 + @nbuilt += 1 if status == 0 diff --git a/moonbuild/core/singleprocessexecutor.moon b/moonbuild/core/singleprocessexecutor.moon index 2b93f9c..95bfa1a 100644 --- a/moonbuild/core/singleprocessexecutor.moon +++ b/moonbuild/core/singleprocessexecutor.moon @@ -4,13 +4,21 @@ class Executor new: (@dag, @nparallel) => execute: (opts) => + nbuilt = 0 + block = @dag\buildablenodes! while #block != 0 for node in *block - node\build opts + nbuilt +=1 if 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 + + unless opts.quiet + if nbuilt == 0 + print "Nothing to be done" + else + print "Built #{nbuilt} targets" diff --git a/moonbuild/init.moon b/moonbuild/init.moon new file mode 100644 index 0000000..5c7fd68 --- /dev/null +++ b/moonbuild/init.moon @@ -0,0 +1,60 @@ +import loadfile from require 'moonscript.base' +Context = require 'moonbuild.context' +DepGraph = require 'moonbuild.core.DAG' +Executor = require 'moonbuild.core.executor' +_ = require 'moonbuild._' +import insert from table + +moonbuild = (...) -> + -- build argument table + opts = {} + for i=1, select '#', ... + arg = select i, ... + if (type arg) == 'string' + insert opts, arg + elseif (type arg) == 'table' + for k, v in pairs arg + opts[k] = v if (type k) != 'number' + for i, v in ipairs arg + insert opts, v + else + error "Invalid argument type #{type arg} for moonbuild" + + -- resolve arguments + buildfile = opts.buildfile or opts.b or 'Build.moon' + opts.buildfile = buildfile + parallel = opts.parallel or opts.j or 1 + parallel = true if parallel == 'y' + opts.parallel = parallel + quiet = opts.quiet or opts.q or false + opts.quiet = quiet + force = opts.force or opts.f or false + opts.force = force + verbose = opts.verbose or opts.v or false + opts.verbose = verbose + + -- create context and DAG + ctx = Context! + ctx\load (loadfile buildfile), opts + print "Loaded buildfile" if verbose + ctx\init! + print "Initialized buildfile" if verbose + targets = #opts==0 and ctx.defaulttargets or opts + dag = DepGraph ctx, targets + print "Created dependancy graph" if verbose + + -- and build + nparallel = parallel == true and Executor\getmaxparallel! or parallel + print "Building with #{nparallel} max parallel process#{nparallel>1 and "es" or ""}" if verbose + executor = Executor dag, nparallel + executor\execute opts + print "Finished" if verbose + +table = { + :moonbuild, :_ + :Context, :DepGraph, :Executor +} + +setmetatable table, + __call: (...) => moonbuild ... + __index: (name) => require "moonbuild.#{name}" diff --git a/out/moonbuild b/out/moonbuild index 73c4ee0..9655c99 100755 --- a/out/moonbuild +++ b/out/moonbuild @@ -636,6 +636,7 @@ common.includes = includes common.patget = patget common.patset = patset common.patsubst = patsubst +common.exclude = exclude common.min = min common.max = max common.minmax = minmax @@ -1646,10 +1647,10 @@ end do local _ENV = _ENV package.preload[ "moonbuild.core.DAG" ] = function( ... ) local arg = _G.arg; -local first, filter, foreach, flatten, patsubst +local first, filter, foreach, flatten, patsubst, includes do local _obj_0 = require('moonbuild._common') - first, filter, foreach, flatten, patsubst = _obj_0.first, _obj_0.filter, _obj_0.foreach, _obj_0.flatten, _obj_0.patsubst + first, filter, foreach, flatten, patsubst, includes = _obj_0.first, _obj_0.filter, _obj_0.foreach, _obj_0.flatten, _obj_0.patsubst, _obj_0.includes end local runwithcontext runwithcontext = require('moonbuild.compat.ctx').runwithcontext @@ -1659,8 +1660,11 @@ do local _obj_0 = require('moonbuild._fs') exists, parent, mkdirs, clearentry, disableentry, attributes = _obj_0.exists, _obj_0.parent, _obj_0.mkdirs, _obj_0.clearentry, _obj_0.disableentry, _obj_0.attributes end -local sort -sort = table.sort +local sort, insert, remove +do + local _obj_0 = table + sort, insert, remove = _obj_0.sort, _obj_0.insert, _obj_0.remove +end local huge huge = math.huge local DepNode, FileTarget @@ -1774,6 +1778,47 @@ do end end return _accum_0 + end, + reset = function(self) + for k, n in pairs(self.nodes) do + n.built = false + end + end, + resetchildren = function(self, names) + local done = { } + local stack + do + local _accum_0 = { } + local _len_0 = 1 + for _index_0 = 1, #names do + local v = names[_index_0] + _accum_0[_len_0] = v + _len_0 = _len_0 + 1 + end + stack = _accum_0 + end + while #stack ~= 0 do + local _continue_0 = false + repeat + local name = remove(stack) + if done[name] then + _continue_0 = true + break + end + done[name] = true + local node = self.nodes[name] + node.built = false + local _list_0 = (node:children()) + for _index_0 = 1, #_list_0 do + local n = _list_0[_index_0] + insert(stack, n) + end + _continue_0 = true + until true + if not _continue_0 then + break + end + end end } _base_0.__index = _base_0 @@ -1806,6 +1851,17 @@ end do local _class_0 local _base_0 = { + children = function(self) + local _accum_0 = { } + local _len_0 = 1 + for k, n in pairs(self.dag.nodes) do + if (includes(n.ins, self.name)) or (includes(n.after, self.name)) then + _accum_0[_len_0] = k + _len_0 = _len_0 + 1 + end + end + return _accum_0 + end, canbuild = function(self) local _list_0 = flatten({ self.ins, @@ -1832,16 +1888,17 @@ do end local force = opts.force or false local quiet = opts.quiet or false - if self.built then - return + if self.built or #self.buildfunctions == 0 then + return false end if not (force or self:shouldbuild()) then - return + return false end - if not (quiet or #self.buildfunctions == 0) then + if not (quiet) then print(tostring(self.type == 'virtual' and "Running" or "Building") .. " " .. tostring(self.name)) end - return self:actuallybuild() + self:actuallybuild() + return true end, shouldbuild = function(self) if #self.outs == 0 or #self.ins == 0 or self.type == 'virtual' then @@ -2186,9 +2243,20 @@ end end end +do +local _ENV = _ENV +package.preload[ "moonbuild.core.executor" ] = function( ... ) local arg = _G.arg; +local ok, MultiProcessExecutor = pcall(function() + return require('moonbuild.core.multiprocessexecutor') +end) +return ok and MultiProcessExecutor or require('moonbuild.core.singleprocessexecutor') +end +end + do local _ENV = _ENV package.preload[ "moonbuild.core.multiprocessexecutor" ] = function( ... ) local arg = _G.arg; +local SingleProcessExecutor = require('moonbuild.core.singleprocessexecutor') local fork, _exit do local _obj_0 = require('posix.unistd') @@ -2208,6 +2276,9 @@ do local _class_0 local _base_0 = { execute = function(self, opts) + if self.nparallel == 1 then + return (SingleProcessExecutor(self.dag, 1)):execute(opts) + end local block = self.dag:buildablenodes() while #block ~= 0 do for _index_0 = 1, #block do @@ -2255,6 +2326,13 @@ do error("Node " .. tostring(name) .. " wasn't built") end end + if not (opts.quiet) then + if self.nbuilt == 0 then + return print("Nothing to be done") + else + return print("Built " .. tostring(self.nbuilt) .. " targets") + end + end end, addprocess = function(self, node, opts) if node.sync then @@ -2275,13 +2353,14 @@ do self.nprocesses = self.nprocesses + 1 self.building[node] = true else - local ok, err = pcall(function() + local ok, status = pcall(function() return node:build(opts) end) if ok then + _exit(status and 0 or 2) return _exit(0) else - stderr:write(err) + stderr:write(status) return _exit(1) end end @@ -2291,13 +2370,16 @@ do if not (pid) then error("Failed to wait") end - if ty ~= 'exited' or status ~= 0 then + if ty ~= 'exited' or status ~= 0 and status ~= 2 then error("Failed to build " .. tostring(self.processes[pid].name)) end self.processes[pid].built = true self.processes[pid]:updatecache() self.processes[pid] = nil self.nprocesses = self.nprocesses - 1 + if status == 0 then + self.nbuilt = self.nbuilt + 1 + end end } _base_0.__index = _base_0 @@ -2307,6 +2389,7 @@ do self.processes = { } self.nprocesses = 0 self.building = { } + self.nbuilt = 0 end, __base = _base_0, __name = "Executor" @@ -2348,11 +2431,14 @@ do local _class_0 local _base_0 = { execute = function(self, opts) + local nbuilt = 0 local block = self.dag:buildablenodes() while #block ~= 0 do for _index_0 = 1, #block do local node = block[_index_0] - node:build(opts) + if node:build(opts) then + nbuilt = nbuilt + 1 + end node:updatecache() node.built = true end @@ -2363,6 +2449,13 @@ do error("Node " .. tostring(name) .. " wasn't built") end end + if not (opts.quiet) then + if nbuilt == 0 then + return print("Nothing to be done") + else + return print("Built " .. tostring(nbuilt) .. " targets") + end + end end } _base_0.__index = _base_0 @@ -2498,6 +2591,92 @@ end end end +do +local _ENV = _ENV +package.preload[ "moonbuild.init" ] = function( ... ) local arg = _G.arg; +local loadfile +loadfile = require('moonscript.base').loadfile +local Context = require('moonbuild.context') +local DepGraph = require('moonbuild.core.DAG') +local Executor = require('moonbuild.core.executor') +local _ = require('moonbuild._') +local insert +insert = table.insert +local moonbuild +moonbuild = function(...) + local opts = { } + for i = 1, select('#', ...) do + local arg = select(i, ...) + if (type(arg)) == 'string' then + insert(opts, arg) + elseif (type(arg)) == 'table' then + for k, v in pairs(arg) do + if (type(k)) ~= 'number' then + opts[k] = v + end + end + for i, v in ipairs(arg) do + insert(opts, v) + end + else + error("Invalid argument type " .. tostring(type(arg)) .. " for moonbuild") + end + end + local buildfile = opts.buildfile or opts.b or 'Build.moon' + opts.buildfile = buildfile + local parallel = opts.parallel or opts.j or 1 + if parallel == 'y' then + parallel = true + end + opts.parallel = parallel + local quiet = opts.quiet or opts.q or false + opts.quiet = quiet + local force = opts.force or opts.f or false + opts.force = force + local verbose = opts.verbose or opts.v or false + opts.verbose = verbose + local ctx = Context() + ctx:load((loadfile(buildfile)), opts) + if verbose then + print("Loaded buildfile") + end + ctx:init() + if verbose then + print("Initialized buildfile") + end + local targets = #opts == 0 and ctx.defaulttargets or opts + local dag = DepGraph(ctx, targets) + if verbose then + print("Created dependancy graph") + end + local nparallel = parallel == true and Executor:getmaxparallel() or parallel + if verbose then + print("Building with " .. tostring(nparallel) .. " max parallel process" .. tostring(nparallel > 1 and "es" or "")) + end + local executor = Executor(dag, nparallel) + executor:execute(opts) + if verbose then + return print("Finished") + end +end +local table = { + moonbuild = moonbuild, + _ = _, + Context = Context, + DepGraph = DepGraph, + Executor = Executor +} +return setmetatable(table, { + __call = function(self, ...) + return moonbuild(...) + end, + __index = function(self, name) + return require("moonbuild." .. tostring(name)) + end +}) +end +end + end local loadfile @@ -2505,6 +2684,7 @@ loadfile = require('moonscript.base').loadfile local Context = require('moonbuild.context') local Variable = require('moonbuild.core.Variable') local DepGraph = require('moonbuild.core.DAG') +local Executor = require('moonbuild.core.executor') local parseargs parseargs = require('moonbuild._cmd.common').parseargs local sort, concat @@ -2600,24 +2780,12 @@ local dag = DepGraph(ctx, targets) if args.verbose then print("Created dependancy graph") end -if args.parallel == 1 then - local Executor = require('moonbuild.core.singleprocessexecutor') - local executor = Executor(dag, args.parallel) - executor:execute(args) -else - local ok, Executor = pcall(function() - return require('moonbuild.core.multiprocessexecutor') - end) - if not (ok) then - Executor = require('moonbuild.core.singleprocessexecutor') - end - local nparallel = args.parallel == 'y' and Executor:getmaxparallel() or args.parallel - if args.verbose then - print("Building with " .. tostring(nparallel) .. " max parallel process" .. tostring(nparallel > 1 and "es" or "")) - end - local executor = Executor(dag, nparallel) - executor:execute(args) +local nparallel = args.parallel == 'y' and Executor:getmaxparallel() or args.parallel +if args.verbose then + print("Building with " .. tostring(nparallel) .. " max parallel process" .. tostring(nparallel > 1 and "es" or "")) end +local executor = Executor(dag, nparallel) +executor:execute(args) if args.verbose then return print("Finished") end \ No newline at end of file diff --git a/rock.yml b/rock.yml index 3dc5b0c..a907d2e 100644 --- a/rock.yml +++ b/rock.yml @@ -22,3 +22,5 @@ build: install: bin: moonbuild: out/moonbuild + modules: + moonbuild: out/moonbuild.lua