forked from Elyssaen/endless-sky
-
Notifications
You must be signed in to change notification settings - Fork 0
/
SConstruct
208 lines (183 loc) · 8.06 KB
/
SConstruct
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# steamrt scons is v2.1.0
# https://repo.steampowered.com/steamrt-images-scout/snapshots/latest-container-runtime-depot/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-scout-sources.sources.txt
# Documentation available at https://scons.org/doc/2.1.0/HTML/scons-user/a10706.html
import os
import platform
from SCons.Node.FS import Dir
def pathjoin(*args):
return os.path.join(*args)
# Load environment variables, including some that should be renamed.
# If we are compiling on Windows, then we need to change the toolset to MinGW.
is_windows_host = platform.system().startswith('Windows')
scons_toolset = ['mingw' if is_windows_host else 'default']
env = DefaultEnvironment(tools = scons_toolset, ENV = os.environ)
if 'CXX' in os.environ:
env['CXX'] = os.environ['CXX']
if 'CXXFLAGS' in os.environ:
env.Append(CCFLAGS = os.environ['CXXFLAGS'])
if 'LDFLAGS' in os.environ:
env.Append(LINKFLAGS = os.environ['LDFLAGS'])
if 'AR' in os.environ:
env['AR'] = os.environ['AR']
if 'DIR_ESLIB' in os.environ:
path = os.environ['DIR_ESLIB']
env.Prepend(CPPPATH = [pathjoin(path, 'include')])
env.Append(LIBPATH = [pathjoin(path, 'lib')])
# Don't spawn a console window by default on Windows builds.
if is_windows_host:
env.Append(LINKFLAGS = ["-mwindows"])
opts = Variables()
opts.AddVariables(
EnumVariable("mode", "Compilation mode", "release", allowed_values=("release", "debug", "profile")),
PathVariable("BUILDDIR", "Directory to store compiled object files in", "build", PathVariable.PathIsDirCreate),
PathVariable("BIN_DIR", "Directory to store binaries in", ".", PathVariable.PathIsDirCreate),
PathVariable("DESTDIR", "Destination root directory, e.g. if building a package", "", PathVariable.PathAccept),
PathVariable("PREFIX", "Directory to install under (will be prefixed by DESTDIR)", "/usr/local", PathVariable.PathIsDirCreate),
)
opts.Update(env)
Help(opts.GenerateHelpText(env))
# Required build flags. To enable SSE or other optimizations, pass CXXFLAGS via the environment
# $ CXXFLAGS=-msse3 scons
# $ CXXFLAGS=-march=native scons
# or modify the `flags` variable:
flags = ["-std=c++11", "-Wall", "-Werror", "-Wold-style-cast"]
if env["mode"] != "debug":
flags += ["-O3", "-flto"]
env.Append(LINKFLAGS = ["-O3", "-flto"])
if env["mode"] == "debug":
flags += ["-g"]
elif env["mode"] == "profile":
flags += ["-pg"]
env.Append(LINKFLAGS = ["-pg"])
env.Append(CCFLAGS = flags)
# Always use `ar` to create the symbol table, and don't use ranlib at all, since it fails to preserve
# LTO information, even when passed the plugin path, when run in Steam's "Scout" runtime.
env['RANLIBCOM'] = ''
# TODO: can we derive thin archive support from the host system somehow? Or just always use it?
create_thin_archives = env.get('AR', '').startswith('gcc')
env.Replace(ARFLAGS = 'rcsT' if create_thin_archives else 'rcs')
# The Steam runtime fails to correctly invoke the LTO plugin for gcc-5, so pass it explicitly
chroot_name = os.environ.get('SCHROOT_CHROOT_NAME', '')
if 'steamrt_scout' in chroot_name:
# MAYBE: read g++ version to determine correct path to the LTO plugin
plugin_path = '--plugin={}'.format(os.environ.get('LTO_PLUGIN_PATH', '/usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so'))
env.Append(ARFLAGS = [plugin_path])
game_libs = [
"winmm",
"mingw32",
"sdl2main",
"sdl2.dll",
"png.dll",
"turbojpeg.dll",
"jpeg.dll",
"openal32.dll",
"glew32.dll",
"opengl32",
] if is_windows_host else [
"SDL2",
"png",
"jpeg",
"GL",
"GLEW",
"openal",
"pthread",
]
env.Append(LIBS = game_libs)
# libmad is not in the Steam runtime, so link it statically:
if 'steamrt_scout_i386' in chroot_name:
env.Append(LIBS = File("/usr/lib/i386-linux-gnu/libmad.a"))
elif 'steamrt_scout_amd64' in chroot_name:
env.Append(LIBS = File("/usr/lib/x86_64-linux-gnu/libmad.a"))
else:
env.Append(LIBS = "mad")
binDirectory = '' if env["BIN_DIR"] == '.' else pathjoin(env["BIN_DIR"], env["mode"])
buildDirectory = pathjoin(env["BUILDDIR"], env["mode"])
libDirectory = pathjoin("lib", env["mode"])
VariantDir(buildDirectory, "source", duplicate = 0)
# Find all regular source files.
def RecursiveGlob(pattern, dir_name=buildDirectory):
# Start with source files in subdirectories.
matches = [RecursiveGlob(pattern, sub_dir) for sub_dir in Glob(pathjoin(str(dir_name), "*"))
if isinstance(sub_dir, Dir)]
# Add source files in this directory, except for main.cpp
matches += Glob(pathjoin(str(dir_name), pattern))
matches = [i for i in matches if not "/main.cpp" in str(i)]
return matches
# By default, invoking scons will build the backing archive file and then the game binary.
sourceLib = env.StaticLibrary(pathjoin(libDirectory, "endless-sky"), RecursiveGlob("*.cpp", buildDirectory))
exeObjs = [Glob(pathjoin(buildDirectory, f)) for f in ("main.cpp",)]
if is_windows_host:
windows_icon = env.RES(pathjoin(buildDirectory, "WinApp.rc"))
exeObjs.append(windows_icon)
sky = env.Program(pathjoin(binDirectory, "endless-sky"), exeObjs + sourceLib)
env.Default(sky)
# The testing infrastructure ignores "mode" specification (i.e. we only test optimized output).
# (If we add support for code coverage output, this will likely need to change.)
testBuildDirectory = pathjoin("tests", env["BUILDDIR"])
VariantDir(testBuildDirectory, pathjoin("tests", "src"), duplicate = 0)
test = env.Program(
target=pathjoin("tests", "endless-sky-tests"),
source=RecursiveGlob("*.cpp", testBuildDirectory) + sourceLib,
# Add Catch header & additional test includes to the existing search paths
CPPPATH=(env.get('CPPPATH', []) + [pathjoin('tests', 'include')]),
# Do not link against the actual implementations of SDL, OpenGL, etc.
LIBS=[],
# Pass the necessary link flags for a console program.
LINKFLAGS=[x for x in env.get('LINKFLAGS', []) if x not in ('-mwindows',)]
)
# Invoking scons with the `build-tests` target will build the unit test framework
env.Alias("build-tests", test)
# Invoking scons with the `test` target will build (if necessary) and
# execute the unit test framework (always). All non-hidden tests are run.
catch2_args = " " + " ".join([
"-i",
"--warn NoAssertions",
"--order rand",
"--rng-seed 'time'",
])
test_runner = env.Action(test[0].abspath + catch2_args, 'Running tests...')
env.Alias("test", test, test_runner)
env.AlwaysBuild("test")
# Install the binary:
env.Install("$DESTDIR$PREFIX/games", sky)
# Install the desktop file:
env.Install("$DESTDIR$PREFIX/share/applications", "endless-sky.desktop")
# Install app center metadata:
env.Install("$DESTDIR$PREFIX/share/appdata", "endless-sky.appdata.xml")
# Install icons, keeping track of all the paths.
# Most Ubuntu apps supply 16, 22, 24, 32, 48, and 256, and sometimes others.
sizes = ["16x16", "22x22", "24x24", "32x32", "48x48", "128x128", "256x256", "512x512"]
icons = []
for size in sizes:
destination = "$DESTDIR$PREFIX/share/icons/hicolor/" + size + "/apps/endless-sky.png"
icons.append(destination)
env.InstallAs(destination, "icons/icon_" + size + ".png")
# If any of those icons changed, also update the cache.
# Do not update the cache if we're not installing into "usr".
# (For example, this "install" may actually be creating a Debian package.)
if env.get("PREFIX").startswith("/usr/"):
env.Command(
[],
icons,
"gtk-update-icon-cache -t $DESTDIR$PREFIX/share/icons/hicolor/")
# Install the man page.
env.Command(
"$DESTDIR$PREFIX/share/man/man6/endless-sky.6.gz",
"endless-sky.6",
"gzip -c $SOURCE > $TARGET")
# Install the data files.
def RecursiveInstall(env, target, source):
rootIndex = len(env.Dir(source).abspath) + 1
for node in env.Glob(pathjoin(source, '*')):
if node.isdir():
name = node.abspath[rootIndex:]
RecursiveInstall(env, pathjoin(target, name), node.abspath)
else:
env.Install(target, node)
RecursiveInstall(env, "$DESTDIR$PREFIX/share/games/endless-sky/data", "data")
RecursiveInstall(env, "$DESTDIR$PREFIX/share/games/endless-sky/images", "images")
RecursiveInstall(env, "$DESTDIR$PREFIX/share/games/endless-sky/sounds", "sounds")
env.Install("$DESTDIR$PREFIX/share/games/endless-sky", "credits.txt")
env.Install("$DESTDIR$PREFIX/share/games/endless-sky", "keys.txt")
# Make the word "install" in the command line do an installation.
env.Alias("install", "$DESTDIR$PREFIX")