SConscript revision 5016
16157Snate@binkert.org# -*- mode:python -*-
26157Snate@binkert.org
36157Snate@binkert.org# Copyright (c) 2004-2005 The Regents of The University of Michigan
46157Snate@binkert.org# All rights reserved.
56157Snate@binkert.org#
66157Snate@binkert.org# Redistribution and use in source and binary forms, with or without
76157Snate@binkert.org# modification, are permitted provided that the following conditions are
86157Snate@binkert.org# met: redistributions of source code must retain the above copyright
96157Snate@binkert.org# notice, this list of conditions and the following disclaimer;
106157Snate@binkert.org# redistributions in binary form must reproduce the above copyright
116157Snate@binkert.org# notice, this list of conditions and the following disclaimer in the
126157Snate@binkert.org# documentation and/or other materials provided with the distribution;
136157Snate@binkert.org# neither the name of the copyright holders nor the names of its
146157Snate@binkert.org# contributors may be used to endorse or promote products derived from
156157Snate@binkert.org# this software without specific prior written permission.
166157Snate@binkert.org#
176157Snate@binkert.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
186157Snate@binkert.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
196157Snate@binkert.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
206157Snate@binkert.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
216157Snate@binkert.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
226157Snate@binkert.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
236157Snate@binkert.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
246157Snate@binkert.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
256157Snate@binkert.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
266157Snate@binkert.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
276157Snate@binkert.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
286157Snate@binkert.org#
296157Snate@binkert.org# Authors: Nathan Binkert
306157Snate@binkert.org
316157Snate@binkert.orgimport imp
326157Snate@binkert.orgimport os
336157Snate@binkert.orgimport sys
346157Snate@binkert.org
356157Snate@binkert.orgfrom os.path import basename
366157Snate@binkert.orgfrom os.path import join as joinpath
376157Snate@binkert.orgfrom os.path import exists
386157Snate@binkert.orgfrom os.path import isdir
396157Snate@binkert.orgfrom os.path import isfile
4010133Sandreas.hansson@arm.com
4110133Sandreas.hansson@arm.comimport SCons
4210133Sandreas.hansson@arm.com
4310133Sandreas.hansson@arm.com# This file defines how to build a particular configuration of M5
4410133Sandreas.hansson@arm.com# based on variable settings in the 'env' build environment.
4510133Sandreas.hansson@arm.com
4610133Sandreas.hansson@arm.comImport('*')
4710133Sandreas.hansson@arm.com
4810133Sandreas.hansson@arm.com# Children need to see the environment
4910133Sandreas.hansson@arm.comExport('env')
5010133Sandreas.hansson@arm.com
5110133Sandreas.hansson@arm.comdef sort_list(_list):
5210133Sandreas.hansson@arm.com    """return a sorted copy of '_list'"""
5310133Sandreas.hansson@arm.com    if isinstance(_list, list):
5410133Sandreas.hansson@arm.com        _list = _list[:]
5510133Sandreas.hansson@arm.com    else:
5610133Sandreas.hansson@arm.com        _list = list(_list)
5710133Sandreas.hansson@arm.com    _list.sort()
5810133Sandreas.hansson@arm.com    return _list
5910133Sandreas.hansson@arm.com
6010133Sandreas.hansson@arm.comclass PySourceFile(object):
6110133Sandreas.hansson@arm.com    def __init__(self, package, source):
628492Snilay@cs.wisc.edu        filename = str(source)
636168Snate@binkert.org        pyname = basename(filename)
646168Snate@binkert.org        assert pyname.endswith('.py')
656157Snate@binkert.org        name = pyname[:-3]
666157Snate@binkert.org        path = package.split('.')
676157Snate@binkert.org        modpath = path
686157Snate@binkert.org        if name != '__init__':
696157Snate@binkert.org            modpath += [name]
706157Snate@binkert.org        modpath = '.'.join(modpath)
716157Snate@binkert.org
726157Snate@binkert.org        arcpath = package.split('.') + [ pyname + 'c' ]
736157Snate@binkert.org        arcname = joinpath(*arcpath)
746157Snate@binkert.org
756157Snate@binkert.org        self.source = source
766157Snate@binkert.org        self.pyname = pyname
776157Snate@binkert.org        self.srcpath = source.srcnode().abspath
786157Snate@binkert.org        self.package = package
796157Snate@binkert.org        self.modpath = modpath
806157Snate@binkert.org        self.arcname = arcname
816157Snate@binkert.org        self.filename = filename
826157Snate@binkert.org        self.compiled = File(filename + 'c')
836157Snate@binkert.org
846157Snate@binkert.org########################################################################
856157Snate@binkert.org# Code for adding source files of various types
866157Snate@binkert.org#
876157Snate@binkert.orgcc_sources = []
886157Snate@binkert.orgdef Source(source):
896157Snate@binkert.org    '''Add a C/C++ source file to the build'''
906157Snate@binkert.org    if not isinstance(source, SCons.Node.FS.File):
916157Snate@binkert.org        source = File(source)
926157Snate@binkert.org
936157Snate@binkert.org    cc_sources.append(source)
946157Snate@binkert.org
956157Snate@binkert.orgpy_sources = []
966157Snate@binkert.orgdef PySource(package, source):
976157Snate@binkert.org    '''Add a python source file to the named package'''
986157Snate@binkert.org    if not isinstance(source, SCons.Node.FS.File):
996157Snate@binkert.org        source = File(source)
1006157Snate@binkert.org
1016157Snate@binkert.org    source = PySourceFile(package, source)
1026157Snate@binkert.org    py_sources.append(source)
1036157Snate@binkert.org
1046157Snate@binkert.orgsim_objects_fixed = False
1056157Snate@binkert.orgsim_object_modfiles = set()
1066157Snate@binkert.orgdef SimObject(source):
1076157Snate@binkert.org    '''Add a SimObject python file as a python source object and add
1088483Sgblack@eecs.umich.edu    it to a list of sim object modules'''
1098483Sgblack@eecs.umich.edu
1106157Snate@binkert.org    if sim_objects_fixed:
1116882SBrad.Beckmann@amd.com        raise AttributeError, "Too late to call SimObject now."
1126286Snate@binkert.org
1136286Snate@binkert.org    if not isinstance(source, SCons.Node.FS.File):
1148092Snilay@cs.wisc.edu        source = File(source)
1156286Snate@binkert.org
1166286Snate@binkert.org    PySource('m5.objects', source)
1176157Snate@binkert.org    modfile = basename(str(source))
11811208Sjoseph.gross@amd.com    assert modfile.endswith('.py')
1196157Snate@binkert.org    modname = modfile[:-3]
12010301Snilay@cs.wisc.edu    sim_object_modfiles.add(modname)
1216157Snate@binkert.org
1226157Snate@binkert.orgswig_sources = []
12311122Snilay@cs.wisc.edudef SwigSource(package, source):
12410301Snilay@cs.wisc.edu    '''Add a swig file to build'''
1259363Snilay@cs.wisc.edu    if not isinstance(source, SCons.Node.FS.File):
12610301Snilay@cs.wisc.edu        source = File(source)
1276286Snate@binkert.org    val = source,package
12810301Snilay@cs.wisc.edu    swig_sources.append(val)
12910301Snilay@cs.wisc.edu
13010301Snilay@cs.wisc.edu# Children should have access
13110301Snilay@cs.wisc.eduExport('Source')
1326157Snate@binkert.orgExport('PySource')
13310301Snilay@cs.wisc.eduExport('SimObject')
13410301Snilay@cs.wisc.eduExport('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