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