238 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Raku
		
	
	
	
	
	
			
		
		
	
	
			238 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			Raku
		
	
	
	
	
	
-- $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"}
 | 
						|
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"},
 | 
						|
}
 |