| -- Copyright (C) 1997-2014 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 |