-- 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 table_insert = table.insert
local table_getn = table.getn
local filetime = pm.filetime

-- Define some variables.

CCOMPILER = "gcc"
CC = "%CCOMPILER% %CBUILDFLAGS% %CDYNINCLUDES% %CINCLUDES% %CDEFINES% %CEXTRAFLAGS% -c -o %out% %in%"
CPROGRAM = "%CCOMPILER% %CBUILDFLAGS% %CLINKFLAGS% %CEXTRAFLAGS% -o %out% %in% %CLIBRARIES%"
CDEPENDS = "%CCOMPILER% %CBUILDFLAGS% %CDYNINCLUDES% %CINCLUDES% %CDEFINES% %CEXTRAFLAGS% -MM -MG %in% > %out%"
AR = "%RM% %out% && ar cr %out% %in% && ranlib %out%"

CBUILDFLAGS = "-g -O"
CINCLUDES = {}
CDEFINES = {}
CEXTRAFLAGS = ""
CLINKFLAGS = ""
CDYNINCLUDES = ""
CLIBRARIES = ""

--- Manage C file dependencies ----------------------------------------------

local dependency_cache = {}
local function load_dependency_file(fn)
	local o = dependency_cache[fn]
	if o then
		return o
	end
	
	-- Read in the dependency file.
	
	local f = io_open(fn)
	if not f then
		print("failed to open "..fn)
		return nil
	end
	f = f:read("*a")
	
	-- Massage the dependency file into a string containing one unescaped
	-- filename per line.
	
	f = string_gsub(f, "^.*[^\\]: *", "")
	f = string_gsub(f, "\\\r?\n", "")
	f = string_gsub(f, "([^\\]) +", "%1\n")
	f = string_gsub(f, "\\", "")
	
	-- Parse the string.
	
	o = {}
	for l in string_gfind(f, "[^\n]+") do
		table_insert(o, l)
	end
	
	dependency_cache[fn] = 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 obj = simple {
			CDYNINCLUDES = self.CDYNINCLUDES,
			command = self.makedepends,
			outputs = {"%U%-%I%.d"},
			unpack(inputs)
		}
		local o = obj:__build()
		local depends = load_dependency_file(o[1])
		if not depends then
			self:__error("could not determine the dependencies for ",
				pm.rendertable(inputs))
		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
					self.CDYNINCLUDES = self.CDYNINCLUDES..' "-I'..string_gsub(o[1], "/[^/]*$", "")..'"'
				end
			end
		end
	end
}

-- These are the publically useful clauses.

cfile = simple_with_clike_dependencies {
	class = "cfile",
	command = {"%CC%"},
	outputs = {"%U%-%I%.o"},
}

cprogram = simple {
	class = "cprogram",
	command = {"%CPROGRAM%"},
	outputs = {"%U%-%I%"},
}

clibrary = simple {
	class = "clibrary",
	command = {
		"%AR%"
	},
	outputs = {"%U%-%I%.a"},
}