gmake2_cpp.lua 20 KB

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