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