gmake2.lua 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. --
  2. -- gmake2.lua
  3. -- (c) 2016-2017 Jason Perkins, Blizzard Entertainment and the Premake project
  4. --
  5. local p = premake
  6. local project = p.project
  7. p.modules.gmake2 = {}
  8. p.modules.gmake2._VERSION = p._VERSION
  9. local gmake2 = p.modules.gmake2
  10. --
  11. -- Write out the default configuration rule for a workspace or project.
  12. --
  13. -- @param target
  14. -- The workspace or project object for which a makefile is being generated.
  15. --
  16. function gmake2.defaultconfig(target)
  17. -- find the right configuration iterator function for this object
  18. local eachconfig = iif(target.project, project.eachconfig, p.workspace.eachconfig)
  19. local defaultconfig = nil
  20. -- find the right default configuration platform, grab first configuration that matches
  21. if target.defaultplatform then
  22. for cfg in eachconfig(target) do
  23. if cfg.platform == target.defaultplatform then
  24. defaultconfig = cfg
  25. break
  26. end
  27. end
  28. end
  29. -- grab the first configuration and write the block
  30. if not defaultconfig then
  31. local iter = eachconfig(target)
  32. defaultconfig = iter()
  33. end
  34. if defaultconfig then
  35. _p('ifndef config')
  36. _x(' config=%s', defaultconfig.shortname)
  37. _p('endif')
  38. _p('')
  39. end
  40. end
  41. ---
  42. -- Escape a string so it can be written to a makefile.
  43. ---
  44. function gmake2.esc(value)
  45. result = value:gsub("\\", "\\\\")
  46. result = result:gsub("\"", "\\\"")
  47. result = result:gsub(" ", "\\ ")
  48. result = result:gsub("%(", "\\(")
  49. result = result:gsub("%)", "\\)")
  50. -- leave $(...) shell replacement sequences alone
  51. result = result:gsub("$\\%((.-)\\%)", "$(%1)")
  52. return result
  53. end
  54. --
  55. -- Get the makefile file name for a workspace or a project. If this object is the
  56. -- only one writing to a location then I can use "Makefile". If more than one object
  57. -- writes to the same location I use name + ".make" to keep it unique.
  58. --
  59. function gmake2.getmakefilename(this, searchprjs)
  60. local count = 0
  61. for wks in p.global.eachWorkspace() do
  62. if wks.location == this.location then
  63. count = count + 1
  64. end
  65. if searchprjs then
  66. for _, prj in ipairs(wks.projects) do
  67. if prj.location == this.location then
  68. count = count + 1
  69. end
  70. end
  71. end
  72. end
  73. if count == 1 then
  74. return "Makefile"
  75. else
  76. return ".make"
  77. end
  78. end
  79. --
  80. -- Output a makefile header.
  81. --
  82. -- @param target
  83. -- The workspace or project object for which the makefile is being generated.
  84. --
  85. function gmake2.header(target)
  86. local kind = iif(target.project, "project", "workspace")
  87. _p('# %s %s makefile autogenerated by Premake', p.action.current().shortname, kind)
  88. _p('')
  89. gmake2.defaultconfig(target)
  90. _p('ifndef verbose')
  91. _p(' SILENT = @')
  92. _p('endif')
  93. _p('')
  94. end
  95. --
  96. -- Rules for file ops based on the shell type. Can't use defines and $@ because
  97. -- it screws up the escaping of spaces and parethesis (anyone know a fix?)
  98. --
  99. function gmake2.mkdir(dirname)
  100. _p('ifeq (posix,$(SHELLTYPE))')
  101. _p('\t$(SILENT) mkdir -p %s', dirname)
  102. _p('else')
  103. _p('\t$(SILENT) mkdir $(subst /,\\\\,%s)', dirname)
  104. _p('endif')
  105. end
  106. function gmake2.mkdirRules(dirname)
  107. _p('%s:', dirname)
  108. _p('\t@echo Creating %s', dirname)
  109. gmake2.mkdir(dirname)
  110. _p('')
  111. end
  112. --
  113. -- Format a list of values to be safely written as part of a variable assignment.
  114. --
  115. function gmake2.list(value, quoted)
  116. quoted = false
  117. if #value > 0 then
  118. if quoted then
  119. local result = ""
  120. for _, v in ipairs (value) do
  121. if #result then
  122. result = result .. " "
  123. end
  124. result = result .. p.quoted(v)
  125. end
  126. return result
  127. else
  128. return " " .. table.concat(value, " ")
  129. end
  130. else
  131. return ""
  132. end
  133. end
  134. --
  135. -- Convert an arbitrary string (project name) to a make variable name.
  136. --
  137. function gmake2.tovar(value)
  138. value = value:gsub("[ -]", "_")
  139. value = value:gsub("[()]", "")
  140. return value
  141. end
  142. function gmake2.getToolSet(cfg)
  143. local default = iif(cfg.system == p.MACOSX, "clang", "gcc")
  144. local toolset = p.tools[_OPTIONS.cc or cfg.toolset or default]
  145. if not toolset then
  146. error("Invalid toolset '" .. cfg.toolset .. "'")
  147. end
  148. return toolset
  149. end
  150. function gmake2.outputSection(prj, callback)
  151. local root = {}
  152. for cfg in project.eachconfig(prj) do
  153. -- identify the toolset used by this configurations (would be nicer if
  154. -- this were computed and stored with the configuration up front)
  155. local toolset = gmake2.getToolSet(cfg)
  156. local settings = {}
  157. local funcs = callback(cfg)
  158. for i = 1, #funcs do
  159. local c = p.capture(function ()
  160. funcs[i](cfg, toolset)
  161. end)
  162. if #c > 0 then
  163. table.insert(settings, c)
  164. end
  165. end
  166. if not root.settings then
  167. root.settings = table.arraycopy(settings)
  168. else
  169. root.settings = table.intersect(root.settings, settings)
  170. end
  171. root[cfg] = settings
  172. end
  173. if #root.settings > 0 then
  174. for _, v in ipairs(root.settings) do
  175. p.outln(v)
  176. end
  177. p.outln('')
  178. end
  179. local first = true
  180. for cfg in project.eachconfig(prj) do
  181. local settings = table.difference(root[cfg], root.settings)
  182. if #settings > 0 then
  183. if first then
  184. _x('ifeq ($(config),%s)', cfg.shortname)
  185. first = false
  186. else
  187. _x('else ifeq ($(config),%s)', cfg.shortname)
  188. end
  189. for k, v in ipairs(settings) do
  190. p.outln(v)
  191. end
  192. _p('')
  193. end
  194. end
  195. if not first then
  196. p.outln('endif')
  197. p.outln('')
  198. end
  199. end
  200. -- convert a rule property into a string
  201. ---------------------------------------------------------------------------
  202. --
  203. -- Handlers for the individual makefile elements that can be shared
  204. -- between the different language projects.
  205. --
  206. ---------------------------------------------------------------------------
  207. function gmake2.phonyRules(prj)
  208. _p('.PHONY: clean prebuild')
  209. _p('')
  210. end
  211. function gmake2.shellType()
  212. _p('SHELLTYPE := posix')
  213. _p('ifeq (.exe,$(findstring .exe,$(ComSpec)))')
  214. _p('\tSHELLTYPE := msdos')
  215. _p('endif')
  216. _p('')
  217. end
  218. function gmake2.target(cfg, toolset)
  219. p.outln('TARGETDIR = ' .. project.getrelative(cfg.project, cfg.buildtarget.directory))
  220. p.outln('TARGET = $(TARGETDIR)/' .. cfg.buildtarget.name)
  221. end
  222. function gmake2.objdir(cfg, toolset)
  223. p.outln('OBJDIR = ' .. project.getrelative(cfg.project, cfg.objdir))
  224. end
  225. function gmake2.settings(cfg, toolset)
  226. if #cfg.makesettings > 0 then
  227. for _, value in ipairs(cfg.makesettings) do
  228. p.outln(value)
  229. end
  230. end
  231. local value = toolset.getmakesettings(cfg)
  232. if value then
  233. p.outln(value)
  234. end
  235. end
  236. function gmake2.buildCmds(cfg, event)
  237. _p('define %sCMDS', event:upper())
  238. local steps = cfg[event .. "commands"]
  239. local msg = cfg[event .. "message"]
  240. if #steps > 0 then
  241. steps = os.translateCommandsAndPaths(steps, cfg.project.basedir, cfg.project.location)
  242. msg = msg or string.format("Running %s commands", event)
  243. _p('\t@echo %s', msg)
  244. _p('\t%s', table.implode(steps, "", "", "\n\t"))
  245. end
  246. _p('endef')
  247. end
  248. function gmake2.preBuildCmds(cfg, toolset)
  249. gmake2.buildCmds(cfg, "prebuild")
  250. end
  251. function gmake2.preLinkCmds(cfg, toolset)
  252. gmake2.buildCmds(cfg, "prelink")
  253. end
  254. function gmake2.postBuildCmds(cfg, toolset)
  255. gmake2.buildCmds(cfg, "postbuild")
  256. end
  257. function gmake2.targetDirRules(cfg, toolset)
  258. gmake2.mkdirRules("$(TARGETDIR)")
  259. end
  260. function gmake2.objDirRules(cfg, toolset)
  261. gmake2.mkdirRules("$(OBJDIR)")
  262. end
  263. function gmake2.preBuildRules(cfg, toolset)
  264. _p('prebuild: | $(OBJDIR)')
  265. _p('\t$(PREBUILDCMDS)')
  266. _p('')
  267. end
  268. include("gmake2_cpp.lua")
  269. include("gmake2_csharp.lua")
  270. include("gmake2_makefile.lua")
  271. include("gmake2_utility.lua")
  272. include("gmake2_workspace.lua")
  273. return gmake2