1from waflib import Logs
2from waflib import Options
3from waflib import Utils
4
5
6class CompilerTraits(object):
7	def get_warnings_flags(self, level):
8		"""get_warnings_flags(level) -> list of cflags"""
9		raise NotImplementedError
10
11	def get_optimization_flags(self, level):
12		"""get_optimization_flags(level) -> list of cflags"""
13		raise NotImplementedError
14
15	def get_debug_flags(self, level):
16		"""get_debug_flags(level) -> (list of cflags, list of cppdefines)"""
17		raise NotImplementedError
18
19
20class GccTraits(CompilerTraits):
21	def __init__(self):
22		super(GccTraits, self).__init__()
23		# cumulative list of warnings per level
24		self.warnings_flags = [['-Wall'], ['-Werror'], ['-Wextra']]
25
26	def get_warnings_flags(self, level):
27		warnings = []
28		for l in range(level):
29			if l < len(self.warnings_flags):
30				warnings.extend(self.warnings_flags[l])
31			else:
32				break
33		return warnings
34
35	def get_optimization_flags(self, level):
36		if level == 0:
37			return ['-O0']
38		elif level == 1:
39			return ['-O']
40		elif level == 2:
41			return ['-O2']
42		elif level == 3:
43			return ['-O3']
44
45	def get_debug_flags(self, level):
46		if level == 0:
47			return (['-g0'], ['NDEBUG'])
48		elif level == 1:
49			return (['-g'], [])
50		elif level >= 2:
51			return (['-ggdb', '-g3'], ['_DEBUG'])
52
53
54class IccTraits(CompilerTraits):
55	def __init__(self):
56		super(IccTraits, self).__init__()
57		# cumulative list of warnings per level
58		# icc is _very_ verbose with -Wall, -Werror is barely achievable
59		self.warnings_flags = [[], [], ['-Wall']]
60
61	def get_warnings_flags(self, level):
62		warnings = []
63		for l in range(level):
64			if l < len(self.warnings_flags):
65				warnings.extend(self.warnings_flags[l])
66			else:
67				break
68		return warnings
69
70	def get_optimization_flags(self, level):
71		if level == 0:
72			return ['-O0']
73		elif level == 1:
74			return ['-O']
75		elif level == 2:
76			return ['-O2']
77		elif level == 3:
78			return ['-O3']
79
80	def get_debug_flags(self, level):
81		if level == 0:
82			return (['-g0'], ['NDEBUG'])
83		elif level == 1:
84			return (['-g'], [])
85		elif level >= 2:
86			return (['-ggdb', '-g3'], ['_DEBUG'])
87
88
89
90class MsvcTraits(CompilerTraits):
91	def __init__(self):
92		super(MsvcTraits, self).__init__()
93		# cumulative list of warnings per level
94		self.warnings_flags = [['/W2'], ['/WX'], ['/Wall']]
95
96	def get_warnings_flags(self, level):
97		warnings = []
98		for l in range(level):
99			if l < len(self.warnings_flags):
100				warnings.extend(self.warnings_flags[l])
101			else:
102				break
103		return warnings
104
105	def get_optimization_flags(self, level):
106		if level == 0:
107			return ['/Od']
108		elif level == 1:
109			return []
110		elif level == 2:
111			return ['/O2']
112		elif level == 3:
113			return ['/Ox']
114
115	def get_debug_flags(self, level):
116		if level == 0:
117			return ([], ['NDEBUG'])
118		elif level == 1:
119			return (['/ZI', '/RTC1'], [])
120		elif level >= 2:
121			return (['/ZI', '/RTC1'], ['_DEBUG'])
122
123
124gcc = GccTraits()
125icc = IccTraits()
126msvc = MsvcTraits()
127
128# how to map env['COMPILER_CC'] or env['COMPILER_CXX'] into a traits object
129compiler_mapping = {
130	'gcc': gcc,
131	'g++': gcc,
132	'msvc': msvc,
133	'icc': icc,
134	'icpc': icc,
135}
136
137profiles = {
138	# profile name: [optimization_level, warnings_level, debug_level]
139	'default': [2, 1, 1],
140	'debug': [0, 2, 3],
141	'release': [3, 1, 0],
142	}
143
144default_profile = 'default'
145
146def options(opt):
147	assert default_profile in profiles
148	opt.add_option('-d', '--build-profile',
149		       action='store',
150		       default=default_profile,
151		       help=("Specify the build profile.  "
152			     "Build profiles control the default compilation flags"
153			     " used for C/C++ programs, if CCFLAGS/CXXFLAGS are not"
154			     " set set in the environment. [Allowed Values: %s]"
155			     % ", ".join([repr(p) for p in list(profiles.keys())])),
156		       choices=list(profiles.keys()),
157		       dest='build_profile')
158
159def configure(conf):
160	cc = conf.env['COMPILER_CC'] or None
161	cxx = conf.env['COMPILER_CXX'] or None
162	if not (cc or cxx):
163		raise Utils.WafError("neither COMPILER_CC nor COMPILER_CXX are defined; "
164				     "maybe the compiler_cc or compiler_cxx tool has not been configured yet?")
165
166	try:
167		compiler = compiler_mapping[cc]
168	except KeyError:
169		try:
170			compiler = compiler_mapping[cxx]
171		except KeyError:
172			Logs.warn("No compiler flags support for compiler %r or %r"
173				  % (cc, cxx))
174			return
175
176	opt_level, warn_level, dbg_level = profiles[Options.options.build_profile]
177
178	optimizations = compiler.get_optimization_flags(opt_level)
179	debug, debug_defs = compiler.get_debug_flags(dbg_level)
180	warnings = compiler.get_warnings_flags(warn_level)
181
182	if cc and not conf.env['CCFLAGS']:
183		conf.env.append_value('CCFLAGS', optimizations)
184		conf.env.append_value('CCFLAGS', debug)
185		conf.env.append_value('CCFLAGS', warnings)
186		conf.env.append_value('CCDEFINES', debug_defs)
187	if cxx and not conf.env['CXXFLAGS']:
188		conf.env.append_value('CXXFLAGS', optimizations)
189		conf.env.append_value('CXXFLAGS', debug)
190		conf.env.append_value('CXXFLAGS', warnings)
191		conf.env.append_value('CXXDEFINES', debug_defs)
192