mirror of
https://github.com/holub/mame
synced 2025-04-17 22:13:04 +03:00

* Updated to GENie 1181. * Applied local fix for GENie resource include directory bug, sent upstream as bkaradzic/GENie#572. * Set MSVC flags to use conformant preprocessor, standards conformance mode, and assume UTF-8 encoding.
389 lines
14 KiB
Lua
389 lines
14 KiB
Lua
--
|
|
-- GENie - Project generator tool
|
|
-- https://github.com/bkaradzic/GENie#license
|
|
--
|
|
|
|
premake.ninja.cpp = { }
|
|
local ninja = premake.ninja
|
|
local cpp = premake.ninja.cpp
|
|
local p = premake
|
|
|
|
local function wrap_ninja_cmd(c)
|
|
if os.is("windows") then
|
|
return 'cmd /c "' .. c .. '"'
|
|
else
|
|
return c
|
|
end
|
|
end
|
|
|
|
-- generate project + config build file
|
|
function ninja.generate_cpp(prj)
|
|
local pxy = ninja.get_proxy("prj", prj)
|
|
local tool = premake.gettool(prj)
|
|
|
|
-- build a list of supported target platforms that also includes a generic build
|
|
local platforms = premake.filterplatforms(prj.solution, tool.platforms, "Native")
|
|
|
|
for _, platform in ipairs(platforms) do
|
|
for cfg in p.eachconfig(pxy, platform) do
|
|
p.generate(cfg, cfg:getprojectfilename(), function() cpp.generate_config(prj, cfg) end)
|
|
end
|
|
end
|
|
end
|
|
|
|
function cpp.generate_config(prj, cfg)
|
|
local tool = premake.gettool(prj)
|
|
|
|
_p('# Ninja build project file autogenerated by GENie')
|
|
_p('# https://github.com/bkaradzic/GENie')
|
|
_p("")
|
|
|
|
-- needed for implicit outputs, introduced in 1.7
|
|
_p("ninja_required_version = 1.7")
|
|
_p("")
|
|
|
|
local flags = {
|
|
defines = ninja.list(tool.getdefines(cfg.defines)),
|
|
includes = ninja.list(table.join(tool.getincludedirs(cfg.includedirs), tool.getquoteincludedirs(cfg.userincludedirs), tool.getsystemincludedirs(cfg.systemincludedirs))),
|
|
cppflags = ninja.list(tool.getcppflags(cfg)),
|
|
asmflags = ninja.list(table.join(tool.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_asm)),
|
|
cflags = ninja.list(table.join(tool.getcflags(cfg), cfg.buildoptions, cfg.buildoptions_c)),
|
|
cxxflags = ninja.list(table.join(tool.getcflags(cfg), tool.getcxxflags(cfg), cfg.buildoptions, cfg.buildoptions_cpp)),
|
|
objcflags = ninja.list(table.join(tool.getcflags(cfg), tool.getcxxflags(cfg), cfg.buildoptions, cfg.buildoptions_objc)),
|
|
}
|
|
|
|
_p("")
|
|
_p("# core rules for " .. cfg.name)
|
|
_p("rule cc")
|
|
_p(" command = " .. wrap_ninja_cmd(tool.cc .. " $defines $includes $flags -MMD -MF $out.d -c -o $out $in"))
|
|
_p(" description = cc $out")
|
|
_p(" depfile = $out.d")
|
|
_p(" deps = gcc")
|
|
_p("")
|
|
_p("rule cxx")
|
|
_p(" command = " .. wrap_ninja_cmd(tool.cxx .. " $defines $includes $flags -MMD -MF $out.d -c -o $out $in"))
|
|
_p(" description = cxx $out")
|
|
_p(" depfile = $out.d")
|
|
_p(" deps = gcc")
|
|
_p("")
|
|
|
|
if cfg.flags.UseObjectResponseFile then
|
|
_p("rule ar")
|
|
_p(" command = " .. wrap_ninja_cmd(tool.ar .. " $flags $out @$out.rsp " .. (os.is("MacOSX") and " 2>&1 > /dev/null | sed -e '/.o) has no symbols$$/d'" or "")))
|
|
_p(" description = ar $out")
|
|
_p(" rspfile = $out.rsp")
|
|
_p(" rspfile_content = $in $libs")
|
|
_p("")
|
|
else
|
|
_p("rule ar")
|
|
_p(" command = " .. wrap_ninja_cmd(tool.ar .. " $flags $out $in $libs" .. (os.is("MacOSX") and " 2>&1 > /dev/null | sed -e '/.o) has no symbols$$/d'" or "")))
|
|
_p(" description = ar $out")
|
|
_p("")
|
|
end
|
|
|
|
local link = iif(cfg.language == "C", tool.cc, tool.cxx)
|
|
_p("rule link")
|
|
local startgroup = ''
|
|
local endgroup = ''
|
|
if (cfg.flags.LinkSupportCircularDependencies) then
|
|
startgroup = '-Wl,--start-group'
|
|
endgroup = '-Wl,--end-group'
|
|
end
|
|
|
|
local rspfile_content = "$all_outputfiles $walibs " .. string.format("%s $libs %s", startgroup, endgroup)
|
|
if cfg.flags.UseLDResponseFile then
|
|
_p(" command = " .. wrap_ninja_cmd("$pre_link " .. link .. " -o $out @$out.rsp $all_ldflags $post_build"))
|
|
_p(" description = link $out")
|
|
_p(" rspfile = $out.rsp")
|
|
_p(" rspfile_content = %s", rspfile_content)
|
|
_p("")
|
|
else
|
|
_p(" command = " .. wrap_ninja_cmd("$pre_link " .. link .. " -o $out " .. rspfile_content .. " $all_ldflags $post_build"))
|
|
_p(" description = link $out")
|
|
_p("")
|
|
end
|
|
|
|
_p("rule exec")
|
|
_p(" command = " .. wrap_ninja_cmd("$command"))
|
|
_p(" description = Run $type commands")
|
|
_p("")
|
|
|
|
if #cfg.prebuildcommands > 0 then
|
|
_p("build __prebuildcommands_" .. premake.esc(prj.name) .. ": exec")
|
|
_p(1, "command = " .. wrap_ninja_cmd("echo Running pre-build commands && " .. table.implode(cfg.prebuildcommands, "", "", " && ")))
|
|
_p(1, "type = pre-build")
|
|
_p("")
|
|
end
|
|
|
|
cfg.pchheader_full = cfg.pchheader
|
|
for _, incdir in ipairs(cfg.includedirs) do
|
|
-- convert this back to an absolute path for os.isfile()
|
|
local abspath = path.getabsolute(path.join(cfg.project.location, cfg.shortname, incdir))
|
|
|
|
local testname = path.join(abspath, cfg.pchheader_full)
|
|
if os.isfile(testname) then
|
|
cfg.pchheader_full = path.getrelative(cfg.location, testname)
|
|
break
|
|
end
|
|
end
|
|
|
|
cpp.custombuildtask(prj, cfg)
|
|
|
|
cpp.dependencyRules(prj, cfg)
|
|
|
|
cpp.file_rules(prj, cfg, flags)
|
|
|
|
local objfiles = {}
|
|
|
|
for _, file in ipairs(cfg.files) do
|
|
if path.issourcefile(file) then
|
|
table.insert(objfiles, cpp.objectname(cfg, file))
|
|
end
|
|
end
|
|
_p('')
|
|
|
|
cpp.linker(prj, cfg, objfiles, tool, flags)
|
|
|
|
_p("")
|
|
end
|
|
|
|
function cpp.custombuildtask(prj, cfg)
|
|
local cmd_index = 1
|
|
|
|
local ninja_rules = {}
|
|
local ninja_builds = {}
|
|
|
|
local prebuildsuffix = #cfg.prebuildcommands > 0 and "||__prebuildcommands_" .. premake.esc(prj.name) or ""
|
|
|
|
for _, custombuildtask in ipairs(prj.custombuildtask or {}) do
|
|
for _, buildtask in ipairs(custombuildtask or {}) do
|
|
|
|
local commands = {}
|
|
for _, cmd in ipairs(buildtask[4] or {}) do
|
|
local num = 1
|
|
|
|
-- replace dependencies in the command with actual file paths
|
|
for _, depdata in ipairs(buildtask[3] or {}) do
|
|
cmd = string.gsub(cmd,"%$%(" .. num .."%)", string.format("%s ", path.getrelative(cfg.location, depdata)))
|
|
num = num + 1
|
|
end
|
|
|
|
-- replace $(<) and $(@) with $in and $out
|
|
cmd = string.gsub(cmd, '%$%(<%)', '$in')
|
|
cmd = string.gsub(cmd, '%$%(@%)', '$out')
|
|
|
|
table.insert(commands, cmd)
|
|
end
|
|
|
|
ninja_rules["cmd_" .. cmd_index] = commands
|
|
|
|
table.insert(ninja_builds, {
|
|
file_in = buildtask[1],
|
|
file_out = buildtask[2],
|
|
rule = "cmd_" .. cmd_index,
|
|
deps = buildtask[3]
|
|
})
|
|
|
|
cmd_index = cmd_index + 1
|
|
end
|
|
end
|
|
|
|
_p("# custom build rules")
|
|
|
|
for rule, commands in pairs(ninja_rules) do
|
|
_p("rule " .. rule)
|
|
_p(1, "command = " .. wrap_ninja_cmd(table.concat(commands, " && ")))
|
|
end
|
|
|
|
|
|
for i = 1,#ninja_builds do
|
|
local build = ninja_builds[i]
|
|
|
|
local deps = ''
|
|
for i, dep in ipairs(build.deps) do
|
|
deps = deps .. path.getrelative(cfg.location, dep) .. ' '
|
|
end
|
|
|
|
_p("build " .. path.getrelative(cfg.location, build.file_out) .. ': ' .. build.rule .. ' ' .. path.getrelative(cfg.location, build.file_in) .. ' | ' .. deps .. prebuildsuffix)
|
|
_p("")
|
|
end
|
|
end
|
|
|
|
function cpp.dependencyRules(prj, cfg)
|
|
local extra_deps = {}
|
|
local order_deps = {}
|
|
local extra_flags = {}
|
|
|
|
for _, dependency in ipairs(prj.dependency or {}) do
|
|
for _, dep in ipairs(dependency or {}) do
|
|
-- This is assuming that the depending object is (going to be) an .o file
|
|
local objfilename = cpp.objectname(cfg, path.getrelative(prj.location, dep[1]))
|
|
local dependency = path.getrelative(cfg.location, dep[2])
|
|
|
|
-- ensure a table exists for the dependent object file
|
|
if extra_deps[objfilename] == nil then
|
|
extra_deps[objfilename] = {}
|
|
end
|
|
|
|
table.insert(extra_deps[objfilename], dependency)
|
|
end
|
|
end
|
|
|
|
local pchfilename = cfg.pchheader_full and cpp.pchname(cfg, cfg.pchheader_full) or ''
|
|
for _, file in ipairs(cfg.files) do
|
|
local objfilename = file == cfg.pchheader and cpp.pchname(cfg, file) or cpp.objectname(cfg, file)
|
|
if path.issourcefile(file) or file == cfg.pchheader then
|
|
if #cfg.prebuildcommands > 0 then
|
|
if order_deps[objfilename] == nil then
|
|
order_deps[objfilename] = {}
|
|
end
|
|
table.insert(order_deps[objfilename], '__prebuildcommands_' .. premake.esc(prj.name))
|
|
end
|
|
end
|
|
if path.issourcefile(file) then
|
|
if cfg.pchheader_full and not cfg.flags.NoPCH then
|
|
local nopch = table.icontains(prj.nopch, file)
|
|
if not nopch then
|
|
local suffix = path.isobjcfile(file) and '_objc' or ''
|
|
if extra_deps[objfilename] == nil then
|
|
extra_deps[objfilename] = {}
|
|
end
|
|
table.insert(extra_deps[objfilename], pchfilename .. suffix .. ".gch")
|
|
|
|
if extra_flags[objfilename] == nil then
|
|
extra_flags[objfilename] = {}
|
|
end
|
|
table.insert(extra_flags[objfilename], '-include ' .. pchfilename .. suffix)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- store prepared deps for file_rules() phase
|
|
cfg.extra_deps = extra_deps
|
|
cfg.order_deps = order_deps
|
|
cfg.extra_flags = extra_flags
|
|
end
|
|
|
|
function cpp.objectname(cfg, file)
|
|
return path.join(cfg.objectsdir, path.trimdots(path.removeext(file)) .. ".o")
|
|
end
|
|
|
|
function cpp.pchname(cfg, file)
|
|
return path.join(cfg.objectsdir, path.trimdots(file))
|
|
end
|
|
|
|
function cpp.file_rules(prj,cfg, flags)
|
|
_p("# build files")
|
|
|
|
for _, file in ipairs(cfg.files) do
|
|
_p("# FILE: " .. file)
|
|
if cfg.pchheader_full == file then
|
|
local pchfilename = cpp.pchname(cfg, file)
|
|
local extra_deps = #cfg.extra_deps and '| ' .. table.concat(cfg.extra_deps[pchfilename] or {}, ' ') or ''
|
|
local order_deps = #cfg.order_deps and '|| ' .. table.concat(cfg.order_deps[pchfilename] or {}, ' ') or ''
|
|
local extra_flags = #cfg.extra_flags and ' ' .. table.concat(cfg.extra_flags[pchfilename] or {}, ' ') or ''
|
|
_p("build " .. pchfilename .. ".gch : cxx " .. file .. extra_deps .. order_deps)
|
|
_p(1, "flags = " .. flags['cxxflags'] .. extra_flags .. iif(prj.language == "C", "-x c-header", "-x c++-header"))
|
|
_p(1, "includes = " .. flags.includes)
|
|
_p(1, "defines = " .. flags.defines)
|
|
|
|
_p("build " .. pchfilename .. "_objc.gch : cxx " .. file .. extra_deps .. order_deps)
|
|
_p(1, "flags = " .. flags['objcflags'] .. extra_flags .. iif(prj.language == "C", "-x objective-c-header", "-x objective-c++-header"))
|
|
_p(1, "includes = " .. flags.includes)
|
|
_p(1, "defines = " .. flags.defines)
|
|
elseif path.issourcefile(file) then
|
|
local objfilename = cpp.objectname(cfg, file)
|
|
local extra_deps = #cfg.extra_deps and '| ' .. table.concat(cfg.extra_deps[objfilename] or {}, ' ') or ''
|
|
local order_deps = #cfg.order_deps and '|| ' .. table.concat(cfg.order_deps[objfilename] or {}, ' ') or ''
|
|
local extra_flags = #cfg.extra_flags and ' ' .. table.concat(cfg.extra_flags[objfilename] or {}, ' ') or ''
|
|
|
|
local cflags = "cflags"
|
|
if path.isobjcfile(file) then
|
|
_p("build " .. objfilename .. ": cxx " .. file .. extra_deps .. order_deps)
|
|
cflags = "objcflags"
|
|
elseif path.isasmfile(file) then
|
|
_p("build " .. objfilename .. ": cc " .. file .. extra_deps .. order_deps)
|
|
cflags = "asmflags"
|
|
elseif path.iscfile(file) and not cfg.options.ForceCPP then
|
|
_p("build " .. objfilename .. ": cc " .. file .. extra_deps .. order_deps)
|
|
else
|
|
_p("build " .. objfilename .. ": cxx " .. file .. extra_deps .. order_deps)
|
|
cflags = "cxxflags"
|
|
end
|
|
|
|
_p(1, "flags = " .. flags[cflags] .. extra_flags)
|
|
_p(1, "includes = " .. flags.includes)
|
|
_p(1, "defines = " .. flags.defines)
|
|
elseif path.isresourcefile(file) then
|
|
-- TODO
|
|
end
|
|
end
|
|
|
|
_p("")
|
|
end
|
|
|
|
function cpp.linker(prj, cfg, objfiles, tool)
|
|
local all_ldflags = ninja.list(table.join(tool.getlibdirflags(cfg), tool.getldflags(cfg), cfg.linkoptions))
|
|
local prebuildsuffix = #cfg.prebuildcommands > 0 and ("||__prebuildcommands_" .. premake.esc(prj.name)) or ""
|
|
local libs = {}
|
|
local walibs = {}
|
|
local lddeps = {}
|
|
|
|
if #cfg.wholearchive > 0 then
|
|
for _, linkcfg in ipairs(premake.getlinks(cfg, "siblings", "object")) do
|
|
local linkpath = path.rebase(linkcfg.linktarget.fullpath, linkcfg.location, cfg.location)
|
|
table.insert(lddeps, linkpath)
|
|
|
|
if table.icontains(cfg.wholearchive, linkcfg.project.name) then
|
|
table.insert(walibs, table.concat(tool.wholearchive(linkpath), ' '))
|
|
else
|
|
table.insert(libs, linkpath)
|
|
end
|
|
end
|
|
else
|
|
lddeps = premake.getlinks(cfg, "siblings", "fullpath")
|
|
libs = lddeps
|
|
end
|
|
|
|
lddeps = ninja.list(lddeps)
|
|
libs = ninja.list(libs) .. " " .. ninja.list(tool.getlinkflags(cfg))
|
|
walibs = ninja.list(walibs)
|
|
|
|
local function writevars()
|
|
_p(1, "all_ldflags = " .. all_ldflags)
|
|
_p(1, "libs = " .. libs)
|
|
_p(1, "walibs = " .. walibs)
|
|
_p(1, "all_outputfiles = " .. table.concat(objfiles, " "))
|
|
if #cfg.prelinkcommands > 0 then
|
|
_p(1, 'pre_link = echo Running pre-link commands && ' .. table.implode(cfg.prelinkcommands, "", "", " && ") .. " && ")
|
|
end
|
|
if #cfg.postbuildcommands > 0 then
|
|
_p(1, 'post_build = && echo Running post-build commands && ' .. table.implode(cfg.postbuildcommands, "", "", " && "))
|
|
end
|
|
end
|
|
|
|
if cfg.kind == "StaticLib" then
|
|
local ar_flags = ninja.list(tool.getarchiveflags(cfg, cfg, false))
|
|
_p("# link static lib")
|
|
_p("build " .. cfg:getoutputfilename() .. ": ar " .. table.concat(objfiles, " ") .. " | " .. lddeps .. prebuildsuffix)
|
|
_p(1, "flags = " .. ninja.list(tool.getarchiveflags(cfg, cfg, false)))
|
|
_p(1, "all_outputfiles = " .. table.concat(objfiles, " "))
|
|
elseif cfg.kind == "SharedLib" or cfg.kind == "Bundle" then
|
|
local output = cfg:getoutputfilename()
|
|
_p("# link shared lib")
|
|
_p("build " .. output .. ": link " .. table.concat(objfiles, " ") .. " | " .. lddeps .. prebuildsuffix)
|
|
writevars()
|
|
elseif (cfg.kind == "ConsoleApp") or (cfg.kind == "WindowedApp") then
|
|
_p("# link executable")
|
|
_p("build " .. cfg:getoutputfilename() .. ": link " .. table.concat(objfiles, " ") .. " | " .. lddeps .. prebuildsuffix)
|
|
writevars()
|
|
else
|
|
p.error("ninja action doesn't support this kind of target " .. cfg.kind)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|