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
31import imp
32import os
33import sys
34
35from os.path import basename
36from os.path import isdir, join as joinpath
37from os.path import exists
38from os.path import isdir
39from os.path import isfile
35from os.path import basename, exists, isdir, isfile, join as joinpath
36
37import 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
42Import('*')
43
44# Children need to see the environment
45Export('env')
46
47def 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
56class 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#
83cc_sources = []
84def 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
91py_sources = []
92def 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
100sim_objects_fixed = False
101sim_object_modfiles = set()
102def 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
118swig_sources = []
119def 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
127Export('Source')
128Export('PySource')
129Export('SimObject')
130Export('SwigSource')
131
132########################################################################
133#
134# Trace Flags
135#
136all_flags = {}
137trace_flags = []
138def 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
145def 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
161Export('TraceFlag')
162Export('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.
173env.Append(CPPPATH=Dir('.'))
174
175# Add a flag defining what THE_ISA should be for all compilation
176env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())])
177
178########################################################################
179#
180# Walk the tree and execute all SConscripts in 'src' subdirectories
181#
182
183for base_dir in base_dir_list:
184 src_dir = joinpath(base_dir, 'src')
185 if not isdir(src_dir):
186 continue
187 here = Dir('.').srcnode().abspath
188 for root, dirs, files in os.walk(src_dir, topdown=True):
189 if root == here:
190 # we don't want to recurse back into this SConscript
191 continue
192
193 if 'SConscript' in files:
194 build_dir = joinpath(env['BUILDDIR'], root[len(src_dir) + 1:])
195 SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
196
197for opt in env.ExportOptions:
198 env.ConfigFile(opt)
199
200########################################################################
201#
202# Prevent any SimObjects from being added after this point, they
203# should all have been added in the SConscripts above
204#
205sim_objects_fixed = True
206
207########################################################################
208#
209# Manually turn python/generate.py into a python module and import it
210#
211generate_file = File('python/generate.py')
212generate_module = imp.new_module('generate')
213sys.modules['generate'] = generate_module
214exec file(generate_file.srcnode().abspath, 'r') in generate_module.__dict__
215
216########################################################################
217#
218# build a generate
219#
220from generate import Generate
221optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions])
222generate = Generate(py_sources, sim_object_modfiles, optionDict)
223m5 = generate.m5
224
225########################################################################
226#
227# calculate extra dependencies
228#
229module_depends = ["m5", "m5.SimObject", "m5.params"]
230module_depends = [ File(generate.py_modules[dep]) for dep in module_depends ]
231file_depends = [ generate_file ]
232depends = module_depends + file_depends
233
234########################################################################
235#
236# Commands for the basic automatically generated python files
237#
238
239# Generate a file with all of the compile options in it
240env.Command('python/m5/defines.py', Value(optionDict),
241 generate.makeDefinesPyFile)
242PySource('m5', 'python/m5/defines.py')
243
244# Generate a file that wraps the basic top level files
245env.Command('python/m5/info.py',
246 [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
247 generate.makeInfoPyFile)
248PySource('m5', 'python/m5/info.py')
249
250# Generate an __init__.py file for the objects package
251env.Command('python/m5/objects/__init__.py',
252 [ Value(o) for o in sort_list(sim_object_modfiles) ],
253 generate.makeObjectsInitFile)
254PySource('m5.objects', 'python/m5/objects/__init__.py')
255
256########################################################################
257#
258# Create all of the SimObject param headers and enum headers
259#
260
261# Generate all of the SimObject param struct header files
262params_hh_files = []
263for name,simobj in generate.sim_objects.iteritems():
264 extra_deps = [ File(generate.py_modules[simobj.__module__]) ]
265
266 hh_file = File('params/%s.hh' % name)
267 params_hh_files.append(hh_file)
268 env.Command(hh_file, Value(name), generate.createSimObjectParam)
269 env.Depends(hh_file, depends + extra_deps)
270
271# Generate any parameter header files needed
272for name,param in generate.params.iteritems():
273 if isinstance(param, m5.params.VectorParamDesc):
274 ext = 'vptype'
275 else:
276 ext = 'ptype'
277
278 i_file = File('params/%s_%s.i' % (name, ext))
279 env.Command(i_file, Value(name), generate.createSwigParam)
280 env.Depends(i_file, depends)
281
282# Generate all enum header files
283for name,enum in generate.enums.iteritems():
284 extra_deps = [ File(generate.py_modules[enum.__module__]) ]
285
286 cc_file = File('enums/%s.cc' % name)
287 env.Command(cc_file, Value(name), generate.createEnumStrings)
288 env.Depends(cc_file, depends + extra_deps)
289 Source(cc_file)
290
291 hh_file = File('enums/%s.hh' % name)
292 env.Command(hh_file, Value(name), generate.createEnumParam)
293 env.Depends(hh_file, depends + extra_deps)
294
295# Build the big monolithic swigged params module (wraps all SimObject
296# param structs and enum structs)
297params_file = File('params/params.i')
298names = sort_list(generate.sim_objects.keys())
299env.Command(params_file, [ Value(v) for v in names ],
300 generate.buildParams)
301env.Depends(params_file, params_hh_files + depends)
302SwigSource('m5.objects', params_file)
303
304# Build all swig modules
305swig_modules = []
306for source,package in swig_sources:
307 filename = str(source)
308 assert filename.endswith('.i')
309
310 base = '.'.join(filename.split('.')[:-1])
311 module = basename(base)
312 cc_file = base + '_wrap.cc'
313 py_file = base + '.py'
314
315 env.Command([cc_file, py_file], source,
316 '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
317 '-o ${TARGETS[0]} $SOURCES')
318 env.Depends(py_file, source)
319 env.Depends(cc_file, source)
320
321 swig_modules.append(Value(module))
322 Source(cc_file)
323 PySource(package, py_file)
324
325# Generate the main swig init file
326env.Command('swig/init.cc', swig_modules, generate.makeSwigInit)
327Source('swig/init.cc')
328
329# Generate traceflags.py
330flags = [ Value(f) for f in trace_flags ]
331env.Command('base/traceflags.py', flags, generate.traceFlagsPy)
332PySource('m5', 'base/traceflags.py')
333
334env.Command('base/traceflags.hh', flags, generate.traceFlagsHH)
335env.Command('base/traceflags.cc', flags, generate.traceFlagsCC)
336Source('base/traceflags.cc')
337
338# Build the zip file
339py_compiled = []
340py_zip_depends = []
341for source in py_sources:
342 env.Command(source.compiled, source.source, generate.compilePyFile)
343 py_compiled.append(source.compiled)
344
345 # make the zipfile depend on the archive name so that the archive
346 # is rebuilt if the name changes
347 py_zip_depends.append(Value(source.arcname))
348
349# Add the zip file target to the environment.
350m5zip = File('m5py.zip')
351env.Command(m5zip, py_compiled, generate.buildPyZip)
352env.Depends(m5zip, py_zip_depends)
353
354########################################################################
355#
356# Define binaries. Each different build type (debug, opt, etc.) gets
357# a slightly different build environment.
358#
359
360# List of constructed environments to pass back to SConstruct
361envList = []
362
363# This function adds the specified sources to the given build
364# environment, and returns a list of all the corresponding SCons
365# Object nodes (including an extra one for date.cc). We explicitly
366# add the Object nodes so we can set up special dependencies for
367# date.cc.
368def make_objs(sources, env):
369 objs = [env.Object(s) for s in sources]
370 # make date.cc depend on all other objects so it always gets
371 # recompiled whenever anything else does
372 date_obj = env.Object('base/date.cc')
373 env.Depends(date_obj, objs)
374 objs.append(date_obj)
375 return objs
376
377# Function to create a new build environment as clone of current
378# environment 'env' with modified object suffix and optional stripped
379# binary. Additional keyword arguments are appended to corresponding
380# build environment vars.
381def makeEnv(label, objsfx, strip = False, **kwargs):
382 newEnv = env.Copy(OBJSUFFIX=objsfx)
383 newEnv.Label = label
384 newEnv.Append(**kwargs)
385 exe = 'm5.' + label # final executable
386 bin = exe + '.bin' # executable w/o appended Python zip archive
387 newEnv.Program(bin, make_objs(cc_sources, newEnv))
388 if strip:
389 stripped_bin = bin + '.stripped'
390 if sys.platform == 'sunos5':
391 cmd = 'cp $SOURCE $TARGET; strip $TARGET'
392 else:
393 cmd = 'strip $SOURCE -o $TARGET'
394 newEnv.Command(stripped_bin, bin, cmd)
395 bin = stripped_bin
396 targets = newEnv.Concat(exe, [bin, 'm5py.zip'])
397 newEnv.M5Binary = targets[0]
398 envList.append(newEnv)
399
400# Debug binary
401ccflags = {}
402if env['GCC']:
403 if sys.platform == 'sunos5':
404 ccflags['debug'] = '-gstabs+'
405 else:
406 ccflags['debug'] = '-ggdb3'
407 ccflags['opt'] = '-g -O3'
408 ccflags['fast'] = '-O3'
409 ccflags['prof'] = '-O3 -g -pg'
410elif env['SUNCC']:
411 ccflags['debug'] = '-g0'
412 ccflags['opt'] = '-g -O'
413 ccflags['fast'] = '-fast'
414 ccflags['prof'] = '-fast -g -pg'
415elif env['ICC']:
416 ccflags['debug'] = '-g -O0'
417 ccflags['opt'] = '-g -O'
418 ccflags['fast'] = '-fast'
419 ccflags['prof'] = '-fast -g -pg'
420else:
421 print 'Unknown compiler, please fix compiler options'
422 Exit(1)
423
424makeEnv('debug', '.do',
425 CCFLAGS = Split(ccflags['debug']),
426 CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
427
428# Optimized binary
429makeEnv('opt', '.o',
430 CCFLAGS = Split(ccflags['opt']),
431 CPPDEFINES = ['TRACING_ON=1'])
432
433# "Fast" binary
434makeEnv('fast', '.fo', strip = True,
435 CCFLAGS = Split(ccflags['fast']),
436 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
437
438# Profiled binary
439makeEnv('prof', '.po',
440 CCFLAGS = Split(ccflags['prof']),
441 CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
442 LINKFLAGS = '-pg')
443
444Return('envList')