Skip to content

Commit fa16509

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

File tree

11 files changed

+1165
-147
lines changed

11 files changed

+1165
-147
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: 152 additions & 35 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
@@ -144,8 +142,7 @@ function m.linkrules(cfg, toolset)
144142
_p("")
145143
else
146144
local ldname = toolset.gettoolname(cfg, iif(cfg.language == "C", "cc", "cxx"))
147-
local groups = iif(cfg.linkgroups == p.ON, { "-Wl,--start-group", "-Wl,--end-group" }, {"", ""})
148-
local commands = string.format("command = %s -o $out %s $in $links $ldflags %s", ldname, groups[1], groups[2]);
145+
local commands = string.format("command = %s -o $out $in $links $ldflags", ldname);
149146

150147
commands = commands:gsub("^%s*(.-)%s*$", "%1")
151148
commands = commands:gsub("%s+", " ")
@@ -298,12 +295,18 @@ function m.getCFlags(cfg, toolset)
298295

299296
local defines = toolset.getdefines(cfg.defines)
300297
flags = table.join(flags, defines)
298+
299+
local undefines = toolset.getundefines(cfg.undefines)
300+
flags = table.join(flags, undefines)
301301

302-
local includedirs = toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs, cfg.frameworkdirs)
302+
local includedirs = toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs, cfg.frameworkdirs, cfg.includedirsafter)
303303
flags = table.join(flags, includedirs)
304304

305305
local forceincludes = toolset.getforceincludes(cfg)
306306
flags = table.join(flags, forceincludes)
307+
308+
local buildopts = cfg.buildoptions or {}
309+
flags = table.join(flags, buildopts)
307310

308311
return flags
309312
end
@@ -317,12 +320,18 @@ function m.getCxxFlags(cfg, toolset)
317320

318321
local defines = toolset.getdefines(cfg.defines)
319322
flags = table.join(flags, defines)
323+
324+
local undefines = toolset.getundefines(cfg.undefines)
325+
flags = table.join(flags, undefines)
320326

321-
local includedirs = toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs, cfg.frameworkdirs)
327+
local includedirs = toolset.getincludedirs(cfg, cfg.includedirs, cfg.externalincludedirs, cfg.frameworkdirs, cfg.includedirsafter)
322328
flags = table.join(flags, includedirs)
323329

324330
local forceincludes = toolset.getforceincludes(cfg)
325331
flags = table.join(flags, forceincludes)
332+
333+
local buildopts = cfg.buildoptions or {}
334+
flags = table.join(flags, buildopts)
326335

327336
return flags
328337
end
@@ -333,6 +342,9 @@ function m.getLdFlags(cfg, toolset)
333342

334343
local toolFlags = toolset.getldflags(cfg)
335344
flags = table.join(flags, toolFlags)
345+
346+
local linkopts = cfg.linkoptions or {}
347+
flags = table.join(flags, linkopts)
336348

337349
local libdirs = toolset.getLibraryDirectories(cfg)
338350
flags = table.join(flags, libdirs)
@@ -441,6 +453,11 @@ function m.buildFiles(cfg)
441453
onleaf = function(node, depth)
442454
local filecfg = fileconfig.getconfig(node, cfg)
443455
if filecfg and not filecfg.flags.ExcludeFromBuild then
456+
-- Skip files with buildaction "None"
457+
if filecfg.buildaction == "None" then
458+
return
459+
end
460+
444461
local toolset = ninja.gettoolset(cfg)
445462
if not (cfg.pchsource and node.abspath == cfg.pchsource and toolset == p.tools.msc) then
446463
-- Check if this file has custom build commands
@@ -452,6 +469,12 @@ function m.buildFiles(cfg)
452469
table.insert(objList, output)
453470
end
454471
end
472+
elseif filecfg.buildaction == "Copy" then
473+
-- Handle buildaction "Copy"
474+
local output = m.buildCopyFile(cfg, node, filecfg)
475+
if output then
476+
table.insert(objList, output)
477+
end
455478
else
456479
local objFile = m.objectFile(cfg, node, filecfg)
457480
if objFile then
@@ -471,7 +494,24 @@ function m.objectFile(cfg, node, filecfg)
471494
local objdir = path.getrelative(cfg.workspace.location, cfg.objdir)
472495
local ext = path.getextension(node.abspath):lower()
473496

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

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

497566
if rule then
498567
local relPath = path.getrelative(cfg.workspace.location, node.abspath)
499568
local implicitDeps = ""
500569

501-
if pchFile and not filecfg.flags.NoPCH then
570+
if pchFile and filecfg and not filecfg.flags.NoPCH then
502571
implicitDeps = implicitDeps .. " | " .. pchFile
503572
end
504573

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

514583
if rule == "cc" then
515-
_p(" cflags = $%s", flags)
584+
if #extraFlags > 0 then
585+
_p(" cflags = $%s %s", flags, table.concat(extraFlags, " "))
586+
else
587+
_p(" cflags = $%s", flags)
588+
end
516589
else
517-
_p(" cxxflags = $%s", flags)
590+
if #extraFlags > 0 then
591+
_p(" cxxflags = $%s %s", flags, table.concat(extraFlags, " "))
592+
else
593+
_p(" cxxflags = $%s", flags)
594+
end
518595
end
519596
end
520597
end
@@ -563,6 +640,16 @@ function m.buildCustomFile(cfg, node, filecfg)
563640
return outputs
564641
end
565642

643+
function m.buildCopyFile(cfg, node, filecfg)
644+
local relPath = path.getrelative(cfg.workspace.location, node.abspath)
645+
local targetdir = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory)
646+
local output = targetdir .. "/" .. node.name
647+
648+
_p("build %s: copy %s", output, relPath)
649+
650+
return output
651+
end
652+
566653
function m.linkTarget(cfg)
567654
if not cfg._objectFiles or #cfg._objectFiles == 0 then
568655
return
@@ -587,6 +674,22 @@ function m.linkTarget(cfg)
587674
implicitDeps = " | " .. table.concat(wksRelDeps, " ")
588675
end
589676

677+
if cfg.dependson and #cfg.dependson > 0 then
678+
for _, depname in ipairs(cfg.dependson) do
679+
local depprj = p.workspace.findproject(cfg.workspace, depname)
680+
if depprj then
681+
local depcfg = project.getconfig(depprj, cfg.buildcfg, cfg.platform)
682+
if depcfg then
683+
local depTarget = path.getrelative(cfg.workspace.location, depcfg.buildtarget.directory) .. "/" .. depcfg.buildtarget.name
684+
if implicitDeps == "" then
685+
implicitDeps = " |"
686+
end
687+
implicitDeps = implicitDeps .. " " .. depTarget
688+
end
689+
end
690+
end
691+
end
692+
590693
if prelinkTarget then
591694
if implicitDeps == "" then
592695
implicitDeps = " |"
@@ -595,13 +698,9 @@ function m.linkTarget(cfg)
595698
end
596699

597700
local hasPostBuild = #cfg.postbuildcommands > 0 or cfg.postbuildmessage
598-
local linkTargetName = targetPath
599-
600-
if hasPostBuild then
601-
linkTargetName = targetPath .. ".link"
602-
end
603701

604-
_p("build %s: %s %s%s", linkTargetName, rule, table.concat(cfg._objectFiles, " "), implicitDeps)
702+
-- Always use the actual target name for the link rule
703+
_p("build %s: %s %s%s", targetPath, rule, table.concat(cfg._objectFiles, " "), implicitDeps)
605704

606705
if cfg.kind ~= p.STATICLIB then
607706
_p(" ldflags = $ldflags_%s", ninja.key(cfg))
@@ -613,7 +712,7 @@ function m.linkTarget(cfg)
613712
end
614713

615714
if hasPostBuild then
616-
m.buildPostBuildEvents(cfg, linkTargetName, targetPath)
715+
m.buildPostBuildEvents(cfg, targetPath)
617716
end
618717
end
619718

@@ -630,15 +729,15 @@ function m.buildPreBuildEvents(cfg)
630729

631730
if hasMessage and not hasCommands then
632731
_p("build %s: prebuildmessage", prebuildTarget)
633-
_p(" prebuildmessage = %s", cfg.prebuildmessage)
732+
_p(" prebuildmessage = \"%s\"", cfg.prebuildmessage)
634733
elseif hasCommands and not hasMessage then
635734
local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)
636735
local cmdStr = table.concat(commands, " && ")
637736
_p("build %s: prebuild", prebuildTarget)
638737
_p(" prebuildcommands = %s", cmdStr)
639738
else
640739
local commands = os.translateCommandsAndPaths(cfg.prebuildcommands, cfg.project.basedir, cfg.project.location)
641-
local cmdStr = "echo " .. ninja.esc(cfg.prebuildmessage) .. " && " .. table.concat(commands, " && ")
740+
local cmdStr = "echo \"" .. cfg.prebuildmessage .. "\" && " .. table.concat(commands, " && ")
642741
_p("build %s: prebuild", prebuildTarget)
643742
_p(" prebuildcommands = %s", cmdStr)
644743
end
@@ -659,42 +758,45 @@ function m.buildPreLinkEvents(cfg)
659758

660759
if hasMessage and not hasCommands then
661760
_p("build %s: prelinkmessage", prelinkTarget)
662-
_p(" prelinkmessage = %s", cfg.prelinkmessage)
761+
_p(" prelinkmessage = \"%s\"", cfg.prelinkmessage)
663762
elseif hasCommands and not hasMessage then
664763
local commands = os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)
665764
local cmdStr = table.concat(commands, " && ")
666765
_p("build %s: prelink", prelinkTarget)
667766
_p(" prelinkcommands = %s", cmdStr)
668767
else
669768
local commands = os.translateCommandsAndPaths(cfg.prelinkcommands, cfg.project.basedir, cfg.project.location)
670-
local cmdStr = "echo " .. ninja.esc(cfg.prelinkmessage) .. " && " .. table.concat(commands, " && ")
769+
local cmdStr = "echo \"" .. cfg.prelinkmessage .. "\" && " .. table.concat(commands, " && ")
671770
_p("build %s: prelink", prelinkTarget)
672771
_p(" prelinkcommands = %s", cmdStr)
673772
end
674773

675774
return prelinkTarget
676775
end
677776

678-
function m.buildPostBuildEvents(cfg, linkTarget, finalTarget)
777+
function m.buildPostBuildEvents(cfg, targetPath)
679778
local hasMessage = cfg.postbuildmessage ~= nil
680779
local hasCommands = #cfg.postbuildcommands > 0
681780

682781
if not hasMessage and not hasCommands then
683782
return
684783
end
685784

785+
-- Create a phony intermediate target for the postbuild step
786+
local postbuildPhony = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory) .. "/" .. cfg.project.name .. ".postbuild"
787+
686788
if hasMessage and not hasCommands then
687-
_p("build %s: postbuildmessage %s", finalTarget, linkTarget)
688-
_p(" postbuildmessage = %s", cfg.postbuildmessage)
789+
_p("build %s: postbuildmessage | %s", postbuildPhony, targetPath)
790+
_p(" postbuildmessage = \"%s\"", cfg.postbuildmessage)
689791
elseif hasCommands and not hasMessage then
690792
local commands = os.translateCommandsAndPaths(cfg.postbuildcommands, cfg.project.basedir, cfg.project.location)
691793
local cmdStr = table.concat(commands, " && ")
692-
_p("build %s: postbuild %s", finalTarget, linkTarget)
794+
_p("build %s: postbuild | %s", postbuildPhony, targetPath)
693795
_p(" postbuildcommands = %s", cmdStr)
694796
else
695797
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)
798+
local cmdStr = "echo \"" .. cfg.postbuildmessage .. "\" && " .. table.concat(commands, " && ")
799+
_p("build %s: postbuild | %s", postbuildPhony, targetPath)
698800
_p(" postbuildcommands = %s", cmdStr)
699801
end
700802
end
@@ -707,7 +809,14 @@ function m.buildTargets(prj)
707809
local targetPath = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory) .. "/" .. cfg.buildtarget.name
708810
local cfgName = ninja.key(cfg)
709811

710-
_p("build %s: phony %s", cfgName, targetPath)
812+
-- If there are postbuild events, point to the postbuild target instead
813+
local hasPostBuild = #cfg.postbuildcommands > 0 or cfg.postbuildmessage
814+
if hasPostBuild then
815+
local postbuildTarget = path.getrelative(cfg.workspace.location, cfg.buildtarget.directory) .. "/" .. cfg.project.name .. ".postbuild"
816+
_p("build %s: phony %s", cfgName, postbuildTarget)
817+
else
818+
_p("build %s: phony %s", cfgName, targetPath)
819+
end
711820
end
712821

713822
_p("")
@@ -720,7 +829,15 @@ function m.projectPhonies(prj)
720829
local firstCfg = project.getfirstconfig(prj)
721830
if firstCfg then
722831
local targetPath = path.getrelative(firstCfg.workspace.location, firstCfg.buildtarget.directory) .. "/" .. firstCfg.buildtarget.name
723-
_p("build %s: phony %s", prj.name, targetPath)
832+
833+
-- If there are postbuild events in the first config, point to the postbuild target
834+
local hasPostBuild = #firstCfg.postbuildcommands > 0 or firstCfg.postbuildmessage
835+
if hasPostBuild then
836+
local postbuildTarget = path.getrelative(firstCfg.workspace.location, firstCfg.buildtarget.directory) .. "/" .. firstCfg.project.name .. ".postbuild"
837+
_p("build %s: phony %s", prj.name, postbuildTarget)
838+
else
839+
_p("build %s: phony %s", prj.name, targetPath)
840+
end
724841
end
725842

726843
_p("")

0 commit comments

Comments
 (0)