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