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