-- $Id$ -- $HeadURL: https://primemover.svn.sf.net/svnroot/primemover/pm/lib/c.pm $ -- $LastChangedDate: 2007-02-24 01:37:06 +0000 (Sat, 24 Feb 2007) $ -- pm includefile to compile *host* C programs. -- Standard Lua boilerplate. local io_open = io.open local string_gsub = string.gsub local string_gfind = string.gfind local string_find = string.find local table_insert = table.insert local table_getn = table.getn local filetime = pm.filetime -- Define some variables. CCOMPILER = "gcc" CXXCOMPILER = "g++" CC = "%CCOMPILER% %CBUILDFLAGS% %CDYNINCLUDES:cincludes% %CINCLUDES:cincludes% %CDEFINES:cdefines% %CEXTRAFLAGS% -c -o %out% %in%" CXX = "%CXXCOMPILER% %CBUILDFLAGS% %CDYNINCLUDES:cincludes% %CINCLUDES:cincludes% %CDEFINES:cdefines% %CEXTRAFLAGS% -c -o %out% %in%" CPROGRAM = "%CCOMPILER% %CBUILDFLAGS% %CLINKFLAGS% %CEXTRAFLAGS% -o %out% %in% %CLIBRARIES:clibraries%" CXXPROGRAM = "%CXXCOMPILER% %CBUILDFLAGS% %CLINKFLAGS% %CEXTRAFLAGS% -o %out% %in% %CLIBRARIES:clibraries%" CLIBRARY = "rm -f %out% && ar cr %out% %in% && ranlib %out%" CBUILDFLAGS = {"-g", "-Werror", "-std=c89"} CINCLUDES = EMPTY CDEFINES = EMPTY CEXTRAFLAGS = EMPTY CLINKFLAGS = EMPTY CDYNINCLUDES = EMPTY CLIBRARIES = EMPTY --- Custom string modifiers ------------------------------------------------- local function prepend(rule, arg, prefix) if (arg == EMPTY) then return EMPTY end local t = {} for i, j in ipairs(arg) do t[i] = prefix..j end return t end function pm.stringmodifier.cincludes(rule, arg) return prepend(rule, arg, "-I") end function pm.stringmodifier.cdefines(rule, arg) return prepend(rule, arg, "-D") end function pm.stringmodifier.clibraries(rule, arg) if (arg == EMPTY) then return EMPTY end local t = {} for i, j in ipairs(arg) do if string_find(j, "%.a$") then t[i] = j else t[i] = "-l"..j end end return t end --- Manage C file dependencies ---------------------------------------------- local dependency_cache = {} local function calculate_dependencies(filename, includes) -- Cache values, so we don't recalculate dependencies needlessly. local o = dependency_cache[filename] if o then return o end local deps = {} deps[filename] = true local calcdeps = 0 calcdeps = function(filename, file) file = file or io_open(filename) if not file then return end local localincludes = string_gsub(filename, "/[^/]*$", "") if localincludes then localincludes = {localincludes, unpack(includes)} else localincludes = includes end for line in file:lines() do local _, _, f = string_find(line, '^[ \t]*#[ \t]*include[ \t]*["<]([^"]+)[">]') if f then for _, path in ipairs(localincludes) do local subfilename = path.."/"..f local subfile = io_open(subfilename) if subfile then if not deps[subfilename] then deps[subfilename] = true calcdeps(subfilename, subfile) end break end end end end -- Explicit close to avoid having to wait for the garbage collector -- to free up the underlying fd. file:close() end calcdeps(filename) o = {} for i, _ in pairs(deps) do table_insert(o, i) end dependency_cache[filename] = o return o end -- This clause specialises 'simple' to add support for smart dependencies of C -- files. simple_with_clike_dependencies = simple { class = "simple_with_clike_dependencies", makedepends = {"%CDEPENDS%"}, __init = function(self, p) simple.__init(self, p) -- If we're a class, don't verify. if ((type(p) == "table") and p.class) then return end -- If dynamicheaders is an object, turn it into a singleton list. if self.dynamicheaders then if (type(self.dynamicheaders) ~= "table") then self:__error("doesn't know what to do with dynamicheaders, which ", "should be a list or an object but was a ", type(self.dynamicheaders)) end if self.dynamicheaders.class then self.dynamicheaders = {self.dynamicheaders} end end end, __dependencies = function(self, inputs, outputs) local cincludes = self:__index("CINCLUDES") if (type(cincludes) == "string") then cincludes = {cincludes} end local includes = {} for _, i in ipairs(cincludes) do table_insert(includes, self:__expand(i)) end local input = self:__expand(inputs[1]) local depends = calculate_dependencies(input, includes) if not depends then self:__error("could not determine the dependencies for ", pm.rendertable({input})) end if pm.verbose then pm.message('"', input, '" appears to depend on ', pm.rendertable(depends)) end return depends end, __buildadditionalchildren = function(self) self.CDYNINCLUDES = {} if self.dynamicheaders then for _, i in ipairs(self.dynamicheaders) do local o = i:__build() if o[1] then table_insert(self.CDYNINCLUDES, (string_gsub(o[1], "/[^/]*$", ""))) end end end -- If no paths on the list, replace the list with EMPTY so it doesn't -- expand to anything. if (table_getn(self.CDYNINCLUDES) == 0) then self.CDYNINCLUDES = EMPTY end end } -- These are the publically useful clauses. cfile = simple_with_clike_dependencies { class = "cfile", command = {"%CC%"}, outputs = {"%U%-%I%.o"}, } cxxfile = simple_with_clike_dependencies { class = "cxxfile", command = {"%CXX%"}, outputs = {"%U%-%I%.o"}, } cprogram = simple { class = "cprogram", command = {"%CPROGRAM%"}, outputs = {"%U%-%I%"}, } cxxprogram = simple { class = "cxxprogram", command = {"%CXXPROGRAM%"}, outputs = {"%U%-%I%"}, } clibrary = simple { class = "clibrary", command = { "%CLIBRARY%" }, outputs = {"%U%-%I%.a"}, }