CheckVersion("0.5") Import("configure.lua") Import("other/sdl/sdl.lua") Import("other/freetype/freetype.lua") --- Setup Config ------- config = NewConfig() config:Add(OptCCompiler("compiler")) config:Add(OptTestCompileC("stackprotector", "int main(){return 0;}", "-fstack-protector -fstack-protector-all")) config:Add(OptTestCompileC("minmacosxsdk", "int main(){return 0;}", "-mmacosx-version-min=10.7 -isysroot /Developer/SDKs/MacOSX10.7.sdk")) config:Add(OptTestCompileC("buildwithoutsseflag", "#include \nint main(){_mm_pause();return 0;}", "")) config:Add(OptLibrary("zlib", "zlib.h", false)) config:Add(SDL.OptFind("sdl", true)) config:Add(FreeType.OptFind("freetype", true)) config:Finalize("config.lua") generated_src_dir = "build/src" generated_icon_dir = "build/icons" builddir = "build/%(arch)s/%(conf)s" content_src_dir = "datasrc/" python_in_path = ExecuteSilent("python -V") == 0 -- data compiler function Python(name) if family == "windows" then name = str_replace(name, "/", "\\") if not python_in_path then -- Python is usually registered for .py files in Windows return name end end return "python " .. name end function CHash(output, ...) local inputs = TableFlatten({...}) output = PathJoin(generated_src_dir, Path(output)) -- compile all the files local cmd = Python("scripts/cmd5.py") .. " " for index, inname in ipairs(inputs) do cmd = cmd .. Path(inname) .. " " end cmd = cmd .. " > " .. output AddJob(output, "cmd5 " .. output, cmd) for index, inname in ipairs(inputs) do AddDependency(output, inname) end AddDependency(output, "scripts/cmd5.py") return output end function ResCompile(scriptfile, compiler) scriptfile = Path(scriptfile) local output = nil if compiler == "cl" then output = PathJoin(generated_icon_dir, PathBase(PathFilename(scriptfile)) .. ".res") AddJob(output, "rc " .. scriptfile, "rc /fo " .. output .. " " .. scriptfile) elseif compiler == "gcc" or compiler == "clang" then output = PathJoin(generated_icon_dir, PathBase(PathFilename(scriptfile)) .. ".coff") AddJob(output, "windres " .. scriptfile, "windres -i " .. scriptfile .. " -o " .. output) end AddDependency(output, scriptfile) return output end function ContentCompile(action, output) output = PathJoin(generated_src_dir, Path(output)) AddJob( output, action .. " > " .. output, Python("datasrc/compile.py") .. " " .. action .. " > " .. output ) AddDependency(output, "datasrc/compile.py") AddDependency("datasrc/compile.py", "datasrc/content.py", "datasrc/network.py", "datasrc/datatypes.py") return output end function GenerateCommonSettings(settings, conf, arch, compiler) if compiler == "gcc" or compiler == "clang" then settings.cc.flags:Add("-Wall", "-fno-exceptions") end -- Compile zlib if needed local zlib = nil if config.zlib.value == 1 then settings.link.libs:Add("z") if config.zlib.include_path then settings.cc.includes:Add(config.zlib.include_path) end else settings.cc.includes:Add("src/engine/external/zlib") zlib = Compile(settings, Collect("src/engine/external/zlib/*.c")) end local md5 = Compile(settings, Collect("src/engine/external/md5/*.c")) local wavpack = Compile(settings, Collect("src/engine/external/wavpack/*.c")) local png = Compile(settings, Collect("src/engine/external/pnglite/*.c")) local json = Compile(settings, Collect("src/engine/external/json-parser/*.c")) -- globally available libs libs = {zlib=zlib, wavpack=wavpack, png=png, md5=md5, json=json} end function GenerateMacOSSettings(settings, conf, arch, compiler) if arch == "x86" then settings.cc.flags:Add("-arch i386") settings.link.flags:Add("-arch i386") elseif arch == "x86_64" then settings.cc.flags:Add("-arch x86_64") settings.link.flags:Add("-arch x86_64") elseif arch == "ppc" then settings.cc.flags:Add("-arch ppc") settings.link.flags:Add("-arch ppc") elseif arch == "ppc64" then settings.cc.flags:Add("-arch ppc64") settings.link.flags:Add("-arch ppc64") else print("Unknown Architecture '" .. arch .. "'. Supported: x86, x86_64, ppc, ppc64") os.exit(1) end -- c++ stdlib needed settings.cc.flags:Add("--stdlib=libc++") settings.link.flags:Add("--stdlib=libc++") -- this also needs the macOS min SDK version to be at least 10.7 settings.cc.flags:Add("-mmacosx-version-min=10.7") settings.link.flags:Add("-mmacosx-version-min=10.7") if config.minmacosxsdk.value == 1 then settings.cc.flags:Add("-isysroot /Developer/SDKs/MacOSX10.7.sdk") settings.link.flags:Add("-isysroot /Developer/SDKs/MacOSX10.7.sdk") end settings.link.frameworks:Add("Carbon") settings.link.frameworks:Add("AppKit") GenerateCommonSettings(settings, conf, arch, compiler) -- Build server launcher before adding game stuff local serverlaunch = Link(settings, "serverlaunch", Compile(settings, "src/macoslaunch/server.m")) -- Master server, version server and tools BuildEngineCommon(settings) BuildMasterserver(settings) BuildVersionserver(settings) BuildTools(settings) -- Add requirements for Server & Client BuildGameCommon(settings) -- Server settings.link.frameworks:Add("Cocoa") local server_exe = BuildServer(settings) AddDependency(server_exe, serverlaunch) -- Client settings.link.frameworks:Add("OpenGL") settings.link.frameworks:Add("AGL") -- FIXME: the SDL config is applied in BuildClient too but is needed here before so the launcher will compile config.sdl:Apply(settings) settings.link.extrafiles:Merge(Compile(settings, "src/macoslaunch/client.m")) BuildClient(settings) -- Content BuildContent(settings, arch, conf) end function GenerateLinuxSettings(settings, conf, arch, compiler) if arch == "x86" then if config.buildwithoutsseflag.value == false then settings.cc.flags:Add("-msse2") -- for the _mm_pause call end settings.cc.flags:Add("-m32") settings.link.flags:Add("-m32") elseif arch == "x86_64" then settings.cc.flags:Add("-m64") settings.link.flags:Add("-m64") elseif arch == "armv7l" then -- arm 32 bit else print("Unknown Architecture '" .. arch .. "'. Supported: x86, x86_64") os.exit(1) end settings.link.libs:Add("pthread") GenerateCommonSettings(settings, conf, arch, compiler) -- Master server, version server and tools BuildEngineCommon(settings) BuildTools(settings) BuildMasterserver(settings) BuildVersionserver(settings) -- Add requirements for Server & Client BuildGameCommon(settings) -- Server BuildServer(settings) -- Client settings.link.libs:Add("X11") settings.link.libs:Add("GL") BuildClient(settings) -- Content BuildContent(settings, arch, conf) end function GenerateSolarisSettings(settings, conf, arch, compiler) settings.link.libs:Add("socket") settings.link.libs:Add("nsl") GenerateLinuxSettings(settings, conf, arch, compiler) end function GenerateWindowsSettings(settings, conf, target_arch, compiler) if compiler == "cl" then if (target_arch == "x86" and arch ~= "ia32") or (target_arch == "x86_64" and arch ~= "ia64" and arch ~= "amd64") then print("Cross compiling is unsupported on Windows.") os.exit(1) end settings.cc.flags:Add("/wd4244", "/wd4577") elseif compiler == "gcc" or config.compiler.driver == "clang" then if target_arch ~= "x86" and target_arch ~= "x86_64" then print("Unknown Architecture '" .. arch .. "'. Supported: x86, x86_64") os.exit(1) end -- disable visibility attribute support for gcc on windows settings.cc.defines:Add("NO_VIZ") settings.cc.defines:Add("_WIN32_WINNT=0x0501") end -- Unicode support settings.cc.defines:Add("UNICODE") -- Windows headers settings.cc.defines:Add("_UNICODE") -- C-runtime local icons = SharedIcons(compiler) local manifests = SharedManifests(compiler) -- Required libs settings.link.libs:Add("gdi32") settings.link.libs:Add("user32") settings.link.libs:Add("ws2_32") settings.link.libs:Add("ole32") settings.link.libs:Add("shell32") settings.link.libs:Add("advapi32") GenerateCommonSettings(settings, conf, target_arch, compiler) -- Master server, version server and tools BuildEngineCommon(settings) BuildMasterserver(settings) BuildVersionserver(settings) BuildTools(settings) -- Add requirements for Server & Client BuildGameCommon(settings) -- Server local server_settings = settings:Copy() server_settings.link.extrafiles:Add(icons.server) BuildServer(server_settings) -- Client settings.link.extrafiles:Add(icons.client) settings.link.extrafiles:Add(manifests.client) settings.link.libs:Add("opengl32") settings.link.libs:Add("winmm") settings.link.libs:Add("imm32") BuildClient(settings) -- Content BuildContent(settings, target_arch, conf) end function SharedCommonFiles() -- Shared game files, generate only once if not shared_common_files then local network_source = ContentCompile("network_source", "generated/protocol.cpp") local network_header = ContentCompile("network_header", "generated/protocol.h") AddDependency(network_source, network_header, "src/engine/shared/protocol.h") local nethash = CHash("generated/nethash.cpp", "src/engine/shared/protocol.h", "src/game/tuning.h", "src/game/gamecore.cpp", network_header) shared_common_files = {network_source, nethash} end return shared_common_files end function SharedServerFiles() -- Shared server files, generate only once if not shared_server_files then local server_content_source = ContentCompile("server_content_source", "generated/server_data.cpp") local server_content_header = ContentCompile("server_content_header", "generated/server_data.h") AddDependency(server_content_source, server_content_header) shared_server_files = {server_content_source} end return shared_server_files end function SharedClientFiles() -- Shared client files, generate only once if not shared_client_files then local client_content_source = ContentCompile("client_content_source", "generated/client_data.cpp") local client_content_header = ContentCompile("client_content_header", "generated/client_data.h") AddDependency(client_content_source, client_content_header) shared_client_files = {client_content_source} end return shared_client_files end shared_icons = {} function SharedIcons(compiler) if not shared_icons[compiler] then local server_icon = ResCompile("other/icons/teeworlds_srv_" .. compiler .. ".rc", compiler) local client_icon = ResCompile("other/icons/teeworlds_" .. compiler .. ".rc", compiler) shared_icons[compiler] = {server=server_icon, client=client_icon} end return shared_icons[compiler] end function SharedManifests(compiler) if not shared_manifests then local client_manifest = ResCompile("other/manifest/teeworlds.rc", compiler) shared_manifests = {client=client_manifest} end return shared_manifests end function BuildEngineCommon(settings) settings.link.extrafiles:Merge(Compile(settings, Collect("src/engine/shared/*.cpp", "src/base/*.c"))) end function BuildGameCommon(settings) settings.link.extrafiles:Merge(Compile(settings, Collect("src/game/*.cpp"), SharedCommonFiles())) end function BuildClient(settings, family, platform) config.sdl:Apply(settings) config.freetype:Apply(settings) local client = Compile(settings, Collect("src/engine/client/*.cpp")) local game_client = Compile(settings, CollectRecursive("src/game/client/*.cpp"), SharedClientFiles()) local game_editor = Compile(settings, Collect("src/game/editor/*.cpp")) Link(settings, "teeworlds", libs["zlib"], libs["md5"], libs["wavpack"], libs["png"], libs["json"], client, game_client, game_editor) end function BuildServer(settings, family, platform) local server = Compile(settings, Collect("src/engine/server/*.cpp")) local game_server = Compile(settings, CollectRecursive("src/game/server/*.cpp"), SharedServerFiles()) return Link(settings, "teeworlds_srv", libs["zlib"], libs["md5"], server, game_server) end function BuildTools(settings) local tools = {} for i,v in ipairs(Collect("src/tools/*.cpp", "src/tools/*.c")) do local toolname = PathFilename(PathBase(v)) tools[i] = Link(settings, toolname, Compile(settings, v), libs["zlib"], libs["md5"], libs["wavpack"], libs["png"]) end PseudoTarget(settings.link.Output(settings, "pseudo_tools") .. settings.link.extension, tools) end function BuildMasterserver(settings) return Link(settings, "mastersrv", Compile(settings, Collect("src/mastersrv/*.cpp")), libs["zlib"], libs["md5"]) end function BuildVersionserver(settings) return Link(settings, "versionsrv", Compile(settings, Collect("src/versionsrv/*.cpp")), libs["zlib"], libs["md5"]) end function BuildContent(settings, arch, conf) local content = {} table.insert(content, CopyToDir(settings.link.Output(settings, "data"), CollectRecursive(content_src_dir .. "*.png", content_src_dir .. "*.wv", content_src_dir .. "*.ttc", content_src_dir .. "*.ttf", content_src_dir .. "*.txt", content_src_dir .. "*.map", content_src_dir .. "*.rules", content_src_dir .. "*.json"))) if family == "windows" then if arch == "x86_64" then _arch = "64" else _arch = "32" end -- dependencies dl = Python("scripts/download.py") AddJob({ "other/freetype/include/ft2build.h", "other/freetype/windows/lib" .. _arch .. "/freetype.dll", "other/sdl/include/SDL.h", "other/sdl/windows/lib" .. _arch .. "/SDL2.dll" }, "Downloading freetype and SDL2", dl .. " freetype sdl" ) table.insert(content, CopyFile(settings.link.Output(settings, "") .. "/SDL2.dll", "other/sdl/windows/lib" .. _arch .. "/SDL2.dll")) table.insert(content, CopyFile(settings.link.Output(settings, "") .. "/freetype.dll", "other/freetype/windows/lib" .. _arch .. "/freetype.dll")) AddDependency(settings.link.Output(settings, "") .. "/SDL2.dll", "other/sdl/include/SDL.h") AddDependency(settings.link.Output(settings, "") .. "/freetype.dll", "other/freetype/include/ft2build.h") end PseudoTarget(settings.link.Output(settings, "content") .. settings.link.extension, content) end -- create all targets for specified configuration & architecture function GenerateSettings(conf, arch, builddir, compiler, headless) local settings = NewSettings() -- Set compiler if explicitly requested if compiler == "gcc" then SetDriversGCC(settings) elseif compiler == "clang" then SetDriversClang(settings) elseif compiler == "cl" then SetDriversCL(settings) else -- apply compiler settings config.compiler:Apply(settings) compiler = config.compiler.driver end if conf == "debug" then settings.debug = 1 settings.optimize = 0 settings.cc.defines:Add("CONF_DEBUG") else settings.debug = 0 settings.optimize = 1 settings.cc.defines:Add("CONF_RELEASE") end if headless == "on" then settings.cc.defines:Add("CONF_HEADLESS_CLIENT") end -- Generate object files in {builddir}/objs/ settings.cc.Output = function (settings_, input) -- strip input = input:gsub("^src/", "") input = input:gsub("^" .. generated_src_dir .. "/", "") return PathJoin(PathJoin(builddir, "objs"), PathBase(input)) end -- Build output files in {builddir} settings.link.Output = function (settings_, input) return PathJoin(builddir, PathBase(input) .. settings_.config_ext) end settings.cc.includes:Add("src") settings.cc.includes:Add("src/engine/external/pnglite") settings.cc.includes:Add("src/engine/external/wavpack") settings.cc.includes:Add(generated_src_dir) if family == "windows" then GenerateWindowsSettings(settings, conf, arch, compiler) elseif family == "unix" then if platform == "macosx" then GenerateMacOSSettings(settings, conf, arch, compiler) elseif platform == "solaris" then GenerateSolarisSettings(settings, conf, arch, compiler) else -- Linux, BSD GenerateLinuxSettings(settings, conf, arch, compiler) end end return settings end -- String formatting with named parameters, by RiciLake http://lua-users.org/wiki/StringInterpolation function interp(s, tab) return (s:gsub('%%%((%a%w*)%)([-0-9%.]*[cdeEfgGiouxXsq])', function(k, fmt) return tab[k] and ("%"..fmt):format(tab[k]) or '%('..k..')'..fmt end)) end function CopyToDir(dst, ...) local output = {} for filename in TableWalk({...}) do table.insert(output, CopyFile(PathJoin(dst, string.sub(filename, string.len(content_src_dir)+1)), filename)) end return output end function split(str, sep) local vals = {} str:gsub("([^,]+)", function(val) table.insert(vals, val) end) return vals end -- Supported archtitectures: x86, amd64, ppc, ppc64 if ScriptArgs['arch'] then archs = split(ScriptArgs['arch']) else if arch == "ia32" then archs = {"x86"} elseif arch == "ia64" or arch == "amd64" then archs = {"x86_64"} else archs = {arch} end end if ScriptArgs['conf'] then confs = split(ScriptArgs['conf']) else confs = {"debug"} end if ScriptArgs['compiler'] then compiler = ScriptArgs['compiler'] else compiler = nil end if ScriptArgs['builddir'] then builddir = ScriptArgs['builddir'] end if ScriptArgs['headless'] then headless = ScriptArgs['headless'] else headless = nil end targets = {client="teeworlds", server="teeworlds_srv", versionserver="versionsrv", masterserver="mastersrv", tools="pseudo_tools", content="content"} subtargets = {} for t, cur_target in pairs(targets) do subtargets[cur_target] = {} end for a, cur_arch in ipairs(archs) do for c, cur_conf in ipairs(confs) do cur_builddir = interp(builddir, {platform=family, arch=cur_arch, target=cur_target, conf=cur_conf, compiler=compiler}) local settings = GenerateSettings(cur_conf, cur_arch, cur_builddir, compiler, headless) for t, cur_target in pairs(targets) do table.insert(subtargets[cur_target], PathJoin(cur_builddir, cur_target .. settings.link.extension)) end end end for cur_name, cur_target in pairs(targets) do -- Supertarget for all configurations and architectures of that target PseudoTarget(cur_name, subtargets[cur_target]) end PseudoTarget("game", "client", "server", "content") DefaultTarget("game")