-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwscript
404 lines (357 loc) · 16.9 KB
/
wscript
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
#! /usr/bin/env python
#
# wscript - waf build script
#
# Copyright 2016-2018, Daniel Kristensen, Garmin Ltd, or its subsidiaries.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import collections
import os
import shutil
import sys
import pprint
from waflib import Build, Configure, Errors, Options, Logs, Scripting
from waflib.extras import msvs
VERSION="0.0.0"
APPNAME='bfdm'
REVISION=''
# Environment prefix for app-specific settings
env_prefix = APPNAME + "_"
# Set paths for Waf
top = '.'
out = '_out'
# Define classes for custom options
CustomOptions = collections.namedtuple( 'CustomOptions', ['parameter','collection'] )
SupportedPlatform = collections.namedtuple( 'SupportedPlatform', ['name', 'cxx_target', 'description'] )
SupportedMode = collections.namedtuple( 'SupportedMode', ['name', 'msvc_defines', 'msvc_c_flags', 'msvc_cxx_flags', 'msvc_link_flags', 'description'] )
# Configure platforms; the first entry is the default if none is specified.
my_platforms = [
SupportedPlatform( name='win32', cxx_target="x86", description="Windows 32-bit x86 architecture" ),
SupportedPlatform( name='win64', cxx_target="x86_64", description="Windows 64-bit x86 architecture" )
]
# Configure modes; the first entry is the default if none is specified.
# TODO: Allow flags to be abstracted per compiler, but requires more advanced compiler detection
# Set up autoconfigure to reconfigure when wscript or dependent files change
Configure.autoconfig = True
# TODO: Can we get rid of these? Would be nice not to depend on it
common_defines = [
"UNICODE",
"_UNICODE"
]
common_msvc_c_flags = [
# none
]
common_msvc_cxx_flags = [
"/EHsc" # C++ exception support only
]
common_msvc_link_flags = [ "/WX", "/OPT:REF" ]
my_modes = [
SupportedMode
(
name='development',
msvc_defines=common_defines,
# /RTCc is not used because the MSC STL is not guaranteed to be compliant
msvc_c_flags=common_msvc_c_flags + [ "/Od", "/Z7", "/FS", "/RTCsu" ],
msvc_cxx_flags=common_msvc_cxx_flags + [ "/Od", "/Z7", "/FS", "/RTCsu" ],
msvc_link_flags=common_msvc_link_flags + [ "/DEBUG" ],
description=""
),
SupportedMode
(
name='release',
msvc_defines=common_defines,
msvc_c_flags=common_msvc_c_flags + [ "/O2" ],
msvc_cxx_flags=common_msvc_cxx_flags + [ "/O2" ],
msvc_link_flags=common_msvc_link_flags + [],
description=""
)
]
# Aggregate the options so default values can be detected and used automatically
my_custom_options = [
CustomOptions( "platform", my_platforms ),
CustomOptions( "mode", my_modes )
]
# This function is called first
def init( ctx ):
pass # nothing to do here
# This function is called before any other for parsing the command-line
def options( opt ):
# TODO: Define these in a tool that implements it more generically
local_group = opt.add_option_group( APPNAME + " options (may be specified in configure)" )
local_group.add_option(
"-p", "--platform",
dest="platform",
type="string",
action="store",
help="Platform to compile for (use ? for a list, default='" + my_platforms[0].name + "')"
)
local_group.add_option(
"-m", "--mode",
dest="mode",
type="string",
action="store",
help="Build mode (use ? for a list, default='" + my_modes[0].name + "')"
)
# Load tools required to parse option parameters (these load for all build steps).
opt.load('compiler_cxx')
opt.load('msvs')
# This function is called to update the configuration
def configure( conf ):
# check and save configuration options
check_arguments()
for v in my_custom_options:
option_value = getattr( Options.options, v.parameter )
conf.env[env_prefix + v.parameter] = ""
if( None != option_value ):
Logs.debug( "options: presetting " + v.parameter + " = '" + option_value + "'" )
conf.env[env_prefix + v.parameter] = option_value
else:
Logs.debug( "options: no preset value for " + v.parameter )
# Load MSVC tool
conf.load('msvc')
conf.load('globitool', tooldir='_out/globifest/waf')
# Load the WarningLevel tool
conf.load('WarningLevel', tooldir='waftools')
conf.env.append_unique( "warning_level_high_CXXFLAGS", [ "/W4" ] )
conf.env.append_unique( "warning_level_max_CXXFLAGS", [
"/Wall", # Highest warning level
# Disable warnings:
"/wd4514", # unreferenced inline function has been removed (caused by STL headers)
"/wd4710", # function not inlined (caused by STL headers)
"/wd4373", # previous versions of the compiler did not override when parameters only
# differed by const/volatile qualifiers
# (caused by gmock and extraneous const value qualifiers on declarations)
"/wd4820", # 'x' bytes padding added after data member 'y'
"/wd4625", # copy constructor was implicitly defined as deleted
"/wd4626", # assignment operator was implicitly defined as deleted
"/wd5026", # move constructor was implicitly defined as deleted
"/wd5027", # move assignment operator was implicitly defined as deleted
"/wd4355", # 'this' used in base member initializer list
# (RAII is usually better than the design tradeoffs and complexity of 2-stage
# initialization of objects)
# TODO: Maybe remove this if using smart pointers with factory methods
"/wd4371", # Layout of class may have changed from a previous version of the compiler
# (structures used for serialization should be packed and use fixed-width
# types, with testing)
"/wd4623", # Default constructor was implicitly defined as deleted
# Known MSVC bug - MS has not prioritized /Wall compliance in the STL; if
# there's a real defect, it will fail to compile.
# - https://stackoverflow.com/questions/34960038/cant-disable-compiler-warning-in-vs2015
# - https://developercommunity.visualstudio.com/content/problem/172561/warning-c4623-triggers-in-new-compiler.html
"/wd4711" # Function selected for inline expansion
# This warning is purely informational, and expected for optimizations
] )
conf.env.append_unique( "warning_level_warnings-as-errors_CXXFLAGS", [ "/WX" ] )
conf.env.append_unique( "warning_level_warnings-as-errors_LINKFLAGS", [ "/WX" ] )
# Configure flags for each mode; tools are loaded once before the environment is copied to
# avoid multiple configuration loops
# TODO: Do this in the platform/mode tool
base_env = conf.env
for p in my_platforms:
for m in my_modes:
variant_name = p.name + "_" + m.name
Logs.debug( APPNAME + ": configuring variant " + variant_name )
# Derive from the base ConfigSet and detach. This creates a new ConfigSet, which is
# then used as the current set.
new_env = base_env.derive()
new_env.detach()
conf.setenv( env_prefix + variant_name, new_env )
conf.env.append_unique( 'DEFINES', m.msvc_defines )
conf.env.append_unique( 'CFLAGS', m.msvc_c_flags )
conf.env.append_unique( 'CXXFLAGS', m.msvc_cxx_flags )
conf.env.append_unique( 'LINKFLAGS', m.msvc_link_flags )
# Holds effective options calculated in build step
effective_options = {}
# This function is called to perform build actions
def build( bld ):
# TODO: When implementing the platform/mode tool, this if/else should go away
# * platform/mode will set configurations and platforms for msvs
if not bld.variant:
# When custom variant commands are not provided,
# select the appropriate variant from the options
check_arguments()
if( bld.cmd == "msvs" ):
# One solution is generated for all variants, so this is done here instead of running
# multiple msvs_<variant> commands.
Logs.debug( APPNAME + ": Generating msvs solution..." )
bld.waf_command = "waf.bat --platform $(Platform) --mode $(Configuration)"
bld.solution_name = out + os.path.sep + APPNAME + ".sln"
bld.projects_dir = bld.srcnode.make_node(out + os.path.sep + "vsproj")
bld.projects_dir.mkdir()
# Set up configurations
vs_configs = list()
for m in my_modes:
vs_configs.append( m.name )
bld.configurations = vs_configs
# Set up platforms (these are not officially-supported platforms, but MSVS is just a
# GUI in this case since waf generates an NMake solution; it simplifies the selection
# of platform/mode combinations).
vs_platforms = list()
for p in my_platforms:
vs_platforms.append( p.name )
bld.platforms = vs_platforms
# msvs uses the build targets to generate projects
add_taskgens( bld )
else:
# This breaks up single non-variant commands like "build" into variant-specific commands
# Iterate over options to determine which to use for each
for v in my_custom_options:
override_option = getattr( Options.options, v.parameter )
saved_option = getattr( bld.env, env_prefix + v.parameter )
if( override_option != None ):
Logs.debug( "options: using command line parameter for " + v.parameter )
effective_options[v.parameter] = override_option
elif( saved_option != "" ):
Logs.debug( "options: using preconfigured parameter for " + v.parameter )
effective_options[v.parameter] = saved_option
else:
Logs.debug( "options: using default parameter for " + v.parameter )
effective_options[v.parameter] = v.collection[0].name
Logs.info( "options: " + v.parameter + " = '" + effective_options[v.parameter] + "'" )
variant_name = effective_options["platform"] + "_" + effective_options["mode"]
variant_cmd = bld.cmd + "_" + variant_name
Logs.debug( APPNAME + ": executing " + variant_cmd )
# TODO: Turn the parameters into lists to allow multiple variants to be built at once
Scripting.run_command( variant_cmd )
# Create dummy taskgens when individual targets are specified, to avoid errors when
# attempting to run the taskgens for those specific targets in this general-purpose
# build context. The real taskgen will be run in the variant-specific context.
if bld.targets and bld.targets != '*':
for tgt in bld.targets.split(','):
bld( name=tgt )
else:
Logs.debug( APPNAME + ": chaining command " + bld.cmd )
# TODO: platform/mode tool should set the parameters in the build context derivative
# instead of having the effective_parameters global
if( not effective_options ):
Logs.error( "Please use the 'build' command" )
# Override the target according to the platform chosen
if( bld.options.msvc_targets != "" ):
Logs.warning( "Use --platform option to specify MSVC target" )
platform = lookup_option_value( my_platforms, effective_options["platform"] )
bld.options.msvc_targets = platform.cxx_target
# Override the environment for the specific build variant prior to creating TaskGens
mode = lookup_option_value( my_modes, effective_options["mode"] )
variant_name = platform.name + "_" + mode.name
bld.env = bld.all_envs[env_prefix + variant_name]
# Add TaskGen objects to the build for this variant
# TODO: platform/mode should have a callback registered to call for each build variant
add_taskgens( bld )
def dist(ctx):
raise Errors.WafError("Dist not supported yet")
# Create classes for each variant based off of Waf's internal classes
# This is necessary because WAF doesn't like having a single command for build variants
# TODO: Should be able to remove this by resetting BuildContext variables and calling callback
for p in my_platforms:
for m in my_modes:
variant_name = p.name + "_" + m.name
for ctx in ( Build.BuildContext, Build.CleanContext, Build.InstallContext, Build.UninstallContext ):
name = ctx.__name__.replace('Context','').lower()
class tmp(ctx):
cmd = name + '_' + variant_name
variant = variant_name
# LOCAL METHODS BELOW THIS LINE #
def add_taskgens( bld ):
# Common
bld.GLOBITOOL(
target="Common",
project="proj/Common.gproj",
tgt_params=dict(
warning_levels="max warnings-as-errors",
features="cxx warning-level"
),
lnk_params=dict(
warning_levels="max warnings-as-errors",
features="cxx cxxstlib warning-level"
)
)
gtest_c_cxx_flags = [
"/wd4668" # <identifier> is not defined as a preprocessor macro
]
# Building googletest separately to apply different warning level
bld.GLOBITOOL(
target="GoogleTest",
project="proj/GoogleTest.gproj",
tgt_params=dict(
# Disable multithreaded tests to avoid race conditions in the error handler
defines=["GTEST_HAS_PTHREAD=0"],
warning_levels="high",
features="cxx warning-level",
cflags=gtest_c_cxx_flags,
cxxflags=gtest_c_cxx_flags
)
)
bld.GLOBITOOL(
target="bfdp",
project="proj/BfdpApp.gproj",
use="Common",
tgt_params=dict(
warning_levels="max warnings-as-errors",
features="cxx warning-level"
),
lnk_params=dict(
warning_levels="max warnings-as-errors",
features="cxx cxxprogram warning-level",
subsystem = "CONSOLE"
)
)
bld.GLOBITOOL(
target="BfsdlTests",
project="proj/BfsdlTests.gproj",
use="Common GoogleTest",
tgt_params=dict(
warning_levels="max warnings-as-errors",
features="cxx warning-level",
cflags=gtest_c_cxx_flags,
cxxflags=gtest_c_cxx_flags
),
lnk_params=dict(
warning_levels="max warnings-as-errors",
features="cxx cxxprogram warning-level",
subsystem = "CONSOLE"
)
)
def check_arguments():
for v in my_custom_options:
if( getattr( Options.options, v.parameter) == None ):
pass
elif( getattr( Options.options, v.parameter) == "?" ):
print( "Available " + v.parameter + " values:" )
list_parameter_values( v.collection )
sys.exit(0)
elif( lookup_option_value( v.collection, getattr( Options.options, v.parameter) ) == None ):
print( "Unknown value for --" + v.parameter + ", select one of the following:")
list_parameter_values( v.collection )
raise Errors.WafError("Bad argument to --" + v.parameter + ": '" + getattr( Options.options, v.parameter) + "'" )
def list_parameter_values( collection ):
for v in collection:
print( " " + v.name + "\t" + v.description )
def lookup_option_value( collection, value ):
for v in collection:
if( v.name == value ):
return v
return None