Skip to content

Commit c835d19

Browse files
Merge pull request #2554 from nickclark2016/feature/ninja-exporter
Ninja exporter for premake core
2 parents 6f17dac + ee73065 commit c835d19

24 files changed

+6184
-25
lines changed

modules/ninja/_manifest.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--
2+
-- _manifest.lua
3+
-- Define the ninja manifest
4+
-- Author: Nick Clark
5+
-- Copyright (c) 2025 Jess Perkins and the Premake project
6+
--
7+
8+
9+
return {
10+
"_preload.lua",
11+
"ninja.lua",
12+
'ninja_cpp.lua',
13+
'ninja_workspace.lua',
14+
}

modules/ninja/_preload.lua

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
--
2+
-- _preload.lua
3+
-- Define the ninja actions
4+
-- Author: Nick Clark
5+
-- Copyright (c) 2025 Jess Perkins and the Premake project
6+
--
7+
8+
local p = premake
9+
local project = p.project
10+
local getrelative = p.tools.getrelative
11+
12+
newaction {
13+
trigger = "ninja",
14+
shortname = "Ninja",
15+
description = "Generate Ninja build files",
16+
17+
-- Action Capabilities
18+
valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "None" },
19+
valid_languages = { "C", "C++" },
20+
valid_tools = {
21+
cc = {
22+
"gcc",
23+
"clang",
24+
"msc",
25+
"emcc",
26+
"cosmocc",
27+
}
28+
},
29+
toolset = (function()
30+
local target = os.target()
31+
if target == p.MACOSX then
32+
return "clang"
33+
elseif target == p.EMSCRIPTEN then
34+
return "emcc"
35+
elseif target == p.WINDOWS then
36+
return "msc"
37+
else
38+
return "gcc"
39+
end
40+
end)(),
41+
42+
onInitialize = function()
43+
require("ninja")
44+
end,
45+
onWorkspace = function(wks)
46+
p.tools.getrelative = p.modules.ninja.getrelative
47+
p.escaper(p.modules.ninja.esc)
48+
wks.projects = table.filter(wks.projects, function(prj)
49+
return p.action.supports(prj.kind) and prj.kind ~= p.NONE
50+
end)
51+
p.generate(wks, p.modules.ninja.getninjafilename(wks, false), p.modules.ninja.wks.generate)
52+
p.tools.getrelative = getrelative
53+
p.escaper()
54+
end,
55+
onProject = function(prj)
56+
p.tools.getrelative = p.modules.ninja.getrelative
57+
p.escaper(p.modules.ninja.esc)
58+
59+
if not p.action.supports(prj.kind) or prj.kind == p.NONE then
60+
return
61+
end
62+
63+
if project.isc(prj) or project.iscpp(prj) then
64+
p.oven.assignObjectSequences(prj)
65+
p.generate(prj, p.modules.ninja.getprjconfigfilename(prj), p.modules.ninja.cpp.generate)
66+
else
67+
p.warn("Ninja does not support the '%s' language. No build file generated for project '%s'.", prj.language, prj.name)
68+
end
69+
p.tools.getrelative = getrelative
70+
p.escaper()
71+
end,
72+
}
73+
74+
return function(cfg)
75+
return (_ACTION == "ninja")
76+
end

modules/ninja/ninja.lua

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
--
2+
-- ninja.lua
3+
-- Utilities for generating Ninja build files
4+
-- Author: Nick Clark
5+
-- Copyright (c) 2025 Jess Perkins and the Premake project
6+
--
7+
8+
local p = premake
9+
10+
p.modules.ninja = {}
11+
p.modules.ninja._VERSION = p._VERSION
12+
13+
local ninja = p.modules.ninja
14+
15+
function ninja.esc(value)
16+
value = value:gsub("%$", "$$")
17+
value = value:gsub(":", "$:")
18+
value = value:gsub("\n", "$\n")
19+
value = value:gsub('%(', '\\(')
20+
value = value:gsub('%)', '\\)')
21+
value = value:gsub('"', '\\"')
22+
value = value:gsub(" ", "\\ ")
23+
24+
return value
25+
end
26+
27+
function ninja.header(target)
28+
local kind = iif(target.project, "project", "workspace")
29+
_p("# %s %s Ninja build file generated by Premake", target.name, kind)
30+
_p('ninja_required_version = 1.6')
31+
_p('')
32+
end
33+
34+
function ninja.key(cfg)
35+
local name = cfg.project.name
36+
local buildcfg = cfg.buildcfg
37+
if cfg.platform then
38+
return name .. "_" .. (buildcfg or "") .. "_" .. cfg.platform
39+
else
40+
return name .. (buildcfg and ("_" .. buildcfg) or "")
41+
end
42+
end
43+
44+
function ninja.getprjconfigfilename(prj)
45+
return prj.name .. ".ninja"
46+
end
47+
48+
function ninja.getninjafilename(target, searchprjs)
49+
local count = 0
50+
for wks in p.global.eachWorkspace() do
51+
if wks.location == target.location then
52+
count = count + 1
53+
end
54+
55+
if searchprjs then
56+
for _, prj in ipairs(wks.projects) do
57+
if prj.location == target.location then
58+
count = count + 1
59+
end
60+
end
61+
end
62+
end
63+
64+
if count == 1 then
65+
return "build.ninja"
66+
else
67+
return target.name .. ".ninja"
68+
end
69+
end
70+
71+
function ninja.gettoolset(cfg)
72+
local default = p.action.current().toolset
73+
local toolset, version = p.tools.canonical(cfg.toolset or default)
74+
if not toolset then
75+
error("No toolset found for '" .. tostring(cfg.toolset) .. "'")
76+
end
77+
return toolset
78+
end
79+
80+
function ninja.list(value)
81+
if #value > 0 then
82+
return " " .. table.concat(value, " ")
83+
else
84+
return ""
85+
end
86+
end
87+
88+
-- Override tools.getrelative to use workspace-relative paths for Ninja
89+
-- Ninja builds are always executed from the workspace root, so all paths
90+
-- must be relative to the workspace, not the project
91+
function ninja.getrelative(prj, value)
92+
if type(value) == "table" then
93+
local result = {}
94+
for i, name in ipairs(value) do
95+
result[i] = ninja.getrelative(prj, name)
96+
end
97+
return result
98+
else
99+
if value then
100+
local result = value
101+
if path.hasdeferredjoin(result) then
102+
result = path.resolvedeferredjoin(result)
103+
end
104+
-- Use workspace location instead of project location for Ninja
105+
return path.getrelative(prj.workspace.location, result)
106+
end
107+
end
108+
end
109+
110+
include("ninja_cpp.lua")
111+
include("ninja_workspace.lua")
112+
113+
return ninja

0 commit comments

Comments
 (0)