gmake2_utility.lua 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. --
  2. -- gmake2_utility.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.utility = {}
  9. local utility = gmake2.utility
  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. utility.elements = {}
  17. --
  18. -- Generate a GNU make utility project makefile
  19. --
  20. utility.elements.makefile = function(prj)
  21. return {
  22. gmake2.header,
  23. gmake2.phonyRules,
  24. gmake2.shellType,
  25. utility.initialize,
  26. utility.createFileTable,
  27. utility.outputConfigurationSection,
  28. utility.outputFilesSection,
  29. utility.outputRulesSection,
  30. utility.outputFileRuleSection,
  31. }
  32. end
  33. function utility.generate(prj)
  34. p.eol("\n")
  35. p.callArray(utility.elements.makefile, prj)
  36. -- allow the garbage collector to clean things up.
  37. for cfg in project.eachconfig(prj) do
  38. cfg._gmake = nil
  39. end
  40. prj._gmake = nil
  41. end
  42. function utility.initialize(prj)
  43. prj._gmake = prj._gmake or {}
  44. prj._gmake.rules = prj.rules
  45. prj._gmake.filesets = { }
  46. end
  47. function utility.createFileTable(prj)
  48. for cfg in project.eachconfig(prj) do
  49. cfg._gmake = cfg._gmake or {}
  50. cfg._gmake.filesets = {}
  51. cfg._gmake.fileRules = {}
  52. local files = table.shallowcopy(prj._.files)
  53. table.foreachi(files, function(node)
  54. utility.addFile(cfg, node, prj)
  55. end)
  56. for _, f in pairs(cfg._gmake.filesets) do
  57. table.sort(f)
  58. end
  59. cfg._gmake.kinds = table.keys(cfg._gmake.filesets)
  60. table.sort(cfg._gmake.kinds)
  61. prj._gmake.kinds = table.join(prj._gmake.kinds or {}, cfg._gmake.kinds)
  62. end
  63. prj._gmake.kinds = table.unique(prj._gmake.kinds)
  64. table.sort(prj._gmake.kinds)
  65. end
  66. function utility.addFile(cfg, node, prj)
  67. local filecfg = fileconfig.getconfig(node, cfg)
  68. if not filecfg or filecfg.flags.ExcludeFromBuild then
  69. return
  70. end
  71. -- skip generated files, since we try to figure it out manually below.
  72. if node.generated then
  73. return
  74. end
  75. -- process custom build commands.
  76. if fileconfig.hasCustomBuildRule(filecfg) then
  77. local env = table.shallowcopy(filecfg.environ)
  78. env.PathVars = {
  79. ["file.basename"] = { absolute = false, token = node.basename },
  80. ["file.abspath"] = { absolute = true, token = node.abspath },
  81. ["file.relpath"] = { absolute = false, token = node.relpath },
  82. ["file.name"] = { absolute = false, token = node.name },
  83. ["file.path"] = { absolute = true, token = node.path },
  84. }
  85. local shadowContext = p.context.extent(filecfg, env)
  86. local buildoutputs = p.project.getrelative(cfg.project, shadowContext.buildoutputs)
  87. if buildoutputs and #buildoutputs > 0 then
  88. local file = {
  89. buildoutputs = buildoutputs,
  90. source = node.relpath,
  91. buildmessage = shadowContext.buildmessage,
  92. buildcommands = shadowContext.buildcommands,
  93. buildinputs = p.project.getrelative(cfg.project, shadowContext.buildinputs)
  94. }
  95. table.insert(cfg._gmake.fileRules, file)
  96. for _, output in ipairs(buildoutputs) do
  97. utility.addGeneratedFile(cfg, node, output)
  98. end
  99. end
  100. else
  101. utility.addRuleFile(cfg, node)
  102. end
  103. end
  104. function utility.addGeneratedFile(cfg, source, filename)
  105. -- mark that we have generated files.
  106. cfg.project.hasGeneratedFiles = true
  107. -- add generated file to the project.
  108. local files = cfg.project._.files
  109. local node = files[filename]
  110. if not node then
  111. node = fileconfig.new(filename, cfg.project)
  112. files[filename] = node
  113. table.insert(files, node)
  114. end
  115. -- always overwrite the dependency information.
  116. node.dependsOn = source
  117. node.generated = true
  118. -- add to config if not already added.
  119. if not fileconfig.getconfig(node, cfg) then
  120. fileconfig.addconfig(node, cfg)
  121. end
  122. -- add file to the fileset.
  123. local filesets = cfg.project._gmake.filesets
  124. local kind = "CUSTOM"
  125. local fileset = cfg._gmake.filesets[kind] or {}
  126. table.insert(fileset, filename)
  127. cfg._gmake.filesets[kind] = fileset
  128. -- recursively setup rules.
  129. utility.addRuleFile(cfg, node)
  130. end
  131. function utility.addRuleFile(cfg, node)
  132. local rules = cfg.project._gmake.rules
  133. local rule = rules[path.getextension(node.abspath):lower()]
  134. if rule then
  135. local filecfg = fileconfig.getconfig(node, cfg)
  136. local environ = table.shallowcopy(filecfg.environ)
  137. if rule.propertydefinition then
  138. p.rule.prepareEnvironment(rule, environ, cfg)
  139. p.rule.prepareEnvironment(rule, environ, filecfg)
  140. end
  141. local shadowContext = p.context.extent(rule, environ)
  142. local buildoutputs = shadowContext.buildoutputs
  143. local buildmessage = shadowContext.buildmessage
  144. local buildcommands = shadowContext.buildcommands
  145. local buildinputs = shadowContext.buildinputs
  146. buildoutputs = p.project.getrelative(cfg.project, buildoutputs)
  147. if buildoutputs and #buildoutputs > 0 then
  148. local file = {
  149. buildoutputs = buildoutputs,
  150. source = node.relpath,
  151. buildmessage = buildmessage,
  152. buildcommands = buildcommands,
  153. buildinputs = buildinputs
  154. }
  155. table.insert(cfg._gmake.fileRules, file)
  156. for _, output in ipairs(buildoutputs) do
  157. utility.addGeneratedFile(cfg, node, output)
  158. end
  159. end
  160. end
  161. end
  162. --
  163. -- Write out the settings for a particular configuration.
  164. --
  165. utility.elements.configuration = function(cfg)
  166. return {
  167. utility.bindirs,
  168. utility.exepaths,
  169. gmake2.settings,
  170. gmake2.preBuildCmds,
  171. gmake2.preLinkCmds,
  172. gmake2.postBuildCmds,
  173. }
  174. end
  175. function utility.outputConfigurationSection(prj)
  176. _p('# Configurations')
  177. _p('# #############################################')
  178. _p('')
  179. gmake2.outputSection(prj, utility.elements.configuration)
  180. end
  181. function utility.bindirs(cfg, toolset)
  182. local dirs = project.getrelative(cfg.project, cfg.bindirs)
  183. if #dirs > 0 then
  184. p.outln('EXECUTABLE_PATHS = "' .. table.concat(dirs, ":") .. '"')
  185. end
  186. end
  187. function utility.exepaths(cfg, toolset)
  188. local dirs = project.getrelative(cfg.project, cfg.bindirs)
  189. if #dirs > 0 then
  190. p.outln('EXE_PATHS = PATH=$(EXECUTABLE_PATHS):$$PATH;')
  191. end
  192. end
  193. --
  194. -- Write out the file sets.
  195. --
  196. utility.elements.filesets = function(cfg)
  197. local result = {}
  198. for _, kind in ipairs(cfg._gmake.kinds) do
  199. for _, f in ipairs(cfg._gmake.filesets[kind]) do
  200. table.insert(result, function(cfg, toolset)
  201. utility.outputFileset(cfg, kind, f)
  202. end)
  203. end
  204. end
  205. return result
  206. end
  207. function utility.outputFilesSection(prj)
  208. _p('# File sets')
  209. _p('# #############################################')
  210. _p('')
  211. for _, kind in ipairs(prj._gmake.kinds) do
  212. _x('%s :=', kind)
  213. end
  214. _x('')
  215. gmake2.outputSection(prj, utility.elements.filesets)
  216. end
  217. function utility.outputFileset(cfg, kind, file)
  218. _x('%s += %s', kind, file)
  219. end
  220. --
  221. -- Write out the targets.
  222. --
  223. utility.elements.rules = function(cfg)
  224. return {
  225. utility.allRules,
  226. utility.targetRules,
  227. gmake2.targetDirRules,
  228. utility.cleanRules,
  229. }
  230. end
  231. function utility.outputRulesSection(prj)
  232. _p('# Rules')
  233. _p('# #############################################')
  234. _p('')
  235. gmake2.outputSection(prj, utility.elements.rules)
  236. end
  237. function utility.allRules(cfg, toolset)
  238. local allTargets = 'all: $(TARGETDIR) $(TARGET)'
  239. for _, kind in ipairs(cfg._gmake.kinds) do
  240. allTargets = allTargets .. ' $(' .. kind .. ')'
  241. end
  242. _p(allTargets)
  243. _p('\t@:')
  244. _p('')
  245. end
  246. function utility.targetRules(cfg, toolset)
  247. local targets = ''
  248. for _, kind in ipairs(cfg._gmake.kinds) do
  249. targets = targets .. '$(' .. kind .. ') '
  250. end
  251. _p('$(TARGET): %s', targets)
  252. _p('\t$(PREBUILDCMDS)')
  253. _p('\t$(PRELINKCMDS)')
  254. _p('\t$(POSTBUILDCMDS)')
  255. _p('')
  256. end
  257. function utility.cleanRules(cfg, toolset)
  258. _p('clean:')
  259. _p('\t@echo Cleaning %s', cfg.project.name)
  260. _p('')
  261. end
  262. --
  263. -- Output the file compile targets.
  264. --
  265. utility.elements.fileRules = function(cfg)
  266. local funcs = {}
  267. for _, fileRule in ipairs(cfg._gmake.fileRules) do
  268. table.insert(funcs, function(cfg, toolset)
  269. utility.outputFileRules(cfg, fileRule)
  270. end)
  271. end
  272. return funcs
  273. end
  274. function utility.outputFileRuleSection(prj)
  275. _p('# File Rules')
  276. _p('# #############################################')
  277. _p('')
  278. gmake2.outputSection(prj, utility.elements.fileRules)
  279. end
  280. function utility.outputFileRules(cfg, file)
  281. local outputs = table.concat(file.buildoutputs, ' ')
  282. local dependencies = p.esc(file.source)
  283. if file.buildinputs and #file.buildinputs > 0 then
  284. dependencies = dependencies .. " " .. table.concat(p.esc(file.buildinputs), " ")
  285. end
  286. _p('%s: %s', outputs, dependencies)
  287. if file.buildmessage then
  288. _p('\t@echo %s', file.buildmessage)
  289. end
  290. if file.buildcommands then
  291. local cmds = os.translateCommandsAndPaths(file.buildcommands, cfg.project.basedir, cfg.project.location)
  292. for _, cmd in ipairs(cmds) do
  293. if cfg.bindirs and #cfg.bindirs > 0 then
  294. _p('\t$(SILENT) $(EXE_PATHS) %s', cmd)
  295. else
  296. _p('\t$(SILENT) %s', cmd)
  297. end
  298. end
  299. end
  300. end