gmake.lua 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. --
  2. -- d/actions/gmake.lua
  3. -- Define the D makefile action(s).
  4. -- Copyright (c) 2013-2015 Andrew Gough, Manu Evans, and the Premake project
  5. --
  6. local p = premake
  7. local m = p.modules.d
  8. m.make = {}
  9. local dmake = m.make
  10. require ("gmake")
  11. local make = p.make
  12. local cpp = p.make.cpp
  13. local project = p.project
  14. local config = p.config
  15. local fileconfig = p.fileconfig
  16. -- This check may be unnecessary as we only 'require' this file from d.lua
  17. -- IFF the action already exists, however this may help if this file is
  18. -- directly required, rather than d.lua itself.
  19. local gmake = p.action.get( 'gmake' )
  20. if gmake == nil then
  21. error( "Failed to locate prequisite action 'gmake'" )
  22. end
  23. --
  24. -- Patch the gmake action with the allowed tools...
  25. --
  26. gmake.valid_languages = table.join(gmake.valid_languages, { p.D } )
  27. gmake.valid_tools.dc = { "dmd", "gdc", "ldc" }
  28. function m.make.separateCompilation(prj)
  29. local some = false
  30. local all = true
  31. for cfg in project.eachconfig(prj) do
  32. if cfg.compilationmodel == "File" then
  33. some = true
  34. else
  35. all = false
  36. end
  37. end
  38. return iif(all, "all", iif(some, "some", "none"))
  39. end
  40. --
  41. -- Override the GMake action 'onProject' funtion to provide
  42. -- D knowledge...
  43. --
  44. p.override( gmake, "onProject", function(oldfn, prj)
  45. p.escaper(make.esc)
  46. if project.isd(prj) then
  47. local makefile = make.getmakefilename(prj, true)
  48. p.generate(prj, makefile, m.make.generate)
  49. return
  50. end
  51. oldfn(prj)
  52. end)
  53. p.override( make, "objdir", function(oldfn, cfg)
  54. if cfg.project.language ~= "D" or cfg.compilationmodel == "File" then
  55. oldfn(cfg)
  56. end
  57. end)
  58. p.override( make, "objDirRules", function(oldfn, prj)
  59. if prj.language ~= "D" or m.make.separateCompilation(prj) ~= "none" then
  60. oldfn(prj)
  61. end
  62. end)
  63. ---
  64. -- Add namespace for element definition lists for p.callarray()
  65. ---
  66. m.elements = {}
  67. --
  68. -- Generate a GNU make C++ project makefile, with support for the new platforms API.
  69. --
  70. m.elements.makefile = function(prj)
  71. return {
  72. make.header,
  73. make.phonyRules,
  74. m.make.configs,
  75. m.make.objects, -- TODO: This is basically identical to make.cppObjects(), and should ideally be merged/shared
  76. make.shellType,
  77. m.make.targetRules,
  78. make.targetDirRules,
  79. make.objDirRules,
  80. make.cppCleanRules, -- D clean code is identical to C/C++
  81. make.preBuildRules,
  82. make.preLinkRules,
  83. m.make.dFileRules,
  84. }
  85. end
  86. function m.make.generate(prj)
  87. p.callArray(m.elements.makefile, prj)
  88. end
  89. function m.make.buildRule(prj)
  90. _p('$(TARGET): $(SOURCEFILES) $(LDDEPS)')
  91. _p('\t@echo Building %s', prj.name)
  92. _p('\t$(SILENT) $(BUILDCMD)')
  93. _p('\t$(POSTBUILDCMDS)')
  94. end
  95. function m.make.linkRule(prj)
  96. _p('$(TARGET): $(OBJECTS) $(LDDEPS)')
  97. _p('\t@echo Linking %s', prj.name)
  98. _p('\t$(SILENT) $(LINKCMD)')
  99. _p('\t$(POSTBUILDCMDS)')
  100. end
  101. function m.make.targetRules(prj)
  102. local separateCompilation = m.make.separateCompilation(prj)
  103. if separateCompilation == "all" then
  104. m.make.linkRule(prj)
  105. elseif separateCompilation == "none" then
  106. m.make.buildRule(prj)
  107. else
  108. for cfg in project.eachconfig(prj) do
  109. _x('ifeq ($(config),%s)', cfg.shortname)
  110. if cfg.compilationmodel == "File" then
  111. m.make.linkRule(prj)
  112. else
  113. m.make.buildRule(prj)
  114. end
  115. _p('endif')
  116. end
  117. end
  118. _p('')
  119. end
  120. function m.make.dFileRules(prj)
  121. local separateCompilation = m.make.separateCompilation(prj)
  122. if separateCompilation ~= "none" then
  123. make.cppFileRules(prj)
  124. end
  125. end
  126. --
  127. -- Override the 'standard' file rule to support D source files
  128. --
  129. p.override(cpp, "standardFileRules", function(oldfn, prj, node)
  130. -- D file
  131. if path.isdfile(node.abspath) then
  132. _p('\t$(SILENT) $(DC) $(ALL_DFLAGS) $(OUTPUTFLAG) -c $<')
  133. else
  134. oldfn(prj, node)
  135. end
  136. end)
  137. --
  138. -- Let make know it can compile D source files
  139. --
  140. p.override(make, "fileType", function(oldfn, node)
  141. if path.isdfile(node.abspath) then
  142. return "objects"
  143. else
  144. return oldfn(node)
  145. end
  146. end)
  147. --
  148. -- Write out the settings for a particular configuration.
  149. --
  150. m.elements.makeconfig = function(cfg)
  151. return {
  152. m.make.dTools,
  153. make.target,
  154. m.make.target,
  155. make.objdir,
  156. m.make.versions,
  157. m.make.debug,
  158. m.make.imports,
  159. m.make.stringImports,
  160. m.make.dFlags,
  161. make.libs,
  162. make.ldDeps,
  163. make.ldFlags,
  164. m.make.linkCmd,
  165. make.preBuildCmds,
  166. make.preLinkCmds,
  167. make.postBuildCmds,
  168. m.make.allRules,
  169. make.settings,
  170. }
  171. end
  172. function m.make.configs(prj)
  173. for cfg in project.eachconfig(prj) do
  174. -- identify the toolset used by this configurations (would be nicer if
  175. -- this were computed and stored with the configuration up front)
  176. local toolset = m.make.getToolset(cfg)
  177. if not toolset then
  178. error("Invalid toolset '" + (_OPTIONS.dc or cfg.toolset) + "'")
  179. end
  180. _x('ifeq ($(config),%s)', cfg.shortname)
  181. p.callArray(m.elements.makeconfig, cfg, toolset)
  182. _p('endif')
  183. _p('')
  184. end
  185. end
  186. function m.make.getToolset(cfg)
  187. local toolset, err = p.api.checkValue(p.fields.toolset, _OPTIONS.dc or cfg.toolset or "dmd")
  188. if err then
  189. error { msg=err }
  190. end
  191. return p.tools[toolset]
  192. end
  193. function m.make.dTools(cfg, toolset)
  194. local tool = toolset.gettoolname(cfg, "dc")
  195. if tool then
  196. _p(' DC = %s', tool)
  197. end
  198. end
  199. function m.make.target(cfg, toolset)
  200. if cfg.compilationmodel == "File" then
  201. _p(' OUTPUTFLAG = %s', toolset.gettarget('"$@"'))
  202. end
  203. end
  204. function m.make.versions(cfg, toolset)
  205. _p(' VERSIONS +=%s', make.list(toolset.getversions(cfg.versionconstants, cfg.versionlevel)))
  206. end
  207. function m.make.debug(cfg, toolset)
  208. _p(' DEBUG +=%s', make.list(toolset.getdebug(cfg.debugconstants, cfg.debuglevel)))
  209. end
  210. function m.make.imports(cfg, toolset)
  211. local imports = p.esc(toolset.getimportdirs(cfg, cfg.importdirs))
  212. _p(' IMPORTS +=%s', make.list(imports))
  213. end
  214. function m.make.stringImports(cfg, toolset)
  215. local stringImports = p.esc(toolset.getstringimportdirs(cfg, cfg.stringimportdirs))
  216. _p(' STRINGIMPORTS +=%s', make.list(stringImports))
  217. end
  218. function m.make.dFlags(cfg, toolset)
  219. _p(' ALL_DFLAGS += $(DFLAGS)%s $(VERSIONS) $(DEBUG) $(IMPORTS) $(STRINGIMPORTS) $(ARCH)', make.list(table.join(toolset.getdflags(cfg), cfg.buildoptions)))
  220. end
  221. function m.make.linkCmd(cfg, toolset)
  222. if cfg.compilationmodel == "File" then
  223. _p(' LINKCMD = $(DC) ' .. toolset.gettarget("$(TARGET)") .. ' $(ALL_LDFLAGS) $(LIBS) $(OBJECTS)')
  224. -- local cc = iif(p.languages.isc(cfg.language), "CC", "CXX")
  225. -- _p(' LINKCMD = $(%s) -o $(TARGET) $(OBJECTS) $(RESOURCES) $(ARCH) $(ALL_LDFLAGS) $(LIBS)', cc)
  226. else
  227. _p(' BUILDCMD = $(DC) ' .. toolset.gettarget("$(TARGET)") .. ' $(ALL_DFLAGS) $(ALL_LDFLAGS) $(LIBS) $(SOURCEFILES)')
  228. end
  229. end
  230. function m.make.allRules(cfg, toolset)
  231. -- TODO: The C++ version has some special cases for OSX and Windows... check whether they should be here too?
  232. if cfg.compilationmodel == "File" then
  233. _p('all: $(TARGETDIR) $(OBJDIR) prebuild prelink $(TARGET)')
  234. else
  235. _p('all: $(TARGETDIR) prebuild prelink $(TARGET)')
  236. end
  237. _p('\t@:')
  238. -- _p('')
  239. end
  240. --
  241. -- List the objects file for the project, and each configuration.
  242. --
  243. -- TODO: This is basically identical to make.cppObjects(), and should ideally be merged/shared
  244. function m.make.objects(prj)
  245. -- create lists for intermediate files, at the project level and
  246. -- for each configuration
  247. local root = { sourcefiles={}, objects={} }
  248. local configs = {}
  249. for cfg in project.eachconfig(prj) do
  250. configs[cfg] = { sourcefiles={}, objects={} }
  251. end
  252. -- now walk the list of files in the project
  253. local tr = project.getsourcetree(prj)
  254. p.tree.traverse(tr, {
  255. onleaf = function(node, depth)
  256. -- figure out what configurations contain this file, and
  257. -- if it uses custom build rules
  258. local incfg = {}
  259. local inall = true
  260. local custom = false
  261. for cfg in project.eachconfig(prj) do
  262. local filecfg = fileconfig.getconfig(node, cfg)
  263. if filecfg and not filecfg.flags.ExcludeFromBuild then
  264. incfg[cfg] = filecfg
  265. custom = fileconfig.hasCustomBuildRule(filecfg)
  266. else
  267. inall = false
  268. end
  269. end
  270. if not custom then
  271. -- skip files that aren't compiled
  272. if not path.isdfile(node.abspath) then
  273. return
  274. end
  275. local sourcename = node.relpath
  276. -- TODO: assign a unique object file name to avoid collisions
  277. local objectname = "$(OBJDIR)/" .. node.objname .. ".o"
  278. -- if this file exists in all configurations, write it to
  279. -- the project's list of files, else add to specific cfgs
  280. if inall then
  281. table.insert(root.sourcefiles, sourcename)
  282. table.insert(root.objects, objectname)
  283. else
  284. for cfg in project.eachconfig(prj) do
  285. if incfg[cfg] then
  286. table.insert(configs[cfg].sourcefiles, sourcename)
  287. table.insert(configs[cfg].objects, objectname)
  288. end
  289. end
  290. end
  291. else
  292. for cfg in project.eachconfig(prj) do
  293. local filecfg = incfg[cfg]
  294. if filecfg then
  295. -- if the custom build outputs an object file, add it to
  296. -- the link step automatically to match Visual Studio
  297. local output = project.getrelative(prj, filecfg.buildoutputs[1])
  298. if path.isobjectfile(output) then
  299. table.insert(configs[cfg].objects, output)
  300. end
  301. end
  302. end
  303. end
  304. end
  305. })
  306. local separateCompilation = m.make.separateCompilation(prj)
  307. -- now I can write out the lists, project level first...
  308. function listobjects(var, list)
  309. _p('%s \\', var)
  310. for _, objectname in ipairs(list) do
  311. _x('\t%s \\', objectname)
  312. end
  313. _p('')
  314. end
  315. if separateCompilation ~= "all" then
  316. listobjects('SOURCEFILES :=', root.sourcefiles)
  317. end
  318. if separateCompilation ~= "none" then
  319. listobjects('OBJECTS :=', root.objects, 'o')
  320. end
  321. -- ...then individual configurations, as needed
  322. for cfg in project.eachconfig(prj) do
  323. local files = configs[cfg]
  324. if (#files.sourcefiles > 0 and separateCompilation ~= "all") or (#files.objects > 0 and separateCompilation ~= "none") then
  325. _x('ifeq ($(config),%s)', cfg.shortname)
  326. if #files.sourcefiles > 0 and separateCompilation ~= "all" then
  327. listobjects(' SOURCEFILES +=', files.sourcefiles)
  328. end
  329. if #files.objects > 0 and separateCompilation ~= "none" then
  330. listobjects(' OBJECTS +=', files.objects)
  331. end
  332. _p('endif')
  333. end
  334. end
  335. _p('')
  336. end