| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 | ---- gmake2_cpp.lua-- Generate a C/C++ project makefile.-- (c) 2016-2017 Jason Perkins, Blizzard Entertainment and the Premake project--	local p = premake	local gmake2 = p.modules.gmake2	gmake2.cpp       = {}	local cpp        = gmake2.cpp	local project    = p.project	local config     = p.config	local fileconfig = p.fileconfig----- Add namespace for element definition lists for premake.callarray()---	cpp.elements = {}---- Generate a GNU make C++ project makefile, with support for the new platforms API.--	cpp.elements.makefile = function(prj)		return {			gmake2.header,			gmake2.phonyRules,			gmake2.shellType,			cpp.createRuleTable,			cpp.outputConfigurationSection,			cpp.outputPerFileConfigurationSection,			cpp.createFileTable,			cpp.outputFilesSection,			cpp.outputRulesSection,			cpp.outputFileRuleSection,			cpp.dependencies,		}	end	function cpp.generate(prj)		p.eol("\n")		p.callArray(cpp.elements.makefile, prj)		-- allow the garbage collector to clean things up.		for cfg in project.eachconfig(prj) do			cfg._gmake = nil		end		prj._gmake = nil	end	function cpp.initialize()		rule 'cpp'			fileExtension { ".cc", ".cpp", ".cxx", ".mm" }			buildoutputs  { "$(OBJDIR)/%{file.objname}.o" }			buildmessage  '$(notdir $<)'			buildcommands {'$(CXX) %{premake.modules.gmake2.cpp.fileFlags(cfg, file)} $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"'}		rule 'cc'			fileExtension {".c", ".s", ".m"}			buildoutputs  { "$(OBJDIR)/%{file.objname}.o" }			buildmessage  '$(notdir $<)'			buildcommands {'$(CC) %{premake.modules.gmake2.cpp.fileFlags(cfg, file)} $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"'}		rule 'resource'			fileExtension ".rc"			buildoutputs  { "$(OBJDIR)/%{file.objname}.res" }			buildmessage  '$(notdir $<)'			buildcommands {'$(RESCOMP) $< -O coff -o "$@" $(ALL_RESFLAGS)'}		global(nil)	end	function cpp.createRuleTable(prj)		local rules = {}		local function addRule(extension, rule)			if type(extension) == 'table' then				for _, value in ipairs(extension) do					addRule(value, rule)				end			else				rules[extension] = rule			end		end		-- add all rules.		local usedRules = table.join({'cpp', 'cc', 'resource'}, prj.rules)		for _, name in ipairs(usedRules) do			local rule = p.global.getRule(name)			addRule(rule.fileExtension, rule)		end		-- create fileset categories.		local filesets = {			['.o']   = 'OBJECTS',			['.obj'] = 'OBJECTS',			['.cc']  = 'SOURCES',			['.cpp'] = 'SOURCES',			['.cxx'] = 'SOURCES',			['.mm']  = 'SOURCES',			['.c']   = 'SOURCES',			['.s']   = 'SOURCES',			['.m']   = 'SOURCES',			['.rc']  = 'RESOURCES',		}		-- cache the result.		prj._gmake = prj._gmake or {}		prj._gmake.rules = rules		prj._gmake.filesets = filesets	end	function cpp.createFileTable(prj)		for cfg in project.eachconfig(prj) do			cfg._gmake = cfg._gmake or {}			cfg._gmake.filesets = {}			cfg._gmake.fileRules = {}			local files = table.shallowcopy(prj._.files)			table.foreachi(files, function(node)				cpp.addFile(cfg, node)			end)			for _, f in pairs(cfg._gmake.filesets) do				table.sort(f)			end			cfg._gmake.kinds = table.keys(cfg._gmake.filesets)			table.sort(cfg._gmake.kinds)			prj._gmake.kinds = table.join(prj._gmake.kinds or {}, cfg._gmake.kinds)		end		-- we need to reassign object sequences if we generated any files.		if prj.hasGeneratedFiles and p.project.iscpp(prj) then			p.oven.assignObjectSequences(prj)		end		prj._gmake.kinds = table.unique(prj._gmake.kinds)		table.sort(prj._gmake.kinds)	end	function cpp.addFile(cfg, node)		local filecfg = fileconfig.getconfig(node, cfg)		if not filecfg or filecfg.flags.ExcludeFromBuild then			return		end		-- skip generated files, since we try to figure it out manually below.		if node.generated then			return		end		-- process custom build commands.		if fileconfig.hasCustomBuildRule(filecfg) then			local env = table.shallowcopy(filecfg.environ)			env.PathVars = {				["file.basename"]     = { absolute = false, token = node.basename },				["file.abspath"]      = { absolute = true,  token = node.abspath },				["file.relpath"]      = { absolute = false, token = node.relpath },				["file.name"]         = { absolute = false, token = node.name },				["file.objname"]      = { absolute = false, token = node.objname },				["file.path"]         = { absolute = true,  token = node.path },				["file.directory"]    = { absolute = true,  token = path.getdirectory(node.abspath) },				["file.reldirectory"] = { absolute = false, token = path.getdirectory(node.relpath) },			}			local shadowContext = p.context.extent(filecfg, env)			local buildoutputs = p.project.getrelative(cfg.project, shadowContext.buildoutputs)			if buildoutputs and #buildoutputs > 0 then				local file = {					buildoutputs  = buildoutputs,					source        = node.relpath,					buildmessage  = shadowContext.buildmessage,					buildcommands = shadowContext.buildcommands,					buildinputs   = p.project.getrelative(cfg.project, shadowContext.buildinputs)				}				table.insert(cfg._gmake.fileRules, file)				for _, output in ipairs(buildoutputs) do					cpp.addGeneratedFile(cfg, node, output)				end			end		else			cpp.addRuleFile(cfg, node)		end	end	function cpp.determineFiletype(cfg, node)		-- determine which filetype to use		local filecfg = fileconfig.getconfig(node, cfg)		local fileext = path.getextension(node.abspath):lower()		if filecfg and filecfg.compileas then			if p.languages.isc(filecfg.compileas) then				fileext = ".c"			elseif p.languages.iscpp(filecfg.compileas) then				fileext = ".cpp"			end		end		return fileext;	end	function cpp.addGeneratedFile(cfg, source, filename)		-- mark that we have generated files.		cfg.project.hasGeneratedFiles = true		-- add generated file to the project.		local files = cfg.project._.files		local node = files[filename]		if not node then			node = fileconfig.new(filename, cfg.project)			files[filename] = node			table.insert(files, node)		end		-- always overwrite the dependency information.		node.dependsOn = source		node.generated = true		-- add to config if not already added.		if not fileconfig.getconfig(node, cfg) then			fileconfig.addconfig(node, cfg)		end		-- determine which filetype to use		local fileext = cpp.determineFiletype(cfg, node)		-- add file to the fileset.		local filesets = cfg.project._gmake.filesets		local kind     = filesets[fileext] or "CUSTOM"		-- don't link generated object files automatically if it's explicitly		-- disabled.		if path.isobjectfile(filename) and source.linkbuildoutputs == false then			kind = "CUSTOM"		end		local fileset = cfg._gmake.filesets[kind] or {}		table.insert(fileset, filename)		cfg._gmake.filesets[kind] = fileset		local generatedKind = "GENERATED"		local generatedFileset = cfg._gmake.filesets[generatedKind] or {}		table.insert(generatedFileset, filename)		cfg._gmake.filesets[generatedKind] = generatedFileset		-- recursively setup rules.		cpp.addRuleFile(cfg, node)	end	function cpp.addRuleFile(cfg, node)		local rules = cfg.project._gmake.rules		local fileext = cpp.determineFiletype(cfg, node)		local rule = rules[fileext]		if rule then			local filecfg = fileconfig.getconfig(node, cfg)			local environ = table.shallowcopy(filecfg.environ)			if rule.propertydefinition then				p.rule.prepareEnvironment(rule, environ, cfg)				p.rule.prepareEnvironment(rule, environ, filecfg)			end			local shadowContext = p.context.extent(rule, environ)			local buildoutputs  = shadowContext.buildoutputs			local buildmessage  = shadowContext.buildmessage			local buildcommands = shadowContext.buildcommands			local buildinputs   = shadowContext.buildinputs			buildoutputs = p.project.getrelative(cfg.project, buildoutputs)			if buildoutputs and #buildoutputs > 0 then				local file = {					buildoutputs  = buildoutputs,					source        = node.relpath,					buildmessage  = buildmessage,					buildcommands = buildcommands,					buildinputs   = buildinputs				}				table.insert(cfg._gmake.fileRules, file)				for _, output in ipairs(buildoutputs) do					cpp.addGeneratedFile(cfg, node, output)				end			end		end	end---- Write out the settings for a particular configuration.--	cpp.elements.configuration = function(cfg)		return {			cpp.tools,			gmake2.target,			gmake2.objdir,			cpp.pch,			cpp.defines,			cpp.includes,			cpp.forceInclude,			cpp.cppFlags,			cpp.cFlags,			cpp.cxxFlags,			cpp.resFlags,			cpp.libs,			cpp.ldDeps,			cpp.ldFlags,			cpp.linkCmd,			cpp.bindirs,			cpp.exepaths,			gmake2.settings,			gmake2.preBuildCmds,			gmake2.preLinkCmds,			gmake2.postBuildCmds,		}	end	function cpp.outputConfigurationSection(prj)		_p('# Configurations')		_p('# #############################################')		_p('')		gmake2.outputSection(prj, cpp.elements.configuration)	end	function cpp.tools(cfg, toolset)		local tool = toolset.gettoolname(cfg, "cc")		if tool then			_p('ifeq ($(origin CC), default)')			_p('  CC = %s', tool)			_p('endif' )		end		tool = toolset.gettoolname(cfg, "cxx")		if tool then			_p('ifeq ($(origin CXX), default)')			_p('  CXX = %s', tool)			_p('endif' )		end		tool = toolset.gettoolname(cfg, "ar")		if tool then			_p('ifeq ($(origin AR), default)')			_p('  AR = %s', tool)			_p('endif' )		end		tool = toolset.gettoolname(cfg, "rc")		if tool then			_p('RESCOMP = %s', tool)		end	end	function cpp.pch(cfg, toolset)		local pch = p.tools.gcc.getpch(cfg)		-- If there is no header, or if PCH has been disabled, I can early out		if pch == nil then			return		end		p.outln('PCH = ' .. pch)		p.outln('PCH_PLACEHOLDER = $(OBJDIR)/$(notdir $(PCH))')		p.outln('GCH = $(PCH_PLACEHOLDER).gch')	end	function cpp.defines(cfg, toolset)		p.outln('DEFINES +=' .. gmake2.list(table.join(toolset.getdefines(cfg.defines, cfg), toolset.getundefines(cfg.undefines))))	end	function cpp.includes(cfg, toolset)		local includes = toolset.getincludedirs(cfg, cfg.includedirs, cfg.sysincludedirs, cfg.frameworkdirs)		p.outln('INCLUDES +=' .. gmake2.list(includes))	end	function cpp.forceInclude(cfg, toolset)		local includes = toolset.getforceincludes(cfg)		p.outln('FORCE_INCLUDE +=' .. gmake2.list(includes))	end	function cpp.cppFlags(cfg, toolset)		local flags = gmake2.list(toolset.getcppflags(cfg))		p.outln('ALL_CPPFLAGS += $(CPPFLAGS)' .. flags .. ' $(DEFINES) $(INCLUDES)')	end	function cpp.cFlags(cfg, toolset)		local flags = gmake2.list(table.join(toolset.getcflags(cfg), cfg.buildoptions))		p.outln('ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS)' .. flags)	end	function cpp.cxxFlags(cfg, toolset)		local flags = gmake2.list(table.join(toolset.getcxxflags(cfg), cfg.buildoptions))		p.outln('ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS)' .. flags)	end	function cpp.resFlags(cfg, toolset)		local resflags = table.join(toolset.getdefines(cfg.resdefines), toolset.getincludedirs(cfg, cfg.resincludedirs), cfg.resoptions)		p.outln('ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)' .. gmake2.list(resflags))	end	function cpp.libs(cfg, toolset)		local flags = toolset.getlinks(cfg)		p.outln('LIBS +=' .. gmake2.list(flags, true))	end	function cpp.ldDeps(cfg, toolset)		local deps = config.getlinks(cfg, "siblings", "fullpath")		p.outln('LDDEPS +=' .. gmake2.list(p.esc(deps)))	end	function cpp.ldFlags(cfg, toolset)		local flags = table.join(toolset.getLibraryDirectories(cfg), toolset.getrunpathdirs(cfg, table.join(cfg.runpathdirs, config.getsiblingtargetdirs(cfg))), toolset.getldflags(cfg), cfg.linkoptions)		p.outln('ALL_LDFLAGS += $(LDFLAGS)' .. gmake2.list(flags))	end	function cpp.linkCmd(cfg, toolset)		if cfg.kind == p.STATICLIB then			if cfg.architecture == p.UNIVERSAL then				p.outln('LINKCMD = libtool -o "$@" $(OBJECTS)')			else				p.outln('LINKCMD = $(AR) -rcs "$@" $(OBJECTS)')			end		elseif cfg.kind == p.UTILITY then			-- Empty LINKCMD for Utility (only custom build rules)			p.outln('LINKCMD =')		else			-- this was $(TARGET) $(LDFLAGS) $(OBJECTS)			--   but had trouble linking to certain static libs; $(OBJECTS) moved up			-- $(LDFLAGS) moved to end (http://sourceforge.net/p/premake/patches/107/)			-- $(LIBS) moved to end (http://sourceforge.net/p/premake/bugs/279/)			local cc = iif(p.languages.isc(cfg.language), "CC", "CXX")			p.outln('LINKCMD = $(' .. cc .. ') -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)')		end	end	function cpp.bindirs(cfg, toolset)		local dirs = project.getrelative(cfg.project, cfg.bindirs)		if #dirs > 0 then			p.outln('EXECUTABLE_PATHS = "' .. table.concat(dirs, ":") .. '"')		end	end	function cpp.exepaths(cfg, toolset)		local dirs = project.getrelative(cfg.project, cfg.bindirs)		if #dirs > 0 then			p.outln('EXE_PATHS = export PATH=$(EXECUTABLE_PATHS):$$PATH;')		end	end---- Write out the per file configurations.--	function cpp.outputPerFileConfigurationSection(prj)		_p('# Per File Configurations')		_p('# #############################################')		_p('')		for cfg in project.eachconfig(prj) do			table.foreachi(prj._.files, function(node)				local fcfg = fileconfig.getconfig(node, cfg)				if fcfg then					cpp.perFileFlags(cfg, fcfg)				end			end)		end		_p('')	end	function cpp.makeVarName(prj, value, saltValue)		prj._gmake = prj._gmake or {}		prj._gmake.varlist = prj._gmake.varlist or {}		prj._gmake.varlistlength = prj._gmake.varlistlength or 0		local cache = prj._gmake.varlist		local length = prj._gmake.varlistlength		local key = value .. saltValue		if (cache[key] ~= nil) then			return cache[key], false		end		local var = string.format("PERFILE_FLAGS_%d", length)		cache[key] = var		prj._gmake.varlistlength = length + 1		return var, true	end	function cpp.perFileFlags(cfg, fcfg)		local toolset = gmake2.getToolSet(cfg)		local isCFile = path.iscfile(fcfg.name)		local getflags = iif(isCFile, toolset.getcflags, toolset.getcxxflags)		local value = gmake2.list(table.join(getflags(fcfg), fcfg.buildoptions))		if fcfg.defines or fcfg.undefines then			local defs = table.join(toolset.getdefines(fcfg.defines, cfg), toolset.getundefines(fcfg.undefines))			if #defs > 0 then				value = value .. gmake2.list(defs)			end		end		if fcfg.includedirs or fcfg.sysincludedirs or fcfg.frameworkdirs then			local includes = toolset.getincludedirs(cfg, fcfg.includedirs, fcfg.sysincludedirs, fcfg.frameworkdirs)			if #includes > 0 then				value = value ..  gmake2.list(includes)			end		end		if #value > 0 then			local newPerFileFlag = false			fcfg.flagsVariable, newPerFileFlag = cpp.makeVarName(cfg.project, value, iif(isCFile, '_C', '_CPP'))			if newPerFileFlag then				if isCFile then					_p('%s = $(ALL_CFLAGS)%s', fcfg.flagsVariable, value)				else					_p('%s = $(ALL_CXXFLAGS)%s', fcfg.flagsVariable, value)				end			end		end	end	function cpp.fileFlags(cfg, file)		local fcfg = fileconfig.getconfig(file, cfg)		local flags = {}		if cfg.pchheader and not cfg.flags.NoPCH and (not fcfg or not fcfg.flags.NoPCH) then			table.insert(flags, "-include $(PCH_PLACEHOLDER)")		end		if fcfg and fcfg.flagsVariable then			table.insert(flags, string.format("$(%s)", fcfg.flagsVariable))		else			local fileExt = cpp.determineFiletype(cfg, file)			if path.iscfile(fileExt) then				table.insert(flags, "$(ALL_CFLAGS)")			elseif path.iscppfile(fileExt) then				table.insert(flags, "$(ALL_CXXFLAGS)")			end		end		return table.concat(flags, ' ')	end---- Write out the file sets.--	cpp.elements.filesets = function(cfg)		local result = {}		for _, kind in ipairs(cfg._gmake.kinds) do			for _, f in ipairs(cfg._gmake.filesets[kind]) do				table.insert(result, function(cfg, toolset)					cpp.outputFileset(cfg, kind, f)				end)			end		end		return result	end	function cpp.outputFilesSection(prj)		_p('# File sets')		_p('# #############################################')		_p('')		for _, kind in ipairs(prj._gmake.kinds) do			_x('%s :=', kind)		end		_x('')		gmake2.outputSection(prj, cpp.elements.filesets)	end	function cpp.outputFileset(cfg, kind, file)		_x('%s += %s', kind, file)	end---- Write out the targets.--	cpp.elements.rules = function(cfg)		return {			cpp.allRules,			cpp.targetRules,			gmake2.targetDirRules,			gmake2.objDirRules,			cpp.cleanRules,			gmake2.preBuildRules,			cpp.customDeps,			cpp.pchRules,		}	end	function cpp.outputRulesSection(prj)		_p('# Rules')		_p('# #############################################')		_p('')		gmake2.outputSection(prj, cpp.elements.rules)	end	function cpp.allRules(cfg, toolset)		if cfg.system == p.MACOSX and cfg.kind == p.WINDOWEDAPP then			_p('all: $(TARGET) $(dir $(TARGETDIR))PkgInfo $(dir $(TARGETDIR))Info.plist')			_p('\t@:')			_p('')			_p('$(dir $(TARGETDIR))PkgInfo:')			_p('$(dir $(TARGETDIR))Info.plist:')		else			_p('all: $(TARGET)')			_p('\t@:')		end		_p('')	end	function cpp.targetRules(cfg, toolset)		local targets = ''		for _, kind in ipairs(cfg._gmake.kinds) do			if kind ~= 'OBJECTS' and kind ~= 'RESOURCES' then				targets = targets .. '$(' .. kind .. ') '			end		end		targets = targets .. '$(OBJECTS) $(LDDEPS)'		if cfg._gmake.filesets['RESOURCES'] then			targets = targets .. ' $(RESOURCES)'		end		_p('$(TARGET): %s | $(TARGETDIR)', targets)		_p('\t$(PRELINKCMDS)')		_p('\t@echo Linking %s', cfg.project.name)		_p('\t$(SILENT) $(LINKCMD)')		_p('\t$(POSTBUILDCMDS)')		_p('')	end	function cpp.customDeps(cfg, toolset)		for _, kind in ipairs(cfg._gmake.kinds) do			if kind == 'CUSTOM' or kind == 'SOURCES' then				_p('$(%s): | prebuild', kind)			end		end	end	function cpp.cleanRules(cfg, toolset)		_p('clean:')		_p('\t@echo Cleaning %s', cfg.project.name)		_p('ifeq (posix,$(SHELLTYPE))')		_p('\t$(SILENT) rm -f  $(TARGET)')		_p('\t$(SILENT) rm -rf $(GENERATED)')		_p('\t$(SILENT) rm -rf $(OBJDIR)')		_p('else')		_p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))')		_p('\t$(SILENT) if exist $(subst /,\\\\,$(GENERATED)) rmdir /s /q $(subst /,\\\\,$(GENERATED))')		_p('\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))')		_p('endif')		_p('')	end	function cpp.pchRules(cfg, toolset)		_p('ifneq (,$(PCH))')		_p('$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER)')		_p('$(GCH): $(PCH) | prebuild')		_p('\t@echo $(notdir $<)')		local cmd = iif(p.languages.isc(cfg.language), "$(CC) -x c-header $(ALL_CFLAGS)", "$(CXX) -x c++-header $(ALL_CXXFLAGS)")		_p('\t$(SILENT) %s -o "$@" -MF "$(@:%%.gch=%%.d)" -c "$<"', cmd)		_p('$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR)')		_p('ifeq (posix,$(SHELLTYPE))')		_p('\t$(SILENT) touch "$@"')		_p('else')		_p('\t$(SILENT) echo $null >> "$@"')		_p('endif')		_p('else')		_p('$(OBJECTS): | prebuild')		_p('endif')		_p('')	end---- Output the file compile targets.--	cpp.elements.fileRules = function(cfg)		local funcs = {}		for _, fileRule in ipairs(cfg._gmake.fileRules) do			table.insert(funcs, function(cfg, toolset)				cpp.outputFileRules(cfg, fileRule)			end)		end		return funcs	end	function cpp.outputFileRuleSection(prj)		_p('# File Rules')		_p('# #############################################')		_p('')		gmake2.outputSection(prj, cpp.elements.fileRules)	end	function cpp.outputFileRules(cfg, file)		local dependencies = p.esc(file.source)		if file.buildinputs and #file.buildinputs > 0 then			dependencies = dependencies .. " " .. table.concat(p.esc(file.buildinputs), " ")		end		_p('%s: %s', file.buildoutputs[1], dependencies)		if file.buildmessage then			_p('\t@echo %s', file.buildmessage)		end		if file.buildcommands then			local cmds = os.translateCommandsAndPaths(file.buildcommands, cfg.project.basedir, cfg.project.location)			for _, cmd in ipairs(cmds) do				if cfg.bindirs and #cfg.bindirs > 0 then					_p('\t$(SILENT) $(EXE_PATHS) %s', cmd)				else					_p('\t$(SILENT) %s', cmd)				end			end		end		-- TODO: this is a hack with some imperfect side-effects.		--       better solution would be to emit a dummy file for the rule, and then outputs depend on it (must clean up dummy in 'clean')		--       better yet, is to use pattern rules, but we need to detect that all outputs have the same stem		if #file.buildoutputs > 1 then			_p('%s: %s', table.concat({ table.unpack(file.buildoutputs, 2) }, ' '), file.buildoutputs[1])		end	end------------------------------------------------------------------------------- Handlers for individual makefile elements-----------------------------------------------------------------------------	function cpp.dependencies(prj)		-- include the dependencies, built by GCC (with the -MMD flag)		_p('-include $(OBJECTS:%%.o=%%.d)')		_p('ifneq (,$(PCH))')			_p('  -include $(PCH_PLACEHOLDER).d')		_p('endif')	end
 |