SConscript revision 5067:eba818456aa9
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 join as joinpath
37from os.path import exists
38from os.path import isdir
39from os.path import isfile
40
41import SCons
42
43# This file defines how to build a particular configuration of M5
44# based on variable settings in the 'env' build environment.
45
46Import('*')
47
48# Children need to see the environment
49Export('env')
50
51def sort_list(_list):
52    """return a sorted copy of '_list'"""
53    if isinstance(_list, list):
54        _list = _list[:]
55    else:
56        _list = list(_list)
57    _list.sort()
58    return _list
59
60class PySourceFile(object):
61    def __init__(self, package, source):
62        filename = str(source)
63        pyname = basename(filename)
64        assert pyname.endswith('.py')
65        name = pyname[:-3]
66        path = package.split('.')
67        modpath = path
68        if name != '__init__':
69            modpath += [name]
70        modpath = '.'.join(modpath)
71
72        arcpath = package.split('.') + [ pyname + 'c' ]
73        arcname = joinpath(*arcpath)
74
75        self.source = source
76        self.pyname = pyname
77        self.srcpath = source.srcnode().abspath
78        self.package = package
79        self.modpath = modpath
80        self.arcname = arcname
81        self.filename = filename
82        self.compiled = File(filename + 'c')
83
84########################################################################
85# Code for adding source files of various types
86#
87cc_sources = []
88def Source(source):
89    '''Add a C/C++ source file to the build'''
90    if not isinstance(source, SCons.Node.FS.File):
91        source = File(source)
92
93    cc_sources.append(source)
94
95py_sources = []
96def PySource(package, source):
97    '''Add a python source file to the named package'''
98    if not isinstance(source, SCons.Node.FS.File):
99        source = File(source)
100
101    source = PySourceFile(package, source)
102    py_sources.append(source)
103
104sim_objects_fixed = False
105sim_object_modfiles = set()
106def SimObject(source):
107    '''Add a SimObject python file as a python source object and add
108    it to a list of sim object modules'''
109
110    if sim_objects_fixed:
111        raise AttributeError, "Too late to call SimObject now."
112
113    if not isinstance(source, SCons.Node.FS.File):
114        source = File(source)
115
116    PySource('m5.objects', source)
117    modfile = basename(str(source))
118    assert modfile.endswith('.py')
119    modname = modfile[:-3]
120    sim_object_modfiles.add(modname)
121
122swig_sources = []
123def SwigSource(package, source):
124    '''Add a swig file to build'''
125    if not isinstance(source, SCons.Node.FS.File):
126        source = File(source)
127    val = source,package
128    swig_sources.append(val)
129
130# Children should have access
131Export('Source')
132Export('PySource')
133Export('SimObject')
134Export('SwigSource')
135
136########################################################################
137#
138# Set some compiler variables
139#
140
141# Include file paths are rooted in this directory.  SCons will
142# automatically expand '.' to refer to both the source directory and
143# the corresponding build directory to pick up generated include
144# files.
145env.Append(CPPPATH=Dir('.'))
146
147# Add a flag defining what THE_ISA should be for all compilation
148env.Append(CPPDEFINES=[('THE_ISA','%s_ISA' % env['TARGET_ISA'].upper())])
149
150########################################################################
151#
152# Walk the tree and execute all SConscripts
153#
154srcdir = env['SRCDIR']
155for root, dirs, files in os.walk(srcdir, topdown=True):
156    if root == srcdir:
157        # we don't want to recurse back into this SConscript
158        continue
159
160    if 'SConscript' in files:
161        # strip off the srcdir part since scons will try to find the
162        # script in the build directory
163        base = root[len(srcdir) + 1:]
164        SConscript(joinpath(base, 'SConscript'))
165
166for extra in env['EXTRAS'].split(':'):
167    extra = os.path.expanduser(extra)
168    extra = os.path.normpath(extra)
169    env.Append(CPPPATH=[Dir(extra)])
170    for root, dirs, files in os.walk(extra, topdown=True):
171        if 'SConscript' in files:
172            subdir = root[len(os.path.dirname(extra))+1:]
173            build_dir = joinpath(env['BUILDDIR'], subdir)
174            SConscript(joinpath(root, 'SConscript'), build_dir=build_dir)
175
176for opt in env.ExportOptions:
177    env.ConfigFile(opt)
178
179########################################################################
180#
181# Prevent any SimObjects from being added after this point, they
182# should all have been added in the SConscripts above
183#
184sim_objects_fixed = True
185
186########################################################################
187#
188# Manually turn python/generate.py into a python module and import it
189#
190generate_file = File('python/generate.py')
191generate_module = imp.new_module('generate')
192sys.modules['generate'] = generate_module
193exec file(generate_file.srcnode().abspath, 'r') in generate_module.__dict__
194
195########################################################################
196#
197# build a generate
198#
199from generate import Generate
200optionDict = dict([(opt, env[opt]) for opt in env.ExportOptions])
201generate = Generate(py_sources, sim_object_modfiles, optionDict)
202m5 = generate.m5
203
204########################################################################
205#
206# calculate extra dependencies
207#
208module_depends = ["m5", "m5.SimObject", "m5.params"]
209module_depends = [ File(generate.py_modules[dep]) for dep in module_depends ]
210file_depends = [ generate_file ]
211depends = module_depends + file_depends
212
213########################################################################
214#
215# Commands for the basic automatically generated python files
216#
217
218# Generate a file with all of the compile options in it
219env.Command('python/m5/defines.py', Value(optionDict),
220            generate.makeDefinesPyFile)
221PySource('m5', 'python/m5/defines.py')
222
223# Generate a file that wraps the basic top level files
224env.Command('python/m5/info.py',
225            [ '#/AUTHORS', '#/LICENSE', '#/README', '#/RELEASE_NOTES' ],
226            generate.makeInfoPyFile)
227PySource('m5', 'python/m5/info.py')
228
229# Generate an __init__.py file for the objects package
230env.Command('python/m5/objects/__init__.py',
231            [ Value(o) for o in sort_list(sim_object_modfiles) ],
232            generate.makeObjectsInitFile)
233PySource('m5.objects', 'python/m5/objects/__init__.py')
234
235########################################################################
236#
237# Create all of the SimObject param headers and enum headers
238#
239
240# Generate all of the SimObject param struct header files
241params_hh_files = []
242for name,simobj in generate.sim_objects.iteritems():
243    extra_deps = [ File(generate.py_modules[simobj.__module__]) ]
244
245    hh_file = File('params/%s.hh' % name)
246    params_hh_files.append(hh_file)
247    env.Command(hh_file, Value(name), generate.createSimObjectParam)
248    env.Depends(hh_file, depends + extra_deps)
249
250# Generate any parameter header files needed
251for name,param in generate.params.iteritems():
252    if isinstance(param, m5.params.VectorParamDesc):
253        ext = 'vptype'
254    else:
255        ext = 'ptype'
256
257    i_file = File('params/%s_%s.i' % (name, ext))
258    env.Command(i_file, Value(name), generate.createSwigParam)
259    env.Depends(i_file, depends)
260
261# Generate all enum header files
262for name,enum in generate.enums.iteritems():
263    extra_deps = [ File(generate.py_modules[enum.__module__]) ]
264
265    cc_file = File('enums/%s.cc' % name)
266    env.Command(cc_file, Value(name), generate.createEnumStrings)
267    env.Depends(cc_file, depends + extra_deps)
268    Source(cc_file)
269
270    hh_file = File('enums/%s.hh' % name)
271    env.Command(hh_file, Value(name), generate.createEnumParam)
272    env.Depends(hh_file, depends + extra_deps)
273
274# Build the big monolithic swigged params module (wraps all SimObject
275# param structs and enum structs)
276params_file = File('params/params.i')
277names = sort_list(generate.sim_objects.keys())
278env.Command(params_file, [ Value(v) for v in names ],
279            generate.buildParams)
280env.Depends(params_file, params_hh_files + depends)
281SwigSource('m5.objects', params_file)
282
283# Build all swig modules
284swig_modules = []
285for source,package in swig_sources:
286    filename = str(source)
287    assert filename.endswith('.i')
288
289    base = '.'.join(filename.split('.')[:-1])
290    module = basename(base)
291    cc_file = base + '_wrap.cc'
292    py_file = base + '.py'
293
294    env.Command([cc_file, py_file], source,
295                '$SWIG $SWIGFLAGS -outdir ${TARGETS[1].dir} '
296                '-o ${TARGETS[0]} $SOURCES')
297    env.Depends(py_file, source)
298    env.Depends(cc_file, source)
299
300    swig_modules.append(Value(module))
301    Source(cc_file)
302    PySource(package, py_file)
303
304# Generate the main swig init file
305env.Command('swig/init.cc', swig_modules, generate.makeSwigInit)
306Source('swig/init.cc')
307
308# Build the zip file
309py_compiled = []
310py_zip_depends = []
311for source in py_sources:
312    env.Command(source.compiled, source.source, generate.compilePyFile)
313    py_compiled.append(source.compiled)
314
315    # make the zipfile depend on the archive name so that the archive
316    # is rebuilt if the name changes
317    py_zip_depends.append(Value(source.arcname))
318
319# Add the zip file target to the environment.
320m5zip = File('m5py.zip')
321env.Command(m5zip, py_compiled, generate.buildPyZip)
322env.Depends(m5zip, py_zip_depends)
323
324########################################################################
325#
326# Define binaries.  Each different build type (debug, opt, etc.) gets
327# a slightly different build environment.
328#
329
330# List of constructed environments to pass back to SConstruct
331envList = []
332
333# This function adds the specified sources to the given build
334# environment, and returns a list of all the corresponding SCons
335# Object nodes (including an extra one for date.cc).  We explicitly
336# add the Object nodes so we can set up special dependencies for
337# date.cc.
338def make_objs(sources, env):
339    objs = [env.Object(s) for s in sources]
340    # make date.cc depend on all other objects so it always gets
341    # recompiled whenever anything else does
342    date_obj = env.Object('base/date.cc')
343    env.Depends(date_obj, objs)
344    objs.append(date_obj)
345    return objs
346
347# Function to create a new build environment as clone of current
348# environment 'env' with modified object suffix and optional stripped
349# binary.  Additional keyword arguments are appended to corresponding
350# build environment vars.
351def makeEnv(label, objsfx, strip = False, **kwargs):
352    newEnv = env.Copy(OBJSUFFIX=objsfx)
353    newEnv.Label = label
354    newEnv.Append(**kwargs)
355    exe = 'm5.' + label  # final executable
356    bin = exe + '.bin'   # executable w/o appended Python zip archive
357    newEnv.Program(bin, make_objs(cc_sources, newEnv))
358    if strip:
359        stripped_bin = bin + '.stripped'
360        if sys.platform == 'sunos5':
361            cmd = 'cp $SOURCE $TARGET; strip $TARGET'
362        else:
363            cmd = 'strip $SOURCE -o $TARGET'
364        newEnv.Command(stripped_bin, bin, cmd)
365        bin = stripped_bin
366    targets = newEnv.Concat(exe, [bin, 'm5py.zip'])
367    newEnv.M5Binary = targets[0]
368    envList.append(newEnv)
369
370# Debug binary
371ccflags = {}
372if env['GCC']:
373    if sys.platform == 'sunos5':
374        ccflags['debug'] = '-gstabs+'
375    else:
376        ccflags['debug'] = '-ggdb3'
377    ccflags['opt'] = '-g -O3'
378    ccflags['fast'] = '-O3'
379    ccflags['prof'] = '-O3 -g -pg'
380elif env['SUNCC']:
381    ccflags['debug'] = '-g0'
382    ccflags['opt'] = '-g -O'
383    ccflags['fast'] = '-fast'
384    ccflags['prof'] = '-fast -g -pg'
385elif env['ICC']:
386    ccflags['debug'] = '-g -O0'
387    ccflags['opt'] = '-g -O'
388    ccflags['fast'] = '-fast'
389    ccflags['prof'] = '-fast -g -pg'
390else:
391    print 'Unknown compiler, please fix compiler options'
392    Exit(1)
393
394makeEnv('debug', '.do',
395        CCFLAGS = Split(ccflags['debug']),
396        CPPDEFINES = ['DEBUG', 'TRACING_ON=1'])
397
398# Optimized binary
399makeEnv('opt', '.o',
400        CCFLAGS = Split(ccflags['opt']),
401        CPPDEFINES = ['TRACING_ON=1'])
402
403# "Fast" binary
404makeEnv('fast', '.fo', strip = True,
405        CCFLAGS = Split(ccflags['fast']),
406        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'])
407
408# Profiled binary
409makeEnv('prof', '.po',
410        CCFLAGS = Split(ccflags['prof']),
411        CPPDEFINES = ['NDEBUG', 'TRACING_ON=0'],
412        LINKFLAGS = '-pg')
413
414Return('envList')
415