SConscript (13675:afeab32b3655) SConscript (13706:4c1d26d1766e)
1# -*- mode:python -*-
2
3# Copyright (c) 2018 ARM Limited
4#
5# The license below extends only to copyright in the software and shall
6# not be construed as granting a license to any other intellectual
7# property including but not limited to intellectual property relating
8# to a hardware implementation of the functionality of the software
9# licensed hereunder. You may use the software subject to the license
10# terms below provided that you ensure that this notice is replicated
11# unmodified and in its entirety in all distributions of the software,
12# modified or unmodified, in source code or in binary form.
13#
14# Copyright (c) 2004-2005 The Regents of The University of Michigan
15# All rights reserved.
16#
17# Redistribution and use in source and binary forms, with or without
18# modification, are permitted provided that the following conditions are
19# met: redistributions of source code must retain the above copyright
20# notice, this list of conditions and the following disclaimer;
21# redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in the
23# documentation and/or other materials provided with the distribution;
24# neither the name of the copyright holders nor the names of its
25# contributors may be used to endorse or promote products derived from
26# this software without specific prior written permission.
27#
28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39#
40# Authors: Nathan Binkert
41
42from __future__ import print_function
43
44import array
45import bisect
46import functools
47import imp
48import marshal
49import os
50import re
51import subprocess
52import sys
53import zlib
54
55from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
56
57import SCons
58
59from gem5_scons import Transform
60
61# This file defines how to build a particular configuration of gem5
62# based on variable settings in the 'env' build environment.
63
64Import('*')
65
66# Children need to see the environment
67Export('env')
68
69build_env = [(opt, env[opt]) for opt in export_vars]
70
71from m5.util import code_formatter, compareVersions
72
73########################################################################
74# Code for adding source files of various types
75#
76# When specifying a source file of some type, a set of tags can be
77# specified for that file.
78
79class SourceFilter(object):
80 def __init__(self, predicate):
81 self.predicate = predicate
82
83 def __or__(self, other):
84 return SourceFilter(lambda tags: self.predicate(tags) or
85 other.predicate(tags))
86
87 def __and__(self, other):
88 return SourceFilter(lambda tags: self.predicate(tags) and
89 other.predicate(tags))
90
91def with_tags_that(predicate):
92 '''Return a list of sources with tags that satisfy a predicate.'''
93 return SourceFilter(predicate)
94
95def with_any_tags(*tags):
96 '''Return a list of sources with any of the supplied tags.'''
97 return SourceFilter(lambda stags: len(set(tags) & stags) > 0)
98
99def with_all_tags(*tags):
100 '''Return a list of sources with all of the supplied tags.'''
101 return SourceFilter(lambda stags: set(tags) <= stags)
102
103def with_tag(tag):
104 '''Return a list of sources with the supplied tag.'''
105 return SourceFilter(lambda stags: tag in stags)
106
107def without_tags(*tags):
108 '''Return a list of sources without any of the supplied tags.'''
109 return SourceFilter(lambda stags: len(set(tags) & stags) == 0)
110
111def without_tag(tag):
112 '''Return a list of sources with the supplied tag.'''
113 return SourceFilter(lambda stags: tag not in stags)
114
115source_filter_factories = {
116 'with_tags_that': with_tags_that,
117 'with_any_tags': with_any_tags,
118 'with_all_tags': with_all_tags,
119 'with_tag': with_tag,
120 'without_tags': without_tags,
121 'without_tag': without_tag,
122}
123
124Export(source_filter_factories)
125
126class SourceList(list):
127 def apply_filter(self, f):
128 def match(source):
129 return f.predicate(source.tags)
130 return SourceList(filter(match, self))
131
132 def __getattr__(self, name):
133 func = source_filter_factories.get(name, None)
134 if not func:
135 raise AttributeError
136
137 @functools.wraps(func)
138 def wrapper(*args, **kwargs):
139 return self.apply_filter(func(*args, **kwargs))
140 return wrapper
141
142class SourceMeta(type):
143 '''Meta class for source files that keeps track of all files of a
144 particular type.'''
145 def __init__(cls, name, bases, dict):
146 super(SourceMeta, cls).__init__(name, bases, dict)
147 cls.all = SourceList()
148
149class SourceFile(object):
150 '''Base object that encapsulates the notion of a source file.
151 This includes, the source node, target node, various manipulations
152 of those. A source file also specifies a set of tags which
153 describing arbitrary properties of the source file.'''
154 __metaclass__ = SourceMeta
155
156 static_objs = {}
157 shared_objs = {}
158
159 def __init__(self, source, tags=None, add_tags=None):
160 if tags is None:
161 tags='gem5 lib'
162 if isinstance(tags, basestring):
163 tags = set([tags])
164 if not isinstance(tags, set):
165 tags = set(tags)
166 self.tags = tags
167
168 if add_tags:
169 if isinstance(add_tags, basestring):
170 add_tags = set([add_tags])
171 if not isinstance(add_tags, set):
172 add_tags = set(add_tags)
173 self.tags |= add_tags
174
175 tnode = source
176 if not isinstance(source, SCons.Node.FS.File):
177 tnode = File(source)
178
179 self.tnode = tnode
180 self.snode = tnode.srcnode()
181
182 for base in type(self).__mro__:
183 if issubclass(base, SourceFile):
184 base.all.append(self)
185
186 def static(self, env):
187 key = (self.tnode, env['OBJSUFFIX'])
188 if not key in self.static_objs:
189 self.static_objs[key] = env.StaticObject(self.tnode)
190 return self.static_objs[key]
191
192 def shared(self, env):
193 key = (self.tnode, env['OBJSUFFIX'])
194 if not key in self.shared_objs:
195 self.shared_objs[key] = env.SharedObject(self.tnode)
196 return self.shared_objs[key]
197
198 @property
199 def filename(self):
200 return str(self.tnode)
201
202 @property
203 def dirname(self):
204 return dirname(self.filename)
205
206 @property
207 def basename(self):
208 return basename(self.filename)
209
210 @property
211 def extname(self):
212 index = self.basename.rfind('.')
213 if index <= 0:
214 # dot files aren't extensions
215 return self.basename, None
216
217 return self.basename[:index], self.basename[index+1:]
218
219 def __lt__(self, other): return self.filename < other.filename
220 def __le__(self, other): return self.filename <= other.filename
221 def __gt__(self, other): return self.filename > other.filename
222 def __ge__(self, other): return self.filename >= other.filename
223 def __eq__(self, other): return self.filename == other.filename
224 def __ne__(self, other): return self.filename != other.filename
225
226def blobToCpp(data, symbol, cpp_code, hpp_code=None, namespace=None):
227 '''
228 Convert bytes data into C++ .cpp and .hh uint8_t byte array
229 code containing that binary data.
230
231 :param data: binary data to be converted to C++
232 :param symbol: name of the symbol
233 :param cpp_code: append the generated cpp_code to this object
234 :param hpp_code: append the generated hpp_code to this object
235 If None, ignore it. Otherwise, also include it
236 in the .cpp file.
237 :param namespace: namespace to put the symbol into. If None,
238 don't put the symbols into any namespace.
239 '''
240 symbol_len_declaration = 'const std::size_t {}_len'.format(symbol)
241 symbol_declaration = 'const std::uint8_t {}[]'.format(symbol)
242 if hpp_code is not None:
243 cpp_code('''\
244#include "blobs/{}.hh"
245'''.format(symbol))
246 hpp_code('''\
247#include <cstddef>
248#include <cstdint>
249''')
250 if namespace is not None:
251 hpp_code('namespace {} {{'.format(namespace))
252 hpp_code('extern ' + symbol_len_declaration + ';')
253 hpp_code('extern ' + symbol_declaration + ';')
254 if namespace is not None:
255 hpp_code('}')
256 if namespace is not None:
257 cpp_code('namespace {} {{'.format(namespace))
258 if hpp_code is not None:
259 cpp_code(symbol_len_declaration + ' = {};'.format(len(data)))
260 cpp_code(symbol_declaration + ' = {')
261 cpp_code.indent()
262 step = 16
263 for i in xrange(0, len(data), step):
264 x = array.array('B', data[i:i+step])
265 cpp_code(''.join('%d,' % d for d in x))
266 cpp_code.dedent()
267 cpp_code('};')
268 if namespace is not None:
269 cpp_code('}')
270
271def Blob(blob_path, symbol):
272 '''
273 Embed an arbitrary blob into the gem5 executable,
274 and make it accessible to C++ as a byte array.
275 '''
276 blob_path = os.path.abspath(blob_path)
277 blob_out_dir = os.path.join(env['BUILDDIR'], 'blobs')
278 path_noext = joinpath(blob_out_dir, symbol)
279 cpp_path = path_noext + '.cc'
280 hpp_path = path_noext + '.hh'
281 def embedBlob(target, source, env):
282 data = file(str(source[0]), 'r').read()
283 cpp_code = code_formatter()
284 hpp_code = code_formatter()
285 blobToCpp(data, symbol, cpp_code, hpp_code, namespace='Blobs')
286 cpp_path = str(target[0])
287 hpp_path = str(target[1])
288 cpp_dir = os.path.split(cpp_path)[0]
289 if not os.path.exists(cpp_dir):
290 os.makedirs(cpp_dir)
291 cpp_code.write(cpp_path)
292 hpp_code.write(hpp_path)
293 env.Command([cpp_path, hpp_path], blob_path,
294 MakeAction(embedBlob, Transform("EMBED BLOB")))
295 Source(cpp_path)
296
297def GdbXml(xml_id, symbol):
298 Blob(joinpath(gdb_xml_dir, xml_id), symbol)
299
300class Source(SourceFile):
301 ungrouped_tag = 'No link group'
302 source_groups = set()
303
304 _current_group_tag = ungrouped_tag
305
306 @staticmethod
307 def link_group_tag(group):
308 return 'link group: %s' % group
309
310 @classmethod
311 def set_group(cls, group):
312 new_tag = Source.link_group_tag(group)
313 Source._current_group_tag = new_tag
314 Source.source_groups.add(group)
315
316 def _add_link_group_tag(self):
317 self.tags.add(Source._current_group_tag)
318
319 '''Add a c/c++ source file to the build'''
320 def __init__(self, source, tags=None, add_tags=None):
321 '''specify the source file, and any tags'''
322 super(Source, self).__init__(source, tags, add_tags)
323 self._add_link_group_tag()
324
325class PySource(SourceFile):
326 '''Add a python source file to the named package'''
327 invalid_sym_char = re.compile('[^A-z0-9_]')
328 modules = {}
329 tnodes = {}
330 symnames = {}
331
332 def __init__(self, package, source, tags=None, add_tags=None):
333 '''specify the python package, the source file, and any tags'''
334 super(PySource, self).__init__(source, tags, add_tags)
335
336 modname,ext = self.extname
337 assert ext == 'py'
338
339 if package:
340 path = package.split('.')
341 else:
342 path = []
343
344 modpath = path[:]
345 if modname != '__init__':
346 modpath += [ modname ]
347 modpath = '.'.join(modpath)
348
349 arcpath = path + [ self.basename ]
350 abspath = self.snode.abspath
351 if not exists(abspath):
352 abspath = self.tnode.abspath
353
354 self.package = package
355 self.modname = modname
356 self.modpath = modpath
357 self.arcname = joinpath(*arcpath)
358 self.abspath = abspath
359 self.compiled = File(self.filename + 'c')
360 self.cpp = File(self.filename + '.cc')
361 self.symname = PySource.invalid_sym_char.sub('_', modpath)
362
363 PySource.modules[modpath] = self
364 PySource.tnodes[self.tnode] = self
365 PySource.symnames[self.symname] = self
366
367class SimObject(PySource):
368 '''Add a SimObject python file as a python source object and add
369 it to a list of sim object modules'''
370
371 fixed = False
372 modnames = []
373
374 def __init__(self, source, tags=None, add_tags=None):
375 '''Specify the source file and any tags (automatically in
376 the m5.objects package)'''
377 super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
378 if self.fixed:
379 raise AttributeError, "Too late to call SimObject now."
380
381 bisect.insort_right(SimObject.modnames, self.modname)
382
383class ProtoBuf(SourceFile):
384 '''Add a Protocol Buffer to build'''
385
386 def __init__(self, source, tags=None, add_tags=None):
387 '''Specify the source file, and any tags'''
388 super(ProtoBuf, self).__init__(source, tags, add_tags)
389
390 # Get the file name and the extension
391 modname,ext = self.extname
392 assert ext == 'proto'
393
394 # Currently, we stick to generating the C++ headers, so we
395 # only need to track the source and header.
396 self.cc_file = File(modname + '.pb.cc')
397 self.hh_file = File(modname + '.pb.h')
398
399
400exectuable_classes = []
401class ExecutableMeta(type):
402 '''Meta class for Executables.'''
403 all = []
404
405 def __init__(cls, name, bases, d):
406 if not d.pop('abstract', False):
407 ExecutableMeta.all.append(cls)
408 super(ExecutableMeta, cls).__init__(name, bases, d)
409
410 cls.all = []
411
412class Executable(object):
413 '''Base class for creating an executable from sources.'''
414 __metaclass__ = ExecutableMeta
415
416 abstract = True
417
418 def __init__(self, target, *srcs_and_filts):
419 '''Specify the target name and any sources. Sources that are
420 not SourceFiles are evalued with Source().'''
421 super(Executable, self).__init__()
422 self.all.append(self)
423 self.target = target
424
425 isFilter = lambda arg: isinstance(arg, SourceFilter)
426 self.filters = filter(isFilter, srcs_and_filts)
427 sources = filter(lambda a: not isFilter(a), srcs_and_filts)
428
429 srcs = SourceList()
430 for src in sources:
431 if not isinstance(src, SourceFile):
432 src = Source(src, tags=[])
433 srcs.append(src)
434
435 self.sources = srcs
436 self.dir = Dir('.')
437
438 def path(self, env):
439 return self.dir.File(self.target + '.' + env['EXE_SUFFIX'])
440
441 def srcs_to_objs(self, env, sources):
442 return list([ s.static(env) for s in sources ])
443
444 @classmethod
445 def declare_all(cls, env):
446 return list([ instance.declare(env) for instance in cls.all ])
447
448 def declare(self, env, objs=None):
449 if objs is None:
450 objs = self.srcs_to_objs(env, self.sources)
451
1# -*- mode:python -*-
2
3# Copyright (c) 2018 ARM Limited
4#
5# The license below extends only to copyright in the software and shall
6# not be construed as granting a license to any other intellectual
7# property including but not limited to intellectual property relating
8# to a hardware implementation of the functionality of the software
9# licensed hereunder. You may use the software subject to the license
10# terms below provided that you ensure that this notice is replicated
11# unmodified and in its entirety in all distributions of the software,
12# modified or unmodified, in source code or in binary form.
13#
14# Copyright (c) 2004-2005 The Regents of The University of Michigan
15# All rights reserved.
16#
17# Redistribution and use in source and binary forms, with or without
18# modification, are permitted provided that the following conditions are
19# met: redistributions of source code must retain the above copyright
20# notice, this list of conditions and the following disclaimer;
21# redistributions in binary form must reproduce the above copyright
22# notice, this list of conditions and the following disclaimer in the
23# documentation and/or other materials provided with the distribution;
24# neither the name of the copyright holders nor the names of its
25# contributors may be used to endorse or promote products derived from
26# this software without specific prior written permission.
27#
28# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
31# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
32# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
33# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
34# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
35# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
36# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
37# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
38# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39#
40# Authors: Nathan Binkert
41
42from __future__ import print_function
43
44import array
45import bisect
46import functools
47import imp
48import marshal
49import os
50import re
51import subprocess
52import sys
53import zlib
54
55from os.path import basename, dirname, exists, isdir, isfile, join as joinpath
56
57import SCons
58
59from gem5_scons import Transform
60
61# This file defines how to build a particular configuration of gem5
62# based on variable settings in the 'env' build environment.
63
64Import('*')
65
66# Children need to see the environment
67Export('env')
68
69build_env = [(opt, env[opt]) for opt in export_vars]
70
71from m5.util import code_formatter, compareVersions
72
73########################################################################
74# Code for adding source files of various types
75#
76# When specifying a source file of some type, a set of tags can be
77# specified for that file.
78
79class SourceFilter(object):
80 def __init__(self, predicate):
81 self.predicate = predicate
82
83 def __or__(self, other):
84 return SourceFilter(lambda tags: self.predicate(tags) or
85 other.predicate(tags))
86
87 def __and__(self, other):
88 return SourceFilter(lambda tags: self.predicate(tags) and
89 other.predicate(tags))
90
91def with_tags_that(predicate):
92 '''Return a list of sources with tags that satisfy a predicate.'''
93 return SourceFilter(predicate)
94
95def with_any_tags(*tags):
96 '''Return a list of sources with any of the supplied tags.'''
97 return SourceFilter(lambda stags: len(set(tags) & stags) > 0)
98
99def with_all_tags(*tags):
100 '''Return a list of sources with all of the supplied tags.'''
101 return SourceFilter(lambda stags: set(tags) <= stags)
102
103def with_tag(tag):
104 '''Return a list of sources with the supplied tag.'''
105 return SourceFilter(lambda stags: tag in stags)
106
107def without_tags(*tags):
108 '''Return a list of sources without any of the supplied tags.'''
109 return SourceFilter(lambda stags: len(set(tags) & stags) == 0)
110
111def without_tag(tag):
112 '''Return a list of sources with the supplied tag.'''
113 return SourceFilter(lambda stags: tag not in stags)
114
115source_filter_factories = {
116 'with_tags_that': with_tags_that,
117 'with_any_tags': with_any_tags,
118 'with_all_tags': with_all_tags,
119 'with_tag': with_tag,
120 'without_tags': without_tags,
121 'without_tag': without_tag,
122}
123
124Export(source_filter_factories)
125
126class SourceList(list):
127 def apply_filter(self, f):
128 def match(source):
129 return f.predicate(source.tags)
130 return SourceList(filter(match, self))
131
132 def __getattr__(self, name):
133 func = source_filter_factories.get(name, None)
134 if not func:
135 raise AttributeError
136
137 @functools.wraps(func)
138 def wrapper(*args, **kwargs):
139 return self.apply_filter(func(*args, **kwargs))
140 return wrapper
141
142class SourceMeta(type):
143 '''Meta class for source files that keeps track of all files of a
144 particular type.'''
145 def __init__(cls, name, bases, dict):
146 super(SourceMeta, cls).__init__(name, bases, dict)
147 cls.all = SourceList()
148
149class SourceFile(object):
150 '''Base object that encapsulates the notion of a source file.
151 This includes, the source node, target node, various manipulations
152 of those. A source file also specifies a set of tags which
153 describing arbitrary properties of the source file.'''
154 __metaclass__ = SourceMeta
155
156 static_objs = {}
157 shared_objs = {}
158
159 def __init__(self, source, tags=None, add_tags=None):
160 if tags is None:
161 tags='gem5 lib'
162 if isinstance(tags, basestring):
163 tags = set([tags])
164 if not isinstance(tags, set):
165 tags = set(tags)
166 self.tags = tags
167
168 if add_tags:
169 if isinstance(add_tags, basestring):
170 add_tags = set([add_tags])
171 if not isinstance(add_tags, set):
172 add_tags = set(add_tags)
173 self.tags |= add_tags
174
175 tnode = source
176 if not isinstance(source, SCons.Node.FS.File):
177 tnode = File(source)
178
179 self.tnode = tnode
180 self.snode = tnode.srcnode()
181
182 for base in type(self).__mro__:
183 if issubclass(base, SourceFile):
184 base.all.append(self)
185
186 def static(self, env):
187 key = (self.tnode, env['OBJSUFFIX'])
188 if not key in self.static_objs:
189 self.static_objs[key] = env.StaticObject(self.tnode)
190 return self.static_objs[key]
191
192 def shared(self, env):
193 key = (self.tnode, env['OBJSUFFIX'])
194 if not key in self.shared_objs:
195 self.shared_objs[key] = env.SharedObject(self.tnode)
196 return self.shared_objs[key]
197
198 @property
199 def filename(self):
200 return str(self.tnode)
201
202 @property
203 def dirname(self):
204 return dirname(self.filename)
205
206 @property
207 def basename(self):
208 return basename(self.filename)
209
210 @property
211 def extname(self):
212 index = self.basename.rfind('.')
213 if index <= 0:
214 # dot files aren't extensions
215 return self.basename, None
216
217 return self.basename[:index], self.basename[index+1:]
218
219 def __lt__(self, other): return self.filename < other.filename
220 def __le__(self, other): return self.filename <= other.filename
221 def __gt__(self, other): return self.filename > other.filename
222 def __ge__(self, other): return self.filename >= other.filename
223 def __eq__(self, other): return self.filename == other.filename
224 def __ne__(self, other): return self.filename != other.filename
225
226def blobToCpp(data, symbol, cpp_code, hpp_code=None, namespace=None):
227 '''
228 Convert bytes data into C++ .cpp and .hh uint8_t byte array
229 code containing that binary data.
230
231 :param data: binary data to be converted to C++
232 :param symbol: name of the symbol
233 :param cpp_code: append the generated cpp_code to this object
234 :param hpp_code: append the generated hpp_code to this object
235 If None, ignore it. Otherwise, also include it
236 in the .cpp file.
237 :param namespace: namespace to put the symbol into. If None,
238 don't put the symbols into any namespace.
239 '''
240 symbol_len_declaration = 'const std::size_t {}_len'.format(symbol)
241 symbol_declaration = 'const std::uint8_t {}[]'.format(symbol)
242 if hpp_code is not None:
243 cpp_code('''\
244#include "blobs/{}.hh"
245'''.format(symbol))
246 hpp_code('''\
247#include <cstddef>
248#include <cstdint>
249''')
250 if namespace is not None:
251 hpp_code('namespace {} {{'.format(namespace))
252 hpp_code('extern ' + symbol_len_declaration + ';')
253 hpp_code('extern ' + symbol_declaration + ';')
254 if namespace is not None:
255 hpp_code('}')
256 if namespace is not None:
257 cpp_code('namespace {} {{'.format(namespace))
258 if hpp_code is not None:
259 cpp_code(symbol_len_declaration + ' = {};'.format(len(data)))
260 cpp_code(symbol_declaration + ' = {')
261 cpp_code.indent()
262 step = 16
263 for i in xrange(0, len(data), step):
264 x = array.array('B', data[i:i+step])
265 cpp_code(''.join('%d,' % d for d in x))
266 cpp_code.dedent()
267 cpp_code('};')
268 if namespace is not None:
269 cpp_code('}')
270
271def Blob(blob_path, symbol):
272 '''
273 Embed an arbitrary blob into the gem5 executable,
274 and make it accessible to C++ as a byte array.
275 '''
276 blob_path = os.path.abspath(blob_path)
277 blob_out_dir = os.path.join(env['BUILDDIR'], 'blobs')
278 path_noext = joinpath(blob_out_dir, symbol)
279 cpp_path = path_noext + '.cc'
280 hpp_path = path_noext + '.hh'
281 def embedBlob(target, source, env):
282 data = file(str(source[0]), 'r').read()
283 cpp_code = code_formatter()
284 hpp_code = code_formatter()
285 blobToCpp(data, symbol, cpp_code, hpp_code, namespace='Blobs')
286 cpp_path = str(target[0])
287 hpp_path = str(target[1])
288 cpp_dir = os.path.split(cpp_path)[0]
289 if not os.path.exists(cpp_dir):
290 os.makedirs(cpp_dir)
291 cpp_code.write(cpp_path)
292 hpp_code.write(hpp_path)
293 env.Command([cpp_path, hpp_path], blob_path,
294 MakeAction(embedBlob, Transform("EMBED BLOB")))
295 Source(cpp_path)
296
297def GdbXml(xml_id, symbol):
298 Blob(joinpath(gdb_xml_dir, xml_id), symbol)
299
300class Source(SourceFile):
301 ungrouped_tag = 'No link group'
302 source_groups = set()
303
304 _current_group_tag = ungrouped_tag
305
306 @staticmethod
307 def link_group_tag(group):
308 return 'link group: %s' % group
309
310 @classmethod
311 def set_group(cls, group):
312 new_tag = Source.link_group_tag(group)
313 Source._current_group_tag = new_tag
314 Source.source_groups.add(group)
315
316 def _add_link_group_tag(self):
317 self.tags.add(Source._current_group_tag)
318
319 '''Add a c/c++ source file to the build'''
320 def __init__(self, source, tags=None, add_tags=None):
321 '''specify the source file, and any tags'''
322 super(Source, self).__init__(source, tags, add_tags)
323 self._add_link_group_tag()
324
325class PySource(SourceFile):
326 '''Add a python source file to the named package'''
327 invalid_sym_char = re.compile('[^A-z0-9_]')
328 modules = {}
329 tnodes = {}
330 symnames = {}
331
332 def __init__(self, package, source, tags=None, add_tags=None):
333 '''specify the python package, the source file, and any tags'''
334 super(PySource, self).__init__(source, tags, add_tags)
335
336 modname,ext = self.extname
337 assert ext == 'py'
338
339 if package:
340 path = package.split('.')
341 else:
342 path = []
343
344 modpath = path[:]
345 if modname != '__init__':
346 modpath += [ modname ]
347 modpath = '.'.join(modpath)
348
349 arcpath = path + [ self.basename ]
350 abspath = self.snode.abspath
351 if not exists(abspath):
352 abspath = self.tnode.abspath
353
354 self.package = package
355 self.modname = modname
356 self.modpath = modpath
357 self.arcname = joinpath(*arcpath)
358 self.abspath = abspath
359 self.compiled = File(self.filename + 'c')
360 self.cpp = File(self.filename + '.cc')
361 self.symname = PySource.invalid_sym_char.sub('_', modpath)
362
363 PySource.modules[modpath] = self
364 PySource.tnodes[self.tnode] = self
365 PySource.symnames[self.symname] = self
366
367class SimObject(PySource):
368 '''Add a SimObject python file as a python source object and add
369 it to a list of sim object modules'''
370
371 fixed = False
372 modnames = []
373
374 def __init__(self, source, tags=None, add_tags=None):
375 '''Specify the source file and any tags (automatically in
376 the m5.objects package)'''
377 super(SimObject, self).__init__('m5.objects', source, tags, add_tags)
378 if self.fixed:
379 raise AttributeError, "Too late to call SimObject now."
380
381 bisect.insort_right(SimObject.modnames, self.modname)
382
383class ProtoBuf(SourceFile):
384 '''Add a Protocol Buffer to build'''
385
386 def __init__(self, source, tags=None, add_tags=None):
387 '''Specify the source file, and any tags'''
388 super(ProtoBuf, self).__init__(source, tags, add_tags)
389
390 # Get the file name and the extension
391 modname,ext = self.extname
392 assert ext == 'proto'
393
394 # Currently, we stick to generating the C++ headers, so we
395 # only need to track the source and header.
396 self.cc_file = File(modname + '.pb.cc')
397 self.hh_file = File(modname + '.pb.h')
398
399
400exectuable_classes = []
401class ExecutableMeta(type):
402 '''Meta class for Executables.'''
403 all = []
404
405 def __init__(cls, name, bases, d):
406 if not d.pop('abstract', False):
407 ExecutableMeta.all.append(cls)
408 super(ExecutableMeta, cls).__init__(name, bases, d)
409
410 cls.all = []
411
412class Executable(object):
413 '''Base class for creating an executable from sources.'''
414 __metaclass__ = ExecutableMeta
415
416 abstract = True
417
418 def __init__(self, target, *srcs_and_filts):
419 '''Specify the target name and any sources. Sources that are
420 not SourceFiles are evalued with Source().'''
421 super(Executable, self).__init__()
422 self.all.append(self)
423 self.target = target
424
425 isFilter = lambda arg: isinstance(arg, SourceFilter)
426 self.filters = filter(isFilter, srcs_and_filts)
427 sources = filter(lambda a: not isFilter(a), srcs_and_filts)
428
429 srcs = SourceList()
430 for src in sources:
431 if not isinstance(src, SourceFile):
432 src = Source(src, tags=[])
433 srcs.append(src)
434
435 self.sources = srcs
436 self.dir = Dir('.')
437
438 def path(self, env):
439 return self.dir.File(self.target + '.' + env['EXE_SUFFIX'])
440
441 def srcs_to_objs(self, env, sources):
442 return list([ s.static(env) for s in sources ])
443
444 @classmethod
445 def declare_all(cls, env):
446 return list([ instance.declare(env) for instance in cls.all ])
447
448 def declare(self, env, objs=None):
449 if objs is None:
450 objs = self.srcs_to_objs(env, self.sources)
451
452 env = env.Clone()
453 env['BIN_RPATH_PREFIX'] = os.path.relpath(
454 env['BUILDDIR'], self.path(env).dir.abspath)
455
452 if env['STRIP_EXES']:
453 stripped = self.path(env)
454 unstripped = env.File(str(stripped) + '.unstripped')
455 if sys.platform == 'sunos5':
456 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
457 else:
458 cmd = 'strip $SOURCE -o $TARGET'
459 env.Program(unstripped, objs)
460 return env.Command(stripped, unstripped,
461 MakeAction(cmd, Transform("STRIP")))
462 else:
463 return env.Program(self.path(env), objs)
464
465class UnitTest(Executable):
466 '''Create a UnitTest'''
467 def __init__(self, target, *srcs_and_filts, **kwargs):
468 super(UnitTest, self).__init__(target, *srcs_and_filts)
469
470 self.main = kwargs.get('main', False)
471
472 def declare(self, env):
473 sources = list(self.sources)
474 for f in self.filters:
475 sources += Source.all.apply_filter(f)
476 objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS']
477 if self.main:
478 objs += env['MAIN_OBJS']
479 return super(UnitTest, self).declare(env, objs)
480
481class GTest(Executable):
482 '''Create a unit test based on the google test framework.'''
483 all = []
484 def __init__(self, *srcs_and_filts, **kwargs):
485 super(GTest, self).__init__(*srcs_and_filts)
486
487 self.skip_lib = kwargs.pop('skip_lib', False)
488
489 @classmethod
490 def declare_all(cls, env):
491 env = env.Clone()
492 env.Append(LIBS=env['GTEST_LIBS'])
493 env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
494 env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib')
495 env['GTEST_OUT_DIR'] = \
496 Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX'])
497 return super(GTest, cls).declare_all(env)
498
499 def declare(self, env):
500 sources = list(self.sources)
501 if not self.skip_lib:
502 sources += env['GTEST_LIB_SOURCES']
503 for f in self.filters:
504 sources += Source.all.apply_filter(f)
505 objs = self.srcs_to_objs(env, sources)
506
507 binary = super(GTest, self).declare(env, objs)
508
509 out_dir = env['GTEST_OUT_DIR']
510 xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml')
511 AlwaysBuild(env.Command(xml_file, binary,
512 "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}"))
513
514 return binary
515
516class Gem5(Executable):
517 '''Create a gem5 executable.'''
518
519 def __init__(self, target):
520 super(Gem5, self).__init__(target)
521
522 def declare(self, env):
523 objs = env['MAIN_OBJS'] + env['STATIC_OBJS']
524 return super(Gem5, self).declare(env, objs)
525
526
527# Children should have access
528Export('Blob')
529Export('GdbXml')
530Export('Source')
531Export('PySource')
532Export('SimObject')
533Export('ProtoBuf')
534Export('Executable')
535Export('UnitTest')
536Export('GTest')
537
538########################################################################
539#
540# Debug Flags
541#
542debug_flags = {}
543def DebugFlag(name, desc=None):
544 if name in debug_flags:
545 raise AttributeError, "Flag %s already specified" % name
546 debug_flags[name] = (name, (), desc)
547
548def CompoundFlag(name, flags, desc=None):
549 if name in debug_flags:
550 raise AttributeError, "Flag %s already specified" % name
551
552 compound = tuple(flags)
553 debug_flags[name] = (name, compound, desc)
554
555Export('DebugFlag')
556Export('CompoundFlag')
557
558########################################################################
559#
560# Set some compiler variables
561#
562
563# Include file paths are rooted in this directory. SCons will
564# automatically expand '.' to refer to both the source directory and
565# the corresponding build directory to pick up generated include
566# files.
567env.Append(CPPPATH=Dir('.'))
568
569for extra_dir in extras_dir_list:
570 env.Append(CPPPATH=Dir(extra_dir))
571
572# Workaround for bug in SCons version > 0.97d20071212
573# Scons bug id: 2006 gem5 Bug id: 308
574for root, dirs, files in os.walk(base_dir, topdown=True):
575 Dir(root[len(base_dir) + 1:])
576
577########################################################################
578#
579# Walk the tree and execute all SConscripts in subdirectories
580#
581
582here = Dir('.').srcnode().abspath
583for root, dirs, files in os.walk(base_dir, topdown=True):
584 if root == here:
585 # we don't want to recurse back into this SConscript
586 continue
587
588 if 'SConscript' in files:
589 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
590 Source.set_group(build_dir)
591 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
592
593for extra_dir in extras_dir_list:
594 prefix_len = len(dirname(extra_dir)) + 1
595
596 # Also add the corresponding build directory to pick up generated
597 # include files.
598 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
599
600 for root, dirs, files in os.walk(extra_dir, topdown=True):
601 # if build lives in the extras directory, don't walk down it
602 if 'build' in dirs:
603 dirs.remove('build')
604
605 if 'SConscript' in files:
606 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
607 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
608
609for opt in export_vars:
610 env.ConfigFile(opt)
611
612def makeTheISA(source, target, env):
613 isas = [ src.get_contents() for src in source ]
614 target_isa = env['TARGET_ISA']
615 def define(isa):
616 return isa.upper() + '_ISA'
617
618 def namespace(isa):
619 return isa[0].upper() + isa[1:].lower() + 'ISA'
620
621
622 code = code_formatter()
623 code('''\
624#ifndef __CONFIG_THE_ISA_HH__
625#define __CONFIG_THE_ISA_HH__
626
627''')
628
629 # create defines for the preprocessing and compile-time determination
630 for i,isa in enumerate(isas):
631 code('#define $0 $1', define(isa), i + 1)
632 code()
633
634 # create an enum for any run-time determination of the ISA, we
635 # reuse the same name as the namespaces
636 code('enum class Arch {')
637 for i,isa in enumerate(isas):
638 if i + 1 == len(isas):
639 code(' $0 = $1', namespace(isa), define(isa))
640 else:
641 code(' $0 = $1,', namespace(isa), define(isa))
642 code('};')
643
644 code('''
645
646#define THE_ISA ${{define(target_isa)}}
647#define TheISA ${{namespace(target_isa)}}
648#define THE_ISA_STR "${{target_isa}}"
649
650#endif // __CONFIG_THE_ISA_HH__''')
651
652 code.write(str(target[0]))
653
654env.Command('config/the_isa.hh', map(Value, all_isa_list),
655 MakeAction(makeTheISA, Transform("CFG ISA", 0)))
656
657def makeTheGPUISA(source, target, env):
658 isas = [ src.get_contents() for src in source ]
659 target_gpu_isa = env['TARGET_GPU_ISA']
660 def define(isa):
661 return isa.upper() + '_ISA'
662
663 def namespace(isa):
664 return isa[0].upper() + isa[1:].lower() + 'ISA'
665
666
667 code = code_formatter()
668 code('''\
669#ifndef __CONFIG_THE_GPU_ISA_HH__
670#define __CONFIG_THE_GPU_ISA_HH__
671
672''')
673
674 # create defines for the preprocessing and compile-time determination
675 for i,isa in enumerate(isas):
676 code('#define $0 $1', define(isa), i + 1)
677 code()
678
679 # create an enum for any run-time determination of the ISA, we
680 # reuse the same name as the namespaces
681 code('enum class GPUArch {')
682 for i,isa in enumerate(isas):
683 if i + 1 == len(isas):
684 code(' $0 = $1', namespace(isa), define(isa))
685 else:
686 code(' $0 = $1,', namespace(isa), define(isa))
687 code('};')
688
689 code('''
690
691#define THE_GPU_ISA ${{define(target_gpu_isa)}}
692#define TheGpuISA ${{namespace(target_gpu_isa)}}
693#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
694
695#endif // __CONFIG_THE_GPU_ISA_HH__''')
696
697 code.write(str(target[0]))
698
699env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
700 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
701
702########################################################################
703#
704# Prevent any SimObjects from being added after this point, they
705# should all have been added in the SConscripts above
706#
707SimObject.fixed = True
708
709class DictImporter(object):
710 '''This importer takes a dictionary of arbitrary module names that
711 map to arbitrary filenames.'''
712 def __init__(self, modules):
713 self.modules = modules
714 self.installed = set()
715
716 def __del__(self):
717 self.unload()
718
719 def unload(self):
720 import sys
721 for module in self.installed:
722 del sys.modules[module]
723 self.installed = set()
724
725 def find_module(self, fullname, path):
726 if fullname == 'm5.defines':
727 return self
728
729 if fullname == 'm5.objects':
730 return self
731
732 if fullname.startswith('_m5'):
733 return None
734
735 source = self.modules.get(fullname, None)
736 if source is not None and fullname.startswith('m5.objects'):
737 return self
738
739 return None
740
741 def load_module(self, fullname):
742 mod = imp.new_module(fullname)
743 sys.modules[fullname] = mod
744 self.installed.add(fullname)
745
746 mod.__loader__ = self
747 if fullname == 'm5.objects':
748 mod.__path__ = fullname.split('.')
749 return mod
750
751 if fullname == 'm5.defines':
752 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
753 return mod
754
755 source = self.modules[fullname]
756 if source.modname == '__init__':
757 mod.__path__ = source.modpath
758 mod.__file__ = source.abspath
759
760 exec file(source.abspath, 'r') in mod.__dict__
761
762 return mod
763
764import m5.SimObject
765import m5.params
766from m5.util import code_formatter
767
768m5.SimObject.clear()
769m5.params.clear()
770
771# install the python importer so we can grab stuff from the source
772# tree itself. We can't have SimObjects added after this point or
773# else we won't know about them for the rest of the stuff.
774importer = DictImporter(PySource.modules)
775sys.meta_path[0:0] = [ importer ]
776
777# import all sim objects so we can populate the all_objects list
778# make sure that we're working with a list, then let's sort it
779for modname in SimObject.modnames:
780 exec('from m5.objects import %s' % modname)
781
782# we need to unload all of the currently imported modules so that they
783# will be re-imported the next time the sconscript is run
784importer.unload()
785sys.meta_path.remove(importer)
786
787sim_objects = m5.SimObject.allClasses
788all_enums = m5.params.allEnums
789
790for name,obj in sorted(sim_objects.iteritems()):
791 for param in obj._params.local.values():
792 # load the ptype attribute now because it depends on the
793 # current version of SimObject.allClasses, but when scons
794 # actually uses the value, all versions of
795 # SimObject.allClasses will have been loaded
796 param.ptype
797
798########################################################################
799#
800# calculate extra dependencies
801#
802module_depends = ["m5", "m5.SimObject", "m5.params"]
803depends = [ PySource.modules[dep].snode for dep in module_depends ]
804depends.sort(key = lambda x: x.name)
805
806########################################################################
807#
808# Commands for the basic automatically generated python files
809#
810
811# Generate Python file containing a dict specifying the current
812# buildEnv flags.
813def makeDefinesPyFile(target, source, env):
814 build_env = source[0].get_contents()
815
816 code = code_formatter()
817 code("""
818import _m5.core
819import m5.util
820
821buildEnv = m5.util.SmartDict($build_env)
822
823compileDate = _m5.core.compileDate
824_globals = globals()
825for key,val in _m5.core.__dict__.iteritems():
826 if key.startswith('flag_'):
827 flag = key[5:]
828 _globals[flag] = val
829del _globals
830""")
831 code.write(target[0].abspath)
832
833defines_info = Value(build_env)
834# Generate a file with all of the compile options in it
835env.Command('python/m5/defines.py', defines_info,
836 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
837PySource('m5', 'python/m5/defines.py')
838
839# Generate python file containing info about the M5 source code
840def makeInfoPyFile(target, source, env):
841 code = code_formatter()
842 for src in source:
843 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
844 code('$src = ${{repr(data)}}')
845 code.write(str(target[0]))
846
847# Generate a file that wraps the basic top level files
848env.Command('python/m5/info.py',
849 [ '#/COPYING', '#/LICENSE', '#/README', ],
850 MakeAction(makeInfoPyFile, Transform("INFO")))
851PySource('m5', 'python/m5/info.py')
852
853########################################################################
854#
855# Create all of the SimObject param headers and enum headers
856#
857
858def createSimObjectParamStruct(target, source, env):
859 assert len(target) == 1 and len(source) == 1
860
861 name = source[0].get_text_contents()
862 obj = sim_objects[name]
863
864 code = code_formatter()
865 obj.cxx_param_decl(code)
866 code.write(target[0].abspath)
867
868def createSimObjectCxxConfig(is_header):
869 def body(target, source, env):
870 assert len(target) == 1 and len(source) == 1
871
872 name = str(source[0].get_contents())
873 obj = sim_objects[name]
874
875 code = code_formatter()
876 obj.cxx_config_param_file(code, is_header)
877 code.write(target[0].abspath)
878 return body
879
880def createEnumStrings(target, source, env):
881 assert len(target) == 1 and len(source) == 2
882
883 name = source[0].get_text_contents()
884 use_python = source[1].read()
885 obj = all_enums[name]
886
887 code = code_formatter()
888 obj.cxx_def(code)
889 if use_python:
890 obj.pybind_def(code)
891 code.write(target[0].abspath)
892
893def createEnumDecls(target, source, env):
894 assert len(target) == 1 and len(source) == 1
895
896 name = source[0].get_text_contents()
897 obj = all_enums[name]
898
899 code = code_formatter()
900 obj.cxx_decl(code)
901 code.write(target[0].abspath)
902
903def createSimObjectPyBindWrapper(target, source, env):
904 name = source[0].get_text_contents()
905 obj = sim_objects[name]
906
907 code = code_formatter()
908 obj.pybind_decl(code)
909 code.write(target[0].abspath)
910
911# Generate all of the SimObject param C++ struct header files
912params_hh_files = []
913for name,simobj in sorted(sim_objects.iteritems()):
914 py_source = PySource.modules[simobj.__module__]
915 extra_deps = [ py_source.tnode ]
916
917 hh_file = File('params/%s.hh' % name)
918 params_hh_files.append(hh_file)
919 env.Command(hh_file, Value(name),
920 MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
921 env.Depends(hh_file, depends + extra_deps)
922
923# C++ parameter description files
924if GetOption('with_cxx_config'):
925 for name,simobj in sorted(sim_objects.iteritems()):
926 py_source = PySource.modules[simobj.__module__]
927 extra_deps = [ py_source.tnode ]
928
929 cxx_config_hh_file = File('cxx_config/%s.hh' % name)
930 cxx_config_cc_file = File('cxx_config/%s.cc' % name)
931 env.Command(cxx_config_hh_file, Value(name),
932 MakeAction(createSimObjectCxxConfig(True),
933 Transform("CXXCPRHH")))
934 env.Command(cxx_config_cc_file, Value(name),
935 MakeAction(createSimObjectCxxConfig(False),
936 Transform("CXXCPRCC")))
937 env.Depends(cxx_config_hh_file, depends + extra_deps +
938 [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
939 env.Depends(cxx_config_cc_file, depends + extra_deps +
940 [cxx_config_hh_file])
941 Source(cxx_config_cc_file)
942
943 cxx_config_init_cc_file = File('cxx_config/init.cc')
944
945 def createCxxConfigInitCC(target, source, env):
946 assert len(target) == 1 and len(source) == 1
947
948 code = code_formatter()
949
950 for name,simobj in sorted(sim_objects.iteritems()):
951 if not hasattr(simobj, 'abstract') or not simobj.abstract:
952 code('#include "cxx_config/${name}.hh"')
953 code()
954 code('void cxxConfigInit()')
955 code('{')
956 code.indent()
957 for name,simobj in sorted(sim_objects.iteritems()):
958 not_abstract = not hasattr(simobj, 'abstract') or \
959 not simobj.abstract
960 if not_abstract and 'type' in simobj.__dict__:
961 code('cxx_config_directory["${name}"] = '
962 '${name}CxxConfigParams::makeDirectoryEntry();')
963 code.dedent()
964 code('}')
965 code.write(target[0].abspath)
966
967 py_source = PySource.modules[simobj.__module__]
968 extra_deps = [ py_source.tnode ]
969 env.Command(cxx_config_init_cc_file, Value(name),
970 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
971 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
972 for name,simobj in sorted(sim_objects.iteritems())
973 if not hasattr(simobj, 'abstract') or not simobj.abstract]
974 Depends(cxx_config_init_cc_file, cxx_param_hh_files +
975 [File('sim/cxx_config.hh')])
976 Source(cxx_config_init_cc_file)
977
978# Generate all enum header files
979for name,enum in sorted(all_enums.iteritems()):
980 py_source = PySource.modules[enum.__module__]
981 extra_deps = [ py_source.tnode ]
982
983 cc_file = File('enums/%s.cc' % name)
984 env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
985 MakeAction(createEnumStrings, Transform("ENUM STR")))
986 env.Depends(cc_file, depends + extra_deps)
987 Source(cc_file)
988
989 hh_file = File('enums/%s.hh' % name)
990 env.Command(hh_file, Value(name),
991 MakeAction(createEnumDecls, Transform("ENUMDECL")))
992 env.Depends(hh_file, depends + extra_deps)
993
994# Generate SimObject Python bindings wrapper files
995if env['USE_PYTHON']:
996 for name,simobj in sorted(sim_objects.iteritems()):
997 py_source = PySource.modules[simobj.__module__]
998 extra_deps = [ py_source.tnode ]
999 cc_file = File('python/_m5/param_%s.cc' % name)
1000 env.Command(cc_file, Value(name),
1001 MakeAction(createSimObjectPyBindWrapper,
1002 Transform("SO PyBind")))
1003 env.Depends(cc_file, depends + extra_deps)
1004 Source(cc_file)
1005
1006# Build all protocol buffers if we have got protoc and protobuf available
1007if env['HAVE_PROTOBUF']:
1008 for proto in ProtoBuf.all:
1009 # Use both the source and header as the target, and the .proto
1010 # file as the source. When executing the protoc compiler, also
1011 # specify the proto_path to avoid having the generated files
1012 # include the path.
1013 env.Command([proto.cc_file, proto.hh_file], proto.tnode,
1014 MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
1015 '--proto_path ${SOURCE.dir} $SOURCE',
1016 Transform("PROTOC")))
1017
1018 # Add the C++ source file
1019 Source(proto.cc_file, tags=proto.tags)
1020elif ProtoBuf.all:
1021 print('Got protobuf to build, but lacks support!')
1022 Exit(1)
1023
1024#
1025# Handle debug flags
1026#
1027def makeDebugFlagCC(target, source, env):
1028 assert(len(target) == 1 and len(source) == 1)
1029
1030 code = code_formatter()
1031
1032 # delay definition of CompoundFlags until after all the definition
1033 # of all constituent SimpleFlags
1034 comp_code = code_formatter()
1035
1036 # file header
1037 code('''
1038/*
1039 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1040 */
1041
1042#include "base/debug.hh"
1043
1044namespace Debug {
1045
1046''')
1047
1048 for name, flag in sorted(source[0].read().iteritems()):
1049 n, compound, desc = flag
1050 assert n == name
1051
1052 if not compound:
1053 code('SimpleFlag $name("$name", "$desc");')
1054 else:
1055 comp_code('CompoundFlag $name("$name", "$desc",')
1056 comp_code.indent()
1057 last = len(compound) - 1
1058 for i,flag in enumerate(compound):
1059 if i != last:
1060 comp_code('&$flag,')
1061 else:
1062 comp_code('&$flag);')
1063 comp_code.dedent()
1064
1065 code.append(comp_code)
1066 code()
1067 code('} // namespace Debug')
1068
1069 code.write(str(target[0]))
1070
1071def makeDebugFlagHH(target, source, env):
1072 assert(len(target) == 1 and len(source) == 1)
1073
1074 val = eval(source[0].get_contents())
1075 name, compound, desc = val
1076
1077 code = code_formatter()
1078
1079 # file header boilerplate
1080 code('''\
1081/*
1082 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1083 */
1084
1085#ifndef __DEBUG_${name}_HH__
1086#define __DEBUG_${name}_HH__
1087
1088namespace Debug {
1089''')
1090
1091 if compound:
1092 code('class CompoundFlag;')
1093 code('class SimpleFlag;')
1094
1095 if compound:
1096 code('extern CompoundFlag $name;')
1097 for flag in compound:
1098 code('extern SimpleFlag $flag;')
1099 else:
1100 code('extern SimpleFlag $name;')
1101
1102 code('''
1103}
1104
1105#endif // __DEBUG_${name}_HH__
1106''')
1107
1108 code.write(str(target[0]))
1109
1110for name,flag in sorted(debug_flags.iteritems()):
1111 n, compound, desc = flag
1112 assert n == name
1113
1114 hh_file = 'debug/%s.hh' % name
1115 env.Command(hh_file, Value(flag),
1116 MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
1117
1118env.Command('debug/flags.cc', Value(debug_flags),
1119 MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
1120Source('debug/flags.cc')
1121
1122# version tags
1123tags = \
1124env.Command('sim/tags.cc', None,
1125 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
1126 Transform("VER TAGS")))
1127env.AlwaysBuild(tags)
1128
1129# Embed python files. All .py files that have been indicated by a
1130# PySource() call in a SConscript need to be embedded into the M5
1131# library. To do that, we compile the file to byte code, marshal the
1132# byte code, compress it, and then generate a c++ file that
1133# inserts the result into an array.
1134def embedPyFile(target, source, env):
1135 def c_str(string):
1136 if string is None:
1137 return "0"
1138 return '"%s"' % string
1139
1140 '''Action function to compile a .py into a code object, marshal
1141 it, compress it, and stick it into an asm file so the code appears
1142 as just bytes with a label in the data section'''
1143
1144 src = file(str(source[0]), 'r').read()
1145
1146 pysource = PySource.tnodes[source[0]]
1147 compiled = compile(src, pysource.abspath, 'exec')
1148 marshalled = marshal.dumps(compiled)
1149 compressed = zlib.compress(marshalled)
1150 data = compressed
1151 sym = pysource.symname
1152
1153 code = code_formatter()
1154 code('''\
1155#include "sim/init.hh"
1156
1157namespace {
1158
1159''')
1160 blobToCpp(data, 'data_' + sym, code)
1161 code('''\
1162
1163
1164EmbeddedPython embedded_${sym}(
1165 ${{c_str(pysource.arcname)}},
1166 ${{c_str(pysource.abspath)}},
1167 ${{c_str(pysource.modpath)}},
1168 data_${sym},
1169 ${{len(data)}},
1170 ${{len(marshalled)}});
1171
1172} // anonymous namespace
1173''')
1174 code.write(str(target[0]))
1175
1176for source in PySource.all:
1177 env.Command(source.cpp, source.tnode,
1178 MakeAction(embedPyFile, Transform("EMBED PY")))
1179 Source(source.cpp, tags=source.tags, add_tags='python')
1180
1181########################################################################
1182#
1183# Define binaries. Each different build type (debug, opt, etc.) gets
1184# a slightly different build environment.
1185#
1186
1187# List of constructed environments to pass back to SConstruct
1188date_source = Source('base/date.cc', tags=[])
1189
1190gem5_binary = Gem5('gem5')
1191
1192# Function to create a new build environment as clone of current
1193# environment 'env' with modified object suffix and optional stripped
1194# binary. Additional keyword arguments are appended to corresponding
1195# build environment vars.
1196def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
1197 # SCons doesn't know to append a library suffix when there is a '.' in the
1198 # name. Use '_' instead.
1199 libname = 'gem5_' + label
1200 secondary_exename = 'm5.' + label
1201
1202 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1203 new_env.Label = label
1204 new_env.Append(**kwargs)
1205
1206 lib_sources = Source.all.with_tag('gem5 lib')
1207
1208 # Without Python, leave out all Python content from the library
1209 # builds. The option doesn't affect gem5 built as a program
1210 if GetOption('without_python'):
1211 lib_sources = lib_sources.without_tag('python')
1212
1213 static_objs = []
1214 shared_objs = []
1215
1216 for s in lib_sources.with_tag(Source.ungrouped_tag):
1217 static_objs.append(s.static(new_env))
1218 shared_objs.append(s.shared(new_env))
1219
1220 for group in Source.source_groups:
1221 srcs = lib_sources.with_tag(Source.link_group_tag(group))
1222 if not srcs:
1223 continue
1224
1225 group_static = [ s.static(new_env) for s in srcs ]
1226 group_shared = [ s.shared(new_env) for s in srcs ]
1227
1228 # If partial linking is disabled, add these sources to the build
1229 # directly, and short circuit this loop.
1230 if disable_partial:
1231 static_objs.extend(group_static)
1232 shared_objs.extend(group_shared)
1233 continue
1234
1235 # Set up the static partially linked objects.
1236 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1237 target = File(joinpath(group, file_name))
1238 partial = env.PartialStatic(target=target, source=group_static)
1239 static_objs.extend(partial)
1240
1241 # Set up the shared partially linked objects.
1242 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1243 target = File(joinpath(group, file_name))
1244 partial = env.PartialShared(target=target, source=group_shared)
1245 shared_objs.extend(partial)
1246
1247 static_date = date_source.static(new_env)
1248 new_env.Depends(static_date, static_objs)
1249 static_objs.extend(static_date)
1250
1251 shared_date = date_source.shared(new_env)
1252 new_env.Depends(shared_date, shared_objs)
1253 shared_objs.extend(shared_date)
1254
1255 main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1256
1257 # First make a library of everything but main() so other programs can
1258 # link against m5.
1259 static_lib = new_env.StaticLibrary(libname, static_objs)
1260 shared_lib = new_env.SharedLibrary(libname, shared_objs)
1261
1262 # Keep track of the object files generated so far so Executables can
1263 # include them.
1264 new_env['STATIC_OBJS'] = static_objs
1265 new_env['SHARED_OBJS'] = shared_objs
1266 new_env['MAIN_OBJS'] = main_objs
1267
1268 new_env['STATIC_LIB'] = static_lib
1269 new_env['SHARED_LIB'] = shared_lib
1270
1271 # Record some settings for building Executables.
1272 new_env['EXE_SUFFIX'] = label
1273 new_env['STRIP_EXES'] = strip
1274
1275 for cls in ExecutableMeta.all:
1276 cls.declare_all(new_env)
1277
1278 new_env.M5Binary = File(gem5_binary.path(new_env))
1279
1280 new_env.Command(secondary_exename, new_env.M5Binary,
1281 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1282
1283 # Set up regression tests.
1284 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1285 variant_dir=Dir('tests').Dir(new_env.Label),
1286 exports={ 'env' : new_env }, duplicate=False)
1287
1288# Start out with the compiler flags common to all compilers,
1289# i.e. they all use -g for opt and -g -pg for prof
1290ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1291 'perf' : ['-g']}
1292
1293# Start out with the linker flags common to all linkers, i.e. -pg for
1294# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1295# no-as-needed and as-needed as the binutils linker is too clever and
1296# simply doesn't link to the library otherwise.
1297ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1298 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1299
1300# For Link Time Optimization, the optimisation flags used to compile
1301# individual files are decoupled from those used at link time
1302# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1303# to also update the linker flags based on the target.
1304if env['GCC']:
1305 if sys.platform == 'sunos5':
1306 ccflags['debug'] += ['-gstabs+']
1307 else:
1308 ccflags['debug'] += ['-ggdb3']
1309 ldflags['debug'] += ['-O0']
1310 # opt, fast, prof and perf all share the same cc flags, also add
1311 # the optimization to the ldflags as LTO defers the optimization
1312 # to link time
1313 for target in ['opt', 'fast', 'prof', 'perf']:
1314 ccflags[target] += ['-O3']
1315 ldflags[target] += ['-O3']
1316
1317 ccflags['fast'] += env['LTO_CCFLAGS']
1318 ldflags['fast'] += env['LTO_LDFLAGS']
1319elif env['CLANG']:
1320 ccflags['debug'] += ['-g', '-O0']
1321 # opt, fast, prof and perf all share the same cc flags
1322 for target in ['opt', 'fast', 'prof', 'perf']:
1323 ccflags[target] += ['-O3']
1324else:
1325 print('Unknown compiler, please fix compiler options')
1326 Exit(1)
1327
1328
1329# To speed things up, we only instantiate the build environments we
1330# need. We try to identify the needed environment for each target; if
1331# we can't, we fall back on instantiating all the environments just to
1332# be safe.
1333target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1334obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1335 'gpo' : 'perf'}
1336
1337def identifyTarget(t):
1338 ext = t.split('.')[-1]
1339 if ext in target_types:
1340 return ext
1341 if ext in obj2target:
1342 return obj2target[ext]
1343 match = re.search(r'/tests/([^/]+)/', t)
1344 if match and match.group(1) in target_types:
1345 return match.group(1)
1346 return 'all'
1347
1348needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1349if 'all' in needed_envs:
1350 needed_envs += target_types
1351
1352disable_partial = False
1353if env['PLATFORM'] == 'darwin':
1354 # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial
1355 # linked objects do not expose symbols that are marked with the
1356 # hidden visibility and consequently building gem5 on Mac OS
1357 # fails. As a workaround, we disable partial linking, however, we
1358 # may want to revisit in the future.
1359 disable_partial = True
1360
1361# Debug binary
1362if 'debug' in needed_envs:
1363 makeEnv(env, 'debug', '.do',
1364 CCFLAGS = Split(ccflags['debug']),
1365 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1366 LINKFLAGS = Split(ldflags['debug']),
1367 disable_partial=disable_partial)
1368
1369# Optimized binary
1370if 'opt' in needed_envs:
1371 makeEnv(env, 'opt', '.o',
1372 CCFLAGS = Split(ccflags['opt']),
1373 CPPDEFINES = ['TRACING_ON=1'],
1374 LINKFLAGS = Split(ldflags['opt']),
1375 disable_partial=disable_partial)
1376
1377# "Fast" binary
1378if 'fast' in needed_envs:
1379 disable_partial = disable_partial and \
1380 env.get('BROKEN_INCREMENTAL_LTO', False) and \
1381 GetOption('force_lto')
1382 makeEnv(env, 'fast', '.fo', strip = True,
1383 CCFLAGS = Split(ccflags['fast']),
1384 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1385 LINKFLAGS = Split(ldflags['fast']),
1386 disable_partial=disable_partial)
1387
1388# Profiled binary using gprof
1389if 'prof' in needed_envs:
1390 makeEnv(env, 'prof', '.po',
1391 CCFLAGS = Split(ccflags['prof']),
1392 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1393 LINKFLAGS = Split(ldflags['prof']),
1394 disable_partial=disable_partial)
1395
1396# Profiled binary using google-pprof
1397if 'perf' in needed_envs:
1398 makeEnv(env, 'perf', '.gpo',
1399 CCFLAGS = Split(ccflags['perf']),
1400 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1401 LINKFLAGS = Split(ldflags['perf']),
1402 disable_partial=disable_partial)
456 if env['STRIP_EXES']:
457 stripped = self.path(env)
458 unstripped = env.File(str(stripped) + '.unstripped')
459 if sys.platform == 'sunos5':
460 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
461 else:
462 cmd = 'strip $SOURCE -o $TARGET'
463 env.Program(unstripped, objs)
464 return env.Command(stripped, unstripped,
465 MakeAction(cmd, Transform("STRIP")))
466 else:
467 return env.Program(self.path(env), objs)
468
469class UnitTest(Executable):
470 '''Create a UnitTest'''
471 def __init__(self, target, *srcs_and_filts, **kwargs):
472 super(UnitTest, self).__init__(target, *srcs_and_filts)
473
474 self.main = kwargs.get('main', False)
475
476 def declare(self, env):
477 sources = list(self.sources)
478 for f in self.filters:
479 sources += Source.all.apply_filter(f)
480 objs = self.srcs_to_objs(env, sources) + env['STATIC_OBJS']
481 if self.main:
482 objs += env['MAIN_OBJS']
483 return super(UnitTest, self).declare(env, objs)
484
485class GTest(Executable):
486 '''Create a unit test based on the google test framework.'''
487 all = []
488 def __init__(self, *srcs_and_filts, **kwargs):
489 super(GTest, self).__init__(*srcs_and_filts)
490
491 self.skip_lib = kwargs.pop('skip_lib', False)
492
493 @classmethod
494 def declare_all(cls, env):
495 env = env.Clone()
496 env.Append(LIBS=env['GTEST_LIBS'])
497 env.Append(CPPFLAGS=env['GTEST_CPPFLAGS'])
498 env['GTEST_LIB_SOURCES'] = Source.all.with_tag('gtest lib')
499 env['GTEST_OUT_DIR'] = \
500 Dir(env['BUILDDIR']).Dir('unittests.' + env['EXE_SUFFIX'])
501 return super(GTest, cls).declare_all(env)
502
503 def declare(self, env):
504 sources = list(self.sources)
505 if not self.skip_lib:
506 sources += env['GTEST_LIB_SOURCES']
507 for f in self.filters:
508 sources += Source.all.apply_filter(f)
509 objs = self.srcs_to_objs(env, sources)
510
511 binary = super(GTest, self).declare(env, objs)
512
513 out_dir = env['GTEST_OUT_DIR']
514 xml_file = out_dir.Dir(str(self.dir)).File(self.target + '.xml')
515 AlwaysBuild(env.Command(xml_file, binary,
516 "${SOURCES[0]} --gtest_output=xml:${TARGETS[0]}"))
517
518 return binary
519
520class Gem5(Executable):
521 '''Create a gem5 executable.'''
522
523 def __init__(self, target):
524 super(Gem5, self).__init__(target)
525
526 def declare(self, env):
527 objs = env['MAIN_OBJS'] + env['STATIC_OBJS']
528 return super(Gem5, self).declare(env, objs)
529
530
531# Children should have access
532Export('Blob')
533Export('GdbXml')
534Export('Source')
535Export('PySource')
536Export('SimObject')
537Export('ProtoBuf')
538Export('Executable')
539Export('UnitTest')
540Export('GTest')
541
542########################################################################
543#
544# Debug Flags
545#
546debug_flags = {}
547def DebugFlag(name, desc=None):
548 if name in debug_flags:
549 raise AttributeError, "Flag %s already specified" % name
550 debug_flags[name] = (name, (), desc)
551
552def CompoundFlag(name, flags, desc=None):
553 if name in debug_flags:
554 raise AttributeError, "Flag %s already specified" % name
555
556 compound = tuple(flags)
557 debug_flags[name] = (name, compound, desc)
558
559Export('DebugFlag')
560Export('CompoundFlag')
561
562########################################################################
563#
564# Set some compiler variables
565#
566
567# Include file paths are rooted in this directory. SCons will
568# automatically expand '.' to refer to both the source directory and
569# the corresponding build directory to pick up generated include
570# files.
571env.Append(CPPPATH=Dir('.'))
572
573for extra_dir in extras_dir_list:
574 env.Append(CPPPATH=Dir(extra_dir))
575
576# Workaround for bug in SCons version > 0.97d20071212
577# Scons bug id: 2006 gem5 Bug id: 308
578for root, dirs, files in os.walk(base_dir, topdown=True):
579 Dir(root[len(base_dir) + 1:])
580
581########################################################################
582#
583# Walk the tree and execute all SConscripts in subdirectories
584#
585
586here = Dir('.').srcnode().abspath
587for root, dirs, files in os.walk(base_dir, topdown=True):
588 if root == here:
589 # we don't want to recurse back into this SConscript
590 continue
591
592 if 'SConscript' in files:
593 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
594 Source.set_group(build_dir)
595 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
596
597for extra_dir in extras_dir_list:
598 prefix_len = len(dirname(extra_dir)) + 1
599
600 # Also add the corresponding build directory to pick up generated
601 # include files.
602 env.Append(CPPPATH=Dir(joinpath(env['BUILDDIR'], extra_dir[prefix_len:])))
603
604 for root, dirs, files in os.walk(extra_dir, topdown=True):
605 # if build lives in the extras directory, don't walk down it
606 if 'build' in dirs:
607 dirs.remove('build')
608
609 if 'SConscript' in files:
610 build_dir = joinpath(env['BUILDDIR'], root[prefix_len:])
611 SConscript(joinpath(root, 'SConscript'), variant_dir=build_dir)
612
613for opt in export_vars:
614 env.ConfigFile(opt)
615
616def makeTheISA(source, target, env):
617 isas = [ src.get_contents() for src in source ]
618 target_isa = env['TARGET_ISA']
619 def define(isa):
620 return isa.upper() + '_ISA'
621
622 def namespace(isa):
623 return isa[0].upper() + isa[1:].lower() + 'ISA'
624
625
626 code = code_formatter()
627 code('''\
628#ifndef __CONFIG_THE_ISA_HH__
629#define __CONFIG_THE_ISA_HH__
630
631''')
632
633 # create defines for the preprocessing and compile-time determination
634 for i,isa in enumerate(isas):
635 code('#define $0 $1', define(isa), i + 1)
636 code()
637
638 # create an enum for any run-time determination of the ISA, we
639 # reuse the same name as the namespaces
640 code('enum class Arch {')
641 for i,isa in enumerate(isas):
642 if i + 1 == len(isas):
643 code(' $0 = $1', namespace(isa), define(isa))
644 else:
645 code(' $0 = $1,', namespace(isa), define(isa))
646 code('};')
647
648 code('''
649
650#define THE_ISA ${{define(target_isa)}}
651#define TheISA ${{namespace(target_isa)}}
652#define THE_ISA_STR "${{target_isa}}"
653
654#endif // __CONFIG_THE_ISA_HH__''')
655
656 code.write(str(target[0]))
657
658env.Command('config/the_isa.hh', map(Value, all_isa_list),
659 MakeAction(makeTheISA, Transform("CFG ISA", 0)))
660
661def makeTheGPUISA(source, target, env):
662 isas = [ src.get_contents() for src in source ]
663 target_gpu_isa = env['TARGET_GPU_ISA']
664 def define(isa):
665 return isa.upper() + '_ISA'
666
667 def namespace(isa):
668 return isa[0].upper() + isa[1:].lower() + 'ISA'
669
670
671 code = code_formatter()
672 code('''\
673#ifndef __CONFIG_THE_GPU_ISA_HH__
674#define __CONFIG_THE_GPU_ISA_HH__
675
676''')
677
678 # create defines for the preprocessing and compile-time determination
679 for i,isa in enumerate(isas):
680 code('#define $0 $1', define(isa), i + 1)
681 code()
682
683 # create an enum for any run-time determination of the ISA, we
684 # reuse the same name as the namespaces
685 code('enum class GPUArch {')
686 for i,isa in enumerate(isas):
687 if i + 1 == len(isas):
688 code(' $0 = $1', namespace(isa), define(isa))
689 else:
690 code(' $0 = $1,', namespace(isa), define(isa))
691 code('};')
692
693 code('''
694
695#define THE_GPU_ISA ${{define(target_gpu_isa)}}
696#define TheGpuISA ${{namespace(target_gpu_isa)}}
697#define THE_GPU_ISA_STR "${{target_gpu_isa}}"
698
699#endif // __CONFIG_THE_GPU_ISA_HH__''')
700
701 code.write(str(target[0]))
702
703env.Command('config/the_gpu_isa.hh', map(Value, all_gpu_isa_list),
704 MakeAction(makeTheGPUISA, Transform("CFG ISA", 0)))
705
706########################################################################
707#
708# Prevent any SimObjects from being added after this point, they
709# should all have been added in the SConscripts above
710#
711SimObject.fixed = True
712
713class DictImporter(object):
714 '''This importer takes a dictionary of arbitrary module names that
715 map to arbitrary filenames.'''
716 def __init__(self, modules):
717 self.modules = modules
718 self.installed = set()
719
720 def __del__(self):
721 self.unload()
722
723 def unload(self):
724 import sys
725 for module in self.installed:
726 del sys.modules[module]
727 self.installed = set()
728
729 def find_module(self, fullname, path):
730 if fullname == 'm5.defines':
731 return self
732
733 if fullname == 'm5.objects':
734 return self
735
736 if fullname.startswith('_m5'):
737 return None
738
739 source = self.modules.get(fullname, None)
740 if source is not None and fullname.startswith('m5.objects'):
741 return self
742
743 return None
744
745 def load_module(self, fullname):
746 mod = imp.new_module(fullname)
747 sys.modules[fullname] = mod
748 self.installed.add(fullname)
749
750 mod.__loader__ = self
751 if fullname == 'm5.objects':
752 mod.__path__ = fullname.split('.')
753 return mod
754
755 if fullname == 'm5.defines':
756 mod.__dict__['buildEnv'] = m5.util.SmartDict(build_env)
757 return mod
758
759 source = self.modules[fullname]
760 if source.modname == '__init__':
761 mod.__path__ = source.modpath
762 mod.__file__ = source.abspath
763
764 exec file(source.abspath, 'r') in mod.__dict__
765
766 return mod
767
768import m5.SimObject
769import m5.params
770from m5.util import code_formatter
771
772m5.SimObject.clear()
773m5.params.clear()
774
775# install the python importer so we can grab stuff from the source
776# tree itself. We can't have SimObjects added after this point or
777# else we won't know about them for the rest of the stuff.
778importer = DictImporter(PySource.modules)
779sys.meta_path[0:0] = [ importer ]
780
781# import all sim objects so we can populate the all_objects list
782# make sure that we're working with a list, then let's sort it
783for modname in SimObject.modnames:
784 exec('from m5.objects import %s' % modname)
785
786# we need to unload all of the currently imported modules so that they
787# will be re-imported the next time the sconscript is run
788importer.unload()
789sys.meta_path.remove(importer)
790
791sim_objects = m5.SimObject.allClasses
792all_enums = m5.params.allEnums
793
794for name,obj in sorted(sim_objects.iteritems()):
795 for param in obj._params.local.values():
796 # load the ptype attribute now because it depends on the
797 # current version of SimObject.allClasses, but when scons
798 # actually uses the value, all versions of
799 # SimObject.allClasses will have been loaded
800 param.ptype
801
802########################################################################
803#
804# calculate extra dependencies
805#
806module_depends = ["m5", "m5.SimObject", "m5.params"]
807depends = [ PySource.modules[dep].snode for dep in module_depends ]
808depends.sort(key = lambda x: x.name)
809
810########################################################################
811#
812# Commands for the basic automatically generated python files
813#
814
815# Generate Python file containing a dict specifying the current
816# buildEnv flags.
817def makeDefinesPyFile(target, source, env):
818 build_env = source[0].get_contents()
819
820 code = code_formatter()
821 code("""
822import _m5.core
823import m5.util
824
825buildEnv = m5.util.SmartDict($build_env)
826
827compileDate = _m5.core.compileDate
828_globals = globals()
829for key,val in _m5.core.__dict__.iteritems():
830 if key.startswith('flag_'):
831 flag = key[5:]
832 _globals[flag] = val
833del _globals
834""")
835 code.write(target[0].abspath)
836
837defines_info = Value(build_env)
838# Generate a file with all of the compile options in it
839env.Command('python/m5/defines.py', defines_info,
840 MakeAction(makeDefinesPyFile, Transform("DEFINES", 0)))
841PySource('m5', 'python/m5/defines.py')
842
843# Generate python file containing info about the M5 source code
844def makeInfoPyFile(target, source, env):
845 code = code_formatter()
846 for src in source:
847 data = ''.join(file(src.srcnode().abspath, 'r').xreadlines())
848 code('$src = ${{repr(data)}}')
849 code.write(str(target[0]))
850
851# Generate a file that wraps the basic top level files
852env.Command('python/m5/info.py',
853 [ '#/COPYING', '#/LICENSE', '#/README', ],
854 MakeAction(makeInfoPyFile, Transform("INFO")))
855PySource('m5', 'python/m5/info.py')
856
857########################################################################
858#
859# Create all of the SimObject param headers and enum headers
860#
861
862def createSimObjectParamStruct(target, source, env):
863 assert len(target) == 1 and len(source) == 1
864
865 name = source[0].get_text_contents()
866 obj = sim_objects[name]
867
868 code = code_formatter()
869 obj.cxx_param_decl(code)
870 code.write(target[0].abspath)
871
872def createSimObjectCxxConfig(is_header):
873 def body(target, source, env):
874 assert len(target) == 1 and len(source) == 1
875
876 name = str(source[0].get_contents())
877 obj = sim_objects[name]
878
879 code = code_formatter()
880 obj.cxx_config_param_file(code, is_header)
881 code.write(target[0].abspath)
882 return body
883
884def createEnumStrings(target, source, env):
885 assert len(target) == 1 and len(source) == 2
886
887 name = source[0].get_text_contents()
888 use_python = source[1].read()
889 obj = all_enums[name]
890
891 code = code_formatter()
892 obj.cxx_def(code)
893 if use_python:
894 obj.pybind_def(code)
895 code.write(target[0].abspath)
896
897def createEnumDecls(target, source, env):
898 assert len(target) == 1 and len(source) == 1
899
900 name = source[0].get_text_contents()
901 obj = all_enums[name]
902
903 code = code_formatter()
904 obj.cxx_decl(code)
905 code.write(target[0].abspath)
906
907def createSimObjectPyBindWrapper(target, source, env):
908 name = source[0].get_text_contents()
909 obj = sim_objects[name]
910
911 code = code_formatter()
912 obj.pybind_decl(code)
913 code.write(target[0].abspath)
914
915# Generate all of the SimObject param C++ struct header files
916params_hh_files = []
917for name,simobj in sorted(sim_objects.iteritems()):
918 py_source = PySource.modules[simobj.__module__]
919 extra_deps = [ py_source.tnode ]
920
921 hh_file = File('params/%s.hh' % name)
922 params_hh_files.append(hh_file)
923 env.Command(hh_file, Value(name),
924 MakeAction(createSimObjectParamStruct, Transform("SO PARAM")))
925 env.Depends(hh_file, depends + extra_deps)
926
927# C++ parameter description files
928if GetOption('with_cxx_config'):
929 for name,simobj in sorted(sim_objects.iteritems()):
930 py_source = PySource.modules[simobj.__module__]
931 extra_deps = [ py_source.tnode ]
932
933 cxx_config_hh_file = File('cxx_config/%s.hh' % name)
934 cxx_config_cc_file = File('cxx_config/%s.cc' % name)
935 env.Command(cxx_config_hh_file, Value(name),
936 MakeAction(createSimObjectCxxConfig(True),
937 Transform("CXXCPRHH")))
938 env.Command(cxx_config_cc_file, Value(name),
939 MakeAction(createSimObjectCxxConfig(False),
940 Transform("CXXCPRCC")))
941 env.Depends(cxx_config_hh_file, depends + extra_deps +
942 [File('params/%s.hh' % name), File('sim/cxx_config.hh')])
943 env.Depends(cxx_config_cc_file, depends + extra_deps +
944 [cxx_config_hh_file])
945 Source(cxx_config_cc_file)
946
947 cxx_config_init_cc_file = File('cxx_config/init.cc')
948
949 def createCxxConfigInitCC(target, source, env):
950 assert len(target) == 1 and len(source) == 1
951
952 code = code_formatter()
953
954 for name,simobj in sorted(sim_objects.iteritems()):
955 if not hasattr(simobj, 'abstract') or not simobj.abstract:
956 code('#include "cxx_config/${name}.hh"')
957 code()
958 code('void cxxConfigInit()')
959 code('{')
960 code.indent()
961 for name,simobj in sorted(sim_objects.iteritems()):
962 not_abstract = not hasattr(simobj, 'abstract') or \
963 not simobj.abstract
964 if not_abstract and 'type' in simobj.__dict__:
965 code('cxx_config_directory["${name}"] = '
966 '${name}CxxConfigParams::makeDirectoryEntry();')
967 code.dedent()
968 code('}')
969 code.write(target[0].abspath)
970
971 py_source = PySource.modules[simobj.__module__]
972 extra_deps = [ py_source.tnode ]
973 env.Command(cxx_config_init_cc_file, Value(name),
974 MakeAction(createCxxConfigInitCC, Transform("CXXCINIT")))
975 cxx_param_hh_files = ["cxx_config/%s.hh" % simobj
976 for name,simobj in sorted(sim_objects.iteritems())
977 if not hasattr(simobj, 'abstract') or not simobj.abstract]
978 Depends(cxx_config_init_cc_file, cxx_param_hh_files +
979 [File('sim/cxx_config.hh')])
980 Source(cxx_config_init_cc_file)
981
982# Generate all enum header files
983for name,enum in sorted(all_enums.iteritems()):
984 py_source = PySource.modules[enum.__module__]
985 extra_deps = [ py_source.tnode ]
986
987 cc_file = File('enums/%s.cc' % name)
988 env.Command(cc_file, [Value(name), Value(env['USE_PYTHON'])],
989 MakeAction(createEnumStrings, Transform("ENUM STR")))
990 env.Depends(cc_file, depends + extra_deps)
991 Source(cc_file)
992
993 hh_file = File('enums/%s.hh' % name)
994 env.Command(hh_file, Value(name),
995 MakeAction(createEnumDecls, Transform("ENUMDECL")))
996 env.Depends(hh_file, depends + extra_deps)
997
998# Generate SimObject Python bindings wrapper files
999if env['USE_PYTHON']:
1000 for name,simobj in sorted(sim_objects.iteritems()):
1001 py_source = PySource.modules[simobj.__module__]
1002 extra_deps = [ py_source.tnode ]
1003 cc_file = File('python/_m5/param_%s.cc' % name)
1004 env.Command(cc_file, Value(name),
1005 MakeAction(createSimObjectPyBindWrapper,
1006 Transform("SO PyBind")))
1007 env.Depends(cc_file, depends + extra_deps)
1008 Source(cc_file)
1009
1010# Build all protocol buffers if we have got protoc and protobuf available
1011if env['HAVE_PROTOBUF']:
1012 for proto in ProtoBuf.all:
1013 # Use both the source and header as the target, and the .proto
1014 # file as the source. When executing the protoc compiler, also
1015 # specify the proto_path to avoid having the generated files
1016 # include the path.
1017 env.Command([proto.cc_file, proto.hh_file], proto.tnode,
1018 MakeAction('$PROTOC --cpp_out ${TARGET.dir} '
1019 '--proto_path ${SOURCE.dir} $SOURCE',
1020 Transform("PROTOC")))
1021
1022 # Add the C++ source file
1023 Source(proto.cc_file, tags=proto.tags)
1024elif ProtoBuf.all:
1025 print('Got protobuf to build, but lacks support!')
1026 Exit(1)
1027
1028#
1029# Handle debug flags
1030#
1031def makeDebugFlagCC(target, source, env):
1032 assert(len(target) == 1 and len(source) == 1)
1033
1034 code = code_formatter()
1035
1036 # delay definition of CompoundFlags until after all the definition
1037 # of all constituent SimpleFlags
1038 comp_code = code_formatter()
1039
1040 # file header
1041 code('''
1042/*
1043 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1044 */
1045
1046#include "base/debug.hh"
1047
1048namespace Debug {
1049
1050''')
1051
1052 for name, flag in sorted(source[0].read().iteritems()):
1053 n, compound, desc = flag
1054 assert n == name
1055
1056 if not compound:
1057 code('SimpleFlag $name("$name", "$desc");')
1058 else:
1059 comp_code('CompoundFlag $name("$name", "$desc",')
1060 comp_code.indent()
1061 last = len(compound) - 1
1062 for i,flag in enumerate(compound):
1063 if i != last:
1064 comp_code('&$flag,')
1065 else:
1066 comp_code('&$flag);')
1067 comp_code.dedent()
1068
1069 code.append(comp_code)
1070 code()
1071 code('} // namespace Debug')
1072
1073 code.write(str(target[0]))
1074
1075def makeDebugFlagHH(target, source, env):
1076 assert(len(target) == 1 and len(source) == 1)
1077
1078 val = eval(source[0].get_contents())
1079 name, compound, desc = val
1080
1081 code = code_formatter()
1082
1083 # file header boilerplate
1084 code('''\
1085/*
1086 * DO NOT EDIT THIS FILE! Automatically generated by SCons.
1087 */
1088
1089#ifndef __DEBUG_${name}_HH__
1090#define __DEBUG_${name}_HH__
1091
1092namespace Debug {
1093''')
1094
1095 if compound:
1096 code('class CompoundFlag;')
1097 code('class SimpleFlag;')
1098
1099 if compound:
1100 code('extern CompoundFlag $name;')
1101 for flag in compound:
1102 code('extern SimpleFlag $flag;')
1103 else:
1104 code('extern SimpleFlag $name;')
1105
1106 code('''
1107}
1108
1109#endif // __DEBUG_${name}_HH__
1110''')
1111
1112 code.write(str(target[0]))
1113
1114for name,flag in sorted(debug_flags.iteritems()):
1115 n, compound, desc = flag
1116 assert n == name
1117
1118 hh_file = 'debug/%s.hh' % name
1119 env.Command(hh_file, Value(flag),
1120 MakeAction(makeDebugFlagHH, Transform("TRACING", 0)))
1121
1122env.Command('debug/flags.cc', Value(debug_flags),
1123 MakeAction(makeDebugFlagCC, Transform("TRACING", 0)))
1124Source('debug/flags.cc')
1125
1126# version tags
1127tags = \
1128env.Command('sim/tags.cc', None,
1129 MakeAction('util/cpt_upgrader.py --get-cc-file > $TARGET',
1130 Transform("VER TAGS")))
1131env.AlwaysBuild(tags)
1132
1133# Embed python files. All .py files that have been indicated by a
1134# PySource() call in a SConscript need to be embedded into the M5
1135# library. To do that, we compile the file to byte code, marshal the
1136# byte code, compress it, and then generate a c++ file that
1137# inserts the result into an array.
1138def embedPyFile(target, source, env):
1139 def c_str(string):
1140 if string is None:
1141 return "0"
1142 return '"%s"' % string
1143
1144 '''Action function to compile a .py into a code object, marshal
1145 it, compress it, and stick it into an asm file so the code appears
1146 as just bytes with a label in the data section'''
1147
1148 src = file(str(source[0]), 'r').read()
1149
1150 pysource = PySource.tnodes[source[0]]
1151 compiled = compile(src, pysource.abspath, 'exec')
1152 marshalled = marshal.dumps(compiled)
1153 compressed = zlib.compress(marshalled)
1154 data = compressed
1155 sym = pysource.symname
1156
1157 code = code_formatter()
1158 code('''\
1159#include "sim/init.hh"
1160
1161namespace {
1162
1163''')
1164 blobToCpp(data, 'data_' + sym, code)
1165 code('''\
1166
1167
1168EmbeddedPython embedded_${sym}(
1169 ${{c_str(pysource.arcname)}},
1170 ${{c_str(pysource.abspath)}},
1171 ${{c_str(pysource.modpath)}},
1172 data_${sym},
1173 ${{len(data)}},
1174 ${{len(marshalled)}});
1175
1176} // anonymous namespace
1177''')
1178 code.write(str(target[0]))
1179
1180for source in PySource.all:
1181 env.Command(source.cpp, source.tnode,
1182 MakeAction(embedPyFile, Transform("EMBED PY")))
1183 Source(source.cpp, tags=source.tags, add_tags='python')
1184
1185########################################################################
1186#
1187# Define binaries. Each different build type (debug, opt, etc.) gets
1188# a slightly different build environment.
1189#
1190
1191# List of constructed environments to pass back to SConstruct
1192date_source = Source('base/date.cc', tags=[])
1193
1194gem5_binary = Gem5('gem5')
1195
1196# Function to create a new build environment as clone of current
1197# environment 'env' with modified object suffix and optional stripped
1198# binary. Additional keyword arguments are appended to corresponding
1199# build environment vars.
1200def makeEnv(env, label, objsfx, strip=False, disable_partial=False, **kwargs):
1201 # SCons doesn't know to append a library suffix when there is a '.' in the
1202 # name. Use '_' instead.
1203 libname = 'gem5_' + label
1204 secondary_exename = 'm5.' + label
1205
1206 new_env = env.Clone(OBJSUFFIX=objsfx, SHOBJSUFFIX=objsfx + 's')
1207 new_env.Label = label
1208 new_env.Append(**kwargs)
1209
1210 lib_sources = Source.all.with_tag('gem5 lib')
1211
1212 # Without Python, leave out all Python content from the library
1213 # builds. The option doesn't affect gem5 built as a program
1214 if GetOption('without_python'):
1215 lib_sources = lib_sources.without_tag('python')
1216
1217 static_objs = []
1218 shared_objs = []
1219
1220 for s in lib_sources.with_tag(Source.ungrouped_tag):
1221 static_objs.append(s.static(new_env))
1222 shared_objs.append(s.shared(new_env))
1223
1224 for group in Source.source_groups:
1225 srcs = lib_sources.with_tag(Source.link_group_tag(group))
1226 if not srcs:
1227 continue
1228
1229 group_static = [ s.static(new_env) for s in srcs ]
1230 group_shared = [ s.shared(new_env) for s in srcs ]
1231
1232 # If partial linking is disabled, add these sources to the build
1233 # directly, and short circuit this loop.
1234 if disable_partial:
1235 static_objs.extend(group_static)
1236 shared_objs.extend(group_shared)
1237 continue
1238
1239 # Set up the static partially linked objects.
1240 file_name = new_env.subst("${OBJPREFIX}lib${OBJSUFFIX}.partial")
1241 target = File(joinpath(group, file_name))
1242 partial = env.PartialStatic(target=target, source=group_static)
1243 static_objs.extend(partial)
1244
1245 # Set up the shared partially linked objects.
1246 file_name = new_env.subst("${SHOBJPREFIX}lib${SHOBJSUFFIX}.partial")
1247 target = File(joinpath(group, file_name))
1248 partial = env.PartialShared(target=target, source=group_shared)
1249 shared_objs.extend(partial)
1250
1251 static_date = date_source.static(new_env)
1252 new_env.Depends(static_date, static_objs)
1253 static_objs.extend(static_date)
1254
1255 shared_date = date_source.shared(new_env)
1256 new_env.Depends(shared_date, shared_objs)
1257 shared_objs.extend(shared_date)
1258
1259 main_objs = [ s.static(new_env) for s in Source.all.with_tag('main') ]
1260
1261 # First make a library of everything but main() so other programs can
1262 # link against m5.
1263 static_lib = new_env.StaticLibrary(libname, static_objs)
1264 shared_lib = new_env.SharedLibrary(libname, shared_objs)
1265
1266 # Keep track of the object files generated so far so Executables can
1267 # include them.
1268 new_env['STATIC_OBJS'] = static_objs
1269 new_env['SHARED_OBJS'] = shared_objs
1270 new_env['MAIN_OBJS'] = main_objs
1271
1272 new_env['STATIC_LIB'] = static_lib
1273 new_env['SHARED_LIB'] = shared_lib
1274
1275 # Record some settings for building Executables.
1276 new_env['EXE_SUFFIX'] = label
1277 new_env['STRIP_EXES'] = strip
1278
1279 for cls in ExecutableMeta.all:
1280 cls.declare_all(new_env)
1281
1282 new_env.M5Binary = File(gem5_binary.path(new_env))
1283
1284 new_env.Command(secondary_exename, new_env.M5Binary,
1285 MakeAction('ln $SOURCE $TARGET', Transform("HARDLINK")))
1286
1287 # Set up regression tests.
1288 SConscript(os.path.join(env.root.abspath, 'tests', 'SConscript'),
1289 variant_dir=Dir('tests').Dir(new_env.Label),
1290 exports={ 'env' : new_env }, duplicate=False)
1291
1292# Start out with the compiler flags common to all compilers,
1293# i.e. they all use -g for opt and -g -pg for prof
1294ccflags = {'debug' : [], 'opt' : ['-g'], 'fast' : [], 'prof' : ['-g', '-pg'],
1295 'perf' : ['-g']}
1296
1297# Start out with the linker flags common to all linkers, i.e. -pg for
1298# prof, and -lprofiler for perf. The -lprofile flag is surrounded by
1299# no-as-needed and as-needed as the binutils linker is too clever and
1300# simply doesn't link to the library otherwise.
1301ldflags = {'debug' : [], 'opt' : [], 'fast' : [], 'prof' : ['-pg'],
1302 'perf' : ['-Wl,--no-as-needed', '-lprofiler', '-Wl,--as-needed']}
1303
1304# For Link Time Optimization, the optimisation flags used to compile
1305# individual files are decoupled from those used at link time
1306# (i.e. you can compile with -O3 and perform LTO with -O0), so we need
1307# to also update the linker flags based on the target.
1308if env['GCC']:
1309 if sys.platform == 'sunos5':
1310 ccflags['debug'] += ['-gstabs+']
1311 else:
1312 ccflags['debug'] += ['-ggdb3']
1313 ldflags['debug'] += ['-O0']
1314 # opt, fast, prof and perf all share the same cc flags, also add
1315 # the optimization to the ldflags as LTO defers the optimization
1316 # to link time
1317 for target in ['opt', 'fast', 'prof', 'perf']:
1318 ccflags[target] += ['-O3']
1319 ldflags[target] += ['-O3']
1320
1321 ccflags['fast'] += env['LTO_CCFLAGS']
1322 ldflags['fast'] += env['LTO_LDFLAGS']
1323elif env['CLANG']:
1324 ccflags['debug'] += ['-g', '-O0']
1325 # opt, fast, prof and perf all share the same cc flags
1326 for target in ['opt', 'fast', 'prof', 'perf']:
1327 ccflags[target] += ['-O3']
1328else:
1329 print('Unknown compiler, please fix compiler options')
1330 Exit(1)
1331
1332
1333# To speed things up, we only instantiate the build environments we
1334# need. We try to identify the needed environment for each target; if
1335# we can't, we fall back on instantiating all the environments just to
1336# be safe.
1337target_types = ['debug', 'opt', 'fast', 'prof', 'perf']
1338obj2target = {'do': 'debug', 'o': 'opt', 'fo': 'fast', 'po': 'prof',
1339 'gpo' : 'perf'}
1340
1341def identifyTarget(t):
1342 ext = t.split('.')[-1]
1343 if ext in target_types:
1344 return ext
1345 if ext in obj2target:
1346 return obj2target[ext]
1347 match = re.search(r'/tests/([^/]+)/', t)
1348 if match and match.group(1) in target_types:
1349 return match.group(1)
1350 return 'all'
1351
1352needed_envs = [identifyTarget(target) for target in BUILD_TARGETS]
1353if 'all' in needed_envs:
1354 needed_envs += target_types
1355
1356disable_partial = False
1357if env['PLATFORM'] == 'darwin':
1358 # Up until Apple LLVM version 10.0.0 (clang-1000.11.45.5), partial
1359 # linked objects do not expose symbols that are marked with the
1360 # hidden visibility and consequently building gem5 on Mac OS
1361 # fails. As a workaround, we disable partial linking, however, we
1362 # may want to revisit in the future.
1363 disable_partial = True
1364
1365# Debug binary
1366if 'debug' in needed_envs:
1367 makeEnv(env, 'debug', '.do',
1368 CCFLAGS = Split(ccflags['debug']),
1369 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'],
1370 LINKFLAGS = Split(ldflags['debug']),
1371 disable_partial=disable_partial)
1372
1373# Optimized binary
1374if 'opt' in needed_envs:
1375 makeEnv(env, 'opt', '.o',
1376 CCFLAGS = Split(ccflags['opt']),
1377 CPPDEFINES = ['TRACING_ON=1'],
1378 LINKFLAGS = Split(ldflags['opt']),
1379 disable_partial=disable_partial)
1380
1381# "Fast" binary
1382if 'fast' in needed_envs:
1383 disable_partial = disable_partial and \
1384 env.get('BROKEN_INCREMENTAL_LTO', False) and \
1385 GetOption('force_lto')
1386 makeEnv(env, 'fast', '.fo', strip = True,
1387 CCFLAGS = Split(ccflags['fast']),
1388 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1389 LINKFLAGS = Split(ldflags['fast']),
1390 disable_partial=disable_partial)
1391
1392# Profiled binary using gprof
1393if 'prof' in needed_envs:
1394 makeEnv(env, 'prof', '.po',
1395 CCFLAGS = Split(ccflags['prof']),
1396 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1397 LINKFLAGS = Split(ldflags['prof']),
1398 disable_partial=disable_partial)
1399
1400# Profiled binary using google-pprof
1401if 'perf' in needed_envs:
1402 makeEnv(env, 'perf', '.gpo',
1403 CCFLAGS = Split(ccflags['perf']),
1404 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
1405 LINKFLAGS = Split(ldflags['perf']),
1406 disable_partial=disable_partial)