From 77ac825d39d7602ac59cead085b962d75060c143 Mon Sep 17 00:00:00 2001 From: Bogdan Marinescu Date: Sat, 5 Mar 2011 20:22:11 +0200 Subject: [PATCH] Targets can be reffered to by name now Now a target can be reffered to also by its name, not only by the corresponding _target object. Also added 'utils.lua' as a separate file in utils/ (it used to be a part of build.lua) --- .gitignore | 14 +++ utils/build.lua | 235 +++++++++++++----------------------------------- utils/utils.lua | 143 +++++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 171 deletions(-) create mode 100644 .gitignore create mode 100644 utils/utils.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..50a490b8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +.build/ +*.hex +*.elf +*.o +*.obj +*.exe +*.bin +*.pyc +inc/romfiles.h +*.swp +.sconf_temp/ +.sconsign.dblite +config.log + diff --git a/utils/build.lua b/utils/build.lua index bbae5782..77abb5dc 100644 --- a/utils/build.lua +++ b/utils/build.lua @@ -4,10 +4,7 @@ module( ..., package.seeall ) local lfs = require "lfs" local sf = string.format - --- Taken from Lake -local dir_sep = package.config:sub( 1, 1 ) -local is_windows = dir_sep == '\\' +utils = require "utils.utils" ------------------------------------------------------------------------------- -- Various helpers @@ -46,6 +43,13 @@ local function table_to_string( t ) return t end +-- Helper: return the extended type of an object (takes into account __type) +local function exttype( o ) + local t = type( o ) + if t == "table" and o.__type then t = o:__type() end + return t +end + --------------------------------------- -- Table utils -- (from http://lua-users.org/wiki/TableUtils) @@ -85,143 +89,6 @@ function table.tostring( tbl ) return "{" .. table.concat( result, "," ) .. "}" end - -------------------------------------------------------------------------------- --- Public utilities - -utils = { dir_sep = dir_sep } - --- Converts a string with items separated by 'sep' into a table -utils.string_to_table = function( s, sep ) - if type( s ) ~= "string" then return end - sep = sep or ' ' - if s:sub( -1, -1 ) ~= sep then s = s .. sep end - s = s:gsub( sf( "^%s*", sep ), "" ) - local t = {} - local fmt = sf( "(.-)%s+", sep ) - for w in s:gmatch( fmt ) do table.insert( t, w ) end - return t -end - --- Split a file name into 'path part' and 'extension part' -utils.split_path = function( s ) - local pos - for i = #s, 1, -1 do - if s:sub( i, i ) == "." then - pos = i - break - end - end - if pos then return s:sub( 1, pos - 1 ), s:sub( pos ) end - return s -end - --- Replace the extension of a give file name -utils.replace_extension = function( s, newext ) - local p, e = utils.split_path( s ) - if e then s = p .. "." .. newext end - return s -end - --- Return 'true' if building from Windows, false otherwise -utils.is_windows = function() - return is_windows -end - --- Prepend each component of a 'pat'-separated string with 'prefix' -utils.prepend_string = function( s, prefix, pat ) - if not s or #s == 0 then return "" end - pat = pat or ' ' - local res = '' - local st = utils.string_to_table( s, pat ) - utils.foreach( st, function( k, v ) res = res .. prefix .. v .. " " end ) - return res -end - --- Like above but consider 'prefix' a path -utils.prepend_path = function( s, prefix, pat ) - return utils.prepend_string( s, prefix .. dir_sep, pat ) -end - --- full mkdir: create all the paths needed for a multipath -utils.full_mkdir = function( path ) - local ptables = utils.string_to_table( path, dir_sep ) - local p, res = '' - for i = 1, #ptables do - p = ( i ~= 1 and p .. dir_sep or p ) .. ptables[ i ] - res = lfs.mkdir( p ) - end - return res -end - --- Concatenate the given paths to form a complete path -utils.concat_path = function( paths ) - return table.concat( paths, dir_sep ) -end - --- Return true if the given array contains the given element, false otherwise -utils.array_element_index = function( arr, element ) - for i = 1, #arr do - if arr[ i ] == element then return i end - end -end - --- Linearize an array with (possibly) embedded arrays into a simple array -utils._linearize_array = function( arr, res ) - if type( arr ) ~= "table" then return end - for i = 1, #arr do - local e = arr[ i ] - if type( e ) == 'table' then - utils._linearize_array( e, res ) - else - table.insert( res, e ) - end - end -end - -utils.linearize_array = function( arr ) - local res = {} - utils._linearize_array( arr, res ) - return res -end - --- Return an array with the keys of a table -utils.table_keys = function( t ) - local keys = {} - utils.foreach( t, function( k, v ) table.insert( keys, k ) end ) - return keys -end - --- Returns true if 'path' is a regular file, false otherwise -utils.is_file = function( path ) - return lfs.attributes( path, "mode" ) == "file" -end - --- Return a list of files in the given directory matching a given mask -utils.get_files = function( path, mask ) - local t = '' - for f in lfs.dir( path ) do - local fname = path .. dir_sep .. f - if lfs.attributes( fname, "mode" ) == "file" and fname:find( mask ) then - t = t .. ' ' .. fname - end - end - return t -end - --- Check if the given command can be executed properly -utils.check_command = function( cmd ) - local res = os.execute( cmd .. " > .build.temp 2>&1" ) - os.remove( ".build.temp" ) - return res -end - --- Execute the given command for each value in a table -utils.foreach = function ( t, cmd ) - if type( t ) ~= "table" then return end - for k, v in pairs( t ) do cmd( k, v ) end -end - ------------------------------------------------------------------------------- -- Dummy 'builder': simply checks the date of a file @@ -250,7 +117,7 @@ _fbuilder.target_name = function( self ) end -- Object type -_fbuilder.objtype = function() +_fbuilder.__type = function() return "_fbuilder" end @@ -264,29 +131,40 @@ _target.new = function( target, dep, command, builder ) setmetatable( self, { __index = _target } ) self.target = target self.command = command - self:set_dependencies( dep ) - self._force_rebuild = #self.dep == 0 self.builder = builder - builder.tlist[ target ] = self + builder:register_target( target, self ) + self:set_dependencies( dep ) + self.dep = self:_build_dependencies( self.origdep ) + self._force_rebuild = #self.dep == 0 builder.runlist[ target ] = false return self end --- Set dependencies _target.set_dependencies = function( self, dep ) - -- Transform 'dep' into a list of objects that support the 'build' method - if type( dep ) == 'string' then - dep = utils.string_to_table( dep ) - elseif type( dep ) ~= 'table' then - dep = {} + self.origdep = dep +end + +-- Set dependencies +-- This uses a proxy table and returns string deps dynamically according +-- to the targets currently registered in the builder +_target._build_dependencies = function( self, dep ) + -- Step 1: start with an array + if type( dep ) == "string" then dep = utils.string_to_table( dep ) end + -- Step 2: linearize "dep" array keeping targets + local filter = function( e ) + local t = exttype( e ) + return t ~= "_ftarget" and t ~= "_target" end - -- Iterate through 'dep' transforming file names in _fbuilder targets + dep = utils.linearize_array( dep, filter ) + -- Step 3: strings are turned into _fbuilder objects if not found as targets; + -- otherwise the corresponding target object is used for i = 1, #dep do - if type( dep[ i ] ) == "string" then - dep[ i ] = _fbuilder.new( self.target, dep[ i ] ) + if type( dep[ i ] ) == 'string' then + local t = self.builder:get_registered_target( dep[ i ] ) + dep[ i ] = t or _fbuilder.new( self.target, dep[ i ] ) end end - self.dep = dep + return dep end -- Set pre-build function @@ -325,14 +203,15 @@ _target.build = function( self ) local docmd = self:target_name() and lfs.attributes( self:target_name(), "mode" ) ~= "file" docmd = docmd or self.builder.global_force_rebuild local initdocmd = docmd - local depends, dep = '', self.dep + self.dep = self:_build_dependencies( self.origdep ) + local depends, dep, previnit = '', self.dep, self.origdep -- Iterate through all dependencies, execute each one in turn local deprunner = function() for i = 1, #dep do local res = dep[ i ]:build() docmd = docmd or res local t = dep[ i ]:target_name() - if dep[ i ]:objtype() == "_target" and t then + if exttype( dep[ i ] ) == "_target" and t then docmd = docmd or get_ftime( t ) > get_ftime( self.target ) end if t then depends = depends .. t .. " " end @@ -343,9 +222,10 @@ _target.build = function( self ) if self._pre_build_function then self._pre_build_function( self, docmd ) end -- If the dependencies changed as a result of running the pre-build function -- run through them again - if dep ~= self.dep then - depends, dep, docmd = '', self.dep, initdocmd - deprunner() + if previnit ~= self.origdep then + self.dep = self:_build_dependencies( self.origdep ) + depends, dep, docmd = '', self.dep, initdocmd + deprunner() end -- If at least one dependency is new rebuild the target docmd = docmd or self._force_rebuild or self.builder.clean_mode @@ -384,7 +264,7 @@ _target.target_name = function( self ) end -- Object type -_target.objtype = function() +_target.__type = function() return "_target" end @@ -409,7 +289,7 @@ builder.new = function( build_dir ) self.build_mode = self.KEEP_DIR self.targets = {} self.targetargs = {} - self.tlist = {} + self._tlist = {} self.runlist = {} return self end @@ -688,7 +568,7 @@ builder._compare_config = function( self, what ) local res = false local crtstate = self:_config_to_string( what ) if not self.clean_mode then - local fconf = io.open( self.build_dir .. dir_sep .. ".builddata." .. what, "rb" ) + local fconf = io.open( self.build_dir .. utils.dir_sep .. ".builddata." .. what, "rb" ) if fconf then local oldstate = fconf:read( "*a" ) fconf:close() @@ -696,7 +576,7 @@ builder._compare_config = function( self, what ) end end -- Write state to build dir - fconf = io.open( self.build_dir .. dir_sep .. ".builddata." .. what, "wb" ) + fconf = io.open( self.build_dir .. utils.dir_sep .. ".builddata." .. what, "wb" ) if fconf then fconf:write( self:_config_to_string( what ) ) fconf:close() @@ -756,7 +636,7 @@ end -- Return the name of a dependency file name corresponding to a C source builder.get_dep_filename = function( self, srcname ) - return utils.replace_extension( self.build_dir .. dir_sep .. linearize_fname( srcname ), "d" ) + return utils.replace_extension( self.build_dir .. utils.dir_sep .. linearize_fname( srcname ), "d" ) end -- Create a return a new C dependency target @@ -780,6 +660,19 @@ builder.target = function( self, dest_target, deps, cmd ) return _target.new( dest_target, deps, cmd, self ) end +-- Register a target (called from _target.new) +builder.register_target = function( self, name, obj ) + self._tlist[ name:gsub( "\\", "/" ) ] = obj +end + +-- Returns a registered target (nil if not found) +builder.get_registered_target = function( self, name ) + return self._tlist[ name:gsub( "\\", "/" ) ] +end + +--------------------------------------- +-- Actual building functions + -- Return the object name corresponding to a source file name builder.obj_name = function( self, name ) local r = self.obj_extension @@ -793,11 +686,11 @@ builder.obj_name = function( self, name ) if self.build_mode == self.KEEP_DIR then return objname elseif self.build_mode == self.BUILD_DIR_LINEARIZED then - return self.build_dir .. dir_sep .. linearize_fname( objname ) + return self.build_dir .. utils.dir_sep .. linearize_fname( objname ) else local si, ei, path, fname = objname:find( "(.+)/(.-)$" ) if not si then fname = objname end - return self.build_dir .. dir_sep .. fname + return self.build_dir .. utils.dir_sep .. fname end end @@ -866,9 +759,9 @@ builder.create_compile_targets = function( self, ftable, res ) local target local deps = self:get_dep_filename( ftable[ i ] ) if ftable[ i ]:find( "%.c$" ) then - target = self:c_target( self:obj_name( ftable[ i ] ), { self.tlist[ deps ] or ftable[ i ] } ) + target = self:c_target( self:obj_name( ftable[ i ] ), { self:get_registered_target( deps ) or ftable[ i ] } ) else - target = self:asm_target( self:obj_name( ftable[ i ] ), { self.tlist[ deps ] or ftable[ i ] } ) + target = self:asm_target( self:obj_name( ftable[ i ] ), { self:get_registered_target( deps ) or ftable[ i ] } ) end -- Post build step: replace dependencies with the ones generated by 'make_depends' target:set_pre_build_function( function( t, _ ) @@ -929,8 +822,8 @@ builder.build = function( self, target ) local res = self.targets[ t ].target:build() if not res then print( sf( '[builder] %s: up to date', t ) ) end if self.clean_mode then - os.remove( self.build_dir .. dir_sep .. ".builddata.comp" ) - os.remove( self.build_dir .. dir_sep .. ".builddata.link" ) + os.remove( self.build_dir .. utils.dir_sep .. ".builddata.comp" ) + os.remove( self.build_dir .. utils.dir_sep .. ".builddata.link" ) end print "[builder] Done building target." return res diff --git a/utils/utils.lua b/utils/utils.lua new file mode 100644 index 00000000..2de48561 --- /dev/null +++ b/utils/utils.lua @@ -0,0 +1,143 @@ +-- Generic utility functions + +module( ..., package.seeall ) + +local lfs = require "lfs" +local sf = string.format + +-- Taken from Lake +dir_sep = package.config:sub( 1, 1 ) +is_os_windows = dir_sep == '\\' + +-- Converts a string with items separated by 'sep' into a table +string_to_table = function( s, sep ) + if type( s ) ~= "string" then return end + sep = sep or ' ' + if s:sub( -1, -1 ) ~= sep then s = s .. sep end + s = s:gsub( sf( "^%s*", sep ), "" ) + local t = {} + local fmt = sf( "(.-)%s+", sep ) + for w in s:gmatch( fmt ) do table.insert( t, w ) end + return t +end + +-- Split a file name into 'path part' and 'extension part' +split_path = function( s ) + local pos + for i = #s, 1, -1 do + if s:sub( i, i ) == "." then + pos = i + break + end + end + if pos then return s:sub( 1, pos - 1 ), s:sub( pos ) end + return s +end + +-- Replace the extension of a give file name +replace_extension = function( s, newext ) + local p, e = split_path( s ) + if e then s = p .. "." .. newext end + return s +end + +-- Return 'true' if building from Windows, false otherwise +is_windows = function() + return is_os_windows +end + +-- Prepend each component of a 'pat'-separated string with 'prefix' +prepend_string = function( s, prefix, pat ) + if not s or #s == 0 then return "" end + pat = pat or ' ' + local res = '' + local st = string_to_table( s, pat ) + foreach( st, function( k, v ) res = res .. prefix .. v .. " " end ) + return res +end + +-- Like above but consider 'prefix' a path +prepend_path = function( s, prefix, pat ) + return prepend_string( s, prefix .. dir_sep, pat ) +end + +-- full mkdir: create all the paths needed for a multipath +full_mkdir = function( path ) + local ptables = string_to_table( path, dir_sep ) + local p, res = '' + for i = 1, #ptables do + p = ( i ~= 1 and p .. dir_sep or p ) .. ptables[ i ] + res = lfs.mkdir( p ) + end + return res +end + +-- Concatenate the given paths to form a complete path +concat_path = function( paths ) + return table.concat( paths, dir_sep ) +end + +-- Return true if the given array contains the given element, false otherwise +array_element_index = function( arr, element ) + for i = 1, #arr do + if arr[ i ] == element then return i end + end +end + +-- Linearize an array with (possibly) embedded arrays into a simple array +_linearize_array = function( arr, res, filter ) + if type( arr ) ~= "table" then return end + for i = 1, #arr do + local e = arr[ i ] + if type( e ) == 'table' and filter( e ) then + _linearize_array( e, res, filter ) + else + table.insert( res, e ) + end + end +end + +linearize_array = function( arr, filter ) + local res = {} + filter = filter or function( v ) return true end + _linearize_array( arr, res, filter ) + return res +end + +-- Return an array with the keys of a table +table_keys = function( t ) + local keys = {} + foreach( t, function( k, v ) table.insert( keys, k ) end ) + return keys +end + +-- Returns true if 'path' is a regular file, false otherwise +is_file = function( path ) + return lfs.attributes( path, "mode" ) == "file" +end + +-- Return a list of files in the given directory matching a given mask +get_files = function( path, mask ) + local t = '' + for f in lfs.dir( path ) do + local fname = path .. dir_sep .. f + if lfs.attributes( fname, "mode" ) == "file" and fname:find( mask ) then + t = t .. ' ' .. fname + end + end + return t +end + +-- Check if the given command can be executed properly +check_command = function( cmd ) + local res = os.execute( cmd .. " > .build.temp 2>&1" ) + os.remove( ".build.temp" ) + return res +end + +-- Execute the given command for each value in a table +foreach = function ( t, cmd ) + if type( t ) ~= "table" then return end + for k, v in pairs( t ) do cmd( k, v ) end +end +