#!/usr/bin/env moon argparse=require 'argparse' require 'moonscript' import loadfile from require 'moonscript.base' import truncate_traceback, rewrite_traceback from require 'moonscript.errors' import trim from require 'moonscript.util' util=require 'moonbuild.util' import exists, mtime, run, min, max, first, flatten, match, patsubst, sortedpairs from util import insert, concat from table 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! -- util functions loadwithscope= (file, scope) -> fn, err=loadfile file error err or "failed to load code" unless fn dumped, err=string.dump fn error err or "failed to dump function" unless dumped load dumped, file, 'b', scope pcall= (fn, ...) -> rewrite=(err) -> trace=debug.traceback '', 2 trunc=truncate_traceback trim trace rewrite_traceback trunc, err 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 -- represents a target class BuildObject 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}" ok, err=pcall -> @.fn ins: ins outs: outs infile: ins[1] outfile: outs[1] name: name 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) => return true if args.noskip return true if #@ins==0 or #@outs==0 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 targets={} defaulttarget='all' 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 buildscope[k]=fn for k, fn in pairs util setmetatable buildscope, __index: (k) => global=rawget _G, k return global if global (...) -> Command k, ... 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 ok, err=pcall buildfn unless ok if err io.stderr\write err, '\n' else io.stderr\write "Unknown error\n" os.exit 1 if args.list io.write "Available targets:\n" io.write "\t#{concat targets, ', '}\n" os.exit 0 if args.deps io.write "Targets:\n" for target, deps in sortedpairs BuildObject\list!, (a, b) -> a.name #{concat target.outs, ', '})" io.write "\n" for name, dep in sortedpairs deps io.write "\t\t#{name} (#{dep.name})\n" os.exit 0 if #args.targets==0 BuildObject\build defaulttarget for target in *args.targets BuildObject\build target