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