Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions modules/ninja/_manifest.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
--
-- _manifest.lua
-- Define the ninja manifest
-- Author: Nick Clark
-- Copyright (c) 2025 Jess Perkins and the Premake project
--


return {
"_preload.lua",
"ninja.lua",
'ninja_cpp.lua',
'ninja_workspace.lua',
}
74 changes: 74 additions & 0 deletions modules/ninja/_preload.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
--
-- _preload.lua
-- Define the ninja actions
-- Author: Nick Clark
-- Copyright (c) 2025 Jess Perkins and the Premake project
--

local p = premake
local project = p.project
local getrelative = p.tools.getrelative

newaction {
trigger = "ninja",
shortname = "Ninja",
description = "Generate Ninja build files",

-- Action Capabilities
valid_kinds = { "ConsoleApp", "WindowedApp", "StaticLib", "SharedLib", "None" },
valid_languages = { "C", "C++" },
valid_tools = {
cc = {
"gcc",
"clang",
"msc",
"emcc",
"cosmocc",
}
},
toolset = (function()
local target = os.target()
if target == p.MACOSX then
return "clang"
elseif target == p.EMSCRIPTEN then
return "emcc"
elseif target == p.WINDOWS then
return "v143"
else
return "gcc"
end
end)(),

onInitialize = function()
require("ninja")
end,
onWorkspace = function(wks)
p.tools.getrelative = p.modules.ninja.getrelative
p.escaper(p.modules.ninja.esc)
wks.projects = table.filter(wks.projects, function(prj)
return p.action.supports(prj.kind) and prj.kind ~= p.NONE
end)
p.generate(wks, p.modules.ninja.getninjafilename(wks, false), p.modules.ninja.wks.generate)
p.tools.getrelative = getrelative
end,
onProject = function(prj)
p.tools.getrelative = p.modules.ninja.getrelative
p.escaper(p.modules.ninja.esc)

if not p.action.supports(prj.kind) or prj.kind == p.NONE then
return
end

if project.isc(prj) or project.iscpp(prj) then
p.oven.assignObjectSequences(prj)
p.generate(prj, p.modules.ninja.getprjconfigfilename(prj), p.modules.ninja.cpp.generate)
else
p.warn("Ninja does not support the '%s' language. No build file generated for project '%s'.", prj.language, prj.name)
end
p.tools.getrelative = getrelative
end,
}

return function(cfg)
return (_ACTION == "ninja")
end
118 changes: 118 additions & 0 deletions modules/ninja/ninja.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
--
-- ninja.lua
-- Utilities for generating Ninja build files
-- Author: Nick Clark
-- Copyright (c) 2025 Jess Perkins and the Premake project
--

local p = premake

p.modules.ninja = {}
p.modules.ninja._VERSION = p._VERSION

local ninja = p.modules.ninja

--
-- Escape a string so it can be written to a Ninja build file.
-- Ninja variables are expanded into shell commands, so we need to escape
-- shell special characters as well as Ninja special characters.
--
function ninja.esc(value)
value = value:gsub("%$", "$$")
value = value:gsub(":", "$:")
value = value:gsub("\n", "$\n")
value = value:gsub(" ", "$ ")
value = value:gsub('%(', '\\(')
value = value:gsub('%)', '\\)')
value = value:gsub('"', '\\"')

return value
end

function ninja.header(target)
local kind = iif(target.project, "project", "workspace")
_p("# %s %s Ninja build file generated by Premake", target.name, kind)
_p('ninja_required_version = 1.6')
_p('')
end

function ninja.key(cfg)
local name = cfg.project.name
local buildcfg = cfg.buildcfg
if cfg.platform then
return name .. "_" .. (buildcfg or "") .. "_" .. cfg.platform
else
return name .. (buildcfg and ("_" .. buildcfg) or "")
end
end

function ninja.getprjconfigfilename(prj)
return prj.name .. ".ninja"
end

function ninja.getninjafilename(target, searchprjs)
local count = 0
for wks in p.global.eachWorkspace() do
if wks.location == target.location then
count = count + 1
end

if searchprjs then
for _, prj in ipairs(wks.projects) do
if prj.location == target.location then
count = count + 1
end
end
end
end

if count == 1 then
return "build.ninja"
else
return target.name .. ".ninja"
end
end

function ninja.gettoolset(cfg)
local default = p.action.current().toolset
local toolset, version = p.tools.canonical(cfg.toolset or default)
if not toolset then
error("No toolset found for '" .. tostring(cfg.toolset) .. "'")
end
return toolset
end

function ninja.list(value)
if #value > 0 then
return " " .. table.concat(value, " ")
else
return ""
end
end

-- Override tools.getrelative to use workspace-relative paths for Ninja
-- Ninja builds are always executed from the workspace root, so all paths
-- must be relative to the workspace, not the project
function ninja.getrelative(prj, value)
if type(value) == "table" then
local result = {}
for i, name in ipairs(value) do
result[i] = ninja.getrelative(prj, name)
end
return result
else
if value then
local result = value
if path.hasdeferredjoin(result) then
result = path.resolvedeferredjoin(result)
end
-- Use workspace location instead of project location for Ninja
return path.getrelative(prj.workspace.location, result)
end
end
end

include("ninja_cpp.lua")
include("ninja_workspace.lua")

return ninja
Loading
Loading