gmake_cpp.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. --
  2. -- make_cpp.lua
  3. -- Generate a C/C++ project makefile.
  4. -- Copyright (c) 2002-2014 Jason Perkins and the Premake project
  5. --
  6. local p = premake
  7. p.make.cpp = {}
  8. local make = p.make
  9. local cpp = p.make.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 p.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. make.header,
  23. make.phonyRules,
  24. make.cppConfigs,
  25. make.cppObjects,
  26. make.shellType,
  27. make.cppTargetRules,
  28. make.cppCustomFilesRules,
  29. make.cppTargetDirRules,
  30. make.cppObjDirRules,
  31. make.cppCleanRules,
  32. make.preBuildRules,
  33. make.preLinkRules,
  34. make.pchRules,
  35. make.cppFileRules,
  36. make.cppDependencies,
  37. }
  38. end
  39. -- should be part of the toolset?
  40. function make.fileTypeExtensions()
  41. return {
  42. ["objects"] = "o",
  43. ["resources"] = "res",
  44. }
  45. end
  46. -- should be part of the toolset?
  47. function make.fileType(node)
  48. local kind
  49. if path.iscppfile(node.abspath) then
  50. kind = "objects"
  51. elseif path.isresourcefile(node.abspath) then
  52. kind = "resources"
  53. end
  54. return kind
  55. end
  56. function make.fileDependency(prj, node)
  57. local filetype = make.fileType(node)
  58. _x('$(OBJDIR)/%s.%s: %s', node.objname, make.fileTypeExtensions()[filetype], node.relpath)
  59. _p('\t@echo $(notdir $<)')
  60. end
  61. function make.cpp.generate(prj)
  62. p.eol("\n")
  63. p.callArray(cpp.elements.makefile, prj)
  64. end
  65. --
  66. -- Write out the commands for compiling a file
  67. --
  68. cpp.elements.standardFileRules = function(prj, node)
  69. return {
  70. make.fileDependency,
  71. cpp.standardFileRules,
  72. }
  73. end
  74. cpp.elements.customFileRules = function(prj, node)
  75. return {
  76. make.fileDependency,
  77. cpp.customFileRules,
  78. }
  79. end
  80. cpp.elements.customBuildRules = function(prj, node)
  81. return {
  82. cpp.customFileRules
  83. }
  84. end
  85. --
  86. -- Write out the settings for a particular configuration.
  87. --
  88. cpp.elements.configuration = function(cfg, toolset)
  89. return {
  90. make.configBegin,
  91. make.cppTools,
  92. make.target,
  93. make.objdir,
  94. make.pch,
  95. make.defines,
  96. make.includes,
  97. make.forceInclude,
  98. make.cppFlags,
  99. make.cFlags,
  100. make.cxxFlags,
  101. make.resFlags,
  102. make.libs,
  103. make.ldDeps,
  104. make.ldFlags,
  105. make.linkCmd,
  106. make.exePaths,
  107. make.preBuildCmds,
  108. make.preLinkCmds,
  109. make.postBuildCmds,
  110. make.cppAllRules,
  111. make.settings,
  112. make.configEnd,
  113. }
  114. end
  115. function make.cppConfigs(prj)
  116. for cfg in project.eachconfig(prj) do
  117. -- identify the toolset used by this configurations (would be nicer if
  118. -- this were computed and stored with the configuration up front)
  119. local toolset = p.tools[_OPTIONS.cc or cfg.toolset or "gcc"]
  120. if not toolset then
  121. error("Invalid toolset '" .. cfg.toolset .. "'")
  122. end
  123. p.callArray(cpp.elements.configuration, cfg, toolset)
  124. _p('')
  125. end
  126. end
  127. function make.exePaths(cfg)
  128. local dirs = project.getrelative(cfg.project, cfg.bindirs)
  129. if #dirs > 0 then
  130. _p(' EXECUTABLE_PATHS = "%s"', table.concat(dirs, ":"))
  131. _p(' EXE_PATHS = export PATH=$(EXECUTABLE_PATHS):$$PATH;')
  132. end
  133. end
  134. --
  135. -- Return the start of the compilation string that corresponds to the 'compileas' enum if set
  136. --
  137. function cpp.compileas(prj, node)
  138. local result
  139. if node["compileas"] then
  140. if p.languages.isc(node.compileas) or node.compileas == p.OBJECTIVEC then
  141. result = '$(CC) $(ALL_CFLAGS)'
  142. elseif p.languages.iscpp(node.compileas) or node.compileas == p.OBJECTIVECPP then
  143. result = '$(CXX) $(ALL_CXXFLAGS)'
  144. end
  145. end
  146. return result
  147. end
  148. --
  149. -- Build command for a single file.
  150. --
  151. function cpp.buildcommand(prj, objext, node)
  152. local flags = cpp.compileas(prj, node)
  153. if not flags then
  154. local iscfile = node and path.iscfile(node.abspath) or false
  155. flags = iif(prj.language == "C" or iscfile, '$(CC) $(ALL_CFLAGS)', '$(CXX) $(ALL_CXXFLAGS)')
  156. end
  157. _p('\t$(SILENT) %s $(FORCE_INCLUDE) -o "$@" -MF "$(@:%%.%s=%%.d)" -c "$<"', flags, objext)
  158. end
  159. --
  160. -- Output the list of file building rules.
  161. --
  162. function make.cppFileRules(prj)
  163. local tr = project.getsourcetree(prj)
  164. p.tree.traverse(tr, {
  165. onleaf = function(node, depth)
  166. -- check to see if this file has custom rules
  167. local rules
  168. for cfg in project.eachconfig(prj) do
  169. local filecfg = fileconfig.getconfig(node, cfg)
  170. if fileconfig.hasCustomBuildRule(filecfg) then
  171. rules = cpp.elements.customBuildRules(prj, node)
  172. break
  173. end
  174. if fileconfig.hasFileSettings(filecfg) then
  175. rules = cpp.elements.customFileRules(prj, node)
  176. break
  177. end
  178. end
  179. if not rules and make.fileType(node) then
  180. rules = cpp.elements.standardFileRules(prj, node)
  181. end
  182. if rules then
  183. p.callArray(rules, prj, node)
  184. end
  185. end
  186. })
  187. _p('')
  188. end
  189. function cpp.standardFileRules(prj, node)
  190. local kind = make.fileType(node)
  191. -- C/C++ file
  192. if kind == "objects" then
  193. cpp.buildcommand(prj, make.fileTypeExtensions()[kind], node)
  194. -- resource file
  195. elseif kind == "resources" then
  196. _p('\t$(SILENT) $(RESCOMP) $< -O coff -o "$@" $(ALL_RESFLAGS)')
  197. end
  198. end
  199. function cpp.customFileRules(prj, node)
  200. for cfg in project.eachconfig(prj) do
  201. local filecfg = fileconfig.getconfig(node, cfg)
  202. if filecfg then
  203. make.configBegin(cfg)
  204. if fileconfig.hasCustomBuildRule(filecfg) then
  205. local output = project.getrelative(prj, filecfg.buildoutputs[1])
  206. local dependencies = filecfg.relpath
  207. if filecfg.buildinputs and #filecfg.buildinputs > 0 then
  208. local inputs = project.getrelative(prj, filecfg.buildinputs)
  209. dependencies = dependencies .. " " .. table.concat(p.esc(inputs), " ")
  210. end
  211. _p('%s: %s', output, dependencies)
  212. _p('\t@echo "%s"', filecfg.buildmessage or ("Building " .. filecfg.relpath))
  213. local cmds = os.translateCommandsAndPaths(filecfg.buildcommands, cfg.project.basedir, cfg.project.location)
  214. for _, cmd in ipairs(cmds) do
  215. if cfg.bindirs and #cfg.bindirs > 0 then
  216. _p('\t$(SILENT) $(EXE_PATHS) %s', cmd)
  217. else
  218. _p('\t$(SILENT) %s', cmd)
  219. end
  220. end
  221. else
  222. cpp.standardFileRules(prj, filecfg)
  223. end
  224. make.configEnd(cfg)
  225. end
  226. end
  227. end
  228. --
  229. -- List the objects file for the project, and each configuration.
  230. --
  231. function make.cppObjects(prj)
  232. -- create lists for intermediate files, at the project level and
  233. -- for each configuration
  234. local root = { objects={}, resources={}, customfiles={} }
  235. local configs = {}
  236. for cfg in project.eachconfig(prj) do
  237. configs[cfg] = { objects={}, resources={}, customfiles={} }
  238. end
  239. -- now walk the list of files in the project
  240. local tr = project.getsourcetree(prj)
  241. p.tree.traverse(tr, {
  242. onleaf = function(node, depth)
  243. -- figure out what configurations contain this file, and
  244. -- if it uses custom build rules
  245. local incfg = {}
  246. local inall = true
  247. local custom = false
  248. for cfg in project.eachconfig(prj) do
  249. local filecfg = fileconfig.getconfig(node, cfg)
  250. if filecfg and not filecfg.flags.ExcludeFromBuild then
  251. incfg[cfg] = filecfg
  252. custom = fileconfig.hasCustomBuildRule(filecfg)
  253. else
  254. inall = false
  255. end
  256. end
  257. if not custom then
  258. -- identify the file type
  259. local kind
  260. if path.iscppfile(node.abspath) then
  261. kind = "objects"
  262. elseif path.isresourcefile(node.abspath) then
  263. kind = "resources"
  264. end
  265. -- skip files that aren't compiled
  266. if not custom and not kind then
  267. return
  268. end
  269. -- assign a unique object file name to avoid collisions
  270. objectname = "$(OBJDIR)/" .. node.objname .. iif(kind == "objects", ".o", ".res")
  271. -- if this file exists in all configurations, write it to
  272. -- the project's list of files, else add to specific cfgs
  273. if inall then
  274. table.insert(root[kind], objectname)
  275. else
  276. for cfg in project.eachconfig(prj) do
  277. if incfg[cfg] then
  278. table.insert(configs[cfg][kind], objectname)
  279. end
  280. end
  281. end
  282. else
  283. for cfg in project.eachconfig(prj) do
  284. local filecfg = incfg[cfg]
  285. if filecfg then
  286. local output = project.getrelative(prj, filecfg.buildoutputs[1])
  287. if path.isobjectfile(output) and (filecfg.linkbuildoutputs == true or filecfg.linkbuildoutputs == nil) then
  288. table.insert(configs[cfg].objects, output)
  289. else
  290. table.insert(configs[cfg].customfiles, output)
  291. end
  292. end
  293. end
  294. end
  295. end
  296. })
  297. -- now I can write out the lists, project level first...
  298. function listobjects(var, list)
  299. _p('%s \\', var)
  300. for _, objectname in ipairs(list) do
  301. _x('\t%s \\', objectname)
  302. end
  303. _p('')
  304. end
  305. listobjects('OBJECTS :=', root.objects, 'o')
  306. listobjects('RESOURCES :=', root.resources, 'res')
  307. listobjects('CUSTOMFILES :=', root.customfiles)
  308. -- ...then individual configurations, as needed
  309. for cfg in project.eachconfig(prj) do
  310. local files = configs[cfg]
  311. if #files.objects > 0 or #files.resources > 0 or #files.customfiles > 0 then
  312. make.configBegin(cfg, toolset)
  313. if #files.objects > 0 then
  314. listobjects(' OBJECTS +=', files.objects)
  315. end
  316. if #files.resources > 0 then
  317. listobjects(' RESOURCES +=', files.resources)
  318. end
  319. if #files.customfiles > 0 then
  320. listobjects(' CUSTOMFILES +=', files.customfiles)
  321. end
  322. make.configEnd(cfg, toolset)
  323. _p('')
  324. end
  325. end
  326. end
  327. ---------------------------------------------------------------------------
  328. --
  329. -- Handlers for individual makefile elements
  330. --
  331. ---------------------------------------------------------------------------
  332. function make.configBegin(cfg, toolset)
  333. if cfg then
  334. _x('ifeq ($(config),%s)', cfg.shortname)
  335. end
  336. end
  337. function make.configEnd(cfg, toolset)
  338. if cfg then
  339. _p('endif')
  340. end
  341. end
  342. function make.cFlags(cfg, toolset)
  343. _p(' ALL_CFLAGS += $(CFLAGS) $(ALL_CPPFLAGS)%s', make.list(table.join(toolset.getcflags(cfg), cfg.buildoptions)))
  344. end
  345. function make.cppAllRules(cfg, toolset)
  346. if cfg.system == p.MACOSX and cfg.kind == p.WINDOWEDAPP then
  347. _p('all: prebuild prelink $(TARGET) $(dir $(TARGETDIR))PkgInfo $(dir $(TARGETDIR))Info.plist')
  348. _p('\t@:')
  349. _p('')
  350. _p('$(dir $(TARGETDIR))PkgInfo:')
  351. _p('$(dir $(TARGETDIR))Info.plist:')
  352. else
  353. _p('all: prebuild prelink $(TARGET)')
  354. _p('\t@:')
  355. end
  356. end
  357. function make.cppFlags(cfg, toolset)
  358. _p(' ALL_CPPFLAGS += $(CPPFLAGS)%s $(DEFINES) $(INCLUDES)', make.list(toolset.getcppflags(cfg)))
  359. end
  360. function make.cxxFlags(cfg, toolset)
  361. _p(' ALL_CXXFLAGS += $(CXXFLAGS) $(ALL_CPPFLAGS)%s', make.list(table.join(toolset.getcxxflags(cfg), cfg.buildoptions)))
  362. end
  363. function make.cppCleanRules(prj)
  364. _p('clean:')
  365. _p('\t@echo Cleaning %s', prj.name)
  366. _p('ifeq (posix,$(SHELLTYPE))')
  367. _p('\t$(SILENT) rm -f $(TARGET)')
  368. _p('\t$(SILENT) rm -rf $(OBJDIR)')
  369. _p('else')
  370. _p('\t$(SILENT) if exist $(subst /,\\\\,$(TARGET)) del $(subst /,\\\\,$(TARGET))')
  371. _p('\t$(SILENT) if exist $(subst /,\\\\,$(OBJDIR)) rmdir /s /q $(subst /,\\\\,$(OBJDIR))')
  372. _p('endif')
  373. _p('')
  374. end
  375. function make.cppDependencies(prj)
  376. -- include the dependencies, built by GCC (with the -MMD flag)
  377. _p('-include $(OBJECTS:%%.o=%%.d)')
  378. _p('ifneq (,$(PCH))')
  379. _p(' -include $(OBJDIR)/$(notdir $(PCH)).d')
  380. _p('endif')
  381. end
  382. function make.cppTargetRules(prj)
  383. _p('$(TARGET): $(GCH) ${CUSTOMFILES} $(OBJECTS) $(LDDEPS) $(RESOURCES) | $(TARGETDIR)')
  384. _p('\t@echo Linking %s', prj.name)
  385. _p('\t$(SILENT) $(LINKCMD)')
  386. _p('\t$(POSTBUILDCMDS)')
  387. _p('')
  388. end
  389. function make.cppCustomFilesRules(prj)
  390. _p('$(CUSTOMFILES): | $(OBJDIR)')
  391. _p('')
  392. end
  393. function make.cppTargetDirRules(prj)
  394. _p('$(TARGETDIR):')
  395. _p('\t@echo Creating $(TARGETDIR)')
  396. make.mkdir('$(TARGETDIR)')
  397. _p('')
  398. end
  399. function make.cppObjDirRules(prj)
  400. _p('$(OBJDIR):')
  401. _p('\t@echo Creating $(OBJDIR)')
  402. make.mkdir('$(OBJDIR)')
  403. _p('')
  404. end
  405. function make.cppTools(cfg, toolset)
  406. local tool = toolset.gettoolname(cfg, "cc")
  407. if tool then
  408. _p(' ifeq ($(origin CC), default)')
  409. _p(' CC = %s', tool)
  410. _p(' endif' )
  411. end
  412. tool = toolset.gettoolname(cfg, "cxx")
  413. if tool then
  414. _p(' ifeq ($(origin CXX), default)')
  415. _p(' CXX = %s', tool)
  416. _p(' endif' )
  417. end
  418. tool = toolset.gettoolname(cfg, "ar")
  419. if tool then
  420. _p(' ifeq ($(origin AR), default)')
  421. _p(' AR = %s', tool)
  422. _p(' endif' )
  423. end
  424. tool = toolset.gettoolname(cfg, "rc")
  425. if tool then
  426. _p(' RESCOMP = %s', tool)
  427. end
  428. end
  429. function make.defines(cfg, toolset)
  430. _p(' DEFINES +=%s', make.list(table.join(toolset.getdefines(cfg.defines, cfg), toolset.getundefines(cfg.undefines))))
  431. end
  432. function make.forceInclude(cfg, toolset)
  433. local includes = toolset.getforceincludes(cfg)
  434. if not cfg.flags.NoPCH and cfg.pchheader then
  435. table.insert(includes, 1, "-include $(OBJDIR)/$(notdir $(PCH))")
  436. end
  437. _x(' FORCE_INCLUDE +=%s', make.list(includes))
  438. end
  439. function make.includes(cfg, toolset)
  440. local includes = toolset.getincludedirs(cfg, cfg.includedirs, cfg.sysincludedirs, cfg.frameworkdirs)
  441. _p(' INCLUDES +=%s', make.list(includes))
  442. end
  443. function make.ldDeps(cfg, toolset)
  444. local deps = config.getlinks(cfg, "siblings", "fullpath")
  445. _p(' LDDEPS +=%s', make.list(p.esc(deps)))
  446. end
  447. function make.ldFlags(cfg, toolset)
  448. local flags = table.join(toolset.getLibraryDirectories(cfg), toolset.getrunpathdirs(cfg, table.join(cfg.runpathdirs, config.getsiblingtargetdirs(cfg))), toolset.getldflags(cfg), cfg.linkoptions)
  449. _p(' ALL_LDFLAGS += $(LDFLAGS)%s', make.list(flags))
  450. end
  451. function make.libs(cfg, toolset)
  452. local flags = toolset.getlinks(cfg)
  453. _p(' LIBS +=%s', make.list(flags, true))
  454. end
  455. function make.linkCmd(cfg, toolset)
  456. if cfg.kind == p.STATICLIB then
  457. if cfg.architecture == p.UNIVERSAL then
  458. _p(' LINKCMD = libtool -o "$@" $(OBJECTS)')
  459. else
  460. _p(' LINKCMD = $(AR) ' .. (toolset.arargs or '-rcs') ..' "$@" $(OBJECTS)')
  461. end
  462. elseif cfg.kind == p.UTILITY then
  463. -- Empty LINKCMD for Utility (only custom build rules)
  464. _p(' LINKCMD =')
  465. else
  466. -- this was $(TARGET) $(LDFLAGS) $(OBJECTS)
  467. -- but had trouble linking to certain static libs; $(OBJECTS) moved up
  468. -- $(LDFLAGS) moved to end (http://sourceforge.net/p/premake/patches/107/)
  469. -- $(LIBS) moved to end (http://sourceforge.net/p/premake/bugs/279/)
  470. local cc = iif(p.languages.isc(cfg.language), "CC", "CXX")
  471. _p(' LINKCMD = $(%s) -o "$@" $(OBJECTS) $(RESOURCES) $(ALL_LDFLAGS) $(LIBS)', cc)
  472. end
  473. end
  474. function make.pch(cfg, toolset)
  475. local pch = p.tools.gcc.getpch(cfg)
  476. -- If there is no header, or if PCH has been disabled, I can early out
  477. if pch == nil then
  478. return
  479. end
  480. _x(' PCH = %s', pch)
  481. _p(' GCH = $(OBJDIR)/$(notdir $(PCH)).gch')
  482. end
  483. function make.pchRules(prj)
  484. _p('ifneq (,$(PCH))')
  485. _p('$(OBJECTS): $(GCH) $(PCH) | $(OBJDIR)')
  486. _p('$(GCH): $(PCH) | $(OBJDIR)')
  487. _p('\t@echo $(notdir $<)')
  488. local cmd = iif(prj.language == "C", "$(CC) -x c-header $(ALL_CFLAGS)", "$(CXX) -x c++-header $(ALL_CXXFLAGS)")
  489. _p('\t$(SILENT) %s -o "$@" -MF "$(@:%%.gch=%%.d)" -c "$<"', cmd)
  490. _p('else')
  491. _p('$(OBJECTS): | $(OBJDIR)')
  492. _p('endif')
  493. _p('')
  494. end
  495. function make.resFlags(cfg, toolset)
  496. local resflags = table.join(toolset.getdefines(cfg.resdefines), toolset.getincludedirs(cfg, cfg.resincludedirs), cfg.resoptions)
  497. _p(' ALL_RESFLAGS += $(RESFLAGS) $(DEFINES) $(INCLUDES)%s', make.list(resflags))
  498. end