Skip to content

Commit 68422b2

Browse files
committed
Add buildoptions, linkoptions support
1 parent 1fc5b66 commit 68422b2

File tree

10 files changed

+969
-143
lines changed

10 files changed

+969
-143
lines changed

modules/ninja/_preload.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ newaction {
3333
elseif target == p.EMSCRIPTEN then
3434
return "emcc"
3535
elseif target == p.WINDOWS then
36-
return "v143"
36+
return "msc"
3737
else
3838
return "gcc"
3939
end

modules/ninja/ninja.lua

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,14 @@ p.modules.ninja._VERSION = p._VERSION
1212

1313
local ninja = p.modules.ninja
1414

15-
--
16-
-- Escape a string so it can be written to a Ninja build file.
17-
-- Ninja variables are expanded into shell commands, so we need to escape
18-
-- shell special characters as well as Ninja special characters.
19-
--
2015
function ninja.esc(value)
2116
value = value:gsub("%$", "$$")
2217
value = value:gsub(":", "$:")
2318
value = value:gsub("\n", "$\n")
24-
value = value:gsub(" ", "$ ")
2519
value = value:gsub('%(', '\\(')
2620
value = value:gsub('%)', '\\)')
2721
value = value:gsub('"', '\\"')
22+
value = value:gsub(" ", "\\ ")
2823

2924
return value
3025
end

modules/ninja/ninja_cpp.lua

Lines changed: 133 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ function m.generate(prj)
3434
_p("") -- Empty line at end of file
3535
end
3636

37-
-- Generate all ninja rules (compile, link, etc.)
3837
function m.rules(prj)
39-
-- Generate rules for each configuration to handle different toolsets
4038
local rulesDone = {}
4139

4240
for cfg in project.eachconfig(prj) do
@@ -298,12 +296,18 @@ function m.getCFlags(cfg, toolset)
298296

299297
local defines = toolset.getdefines(cfg.defines)
300298
flags = table.join(flags, defines)
299+
300+
local undefines = toolset.getundefines(cfg.undefines)
301+
flags = table.join(flags, undefines)
301302

302303
local includedirs = toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs, cfg.frameworkdirs)
303304
flags = table.join(flags, includedirs)
304305

305306
local forceincludes = toolset.getforceincludes(cfg)
306307
flags = table.join(flags, forceincludes)
308+
309+
local buildopts = cfg.buildoptions or {}
310+
flags = table.join(flags, buildopts)
307311

308312
return flags
309313
end
@@ -317,12 +321,18 @@ function m.getCxxFlags(cfg, toolset)
317321

318322
local defines = toolset.getdefines(cfg.defines)
319323
flags = table.join(flags, defines)
324+
325+
local undefines = toolset.getundefines(cfg.undefines)
326+
flags = table.join(flags, undefines)
320327

321328
local includedirs = toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs, cfg.frameworkdirs)
322329
flags = table.join(flags, includedirs)
323330

324331
local forceincludes = toolset.getforceincludes(cfg)
325332
flags = table.join(flags, forceincludes)
333+
334+
local buildopts = cfg.buildoptions or {}
335+
flags = table.join(flags, buildopts)
326336

327337
return flags
328338
end
@@ -333,6 +343,9 @@ function m.getLdFlags(cfg, toolset)
333343

334344
local toolFlags = toolset.getldflags(cfg)
335345
flags = table.join(flags, toolFlags)
346+
347+
local linkopts = cfg.linkoptions or {}
348+
flags = table.join(flags, linkopts)
336349

337350
local libdirs = toolset.getLibraryDirectories(cfg)
338351
flags = table.join(flags, libdirs)
@@ -441,6 +454,11 @@ function m.buildFiles(cfg)
441454
onleaf = function(node, depth)
442455
local filecfg = fileconfig.getconfig(node, cfg)
443456
if filecfg and not filecfg.flags.ExcludeFromBuild then
457+
-- Skip files with buildaction "None"
458+
if filecfg.buildaction == "None" then
459+
return
460+
end
461+
444462
local toolset = ninja.gettoolset(cfg)
445463
if not (cfg.pchsource and node.abspath == cfg.pchsource and toolset == p.tools.msc) then
446464
-- Check if this file has custom build commands
@@ -452,6 +470,12 @@ function m.buildFiles(cfg)
452470
table.insert(objList, output)
453471
end
454472
end
473+
elseif filecfg.buildaction == "Copy" then
474+
-- Handle buildaction "Copy"
475+
local output = m.buildCopyFile(cfg, node, filecfg)
476+
if output then
477+
table.insert(objList, output)
478+
end
455479
else
456480
local objFile = m.objectFile(cfg, node, filecfg)
457481
if objFile then
@@ -471,7 +495,24 @@ function m.objectFile(cfg, node, filecfg)
471495
local objdir = path.getrelative(cfg.workspace.location, cfg.objdir)
472496
local ext = path.getextension(node.abspath):lower()
473497

474-
if path.iscppfile(node.abspath) or path.iscfile(node.abspath) then
498+
local shouldCompile = false
499+
500+
-- Check if buildaction is explicitly set to "Compile"
501+
if filecfg and filecfg.buildaction == "Compile" then
502+
shouldCompile = true
503+
-- Check if compileas is set and is a valid language
504+
elseif filecfg and filecfg.compileas and filecfg.compileas ~= "Default" then
505+
if p.languages.isc(filecfg.compileas) or p.languages.iscpp(filecfg.compileas) then
506+
shouldCompile = true
507+
end
508+
-- Otherwise check if file extension is compilable
509+
else
510+
if path.iscppfile(node.abspath) or path.iscfile(node.abspath) then
511+
shouldCompile = true
512+
end
513+
end
514+
515+
if shouldCompile then
475516
local objname = filecfg.objname or path.getbasename(node.abspath)
476517
local toolset = ninja.gettoolset(cfg)
477518
local objext = toolset.gettooloutputext("cc")
@@ -485,20 +526,49 @@ function m.buildFile(cfg, node, filecfg, objFile, pchFile, prebuildTarget)
485526
local ext = path.getextension(node.abspath):lower()
486527
local rule = nil
487528
local flags = ""
529+
local extraFlags = {}
530+
local toolset = ninja.gettoolset(cfg)
488531

489-
if path.iscfile(node.abspath) then
490-
rule = "cc"
491-
flags = "cflags_" .. ninja.key(cfg)
492-
elseif path.iscppfile(node.abspath) then
493-
rule = "cxx"
494-
flags = "cxxflags_" .. ninja.key(cfg)
532+
if filecfg and filecfg.compileas and filecfg.compileas ~= "Default" then
533+
if p.languages.isc(filecfg.compileas) then
534+
rule = "cc"
535+
flags = "cflags_" .. ninja.key(cfg)
536+
537+
if toolset.shared and toolset.shared.compileas and toolset.shared.compileas["C"] then
538+
table.insert(extraFlags, toolset.shared.compileas["C"])
539+
end
540+
elseif p.languages.iscpp(filecfg.compileas) then
541+
rule = "cxx"
542+
flags = "cxxflags_" .. ninja.key(cfg)
543+
544+
if toolset.shared and toolset.shared.compileas and toolset.shared.compileas["C++"] then
545+
table.insert(extraFlags, toolset.shared.compileas["C++"])
546+
end
547+
end
548+
elseif filecfg and filecfg.buildaction == "Compile" then
549+
-- Use project language for buildaction "Compile" when compileas is not set
550+
if p.languages.isc(cfg.language) then
551+
rule = "cc"
552+
flags = "cflags_" .. ninja.key(cfg)
553+
else
554+
rule = "cxx"
555+
flags = "cxxflags_" .. ninja.key(cfg)
556+
end
557+
else
558+
if path.iscfile(node.abspath) then
559+
rule = "cc"
560+
flags = "cflags_" .. ninja.key(cfg)
561+
elseif path.iscppfile(node.abspath) then
562+
rule = "cxx"
563+
flags = "cxxflags_" .. ninja.key(cfg)
564+
end
495565
end
496566

497567
if rule then
498568
local relPath = path.getrelative(cfg.workspace.location, node.abspath)
499569
local implicitDeps = ""
500570

501-
if pchFile and not filecfg.flags.NoPCH then
571+
if pchFile and filecfg and not filecfg.flags.NoPCH then
502572
implicitDeps = implicitDeps .. " | " .. pchFile
503573
end
504574

@@ -512,9 +582,17 @@ function m.buildFile(cfg, node, filecfg, objFile, pchFile, prebuildTarget)
512582
_p("build %s: %s %s%s", objFile, rule, relPath, implicitDeps)
513583

514584
if rule == "cc" then
515-
_p(" cflags = $%s", flags)
585+
if #extraFlags > 0 then
586+
_p(" cflags = $%s %s", flags, table.concat(extraFlags, " "))
587+
else
588+
_p(" cflags = $%s", flags)
589+
end
516590
else
517-
_p(" cxxflags = $%s", flags)
591+
if #extraFlags > 0 then
592+
_p(" cxxflags = $%s %s", flags, table.concat(extraFlags, " "))
593+
else
594+
_p(" cxxflags = $%s", flags)
595+
end
518596
end
519597
end
520598
end
@@ -563,6 +641,16 @@ function m.buildCustomFile(cfg, node, filecfg)
563641
return outputs
564642
end
565643

644+
function m.buildCopyFile(cfg, node, filecfg)
645+
local relPath = path.getrelative(cfg.workspace.location, node.abspath)
646+
local targetdir = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory)
647+
local output = targetdir .. "/" .. node.name
648+
649+
_p("build %s: copy %s", output, relPath)
650+
651+
return output
652+
end
653+
566654
function m.linkTarget(cfg)
567655
if not cfg._objectFiles or #cfg._objectFiles == 0 then
568656
return
@@ -595,13 +683,9 @@ function m.linkTarget(cfg)
595683
end
596684

597685
local hasPostBuild = #cfg.postbuildcommands > 0 or cfg.postbuildmessage
598-
local linkTargetName = targetPath
599686

600-
if hasPostBuild then
601-
linkTargetName = targetPath .. ".link"
602-
end
603-
604-
_p("build %s: %s %s%s", linkTargetName, rule, table.concat(cfg._objectFiles, " "), implicitDeps)
687+
-- Always use the actual target name for the link rule
688+
_p("build %s: %s %s%s", targetPath, rule, table.concat(cfg._objectFiles, " "), implicitDeps)
605689

606690
if cfg.kind ~= p.STATICLIB then
607691
_p(" ldflags = $ldflags_%s", ninja.key(cfg))
@@ -613,7 +697,7 @@ function m.linkTarget(cfg)
613697
end
614698

615699
if hasPostBuild then
616-
m.buildPostBuildEvents(cfg, linkTargetName, targetPath)
700+
m.buildPostBuildEvents(cfg, targetPath)
617701
end
618702
end
619703

@@ -630,15 +714,15 @@ function m.buildPreBuildEvents(cfg)
630714

631715
if hasMessage and not hasCommands then
632716
_p("build %s: prebuildmessage", prebuildTarget)
633-
_p(" prebuildmessage = %s", cfg.prebuildmessage)
717+
_p(" prebuildmessage = \"%s\"", cfg.prebuildmessage)
634718
elseif hasCommands and not hasMessage then
635719
local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)
636720
local cmdStr = table.concat(commands, " && ")
637721
_p("build %s: prebuild", prebuildTarget)
638722
_p(" prebuildcommands = %s", cmdStr)
639723
else
640724
local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)
641-
local cmdStr = "echo " .. ninja.esc(cfg.prebuildmessage) .. " && " .. table.concat(commands, " && ")
725+
local cmdStr = "echo \"" .. cfg.prebuildmessage .. "\" && " .. table.concat(commands, " && ")
642726
_p("build %s: prebuild", prebuildTarget)
643727
_p(" prebuildcommands = %s", cmdStr)
644728
end
@@ -659,42 +743,45 @@ function m.buildPreLinkEvents(cfg)
659743

660744
if hasMessage and not hasCommands then
661745
_p("build %s: prelinkmessage", prelinkTarget)
662-
_p(" prelinkmessage = %s", cfg.prelinkmessage)
746+
_p(" prelinkmessage = \"%s\"", cfg.prelinkmessage)
663747
elseif hasCommands and not hasMessage then
664748
local commands = os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)
665749
local cmdStr = table.concat(commands, " && ")
666750
_p("build %s: prelink", prelinkTarget)
667751
_p(" prelinkcommands = %s", cmdStr)
668752
else
669753
local commands = os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)
670-
local cmdStr = "echo " .. ninja.esc(cfg.prelinkmessage) .. " && " .. table.concat(commands, " && ")
754+
local cmdStr = "echo \"" .. cfg.prelinkmessage .. "\" && " .. table.concat(commands, " && ")
671755
_p("build %s: prelink", prelinkTarget)
672756
_p(" prelinkcommands = %s", cmdStr)
673757
end
674758

675759
return prelinkTarget
676760
end
677761

678-
function m.buildPostBuildEvents(cfg, linkTarget, finalTarget)
762+
function m.buildPostBuildEvents(cfg, targetPath)
679763
local hasMessage = cfg.postbuildmessage ~= nil
680764
local hasCommands = #cfg.postbuildcommands > 0
681765

682766
if not hasMessage and not hasCommands then
683767
return
684768
end
685769

770+
-- Create a phony intermediate target for the postbuild step
771+
local postbuildPhony = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory) .. "/" .. cfg.project.name .. ".postbuild"
772+
686773
if hasMessage and not hasCommands then
687-
_p("build %s: postbuildmessage %s", finalTarget, linkTarget)
688-
_p(" postbuildmessage = %s", cfg.postbuildmessage)
774+
_p("build %s: postbuildmessage | %s", postbuildPhony, targetPath)
775+
_p(" postbuildmessage = \"%s\"", cfg.postbuildmessage)
689776
elseif hasCommands and not hasMessage then
690777
local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)
691778
local cmdStr = table.concat(commands, " && ")
692-
_p("build %s: postbuild %s", finalTarget, linkTarget)
779+
_p("build %s: postbuild | %s", postbuildPhony, targetPath)
693780
_p(" postbuildcommands = %s", cmdStr)
694781
else
695782
local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)
696-
local cmdStr = "echo " .. ninja.esc(cfg.postbuildmessage) .. " && " .. table.concat(commands, " && ")
697-
_p("build %s: postbuild %s", finalTarget, linkTarget)
783+
local cmdStr = "echo \"" .. cfg.postbuildmessage .. "\" && " .. table.concat(commands, " && ")
784+
_p("build %s: postbuild | %s", postbuildPhony, targetPath)
698785
_p(" postbuildcommands = %s", cmdStr)
699786
end
700787
end
@@ -707,7 +794,14 @@ function m.buildTargets(prj)
707794
local targetPath = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory) .. "/" .. cfg.buildtarget.name
708795
local cfgName = ninja.key(cfg)
709796

710-
_p("build %s: phony %s", cfgName, targetPath)
797+
-- If there are postbuild events, point to the postbuild target instead
798+
local hasPostBuild = #cfg.postbuildcommands > 0 or cfg.postbuildmessage
799+
if hasPostBuild then
800+
local postbuildTarget = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory) .. "/" .. cfg.project.name .. ".postbuild"
801+
_p("build %s: phony %s", cfgName, postbuildTarget)
802+
else
803+
_p("build %s: phony %s", cfgName, targetPath)
804+
end
711805
end
712806

713807
_p("")
@@ -720,7 +814,15 @@ function m.projectPhonies(prj)
720814
local firstCfg = project.getfirstconfig(prj)
721815
if firstCfg then
722816
local targetPath = path.getrelative(firstCfg.workspace.location, firstCfg.buildtarget.directory) .. "/" .. firstCfg.buildtarget.name
723-
_p("build %s: phony %s", prj.name, targetPath)
817+
818+
-- If there are postbuild events in the first config, point to the postbuild target
819+
local hasPostBuild = #firstCfg.postbuildcommands > 0 or firstCfg.postbuildmessage
820+
if hasPostBuild then
821+
local postbuildTarget = path.getrelative(firstCfg.workspace.location, firstCfg.buildtarget.directory) .. "/" .. firstCfg.project.name .. ".postbuild"
822+
_p("build %s: phony %s", prj.name, postbuildTarget)
823+
else
824+
_p("build %s: phony %s", prj.name, targetPath)
825+
end
724826
end
725827

726828
_p("")

modules/ninja/tests/_tests.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ require("ninja")
99

1010
return {
1111
"test_build_rules.lua",
12+
"test_ninja_buildaction.lua",
13+
"test_ninja_compileas.lua",
1214
"test_ninja_config.lua",
1315
"test_ninja_custom_build.lua",
1416
"test_ninja_pch.lua",

0 commit comments

Comments
 (0)