1-- 2-- gmake2_cpp.lua 3-- Generate a C/C++ project makefile. 4-- (c) 2016-2017 Jason Perkins, Blizzard Entertainment and the Premake project 5-- 6 7 local p = premake 8 local gmake2 = p.modules.gmake2 9 10 gmake2.cpp = {} 11 local cpp = gmake2.cpp 12 13 local project = p.project 14 local config = p.config 15 local fileconfig = p.fileconfig 16 17 18--- 19-- Add namespace for element definition lists for premake.callarray() 20--- 21 22 cpp.elements = {} 23 24 25-- 26-- Generate a GNU make C++ project makefile, with support for the new platforms API. 27-- 28 29 cpp.elements.makefile = function(prj) 30 return { 31 gmake2.header, 32 gmake2.phonyRules, 33 gmake2.shellType, 34 cpp.createRuleTable, 35 cpp.outputConfigurationSection, 36 cpp.outputPerFileConfigurationSection, 37 cpp.createFileTable, 38 cpp.outputFilesSection, 39 cpp.outputRulesSection, 40 cpp.outputFileRuleSection, 41 cpp.dependencies, 42 } 43 end 44 45 46 function cpp.generate(prj) 47 p.eol("\n") 48 p.callArray(cpp.elements.makefile, prj) 49 50 -- allow the garbage collector to clean things up. 51 for cfg in project.eachconfig(prj) do 52 cfg._gmake = nil 53 end 54 prj._gmake = nil 55 end 56 57 58 function cpp.initialize() 59 rule 'cpp' 60 fileExtension { ".cc", ".cpp", ".cxx", ".mm" } 61 buildoutputs { "$(OBJDIR)/%{file.objname}.o" } 62 buildmessage '$(notdir $<)' 63 buildcommands {'$(CXX) %{premake.modules.gmake2.cpp.fileFlags(cfg, file)} $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"'} 64 65 rule 'cc' 66 fileExtension {".c", ".s", ".m"} 67 buildoutputs { "$(OBJDIR)/%{file.objname}.o" } 68 buildmessage '$(notdir $<)' 69 buildcommands {'$(CC) %{premake.modules.gmake2.cpp.fileFlags(cfg, file)} $(FORCE_INCLUDE) -o "$@" -MF "$(@:%.o=%.d)" -c "$<"'} 70 71 rule 'resource' 72 fileExtension ".rc" 73 buildoutputs { "$(OBJDIR)/%{file.objname}.res" } 74 buildmessage '$(notdir $<)' 75 buildcommands {'$(RESCOMP) $< -O coff -o "$@" $(ALL_RESFLAGS)'} 76 77 global(nil) 78 end 79 80 81 function cpp.createRuleTable(prj) 82 local rules = {} 83 84 local function addRule(extension, rule) 85 if type(extension) == 'table' then 86 for _, value in ipairs(extension) do 87 addRule(value, rule) 88 end 89 else 90 rules[extension] = rule 91 end 92 end 93 94 -- add all rules. 95 local usedRules = table.join({'cpp', 'cc', 'resource'}, prj.rules) 96 for _, name in ipairs(usedRules) do 97 local rule = p.global.getRule(name) 98 addRule(rule.fileExtension, rule) 99 end 100 101 -- create fileset categories. 102 local filesets = { 103 ['.o'] = 'OBJECTS', 104 ['.obj'] = 'OBJECTS', 105 ['.cc'] = 'SOURCES', 106 ['.cpp'] = 'SOURCES', 107 ['.cxx'] = 'SOURCES', 108 ['.mm'] = 'SOURCES', 109 ['.c'] = 'SOURCES', 110 ['.s'] = 'SOURCES', 111 ['.m'] = 'SOURCES', 112 ['.rc'] = 'RESOURCES', 113 } 114 115 -- cache the result. 116 prj._gmake = prj._gmake or {} 117 prj._gmake.rules = rules 118 prj._gmake.filesets = filesets 119 end 120 121 122 function cpp.createFileTable(prj) 123 for cfg in project.eachconfig(prj) do 124 cfg._gmake = cfg._gmake or {} 125 cfg._gmake.filesets = {} 126 cfg._gmake.fileRules = {} 127 128 local files = table.shallowcopy(prj._.files) 129 table.foreachi(files, function(node) 130 cpp.addFile(cfg, node) 131 end) 132 133 for _, f in pairs(cfg._gmake.filesets) do 134 table.sort(f) 135 end 136 137 cfg._gmake.kinds = table.keys(cfg._gmake.filesets) 138 table.sort(cfg._gmake.kinds) 139 140 prj._gmake.kinds = table.join(prj._gmake.kinds or {}, cfg._gmake.kinds) 141 end 142 143 -- we need to reassign object sequences if we generated any files. 144 if prj.hasGeneratedFiles and p.project.iscpp(prj) then 145 p.oven.assignObjectSequences(prj) 146 end 147 148 prj._gmake.kinds = table.unique(prj._gmake.kinds) 149 table.sort(prj._gmake.kinds) 150 end 151 152 153 function cpp.addFile(cfg, node) 154 local filecfg = fileconfig.getconfig(node, cfg) 155 if not filecfg or filecfg.flags.ExcludeFromBuild then 156 return 157 end 158 159 -- skip generated files, since we try to figure it out manually below. 160 if node.generated then 161 return 162 end 163 164 -- process custom build commands. 165 if fileconfig.hasCustomBuildRule(filecfg) then 166 local env = table.shallowcopy(filecfg.environ) 167 env.PathVars = { 168 ["file.basename"] = { absolute = false, token = node.basename }, 169 ["file.abspath"] = { absolute = true, token = node.abspath }, 170 ["file.relpath"] = { absolute = false, token = node.relpath }, 171 ["file.name"] = { absolute = false, token = node.name }, 172 ["file.objname"] = { absolute = false, token = node.objname }, 173 ["file.path"] = { absolute = true, token = node.path }, 174 ["file.directory"] = { absolute = true, token = path.getdirectory(node.abspath) }, 175 ["file.reldirectory"] = { absolute = false, token = path.getdirectory(node.relpath) }, 176 } 177 178 local shadowContext = p.context.extent(filecfg, env) 179 180 local buildoutputs = p.project.getrelative(cfg.project, shadowContext.buildoutputs) 181 if buildoutputs and #buildoutputs > 0 then 182 local file = { 183 buildoutputs = buildoutputs, 184 source = node.relpath, 185 buildmessage = shadowContext.buildmessage, 186 buildcommands = shadowContext.buildcommands, 187 buildinputs = p.project.getrelative(cfg.project, shadowContext.buildinputs) 188 } 189 table.insert(cfg._gmake.fileRules, file) 190 191 for _, output in ipairs(buildoutputs) do 192 cpp.addGeneratedFile(cfg, node, output) 193 end 194 end 195 else 196 cpp.addRuleFile(cfg, node) 197 end 198 end 199 200 function cpp.determineFiletype(cfg, node) 201 -- determine which filetype to use 202 local filecfg = fileconfig.getconfig(node, cfg) 203 local fileext = path.getextension(node.abspath):lower() 204 if filecfg and filecfg.compileas then 205 if p.languages.isc(filecfg.compileas) then 206 fileext = ".c" 207 elseif p.languages.iscpp(filecfg.compileas) then 208 fileext = ".cpp" 209 end 210 end 211 212 return fileext; 213 end 214 215 function cpp.addGeneratedFile(cfg, source, filename) 216 -- mark that we have generated files. 217 cfg.project.hasGeneratedFiles = true 218 219 -- add generated file to the project. 220 local files = cfg.project._.files 221 local node = files[filename] 222 if not node then 223 node = fileconfig.new(filename, cfg.project) 224 files[filename] = node 225 table.insert(files, node) 226 end 227 228 -- always overwrite the dependency information. 229 node.dependsOn = source 230 node.generated = true 231 232 -- add to config if not already added. 233 if not fileconfig.getconfig(node, cfg) then 234 fileconfig.addconfig(node, cfg) 235 end 236 237 -- determine which filetype to use 238 local fileext = cpp.determineFiletype(cfg, node) 239 -- add file to the fileset. 240 local filesets = cfg.project._gmake.filesets 241 local kind = filesets[fileext] or "CUSTOM" 242 243 -- don't link generated object files automatically if it's explicitly 244 -- disabled. 245 if path.isobjectfile(filename) and source.linkbuildoutputs == false then 246 kind = "CUSTOM" 247 end 248 249 local fileset = cfg._gmake.filesets[kind] or {} 250 table.insert(fileset, filename) 251 cfg._gmake.filesets[kind] = fileset 252 253 local generatedKind = "GENERATED" 254 local generatedFileset = cfg._gmake.filesets[generatedKind] or {} 255 table.insert(generatedFileset, filename) 256 cfg._gmake.filesets[generatedKind] = generatedFileset 257 258 -- recursively setup rules. 259 cpp.addRuleFile(cfg, node) 260 end 261 262 function cpp.addRuleFile(cfg, node) 263 local rules = cfg.project._gmake.rules 264 local fileext = cpp.determineFiletype(cfg, node) 265 local rule = rules[fileext] 266 if rule then 267 268 local filecfg = fileconfig.getconfig(node, cfg) 269 local environ = table.shallowcopy(filecfg.environ) 270 271 if rule.propertydefinition then 272 gmake2.prepareEnvironment(rule, environ, cfg) 273 gmake2.prepareEnvironment(rule, environ, filecfg) 274 end 275 276 local shadowContext = p.context.extent(rule, environ) 277 278 local buildoutputs = shadowContext.buildoutputs 279 local buildmessage = shadowContext.buildmessage 280 local buildcommands = shadowContext.buildcommands 281 local buildinputs = shadowContext.buildinputs 282 283 buildoutputs = p.project.getrelative(cfg.project, buildoutputs) 284 if buildoutputs and #buildoutputs > 0 then 285 local file = { 286 buildoutputs = buildoutputs, 287 source = node.relpath, 288 buildmessage = buildmessage, 289 buildcommands = buildcommands, 290 buildinputs = buildinputs 291 } 292 table.insert(cfg._gmake.fileRules, file) 293 294 for _, output in ipairs(buildoutputs) do 295 cpp.addGeneratedFile(cfg, node, output) 296 end 297 end 298 end 299 end 300 301 302-- 303-- Write out the settings for a particular configuration. 304-- 305 306 cpp.elements.configuration = function(cfg) 307 return { 308 cpp.tools, 309 gmake2.target, 310 gmake2.objdir, 311 cpp.pch, 312 cpp.defines, 313 cpp.includes, 314 cpp.forceInclude, 315 cpp.cppFlags, 316 cpp.cFlags, 317 cpp.cxxFlags, 318 cpp.resFlags, 319 cpp.libs, 320 cpp.ldDeps, 321 cpp.ldFlags, 322 cpp.linkCmd, 323 cpp.bindirs, 324 cpp.exepaths, 325 gmake2.settings, 326 gmake2.preBuildCmds, 327 gmake2.preLinkCmds, 328 gmake2.postBuildCmds, 329 } 330 end 331 332 333 function cpp.outputConfigurationSection(prj) 334 _p('# Configurations') 335 _p('# #############################################') 336 _p('') 337 gmake2.outputSection(prj, cpp.elements.configuration) 338 end 339 340 341 function cpp.tools(cfg, toolset) 342 local tool = toolset.gettoolname(cfg, "cc") 343 if tool then 344 _p('ifeq ($(origin CC), default)') 345 _p(' CC = %s', tool) 346 _p('endif' ) 347 end 348 349 tool = toolset.gettoolname(cfg, "cxx") 350 if tool then 351 _p('ifeq ($(origin CXX), default)') 352 _p(' CXX = %s', tool) 353 _p('endif' ) 354 end 355 356 tool = toolset.gettoolname(cfg, "ar") 357 if tool then 358 _p('ifeq ($(origin AR), default)') 359 _p(' AR = %s', tool) 360 _p('endif' ) 361 end 362 363 tool = toolset.gettoolname(cfg, "rc") 364 if tool then 365 _p('RESCOMP = %s', tool) 366 end 367 end 368 369 370 function cpp.pch(cfg, toolset) 371 -- If there is no header, or if PCH has been disabled, I can early out 372 if not cfg.pchheader or cfg.flags.NoPCH then 373 return 374 end 375 376 -- Visual Studio requires the PCH header to be specified in the same way 377 -- it appears in the #include statements used in the source code; the PCH 378 -- source actual handles the compilation of the header. GCC compiles the 379 -- header file directly, and needs the file's actual file system path in 380 -- order to locate it. 381 382 -- To maximize the compatibility between the two approaches, see if I can 383 -- locate the specified PCH header on one of the include file search paths 384 -- and, if so, adjust the path automatically so the user doesn't have 385 -- add a conditional configuration to the project script. 386 387 local pch = cfg.pchheader 388 local found = false 389 390 -- test locally in the project folder first (this is the most likely location) 391 local testname = path.join(cfg.project.basedir, pch) 392 if os.isfile(testname) then 393 pch = project.getrelative(cfg.project, testname) 394 found = true 395 else 396 -- else scan in all include dirs. 397 for _, incdir in ipairs(cfg.includedirs) do 398 testname = path.join(incdir, pch) 399 if os.isfile(testname) then 400 pch = project.getrelative(cfg.project, testname) 401 found = true 402 break 403 end 404 end 405 end 406 407 if not found then 408 pch = project.getrelative(cfg.project, path.getabsolute(pch)) 409 end 410 411 p.outln('PCH = ' .. pch) 412 p.outln('PCH_PLACEHOLDER = $(OBJDIR)/$(notdir $(PCH))') 413 p.outln('GCH = $(PCH_PLACEHOLDER).gch') 414 end 415 416 417 function cpp.defines(cfg, toolset) 418 p.outln('DEFINES +=' .. gmake2.list(table.join(toolset.getdefines(cfg.defines, cfg), toolset.getundefines(cfg.undefines)))) 419 end 420 421 422 function cpp.includes(cfg, toolset) 423 local includes = toolset.getincludedirs(cfg, cfg.includedirs, cfg.sysincludedirs) 424 p.outln('INCLUDES +=' .. gmake2.list(includes)) 425 end 426 427 428 function cpp.forceInclude(cfg, toolset) 429 local includes = toolset.getforceincludes(cfg) 430 p.outln('FORCE_INCLUDE +=' .. gmake2.list(includes)) 431 end 432 433 434 function cpp.cppFlags(cfg, toolset) 435 local flags = gmake2.list(toolset.getcppflags(cfg)) 436 p.outln('ALL_CPPFLAGS += $(CPPFLAGS)' .. flags .. ' $(DEFINES) $(INCLUDES)') 437 end 438 439 440 function cpp.cFlags(cfg, toolset) 441 local flags = gmake2.list(table.join(toolset.getcflags(cfg), cfg.buildoptions)) 442 p.outln('ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS)' .. flags) 443 end 444 445 446 function cpp.cxxFlags(cfg, toolset) 447 local flags = gmake2.list(table.join(toolset.getcxxflags(cfg), cfg.buildoptions)) 448 p.outln('ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS)' .. flags) 449 end 450 451 452 function cpp.resFlags(cfg, toolset) 453 local resflags = table.join(toolset.getdefines(cfg.resdefines), toolset.getincludedirs(cfg, cfg.resincludedirs), cfg.resoptions) 454 p.outln('ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)' .. gmake2.list(resflags)) 455 end 456 457 458 function cpp.libs(cfg, toolset) 459 local flags = toolset.getlinks(cfg) 460 p.outln('LIBS +=' .. gmake2.list(flags, true)) 461 end 462 463 464 function cpp.ldDeps(cfg, toolset) 465 local deps = config.getlinks(cfg, "siblings", "fullpath") 466 p.outln('LDDEPS +=' .. gmake2.list(p.esc(deps))) 467 end 468 469 470 function cpp.ldFlags(cfg, toolset) 471 local flags = table.join(toolset.getLibraryDirectories(cfg), toolset.getrunpathdirs(cfg, cfg.runpathdirs), toolset.getldflags(cfg), cfg.linkoptions) 472 p.outln('ALL_LDFLAGS += $(LDFLAGS)' .. gmake2.list(flags)) 473 end 474 475 476 function cpp.linkCmd(cfg, toolset) 477 if cfg.kind == p.STATICLIB then 478 if cfg.architecture == p.UNIVERSAL then 479 p.outln('LINKCMD = libtool -o "$@" $(OBJECTS)') 480 else 481 p.outln('LINKCMD = $(AR) -rcs "$@" $(OBJECTS)') 482 end 483 elseif cfg.kind == p.UTILITY then 484 -- Empty LINKCMD for Utility (only custom build rules) 485 p.outln('LINKCMD =') 486 else 487 -- this was $(TARGET) $(LDFLAGS) $(OBJECTS) 488 -- but had trouble linking to certain static libs; $(OBJECTS) moved up 489 -- $(LDFLAGS) moved to end (http://sourceforge.net/p/premake/patches/107/) 490 -- $(LIBS) moved to end (http://sourceforge.net/p/premake/bugs/279/) 491 492 local cc = iif(p.languages.isc(cfg.language), "CC", "CXX") 493 p.outln('LINKCMD = $(' .. cc .. ') -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)') 494 end 495 end 496 497 498 function cpp.bindirs(cfg, toolset) 499 local dirs = project.getrelative(cfg.project, cfg.bindirs) 500 if #dirs > 0 then 501 p.outln('EXECUTABLE_PATHS = "' .. table.concat(dirs, ":") .. '"') 502 end 503 end 504 505 506 function cpp.exepaths(cfg, toolset) 507 local dirs = project.getrelative(cfg.project, cfg.bindirs) 508 if #dirs > 0 then 509 p.outln('EXE_PATHS = export PATH=$(EXECUTABLE_PATHS):$$PATH;') 510 end 511 end 512 513 514-- 515-- Write out the per file configurations. 516-- 517 function cpp.outputPerFileConfigurationSection(prj) 518 _p('# Per File Configurations') 519 _p('# #############################################') 520 _p('') 521 for cfg in project.eachconfig(prj) do 522 table.foreachi(prj._.files, function(node) 523 local fcfg = fileconfig.getconfig(node, cfg) 524 if fcfg then 525 cpp.perFileFlags(cfg, fcfg) 526 end 527 end) 528 end 529 _p('') 530 end 531 532 function cpp.makeVarName(prj, value, saltValue) 533 prj._gmake = prj._gmake or {} 534 prj._gmake.varlist = prj._gmake.varlist or {} 535 prj._gmake.varlistlength = prj._gmake.varlistlength or 0 536 local cache = prj._gmake.varlist 537 local length = prj._gmake.varlistlength 538 539 local key = value .. saltValue 540 541 if (cache[key] ~= nil) then 542 return cache[key], false 543 end 544 545 local var = string.format("PERFILE_FLAGS_%d", length) 546 cache[key] = var 547 548 prj._gmake.varlistlength = length + 1 549 550 return var, true 551 end 552 553 function cpp.perFileFlags(cfg, fcfg) 554 local toolset = gmake2.getToolSet(cfg) 555 556 local isCFile = path.iscfile(fcfg.name) 557 558 local getflags = iif(isCFile, toolset.getcflags, toolset.getcxxflags) 559 local value = gmake2.list(table.join(getflags(fcfg), fcfg.buildoptions)) 560 561 if fcfg.defines or fcfg.undefines then 562 local defs = table.join(toolset.getdefines(fcfg.defines, cfg), toolset.getundefines(fcfg.undefines)) 563 if #defs > 0 then 564 value = value .. gmake2.list(defs) 565 end 566 end 567 568 if fcfg.includedirs or fcfg.sysincludedirs then 569 local includes = toolset.getincludedirs(cfg, fcfg.includedirs, fcfg.sysincludedirs) 570 if #includes > 0 then 571 value = value .. gmake2.list(includes) 572 end 573 end 574 575 if #value > 0 then 576 local newPerFileFlag = false 577 fcfg.flagsVariable, newPerFileFlag = cpp.makeVarName(cfg.project, value, iif(isCFile, '_C', '_CPP')) 578 if newPerFileFlag then 579 if isCFile then 580 _p('%s = $(ALL_CFLAGS)%s', fcfg.flagsVariable, value) 581 else 582 _p('%s = $(ALL_CXXFLAGS)%s', fcfg.flagsVariable, value) 583 end 584 end 585 end 586 end 587 588 function cpp.fileFlags(cfg, file) 589 local fcfg = fileconfig.getconfig(file, cfg) 590 local flags = {} 591 592 if cfg.pchheader and not cfg.flags.NoPCH and (not fcfg or not fcfg.flags.NoPCH) then 593 table.insert(flags, "-include $(PCH_PLACEHOLDER)") 594 end 595 596 if fcfg and fcfg.flagsVariable then 597 table.insert(flags, string.format("$(%s)", fcfg.flagsVariable)) 598 else 599 local fileExt = cpp.determineFiletype(cfg, file) 600 601 if path.iscfile(fileExt) then 602 table.insert(flags, "$(ALL_CFLAGS)") 603 elseif path.iscppfile(fileExt) then 604 table.insert(flags, "$(ALL_CXXFLAGS)") 605 end 606 end 607 608 return table.concat(flags, ' ') 609 end 610 611-- 612-- Write out the file sets. 613-- 614 615 cpp.elements.filesets = function(cfg) 616 local result = {} 617 for _, kind in ipairs(cfg._gmake.kinds) do 618 for _, f in ipairs(cfg._gmake.filesets[kind]) do 619 table.insert(result, function(cfg, toolset) 620 cpp.outputFileset(cfg, kind, f) 621 end) 622 end 623 end 624 return result 625 end 626 627 function cpp.outputFilesSection(prj) 628 _p('# File sets') 629 _p('# #############################################') 630 _p('') 631 632 for _, kind in ipairs(prj._gmake.kinds) do 633 _x('%s :=', kind) 634 end 635 _x('') 636 637 gmake2.outputSection(prj, cpp.elements.filesets) 638 end 639 640 function cpp.outputFileset(cfg, kind, file) 641 _x('%s += %s', kind, file) 642 end 643 644 645-- 646-- Write out the targets. 647-- 648 649 cpp.elements.rules = function(cfg) 650 return { 651 cpp.allRules, 652 cpp.targetRules, 653 gmake2.targetDirRules, 654 gmake2.objDirRules, 655 cpp.cleanRules, 656 gmake2.preBuildRules, 657 cpp.customDeps, 658 cpp.pchRules, 659 } 660 end 661 662 663 function cpp.outputRulesSection(prj) 664 _p('# Rules') 665 _p('# #############################################') 666 _p('') 667 gmake2.outputSection(prj, cpp.elements.rules) 668 end 669 670 671 function cpp.allRules(cfg, toolset) 672 if cfg.system == p.MACOSX and cfg.kind == p.WINDOWEDAPP then 673 _p('all: $(TARGET) $(dir $(TARGETDIR))PkgInfo $(dir $(TARGETDIR))Info.plist') 674 _p('\t@:') 675 _p('') 676 _p('$(dir $(TARGETDIR))PkgInfo:') 677 _p('$(dir $(TARGETDIR))Info.plist:') 678 else 679 _p('all: $(TARGET)') 680 _p('\t@:') 681 end 682 _p('') 683 end 684 685 686 function cpp.targetRules(cfg, toolset) 687 local targets = '' 688 689 for _, kind in ipairs(cfg._gmake.kinds) do 690 if kind ~= 'OBJECTS' and kind ~= 'RESOURCES' then 691 targets = targets .. '$(' .. kind .. ') ' 692 end 693 end 694 695 targets = targets .. '$(OBJECTS) $(LDDEPS)' 696 if cfg._gmake.filesets['RESOURCES'] then 697 targets = targets .. ' $(RESOURCES)' 698 end 699 700 _p('$(TARGET): %s | $(TARGETDIR)', targets) 701 _p('\t$(PRELINKCMDS)') 702 _p('\t@echo Linking %s', cfg.project.name) 703 _p('\t$(SILENT) $(LINKCMD)') 704 _p('\t$(POSTBUILDCMDS)') 705 _p('') 706 end 707 708 709 function cpp.customDeps(cfg, toolset) 710 for _, kind in ipairs(cfg._gmake.kinds) do 711 if kind == 'CUSTOM' or kind == 'SOURCES' then 712 _p('$(%s): | prebuild', kind) 713 end 714 end 715 end 716 717 718 function cpp.cleanRules(cfg, toolset) 719 _p('clean:') 720 _p('\t@echo Cleaning %s', cfg.project.name) 721 _p('ifeq (posix,$(SHELLTYPE))') 722 _p('\t$(SILENT) rm -f $(TARGET)') 723 _p('\t$(SILENT) rm -rf $(GENERATED)') 724 _p('\t$(SILENT) rm -rf $(OBJDIR)') 725 _p('else') 726 _p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))') 727 _p('\t$(SILENT) if exist $(subst /,\\\\,$(GENERATED)) rmdir /s /q $(subst /,\\\\,$(GENERATED))') 728 _p('\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))') 729 _p('endif') 730 _p('') 731 end 732 733 734 function cpp.pchRules(cfg, toolset) 735 _p('ifneq (,$(PCH))') 736 _p('$(OBJECTS): $(GCH) | $(PCH_PLACEHOLDER)') 737 _p('$(GCH): $(PCH) | prebuild') 738 _p('\t@echo $(notdir $<)') 739 local cmd = iif(p.languages.isc(cfg.language), "$(CC) -x c-header $(ALL_CFLAGS)", "$(CXX) -x c++-header $(ALL_CXXFLAGS)") 740 _p('\t$(SILENT) %s -o "$@" -MF "$(@:%%.gch=%%.d)" -c "$<"', cmd) 741 _p('$(PCH_PLACEHOLDER): $(GCH) | $(OBJDIR)') 742 _p('ifeq (posix,$(SHELLTYPE))') 743 _p('\t$(SILENT) touch "$@"') 744 _p('else') 745 _p('\t$(SILENT) echo $null >> "$@"') 746 _p('endif') 747 _p('else') 748 _p('$(OBJECTS): | prebuild') 749 _p('endif') 750 _p('') 751 end 752 753-- 754-- Output the file compile targets. 755-- 756 757 cpp.elements.fileRules = function(cfg) 758 local funcs = {} 759 for _, fileRule in ipairs(cfg._gmake.fileRules) do 760 table.insert(funcs, function(cfg, toolset) 761 cpp.outputFileRules(cfg, fileRule) 762 end) 763 end 764 return funcs 765 end 766 767 768 function cpp.outputFileRuleSection(prj) 769 _p('# File Rules') 770 _p('# #############################################') 771 _p('') 772 gmake2.outputSection(prj, cpp.elements.fileRules) 773 end 774 775 776 function cpp.outputFileRules(cfg, file) 777 local dependencies = p.esc(file.source) 778 if file.buildinputs and #file.buildinputs > 0 then 779 dependencies = dependencies .. " " .. table.concat(p.esc(file.buildinputs), " ") 780 end 781 782 _p('%s: %s', file.buildoutputs[1], dependencies) 783 784 if file.buildmessage then 785 _p('\t@echo %s', file.buildmessage) 786 end 787 788 if file.buildcommands then 789 local cmds = os.translateCommandsAndPaths(file.buildcommands, cfg.project.basedir, cfg.project.location) 790 for _, cmd in ipairs(cmds) do 791 if cfg.bindirs and #cfg.bindirs > 0 then 792 _p('\t$(SILENT) $(EXE_PATHS) %s', cmd) 793 else 794 _p('\t$(SILENT) %s', cmd) 795 end 796 end 797 end 798 799 -- TODO: this is a hack with some imperfect side-effects. 800 -- better solution would be to emit a dummy file for the rule, and then outputs depend on it (must clean up dummy in 'clean') 801 -- better yet, is to use pattern rules, but we need to detect that all outputs have the same stem 802 if #file.buildoutputs > 1 then 803 _p('%s: %s', table.concat({ table.unpack(file.buildoutputs, 2) }, ' '), file.buildoutputs[1]) 804 end 805 end 806 807 808--------------------------------------------------------------------------- 809-- 810-- Handlers for individual makefile elements 811-- 812--------------------------------------------------------------------------- 813 814 815 function cpp.dependencies(prj) 816 -- include the dependencies, built by GCC (with the -MMD flag) 817 _p('-include $(OBJECTS:%%.o=%%.d)') 818 _p('ifneq (,$(PCH))') 819 _p(' -include $(PCH_PLACEHOLDER).d') 820 _p('endif') 821 end 822