premake4.lua (19887B)
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--[[ 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, "wt") 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