premake/premake4.lua
changeset 7925 f090a47eb7f7
child 8149 681eb46b8ac4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/premake/premake4.lua	Sun Nov 10 00:38:37 2013 -0500
@@ -0,0 +1,482 @@
+-- Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
+--
+-- This software is provided 'as-is', without any express or implied
+-- warranty.  In no event will the authors be held liable for any damages
+-- arising from the use of this software.
+--
+-- Permission is granted to anyone to use this software for any purpose,
+-- including commercial applications, and to alter it and redistribute it
+-- freely.
+--
+-- Meta-build system using premake created and maintained by
+-- Benjamin Henning <b.henning@digipen.edu>
+
+--[[
+premake4.lua
+
+	This script sets up the entire premake system. It's responsible for executing
+	all of the definition scripts for the SDL2 library and the entire test suite,
+	or demos for the iOS platform. It handles each specific platform and uses the
+	setup state to generate both the configuration header file needed to build
+	SDL2 and the premake lua script to generate the target project files.
+]]
+
+-- string utility functions
+dofile "util/sdl_string.lua"
+-- utility file wrapper for some useful functions
+dofile "util/sdl_file.lua"
+-- system for defining SDL projects
+dofile "util/sdl_projects.lua"
+-- offers a utility function for finding dependencies specifically on windows
+dofile "util/sdl_depends.lua"
+-- system for generating a *config.h file used to build the SDL2 library
+dofile "util/sdl_gen_config.lua"
+-- functions to handle complicated dependency checks using CMake-esque functions
+dofile "util/sdl_check_compile.lua"
+-- a list of dependency functions for the SDL2 project and any other projects
+dofile "util/sdl_dependency_checkers.lua"
+
+-- the following are various options for configuring the meta-build system
+newoption {
+	trigger = "to",
+	value   = "path",
+	description = "Set the base output directory for the generated and executed lua file."
+}
+
+newoption {
+	trigger = "mingw",
+	description = "Runs the premake generation script targeted to MinGW."
+}
+
+newoption {
+	trigger = "cygwin",
+	description = "Runs the premake generation script targeted to Cygwin."
+}
+
+newoption {
+	trigger = "ios",
+	description = "Runs the premake generation script targeted to iOS."
+}
+
+-- determine the localized destination path
+local baseLoc = "./"
+if _OPTIONS["to"] then
+	baseLoc = _OPTIONS["to"]:gsub("\\", "/")
+end
+
+local deps = SDL_getDependencies()
+for _,v in ipairs(deps) do
+	newoption {
+		trigger = v:lower(),
+		description = "Force on the dependency: " .. v
+	}
+end
+
+-- clean action
+if _ACTION == "clean" then
+	-- this is kept the way it is because premake's default method of cleaning the
+	-- build tree is not very good standalone, whereas the following correctly
+	-- cleans every build option
+	print("Cleaning the build environment...")
+	os.rmdir(baseLoc .. "/SDL2")
+	os.rmdir(baseLoc .. "/SDL2main")
+	os.rmdir(baseLoc .. "/SDL2test")
+	os.rmdir(baseLoc .. "/tests")
+	os.rmdir(baseLoc .. "/Demos")
+	os.rmdir(baseLoc .. "/ipch") -- sometimes shows up
+	os.remove(baseLoc .. "/SDL.sln")
+	os.remove(baseLoc .. "/SDL.suo")
+	os.remove(baseLoc .. "/SDL.v11.suo")
+	os.remove(baseLoc .. "/SDL.sdf")
+	os.remove(baseLoc .. "/SDL.ncb")
+	os.remove(baseLoc .. "/SDL-gen.lua")
+	os.remove(baseLoc .. "/SDL_config_premake.h")
+	os.remove(baseLoc .. "/Makefile")
+	os.rmdir(baseLoc .. "/SDL.xcworkspace")
+	os.exit()
+end
+
+-- only run through standard execution if not in help mode
+if _OPTIONS["help"] == nil then
+	-- load all of the project definitions
+	local results = os.matchfiles("projects/**.lua")
+	for _,dir in ipairs(results) do
+		dofile(dir)
+	end
+
+	-- figure out which configuration template to use
+	local premakeConfigHeader = baseLoc .. "/SDL_config_premake.h"
+	-- minimal configuration is the default
+	local premakeTemplateHeader = "./config/SDL_config_minimal.template.h"
+	if SDL_getos() == "windows" or SDL_getos() == "mingw" then
+		premakeTemplateHeader = "./config/SDL_config_windows.template.h"
+	elseif SDL_getos() == "macosx" then
+		premakeTemplateHeader = "./config/SDL_config_macosx.template.h"
+	elseif SDL_getos() == "ios" then
+		premakeTemplateHeader = "./config/SDL_config_iphoneos.template.h"
+	elseif os.get() == "linux" then
+		premakeTemplateHeader = "./config/SDL_config_linux.template.h"
+	elseif SDL_getos() == "cygwin" then
+		premakeTemplateHeader = "./config/SDL_config_cygwin.template.h"
+	end
+
+	local genFile = baseLoc .. "/SDL-gen.lua"
+	local file = fileopen(genFile, "wt")
+	print("Generating " .. genFile .. "...")
+	-- begin generating the config header file
+	startGeneration(premakeConfigHeader, premakeTemplateHeader)
+
+	-- begin generating the actual premake script
+	file:print(0, "-- Premake script generated by Simple DirectMedia Layer meta-build script")
+	file:print(1, 'solution "SDL"')
+	local platforms = { }
+	local platformsIndexed = { }
+		for n,p in pairs(projects) do
+			if p.platforms and #p.platforms ~= 0 then
+				for k,v in pairs(p.platforms) do
+					platforms[v] = true
+				end
+			end
+		end
+		for n,v in pairs(platforms) do
+			platformsIndexed[#platformsIndexed + 1] = n
+		end
+		file:print(2, implode(platformsIndexed, 'platforms {', '"', '"', ', ', '}'))
+		file:print(2, 'configurations { "Debug", "Release" }')
+		for n,p in pairs(projects) do
+			if p.compat then
+				local proj = {}
+				if p.projectLocation ~= nil then
+					proj.location = p.projectLocation .. "/" .. p.name
+				else
+					proj.location = p.name .. "/"
+				end
+				proj.includedirs = { path.getrelative(baseLoc,
+					path.getdirectory(premakeConfigHeader)),
+					path.getrelative(baseLoc, "../include") }
+				proj.libdirs = { }
+				proj.files = { }
+				local links = { }
+				local dbgCopyTable = { }
+				local relCopyTable = { }
+				-- custom links that shouldn't exist...
+				-- (these should always happen before dependencies)
+				if p.customLinks ~= nil then
+					for k,lnk in pairs(p.customLinks) do
+						table.insert(links, lnk)
+					end
+				end
+				-- setup project dependencies
+				local dependencyLocs = { }
+				if p.projectDependencies ~= nil and #p.projectDependencies ~= 0 then
+					for k,projname in pairs(p.projectDependencies) do
+						local depproj = projects[projname]
+						-- validation that it exists and can be linked to
+						if depproj ~= nil and (depproj.kind == "SharedLib" or depproj.kind == "StaticLib") then
+							if depproj.kind == "SharedLib" then
+								local deplocation = nil
+								if depproj.projectLocation ~= nil then
+									deplocation = depproj.projectLocation .. "/" .. p.name
+								else
+									deplocation = depproj.name .. "/"
+								end
+								table.insert(dependencyLocs, { location = deplocation, name = projname })
+							else -- static lib
+								-- we are now dependent on everything the static lib is dependent on
+								if depproj.customLinks ~= nil then
+									for k,lnk in pairs(depproj.customLinks) do
+										table.insert(links, lnk)
+									end
+								end
+								-- also include links from dependencies
+								for i,d in pairs(depproj.dependencyTree) do
+									if d.links then
+										for k,v in pairs(d.links) do
+											local propPath = v:gsub("\\", "/")
+											table.insert(links, propPath)
+										end
+									end
+								end
+							end
+							-- finally, depend on the project itself
+							table.insert(links, projname)
+						elseif depproj == nil then
+							print("Warning: Missing external dependency for project: ".. p.name ..
+								". Be sure you setup project dependencies in a logical order.")
+						else
+							print("Warning: Cannot link " .. p.name .. " to second project " ..
+								projname .. " because the second project is not a library.")
+						end
+					end
+				end
+				-- iterate across all root directories, matching source directories
+				local dirs = createDirTable(p.sourcedir)
+				-- but first, handle any files specifically set in the project, rather than
+				-- its dependencies
+				-- register c and h files in this directory
+				if (p.files ~= nil and #p.files ~= 0) or (p.paths ~= nil and #p.paths ~= 0) then
+					-- handle all lists of files
+					if p.files ~= nil and #p.files ~= 0 then
+						for k,filepat in pairs(p.files) do
+							for k,f in pairs(os.matchfiles(p.sourcedir .. filepat)) do
+								table.insert(proj.files, path.getrelative(baseLoc, f))
+							end
+						end
+					end -- end props files if
+					-- add all .c/.h files from each path
+					-- handle all related paths
+					if p.paths ~= nil and #p.paths ~= 0 then
+						for j,filepat in ipairs(p.paths) do
+							for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.c")) do
+								table.insert(proj.files, path.getrelative(baseLoc, f))
+							end
+							for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.h")) do
+								table.insert(proj.files, path.getrelative(baseLoc, f))
+							end
+							-- mac osx
+							for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.m")) do
+								table.insert(proj.files, path.getrelative(baseLoc, f))
+							end
+						end
+					end -- end of props paths if
+				end -- end of check for files/paths in main project
+				-- if this project has any configuration flags, add them to the current file
+				if p.config then
+					addConfig(p.config)
+				end
+				-- now, handle files and paths for dependencies
+				for i,props in ipairs(p.dependencyTree) do
+					if props.compat then
+						-- register c and h files in this directory
+						-- handle all lists of files
+						if props.files ~= nil and #props.files ~= 0 then
+							for k,filepat in pairs(props.files) do
+								for k,f in pairs(os.matchfiles(p.sourcedir .. filepat)) do
+									table.insert(proj.files, path.getrelative(baseLoc, f))
+								end
+							end
+						end -- end props files if
+						-- add all .c/.h files from each path
+						-- handle all related paths
+						if props.paths ~= nil and #props.paths ~= 0 then
+							for j,filepat in ipairs(props.paths) do
+								for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.c")) do
+									table.insert(proj.files, path.getrelative(baseLoc, f))
+								end
+								for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.h")) do
+									table.insert(proj.files, path.getrelative(baseLoc, f))
+								end
+								-- mac osx
+								for k,f in pairs(os.matchfiles(p.sourcedir .. filepat .. "*.m")) do
+									table.insert(proj.files, path.getrelative(baseLoc, f))
+								end
+							end
+						end -- end of props paths if
+						-- if this dependency has any special configuration flags, add 'em
+						if props.config then
+							addConfig(props.config)
+						end -- end of props config if check
+					end -- end check for compatibility
+				end -- end of props loop
+				--local debugConfig = configuration("Debug")
+				local debugConfig = {}
+				local releaseConfig = {}
+				debugConfig.defines = { "USING_PREMAKE_CONFIG_H", "_DEBUG" }
+				releaseConfig.defines = { "USING_PREMAKE_CONFIG_H", "NDEBUG" }
+				-- setup per-project defines
+				if p.defines ~= nil then
+					for k,def in pairs(p.defines) do
+						table.insert(debugConfig.defines, def)
+						table.insert(releaseConfig.defines, def)
+					end
+				end
+				debugConfig.buildoptions = { }
+				if SDL_getos() == "windows" then
+					table.insert(debugConfig.buildoptions, "/MDd")
+				end
+				debugConfig.linkoptions = { }
+				releaseConfig.buildoptions = {}
+				releaseConfig.linkoptions = {}
+				local baseBuildDir = "/Build"
+				if os.get() == "windows" then
+					baseBuildDir = "/Win32"
+				end
+				debugConfig.flags = { "Symbols" }
+				debugConfig.targetdir = proj.location .. baseBuildDir .. "/Debug"
+				releaseConfig.flags = { "OptimizeSpeed" }
+				releaseConfig.targetdir = proj.location .. baseBuildDir .. "/Release"
+				-- setup postbuild options
+				local dbgPostbuildcommands = { }
+				local relPostbuildcommands = { }
+				-- handle copying depended shared libraries to correct folders
+				if os.get() == "windows" then
+					for k,deploc in pairs(dependencyLocs) do
+						table.insert(dbgCopyTable, { src = deploc.location .. baseBuildDir .. "/Debug/" .. deploc.name .. ".dll",
+							dst = debugConfig.targetdir .. "/" .. deploc.name .. ".dll" })
+						table.insert(relCopyTable, { src = deploc.location .. baseBuildDir .. "/Release/" .. deploc.name .. ".dll",
+							dst = releaseConfig.targetdir .. "/" .. deploc.name .. ".dll" })
+					end
+				end
+				if p.copy ~= nil then
+					for k,file in pairs(p.copy) do
+						-- the following builds relative paths native to the current system for copying, other
+						-- than the copy command itself, this is essentially cross-platform for paths
+					
+						-- all custom copies should be relative to the current working directory
+						table.insert(dbgCopyTable, { src = path.getrelative(baseLoc, p.sourcedir .. "/" .. file), dst = debugConfig.targetdir .. "/" .. file })
+						table.insert(relCopyTable, { src = path.getrelative(baseLoc, p.sourcedir .. "/" .. file), dst = releaseConfig.targetdir .. "/" .. file })
+					end
+				end
+				for k,file in pairs(dbgCopyTable) do
+					-- all copies should be relative to project location, based on platform
+					local relLocation = "./"
+					--if os.get() == "windows" then
+						relLocation = proj.location
+					--end
+					local fromPath = "./" .. path.getrelative(relLocation, file.src)
+					local toPath = "./" .. path.getrelative(relLocation, file.dst)
+					local toPathParent = path.getdirectory(toPath)
+					local copyCommand = "cp"
+					local destCheck = "if [ ! -d \\\"" .. toPathParent .. "\\\" ]; then mkdir -p \\\"" .. toPathParent .. "\\\"; fi"
+					if SDL_getos() ~= "windows" and fromPath:find("*") ~= nil then
+						-- to path must be a directory for * copies
+						toPath = path.getdirectory(toPath)
+					end
+					if SDL_getos() == "windows" then
+						fromPath = path.translate(fromPath, "/"):gsub("/", "\\\\")
+						toPath = path.translate(toPath, "/"):gsub("/", "\\\\")
+						toPathParent = path.translate(toPathParent, "/"):gsub("/", "\\\\")
+						copyCommand = "copy"
+						destCheck = "if not exist \\\"" .. toPathParent .. "\\\" ( mkdir \\\"" .. toPathParent .. "\\\" )"
+					else
+						fromPath = path.translate(fromPath, nil):gsub("\\", "/")
+						toPath = path.translate(toPath, nil):gsub("\\", "/")
+					end
+					-- command will check for destination directory to exist and, if it doesn't,
+					-- it will make the directory and then copy over any assets
+					local quotedFromPath = fromPath
+					if SDL_getos() == "windows" or fromPath:find("*") == nil then
+						quotedFromPath = '\\"' .. quotedFromPath .. '\\"'
+					end
+					table.insert(dbgPostbuildcommands, destCheck)
+					table.insert(dbgPostbuildcommands,
+						copyCommand .. " " ..
+						quotedFromPath .. " \\\"" ..
+						toPath .. "\\\"")
+				end
+				for k,file in pairs(relCopyTable) do
+					-- all copies should be relative to project location, based on platform
+					local relLocation = "./"
+					relLocation = proj.location
+					local fromPath = "./" .. path.getrelative(relLocation, file.src)
+					local toPath = "./" .. path.getrelative(relLocation, file.dst)
+					local toPathParent = path.getdirectory(toPath)
+					local copyCommand = "cp"
+					local destCheck = "if [ ! -d \\\"" .. toPathParent .. "\\\" ]; then mkdir -p \\\"" .. toPathParent .. "\\\"; fi"
+					if SDL_getos() ~= "windows" and fromPath:find("*") ~= nil then
+						-- to path must be a directory for * copies
+						toPath = path.getdirectory(toPath)
+					end
+					if SDL_getos() == "windows" then
+						fromPath = path.translate(fromPath, "/"):gsub("/", "\\\\")
+						toPath = path.translate(toPath, "/"):gsub("/", "\\\\")
+						toPathParent = path.translate(toPathParent, "/"):gsub("/", "\\\\")
+						copyCommand = "copy"
+						destCheck = "if not exist \\\"" .. toPathParent .. "\\\" ( mkdir \\\"" .. toPathParent .. "\\\" )"
+					else
+						fromPath = path.translate(fromPath, nil):gsub("\\", "/")
+						toPath = path.translate(toPath, nil):gsub("\\", "/")
+					end
+					-- command will check for destination directory to exist and, if it doesn't,
+					-- it will make the directory and then copy over any assets
+					local quotedFromPath = fromPath
+					if SDL_getos() == "windows" or fromPath:find("*") == nil then
+						quotedFromPath = '\\"' .. quotedFromPath .. '\\"'
+					end
+					table.insert(relPostbuildcommands, destCheck)
+					table.insert(relPostbuildcommands,
+						copyCommand .. " " ..
+						quotedFromPath .. " \\\"" ..
+						toPath .. "\\\"")
+				end
+				debugConfig.postbuildcommands = dbgPostbuildcommands
+				debugConfig.links = links
+				releaseConfig.postbuildcommands = relPostbuildcommands
+				releaseConfig.links = links -- release links?
+				for i,d in pairs(p.dependencyTree) do
+					if d.includes then
+						for k,v in pairs(d.includes) do
+							local propPath = v:gsub("\\", "/")
+							proj.includedirs[propPath] = propPath
+						end
+					end
+					if d.libs then
+						for k,v in pairs(d.libs) do
+							local propPath = v:gsub("\\", "/")
+							proj.libdirs[propPath] = propPath
+						end
+					end
+					if d.links then
+						for k,v in pairs(d.links) do
+							local propPath = v:gsub("\\", "/")
+							debugConfig.links[#debugConfig.links + 1] = propPath
+						end
+					end
+				end
+				if #proj.files > 0 then
+					file:print(1, 'project "' .. p.name .. '"')
+					file:print(2, 'targetname "' .. p.name .. '"')
+					-- note: commented out because I think this hack is unnecessary
+					--if iOSMode and p.kind == "ConsoleApp" then
+						-- hack for iOS where we cannot build "tools"/ConsoleApps in
+						-- Xcode for iOS, so we convert them over to WindowedApps
+					--	p.kind = "WindowedApp"
+					--end
+					file:print(2, 'kind "' .. p.kind .. '"')
+					file:print(2, 'language "' .. p.language .. '"')
+					file:print(2, 'location "' .. proj.location .. '"')
+					file:print(2, 'flags { "NoExceptions" }') -- NoRTTI
+					file:print(2, 'buildoptions { }')--"/GS-" }')
+					file:print(2, implode(proj.includedirs, 'includedirs {', '"', '"', ', ', '}'))
+					file:print(2, implode(proj.libdirs, 'libdirs {', '"', '"', ', ', '}'))
+					file:print(2, implode(proj.files, 'files {', '"', '"', ', ', '}'))
+					-- debug configuration
+					file:print(2, 'configuration "Debug"')
+					file:print(3, 'targetdir "' .. debugConfig.targetdir .. '"')
+					-- debug dir is relative to the solution's location
+					file:print(3, 'debugdir "' .. debugConfig.targetdir .. '"')
+					file:print(3, implode(debugConfig.defines, 'defines {', '"', '"', ', ', '}'))
+					file:print(3, implode(debugConfig.links, "links {", '"', '"', ', ', "}"))
+					if SDL_getos() == "mingw" then
+						-- static runtime
+						file:print(3, 'linkoptions { "-lmingw32 -static-libgcc" }')
+					end
+					if SDL_getos() == "cygwin" then
+						file:print(3, 'linkoptions { "-static-libgcc" }')
+					end
+					file:print(3, implode(debugConfig.flags, "flags {", '"', '"', ', ', "}"))
+					file:print(3, implode(debugConfig.postbuildcommands, "postbuildcommands {", '"', '"', ', ', "}"))
+					-- release configuration
+					file:print(2, 'configuration "Release"')
+					file:print(3, 'targetdir "' .. releaseConfig.targetdir .. '"')
+					-- debug dir is relative to the solution's location
+					file:print(3, 'debugdir "' .. releaseConfig.targetdir .. '"')
+					file:print(3, implode(releaseConfig.defines, 'defines {', '"', '"', ', ', '}'))
+					file:print(3, implode(releaseConfig.links, "links {", '"', '"', ', ', "}"))
+					if SDL_getos() == "mingw" then
+						-- static runtime
+						file:print(3, 'linkoptions { "-lmingw32 -static-libgcc" }')
+					end
+					file:print(3, implode(releaseConfig.flags, "flags {", '"', '"', ', ', "}"))
+					file:print(3, implode(releaseConfig.postbuildcommands, "postbuildcommands {", '"', '"', ', ', "}"))
+				end -- end check for valid project (files to build)
+			end -- end compatibility check for projects
+		end -- end for loop for projects
+
+	endGeneration() -- finish generating the config header file
+	file:close()
+
+	-- generation is over, now execute the generated file, setup the premake
+	-- solution, and let premake execute the action and generate the project files
+	dofile(genFile)
+end -- end check for not being in help mode
\ No newline at end of file