premake4.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482
  1. -- Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
  2. --
  3. -- This software is provided 'as-is', without any express or implied
  4. -- warranty. In no event will the authors be held liable for any damages
  5. -- arising from the use of this software.
  6. --
  7. -- Permission is granted to anyone to use this software for any purpose,
  8. -- including commercial applications, and to alter it and redistribute it
  9. -- freely.
  10. --
  11. -- Meta-build system using premake created and maintained by
  12. -- Benjamin Henning <b.henning@digipen.edu>
  13. --[[
  14. premake4.lua
  15. This script sets up the entire premake system. It's responsible for executing
  16. all of the definition scripts for the SDL2 library and the entire test suite,
  17. or demos for the iOS platform. It handles each specific platform and uses the
  18. setup state to generate both the configuration header file needed to build
  19. SDL2 and the premake lua script to generate the target project files.
  20. ]]
  21. -- string utility functions
  22. dofile "util/sdl_string.lua"
  23. -- utility file wrapper for some useful functions
  24. dofile "util/sdl_file.lua"
  25. -- system for defining SDL projects
  26. dofile "util/sdl_projects.lua"
  27. -- offers a utility function for finding dependencies specifically on windows
  28. dofile "util/sdl_depends.lua"
  29. -- system for generating a *config.h file used to build the SDL2 library
  30. dofile "util/sdl_gen_config.lua"
  31. -- functions to handle complicated dependency checks using CMake-esque functions
  32. dofile "util/sdl_check_compile.lua"
  33. -- a list of dependency functions for the SDL2 project and any other projects
  34. dofile "util/sdl_dependency_checkers.lua"
  35. -- the following are various options for configuring the meta-build system
  36. newoption {
  37. trigger = "to",
  38. value = "path",
  39. description = "Set the base output directory for the generated and executed lua file."
  40. }
  41. newoption {
  42. trigger = "mingw",
  43. description = "Runs the premake generation script targeted to MinGW."
  44. }
  45. newoption {
  46. trigger = "cygwin",
  47. description = "Runs the premake generation script targeted to Cygwin."
  48. }
  49. newoption {
  50. trigger = "ios",
  51. description = "Runs the premake generation script targeted to iOS."
  52. }
  53. -- determine the localized destination path
  54. local baseLoc = "./"
  55. if _OPTIONS["to"] then
  56. baseLoc = _OPTIONS["to"]:gsub("\\", "/")
  57. end
  58. local deps = SDL_getDependencies()
  59. for _,v in ipairs(deps) do
  60. newoption {
  61. trigger = v:lower(),
  62. description = "Force on the dependency: " .. v
  63. }
  64. end
  65. -- clean action
  66. if _ACTION == "clean" then
  67. -- this is kept the way it is because premake's default method of cleaning the
  68. -- build tree is not very good standalone, whereas the following correctly
  69. -- cleans every build option
  70. print("Cleaning the build environment...")
  71. os.rmdir(baseLoc .. "/SDL2")
  72. os.rmdir(baseLoc .. "/SDL2main")
  73. os.rmdir(baseLoc .. "/SDL2test")
  74. os.rmdir(baseLoc .. "/tests")
  75. os.rmdir(baseLoc .. "/Demos")
  76. os.rmdir(baseLoc .. "/ipch") -- sometimes shows up
  77. os.remove(baseLoc .. "/SDL.sln")
  78. os.remove(baseLoc .. "/SDL.suo")
  79. os.remove(baseLoc .. "/SDL.v11.suo")
  80. os.remove(baseLoc .. "/SDL.sdf")
  81. os.remove(baseLoc .. "/SDL.ncb")
  82. os.remove(baseLoc .. "/SDL-gen.lua")
  83. os.remove(baseLoc .. "/SDL_config_premake.h")
  84. os.remove(baseLoc .. "/Makefile")
  85. os.rmdir(baseLoc .. "/SDL.xcworkspace")
  86. os.exit()
  87. end
  88. -- only run through standard execution if not in help mode
  89. if _OPTIONS["help"] == nil then
  90. -- load all of the project definitions
  91. local results = os.matchfiles("projects/**.lua")
  92. for _,dir in ipairs(results) do
  93. dofile(dir)
  94. end
  95. -- figure out which configuration template to use
  96. local premakeConfigHeader = baseLoc .. "/SDL_config_premake.h"
  97. -- minimal configuration is the default
  98. local premakeTemplateHeader = "./config/SDL_config_minimal.template.h"
  99. if SDL_getos() == "windows" or SDL_getos() == "mingw" then
  100. premakeTemplateHeader = "./config/SDL_config_windows.template.h"
  101. elseif SDL_getos() == "macosx" then
  102. premakeTemplateHeader = "./config/SDL_config_macosx.template.h"
  103. elseif SDL_getos() == "ios" then
  104. premakeTemplateHeader = "./config/SDL_config_iphoneos.template.h"
  105. elseif os.get() == "linux" then
  106. premakeTemplateHeader = "./config/SDL_config_linux.template.h"
  107. elseif SDL_getos() == "cygwin" then
  108. premakeTemplateHeader = "./config/SDL_config_cygwin.template.h"
  109. end
  110. local genFile = baseLoc .. "/SDL-gen.lua"
  111. local file = fileopen(genFile, "wt")
  112. print("Generating " .. genFile .. "...")
  113. -- begin generating the config header file
  114. startGeneration(premakeConfigHeader, premakeTemplateHeader)
  115. -- begin generating the actual premake script
  116. file:print(0, "-- Premake script generated by Simple DirectMedia Layer meta-build script")
  117. file:print(1, 'solution "SDL"')
  118. local platforms = { }
  119. local platformsIndexed = { }
  120. for n,p in pairs(projects) do
  121. if p.platforms and #p.platforms ~= 0 then
  122. for k,v in pairs(p.platforms) do
  123. platforms[v] = true
  124. end
  125. end
  126. end
  127. for n,v in pairs(platforms) do
  128. platformsIndexed[#platformsIndexed + 1] = n
  129. end
  130. file:print(2, implode(platformsIndexed, 'platforms {', '"', '"', ', ', '}'))
  131. file:print(2, 'configurations { "Debug", "Release" }')
  132. for n,p in pairs(projects) do
  133. if p.compat then
  134. local proj = {}
  135. if p.projectLocation ~= nil then
  136. proj.location = p.projectLocation .. "/" .. p.name
  137. else
  138. proj.location = p.name .. "/"
  139. end
  140. proj.includedirs = { path.getrelative(baseLoc,
  141. path.getdirectory(premakeConfigHeader)),
  142. path.getrelative(baseLoc, "../include") }
  143. proj.libdirs = { }
  144. proj.files = { }
  145. local links = { }
  146. local dbgCopyTable = { }
  147. local relCopyTable = { }
  148. -- custom links that shouldn't exist...
  149. -- (these should always happen before dependencies)
  150. if p.customLinks ~= nil then
  151. for k,lnk in pairs(p.customLinks) do
  152. table.insert(links, lnk)
  153. end
  154. end
  155. -- setup project dependencies
  156. local dependencyLocs = { }
  157. if p.projectDependencies ~= nil and #p.projectDependencies ~= 0 then
  158. for k,projname in pairs(p.projectDependencies) do
  159. local depproj = projects[projname]
  160. -- validation that it exists and can be linked to
  161. if depproj ~= nil and (depproj.kind == "SharedLib" or depproj.kind == "StaticLib") then
  162. if depproj.kind == "SharedLib" then
  163. local deplocation = nil
  164. if depproj.projectLocation ~= nil then
  165. deplocation = depproj.projectLocation .. "/" .. p.name
  166. else
  167. deplocation = depproj.name .. "/"
  168. end
  169. table.insert(dependencyLocs, { location = deplocation, name = projname })
  170. else -- static lib
  171. -- we are now dependent on everything the static lib is dependent on
  172. if depproj.customLinks ~= nil then
  173. for k,lnk in pairs(depproj.customLinks) do
  174. table.insert(links, lnk)
  175. end
  176. end
  177. -- also include links from dependencies
  178. for i,d in pairs(depproj.dependencyTree) do
  179. if d.links then
  180. for k,v in pairs(d.links) do
  181. local propPath = v:gsub("\\", "/")
  182. table.insert(links, propPath)
  183. end
  184. end
  185. end
  186. end
  187. -- finally, depend on the project itself
  188. table.insert(links, projname)
  189. elseif depproj == nil then
  190. print("Warning: Missing external dependency for project: ".. p.name ..
  191. ". Be sure you setup project dependencies in a logical order.")
  192. else
  193. print("Warning: Cannot link " .. p.name .. " to second project " ..
  194. projname .. " because the second project is not a library.")
  195. end
  196. end
  197. end
  198. -- iterate across all root directories, matching source directories
  199. local dirs = createDirTable(p.sourcedir)
  200. -- but first, handle any files specifically set in the project, rather than
  201. -- its dependencies
  202. -- register c and h files in this directory
  203. if (p.files ~= nil and #p.files ~= 0) or (p.paths ~= nil and #p.paths ~= 0) then
  204. -- handle all lists of files
  205. if p.files ~= nil and #p.files ~= 0 then
  206. for k,filepat in pairs(p.files) do
  207. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat)) do
  208. table.insert(proj.files, path.getrelative(baseLoc, f))
  209. end
  210. end
  211. end -- end props files if
  212. -- add all .c/.h files from each path
  213. -- handle all related paths
  214. if p.paths ~= nil and #p.paths ~= 0 then
  215. for j,filepat in ipairs(p.paths) do
  216. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.c")) do
  217. table.insert(proj.files, path.getrelative(baseLoc, f))
  218. end
  219. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.h")) do
  220. table.insert(proj.files, path.getrelative(baseLoc, f))
  221. end
  222. -- mac osx
  223. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.m")) do
  224. table.insert(proj.files, path.getrelative(baseLoc, f))
  225. end
  226. end
  227. end -- end of props paths if
  228. end -- end of check for files/paths in main project
  229. -- if this project has any configuration flags, add them to the current file
  230. if p.config then
  231. addConfig(p.config)
  232. end
  233. -- now, handle files and paths for dependencies
  234. for i,props in ipairs(p.dependencyTree) do
  235. if props.compat then
  236. -- register c and h files in this directory
  237. -- handle all lists of files
  238. if props.files ~= nil and #props.files ~= 0 then
  239. for k,filepat in pairs(props.files) do
  240. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat)) do
  241. table.insert(proj.files, path.getrelative(baseLoc, f))
  242. end
  243. end
  244. end -- end props files if
  245. -- add all .c/.h files from each path
  246. -- handle all related paths
  247. if props.paths ~= nil and #props.paths ~= 0 then
  248. for j,filepat in ipairs(props.paths) do
  249. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.c")) do
  250. table.insert(proj.files, path.getrelative(baseLoc, f))
  251. end
  252. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.h")) do
  253. table.insert(proj.files, path.getrelative(baseLoc, f))
  254. end
  255. -- mac osx
  256. for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.m")) do
  257. table.insert(proj.files, path.getrelative(baseLoc, f))
  258. end
  259. end
  260. end -- end of props paths if
  261. -- if this dependency has any special configuration flags, add 'em
  262. if props.config then
  263. addConfig(props.config)
  264. end -- end of props config if check
  265. end -- end check for compatibility
  266. end -- end of props loop
  267. --local debugConfig = configuration("Debug")
  268. local debugConfig = {}
  269. local releaseConfig = {}
  270. debugConfig.defines = { "USING_PREMAKE_CONFIG_H", "_DEBUG" }
  271. releaseConfig.defines = { "USING_PREMAKE_CONFIG_H", "NDEBUG" }
  272. -- setup per-project defines
  273. if p.defines ~= nil then
  274. for k,def in pairs(p.defines) do
  275. table.insert(debugConfig.defines, def)
  276. table.insert(releaseConfig.defines, def)
  277. end
  278. end
  279. debugConfig.buildoptions = { }
  280. if SDL_getos() == "windows" then
  281. table.insert(debugConfig.buildoptions, "/MDd")
  282. end
  283. debugConfig.linkoptions = { }
  284. releaseConfig.buildoptions = {}
  285. releaseConfig.linkoptions = {}
  286. local baseBuildDir = "/Build"
  287. if os.get() == "windows" then
  288. baseBuildDir = "/Win32"
  289. end
  290. debugConfig.flags = { "Symbols" }
  291. debugConfig.targetdir = proj.location .. baseBuildDir .. "/Debug"
  292. releaseConfig.flags = { "OptimizeSpeed" }
  293. releaseConfig.targetdir = proj.location .. baseBuildDir .. "/Release"
  294. -- setup postbuild options
  295. local dbgPostbuildcommands = { }
  296. local relPostbuildcommands = { }
  297. -- handle copying depended shared libraries to correct folders
  298. if os.get() == "windows" then
  299. for k,deploc in pairs(dependencyLocs) do
  300. table.insert(dbgCopyTable, { src = deploc.location .. baseBuildDir .. "/Debug/" .. deploc.name .. ".dll",
  301. dst = debugConfig.targetdir .. "/" .. deploc.name .. ".dll" })
  302. table.insert(relCopyTable, { src = deploc.location .. baseBuildDir .. "/Release/" .. deploc.name .. ".dll",
  303. dst = releaseConfig.targetdir .. "/" .. deploc.name .. ".dll" })
  304. end
  305. end
  306. if p.copy ~= nil then
  307. for k,file in pairs(p.copy) do
  308. -- the following builds relative paths native to the current system for copying, other
  309. -- than the copy command itself, this is essentially cross-platform for paths
  310. -- all custom copies should be relative to the current working directory
  311. table.insert(dbgCopyTable, { src = path.getrelative(baseLoc, p.sourcedir .. "/" .. file), dst = debugConfig.targetdir .. "/" .. file })
  312. table.insert(relCopyTable, { src = path.getrelative(baseLoc, p.sourcedir .. "/" .. file), dst = releaseConfig.targetdir .. "/" .. file })
  313. end
  314. end
  315. for k,file in pairs(dbgCopyTable) do
  316. -- all copies should be relative to project location, based on platform
  317. local relLocation = "./"
  318. --if os.get() == "windows" then
  319. relLocation = proj.location
  320. --end
  321. local fromPath = "./" .. path.getrelative(relLocation, file.src)
  322. local toPath = "./" .. path.getrelative(relLocation, file.dst)
  323. local toPathParent = path.getdirectory(toPath)
  324. local copyCommand = "cp"
  325. local destCheck = "if [ ! -d \\\"" .. toPathParent .. "\\\" ]; then mkdir -p \\\"" .. toPathParent .. "\\\"; fi"
  326. if SDL_getos() ~= "windows" and fromPath:find("*") ~= nil then
  327. -- to path must be a directory for * copies
  328. toPath = path.getdirectory(toPath)
  329. end
  330. if SDL_getos() == "windows" then
  331. fromPath = path.translate(fromPath, "/"):gsub("/", "\\\\")
  332. toPath = path.translate(toPath, "/"):gsub("/", "\\\\")
  333. toPathParent = path.translate(toPathParent, "/"):gsub("/", "\\\\")
  334. copyCommand = "copy"
  335. destCheck = "if not exist \\\"" .. toPathParent .. "\\\" ( mkdir \\\"" .. toPathParent .. "\\\" )"
  336. else
  337. fromPath = path.translate(fromPath, nil):gsub("\\", "/")
  338. toPath = path.translate(toPath, nil):gsub("\\", "/")
  339. end
  340. -- command will check for destination directory to exist and, if it doesn't,
  341. -- it will make the directory and then copy over any assets
  342. local quotedFromPath = fromPath
  343. if SDL_getos() == "windows" or fromPath:find("*") == nil then
  344. quotedFromPath = '\\"' .. quotedFromPath .. '\\"'
  345. end
  346. table.insert(dbgPostbuildcommands, destCheck)
  347. table.insert(dbgPostbuildcommands,
  348. copyCommand .. " " ..
  349. quotedFromPath .. " \\\"" ..
  350. toPath .. "\\\"")
  351. end
  352. for k,file in pairs(relCopyTable) do
  353. -- all copies should be relative to project location, based on platform
  354. local relLocation = "./"
  355. relLocation = proj.location
  356. local fromPath = "./" .. path.getrelative(relLocation, file.src)
  357. local toPath = "./" .. path.getrelative(relLocation, file.dst)
  358. local toPathParent = path.getdirectory(toPath)
  359. local copyCommand = "cp"
  360. local destCheck = "if [ ! -d \\\"" .. toPathParent .. "\\\" ]; then mkdir -p \\\"" .. toPathParent .. "\\\"; fi"
  361. if SDL_getos() ~= "windows" and fromPath:find("*") ~= nil then
  362. -- to path must be a directory for * copies
  363. toPath = path.getdirectory(toPath)
  364. end
  365. if SDL_getos() == "windows" then
  366. fromPath = path.translate(fromPath, "/"):gsub("/", "\\\\")
  367. toPath = path.translate(toPath, "/"):gsub("/", "\\\\")
  368. toPathParent = path.translate(toPathParent, "/"):gsub("/", "\\\\")
  369. copyCommand = "copy"
  370. destCheck = "if not exist \\\"" .. toPathParent .. "\\\" ( mkdir \\\"" .. toPathParent .. "\\\" )"
  371. else
  372. fromPath = path.translate(fromPath, nil):gsub("\\", "/")
  373. toPath = path.translate(toPath, nil):gsub("\\", "/")
  374. end
  375. -- command will check for destination directory to exist and, if it doesn't,
  376. -- it will make the directory and then copy over any assets
  377. local quotedFromPath = fromPath
  378. if SDL_getos() == "windows" or fromPath:find("*") == nil then
  379. quotedFromPath = '\\"' .. quotedFromPath .. '\\"'
  380. end
  381. table.insert(relPostbuildcommands, destCheck)
  382. table.insert(relPostbuildcommands,
  383. copyCommand .. " " ..
  384. quotedFromPath .. " \\\"" ..
  385. toPath .. "\\\"")
  386. end
  387. debugConfig.postbuildcommands = dbgPostbuildcommands
  388. debugConfig.links = links
  389. releaseConfig.postbuildcommands = relPostbuildcommands
  390. releaseConfig.links = links -- release links?
  391. for i,d in pairs(p.dependencyTree) do
  392. if d.includes then
  393. for k,v in pairs(d.includes) do
  394. local propPath = v:gsub("\\", "/")
  395. proj.includedirs[propPath] = propPath
  396. end
  397. end
  398. if d.libs then
  399. for k,v in pairs(d.libs) do
  400. local propPath = v:gsub("\\", "/")
  401. proj.libdirs[propPath] = propPath
  402. end
  403. end
  404. if d.links then
  405. for k,v in pairs(d.links) do
  406. local propPath = v:gsub("\\", "/")
  407. debugConfig.links[#debugConfig.links + 1] = propPath
  408. end
  409. end
  410. end
  411. if #proj.files > 0 then
  412. file:print(1, 'project "' .. p.name .. '"')
  413. file:print(2, 'targetname "' .. p.name .. '"')
  414. -- note: commented out because I think this hack is unnecessary
  415. --if iOSMode and p.kind == "ConsoleApp" then
  416. -- hack for iOS where we cannot build "tools"/ConsoleApps in
  417. -- Xcode for iOS, so we convert them over to WindowedApps
  418. -- p.kind = "WindowedApp"
  419. --end
  420. file:print(2, 'kind "' .. p.kind .. '"')
  421. file:print(2, 'language "' .. p.language .. '"')
  422. file:print(2, 'location "' .. proj.location .. '"')
  423. file:print(2, 'flags { "NoExceptions" }') -- NoRTTI
  424. file:print(2, 'buildoptions { }')--"/GS-" }')
  425. file:print(2, implode(proj.includedirs, 'includedirs {', '"', '"', ', ', '}'))
  426. file:print(2, implode(proj.libdirs, 'libdirs {', '"', '"', ', ', '}'))
  427. file:print(2, implode(proj.files, 'files {', '"', '"', ', ', '}'))
  428. -- debug configuration
  429. file:print(2, 'configuration "Debug"')
  430. file:print(3, 'targetdir "' .. debugConfig.targetdir .. '"')
  431. -- debug dir is relative to the solution's location
  432. file:print(3, 'debugdir "' .. debugConfig.targetdir .. '"')
  433. file:print(3, implode(debugConfig.defines, 'defines {', '"', '"', ', ', '}'))
  434. file:print(3, implode(debugConfig.links, "links {", '"', '"', ', ', "}"))
  435. if SDL_getos() == "mingw" then
  436. -- static runtime
  437. file:print(3, 'linkoptions { "-lmingw32 -static-libgcc" }')
  438. end
  439. if SDL_getos() == "cygwin" then
  440. file:print(3, 'linkoptions { "-static-libgcc" }')
  441. end
  442. file:print(3, implode(debugConfig.flags, "flags {", '"', '"', ', ', "}"))
  443. file:print(3, implode(debugConfig.postbuildcommands, "postbuildcommands {", '"', '"', ', ', "}"))
  444. -- release configuration
  445. file:print(2, 'configuration "Release"')
  446. file:print(3, 'targetdir "' .. releaseConfig.targetdir .. '"')
  447. -- debug dir is relative to the solution's location
  448. file:print(3, 'debugdir "' .. releaseConfig.targetdir .. '"')
  449. file:print(3, implode(releaseConfig.defines, 'defines {', '"', '"', ', ', '}'))
  450. file:print(3, implode(releaseConfig.links, "links {", '"', '"', ', ', "}"))
  451. if SDL_getos() == "mingw" then
  452. -- static runtime
  453. file:print(3, 'linkoptions { "-lmingw32 -static-libgcc" }')
  454. end
  455. file:print(3, implode(releaseConfig.flags, "flags {", '"', '"', ', ', "}"))
  456. file:print(3, implode(releaseConfig.postbuildcommands, "postbuildcommands {", '"', '"', ', ', "}"))
  457. end -- end check for valid project (files to build)
  458. end -- end compatibility check for projects
  459. end -- end for loop for projects
  460. endGeneration() -- finish generating the config header file
  461. file:close()
  462. -- generation is over, now execute the generated file, setup the premake
  463. -- solution, and let premake execute the action and generate the project files
  464. dofile(genFile)
  465. end -- end check for not being in help mode