822 lines
22 KiB
Lua
822 lines
22 KiB
Lua
--
|
|
-- 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
|
|
gmake2.prepareEnvironment(rule, environ, cfg)
|
|
gmake2.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)
|
|
-- If there is no header, or if PCH has been disabled, I can early out
|
|
if not cfg.pchheader or cfg.flags.NoPCH then
|
|
return
|
|
end
|
|
|
|
-- Visual Studio requires the PCH header to be specified in the same way
|
|
-- it appears in the #include statements used in the source code; the PCH
|
|
-- source actual handles the compilation of the header. GCC compiles the
|
|
-- header file directly, and needs the file's actual file system path in
|
|
-- order to locate it.
|
|
|
|
-- To maximize the compatibility between the two approaches, see if I can
|
|
-- locate the specified PCH header on one of the include file search paths
|
|
-- and, if so, adjust the path automatically so the user doesn't have
|
|
-- add a conditional configuration to the project script.
|
|
|
|
local pch = cfg.pchheader
|
|
local found = false
|
|
|
|
-- test locally in the project folder first (this is the most likely location)
|
|
local testname = path.join(cfg.project.basedir, pch)
|
|
if os.isfile(testname) then
|
|
pch = project.getrelative(cfg.project, testname)
|
|
found = true
|
|
else
|
|
-- else scan in all include dirs.
|
|
for _, incdir in ipairs(cfg.includedirs) do
|
|
testname = path.join(incdir, pch)
|
|
if os.isfile(testname) then
|
|
pch = project.getrelative(cfg.project, testname)
|
|
found = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if not found then
|
|
pch = project.getrelative(cfg.project, path.getabsolute(pch))
|
|
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)
|
|
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, cfg.runpathdirs), 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 then
|
|
local includes = toolset.getincludedirs(cfg, fcfg.includedirs, fcfg.sysincludedirs)
|
|
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
|