e360611a07a329ea15b67311839a5b6e299f91be
[gem5.git] / src / SConscript
1 # -*- mode:python -*-
2
3 # Copyright (c) 2004-2005 The Regents of The University of Michigan
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions are
8 # met: redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer;
10 # redistributions in binary form must reproduce the above copyright
11 # notice, this list of conditions and the following disclaimer in the
12 # documentation and/or other materials provided with the distribution;
13 # neither the name of the copyright holders nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 #
29 # Authors: Nathan Binkert
30
31 import imp
32 import os
33 import sys
34
35 from os.path import basename, exists, isdir, isfile, join as joinpath
36
37 import SCons
38
39 # This file defines how to build a particular configuration of M5
40 # based on variable settings in the 'env' build environment.
41
42 Import('*')
43
44 # Children need to see the environment
45 Export('env')
46
47 def sort_list(_list):
48 """return a sorted copy of '_list'"""
49 if isinstance(_list, list):
50 _list = _list[:]
51 else:
52 _list = list(_list)
53 _list.sort()
54 return _list
55
56 class PySourceFile(object):
57 def __init__(self, package, source):
58 filename = str(source)
59 pyname = basename(filename)
60 assert pyname.endswith('.py')
61 name = pyname[:-3]
62 path = package.split('.')
63 modpath = path
64 if name != '__init__':
65 modpath += [name]
66 modpath = '.'.join(modpath)
67
68 arcpath = package.split('.') + [ pyname + 'c' ]
69 arcname = joinpath(*arcpath)
70
71 self.source = source
72 self.pyname = pyname
73 self.srcpath = source.srcnode().abspath
74 self.package = package
75 self.modpath = modpath
76 self.arcname = arcname
77 self.filename = filename
78 self.compiled = File(filename + 'c')
79
80 ########################################################################
81 # Code for adding source files of various types
82 #
83 cc_sources = []
84 def Source(source):
85 '''Add a C/C++ source file to the build'''
86 if not isinstance(source, SCons.Node.FS.File):
87 source = File(source)
88
89 cc_sources.append(source)
90
91 py_sources = []
92 def PySource(package, source):
93 '''Add a python source file to the named package'''
94 if not isinstance(source, SCons.Node.FS.File):
95 source = File(source)
96
97 source = PySourceFile(package, source)
98 py_sources.append(source)
99
100 sim_objects_fixed = False
101 sim_object_modfiles = set()
102 def SimObject(source):
103 '''Add a SimObject python file as a python source object and add
104 it to a list of sim object modules'''
105
106 if sim_objects_fixed:
107 raise AttributeError, "Too late to call SimObject now."
108
109 if not isinstance(source, SCons.Node.FS.File):
110 source = File(source)
111
112 PySource('m5.objects', source)
113 modfile = basename(str(source))
114 assert modfile.endswith('.py')
115 modname = modfile[:-3]
116 sim_object_modfiles.add(modname)
117
118 swig_sources = []
119 def SwigSource(package, source):
120 '''Add a swig file to build'''
121 if not isinstance(source, SCons.Node.FS.File):
122 source = File(source)
123 val = source,package
124 swig_sources.append(val)
125
126 # Children should have access
127 Export('Source')
128 Export('PySource')
129 Export('SimObject')
130 Export('SwigSource')
131
132 ########################################################################
133 #
134 # Trace Flags
135 #
136 all_flags = {}
137 trace_flags = []
138 def TraceFlag(name, desc=''):
139 if name in all_flags:
140 raise AttributeError, "Flag %s already specified" % name
141 flag = (name, (), desc)
142 trace_flags.append(flag)
143 all_flags[name] = ()
144
145 def CompoundFlag(name, flags, desc=''):
146 if name in all_flags:
147 raise AttributeError, "Flag %s already specified" % name
148
149 compound = tuple(flags)
150 for flag in compound:
151 if flag not in all_flags:
152 raise AttributeError, "Trace flag %s not found" % flag
153 if all_flags[flag]:
154 raise AttributeError, \
155 "Compound flag can't point to another compound flag"
156
157 flag = (name, compound, desc)
158 trace_flags.append(flag)
159 all_flags[name] = compound
160
161 Export('TraceFlag')
162 Export('CompoundFlag')
163
164 ########################################################################
165 #
166 # Set some compiler variables
167 #
168
169 # Include file paths are rooted in this directory. SCons will
170 # automatically expand '.' to refer to both the source directory and
171 # the corresponding build directory to pick up generated include
172 # files.
173 env.Append(CPPPATH=Dir('.'))
174
175 # Add a flag defining what THE_ISA should be for all compilation
176 env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())])
177
178 ########################################################################
179 #
180 # Walk the tree and execute all SConscripts in subdirectories
181 #
182
183 for base_dir in base_dir_list:
184 here = Dir('.').srcnode().abspath
185 for root, dirs, files in os.walk(base_dir, topdown=True):
186 if root == here:
187 # we don't want to recurse back into this SConscript
188 continue
189
190 if 'SConscript' in files:
191 build_dir = joinpath(env['BUILDDIR'], root[len(base_dir) + 1:])
192 SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
193
194 for opt in env.ExportOptions:
195 env.ConfigFile(opt)
196
197 ########################################################################
198 #
199 # Prevent any SimObjects from being added after this point, they
200 # should all have been added in the SConscripts above
201 #
202 sim_objects_fixed = True
203
204 ########################################################################
205 #
206 # Manually turn python/generate.py into a python module and import it
207 #
208 generate_file = File('python/generate.py')
209 generate_module = imp.new_module('generate')
210 sys.modules['generate'] = generate_module
211 exec file(generate_file.srcnode().abspath, 'r') in generate_module.__dict__
212
213 ########################################################################
214 #
215 # build a generate
216 #
217 from generate import Generate
218 optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions])
219 generate = Generate(py_sources, sim_object_modfiles, optionDict)
220 m5 = generate.m5
221
222 ########################################################################
223 #
224 # calculate extra dependencies
225 #
226 module_depends = ["m5", "m5.SimObject", "m5.params"]
227 module_depends = [ File(generate.py_modules[dep]) for dep in module_depends ]
228 file_depends = [ generate_file ]
229 depends = module_depends + file_depends
230
231 ########################################################################
232 #
233 # Commands for the basic automatically generated python files
234 #
235
236 # Generate a file with all of the compile options in it
237 env.Command('python/m5/defines.py', Value(optionDict),
238 generate.makeDefinesPyFile)
239 PySource('m5', 'python/m5/defines.py')
240
241 # Generate a file that wraps the basic top level files
242 env.Command('python/m5/info.py',
243 [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
244 generate.makeInfoPyFile)
245 PySource('m5', 'python/m5/info.py')
246
247 # Generate an __init__.py file for the objects package
248 env.Command('python/m5/objects/__init__.py',
249 [ Value(o) for o in sort_list(sim_object_modfiles) ],
250 generate.makeObjectsInitFile)
251 PySource('m5.objects', 'python/m5/objects/__init__.py')
252
253 ########################################################################
254 #
255 # Create all of the SimObject param headers and enum headers
256 #
257
258 # Generate all of the SimObject param struct header files
259 params_hh_files = []
260 for name,simobj in generate.sim_objects.iteritems():
261 extra_deps = [ File(generate.py_modules[simobj.__module__]) ]
262
263 hh_file = File('params/%s.hh' % name)
264 params_hh_files.append(hh_file)
265 env.Command(hh_file, Value(name), generate.createSimObjectParam)
266 env.Depends(hh_file, depends + extra_deps)
267
268 # Generate any parameter header files needed
269 for name,param in generate.params.iteritems():
270 if isinstance(param, m5.params.VectorParamDesc):
271 ext = 'vptype'
272 else:
273 ext = 'ptype'
274
275 i_file = File('params/%s_%s.i' % (name, ext))
276 env.Command(i_file, Value(name), generate.createSwigParam)
277 env.Depends(i_file, depends)
278
279 # Generate all enum header files
280 for name,enum in generate.enums.iteritems():
281 extra_deps = [ File(generate.py_modules[enum.__module__]) ]
282
283 cc_file = File('enums/%s.cc' % name)
284 env.Command(cc_file, Value(name), generate.createEnumStrings)
285 env.Depends(cc_file, depends + extra_deps)
286 Source(cc_file)
287
288 hh_file = File('enums/%s.hh' % name)
289 env.Command(hh_file, Value(name), generate.createEnumParam)
290 env.Depends(hh_file, depends + extra_deps)
291
292 # Build the big monolithic swigged params module (wraps all SimObject
293 # param structs and enum structs)
294 params_file = File('params/params.i')
295 names = sort_list(generate.sim_objects.keys())
296 env.Command(params_file, [ Value(v) for v in names ],
297 generate.buildParams)
298 env.Depends(params_file, params_hh_files + depends)
299 SwigSource('m5.objects', params_file)
300
301 # Build all swig modules
302 swig_modules = []
303 for source,package in swig_sources:
304 filename = str(source)
305 assert filename.endswith('.i')
306
307 base = '.'.join(filename.split('.')[:-1])
308 module = basename(base)
309 cc_file = base + '_wrap.cc'
310 py_file = base + '.py'
311
312 env.Command([cc_file, py_file], source,
313 '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
314 '-o ${TARGETS[0]} $SOURCES')
315 env.Depends(py_file, source)
316 env.Depends(cc_file, source)
317
318 swig_modules.append(Value(module))
319 Source(cc_file)
320 PySource(package, py_file)
321
322 # Generate the main swig init file
323 env.Command('swig/init.cc', swig_modules, generate.makeSwigInit)
324 Source('swig/init.cc')
325
326 # Generate traceflags.py
327 flags = [ Value(f) for f in trace_flags ]
328 env.Command('base/traceflags.py', flags, generate.traceFlagsPy)
329 PySource('m5', 'base/traceflags.py')
330
331 env.Command('base/traceflags.hh', flags, generate.traceFlagsHH)
332 env.Command('base/traceflags.cc', flags, generate.traceFlagsCC)
333 Source('base/traceflags.cc')
334
335 # Generate program_info.cc
336 env.Command('base/program_info.cc',
337 Value(str(SCons.Node.FS.default_fs.SConstruct_dir)), generate.programInfo)
338
339 # Build the zip file
340 py_compiled = []
341 py_zip_depends = []
342 for source in py_sources:
343 env.Command(source.compiled, source.source, generate.compilePyFile)
344 py_compiled.append(source.compiled)
345
346 # make the zipfile depend on the archive name so that the archive
347 # is rebuilt if the name changes
348 py_zip_depends.append(Value(source.arcname))
349
350 # Add the zip file target to the environment.
351 m5zip = File('m5py.zip')
352 env.Command(m5zip, py_compiled, generate.buildPyZip)
353 env.Depends(m5zip, py_zip_depends)
354
355 ########################################################################
356 #
357 # Define binaries. Each different build type (debug, opt, etc.) gets
358 # a slightly different build environment.
359 #
360
361 # List of constructed environments to pass back to SConstruct
362 envList = []
363
364 # This function adds the specified sources to the given build
365 # environment, and returns a list of all the corresponding SCons
366 # Object nodes (including an extra one for date.cc). We explicitly
367 # add the Object nodes so we can set up special dependencies for
368 # date.cc.
369 def make_objs(sources, env):
370 objs = [env.Object(s) for s in sources]
371
372 # make date.cc depend on all other objects so it always gets
373 # recompiled whenever anything else does
374 date_obj = env.Object('base/date.cc')
375
376 # Make the generation of program_info.cc dependend on all
377 # the other cc files and the compiling of program_info.cc
378 # dependent on all the objects but program_info.o
379 pinfo_obj = env.Object('base/program_info.cc')
380 env.Depends('base/program_info.cc', sources)
381 env.Depends(date_obj, objs)
382 env.Depends(pinfo_obj, objs)
383 objs.extend([date_obj,pinfo_obj])
384 return objs
385
386 # Function to create a new build environment as clone of current
387 # environment 'env' with modified object suffix and optional stripped
388 # binary. Additional keyword arguments are appended to corresponding
389 # build environment vars.
390 def makeEnv(label, objsfx, strip = False, **kwargs):
391 newEnv = env.Copy(OBJSUFFIX=objsfx)
392 newEnv.Label = label
393 newEnv.Append(**kwargs)
394 exe = 'm5.' + label # final executable
395 bin = exe + '.bin' # executable w/o appended Python zip archive
396 newEnv.Program(bin, make_objs(cc_sources, newEnv))
397 if strip:
398 stripped_bin = bin + '.stripped'
399 if sys.platform == 'sunos5':
400 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
401 else:
402 cmd = 'strip $SOURCE -o $TARGET'
403 newEnv.Command(stripped_bin, bin, cmd)
404 bin = stripped_bin
405 targets = newEnv.Concat(exe, [bin, 'm5py.zip'])
406 newEnv.M5Binary = targets[0]
407 envList.append(newEnv)
408
409 # Debug binary
410 ccflags = {}
411 if env['GCC']:
412 if sys.platform == 'sunos5':
413 ccflags['debug'] = '-gstabs+'
414 else:
415 ccflags['debug'] = '-ggdb3'
416 ccflags['opt'] = '-g -O3'
417 ccflags['fast'] = '-O3'
418 ccflags['prof'] = '-O3 -g -pg'
419 elif env['SUNCC']:
420 ccflags['debug'] = '-g0'
421 ccflags['opt'] = '-g -O'
422 ccflags['fast'] = '-fast'
423 ccflags['prof'] = '-fast -g -pg'
424 elif env['ICC']:
425 ccflags['debug'] = '-g -O0'
426 ccflags['opt'] = '-g -O'
427 ccflags['fast'] = '-fast'
428 ccflags['prof'] = '-fast -g -pg'
429 else:
430 print 'Unknown compiler, please fix compiler options'
431 Exit(1)
432
433 makeEnv('debug', '.do',
434 CCFLAGS = Split(ccflags['debug']),
435 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
436
437 # Optimized binary
438 makeEnv('opt', '.o',
439 CCFLAGS = Split(ccflags['opt']),
440 CPPDEFINES = ['TRACING_ON=1'])
441
442 # "Fast" binary
443 makeEnv('fast', '.fo', strip = True,
444 CCFLAGS = Split(ccflags['fast']),
445 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
446
447 # Profiled binary
448 makeEnv('prof', '.po',
449 CCFLAGS = Split(ccflags['prof']),
450 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
451 LINKFLAGS = '-pg')
452
453 Return('envList')