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