Ticket #21970: SConstruct

File SConstruct, 9.2 KB (added by tcwan (TC Wan), 15 years ago)

Top level SConstruct

Line 
1# -*- mode: python -*-
2
3import os
4import os.path
5import new
6from glob import glob
7
8###############################################################
9# Utility functions.
10###############################################################
11
12# Similar to env.WhereIs, but always searches os.environ.
13def find_on_path(filename):
14    paths = os.environ.get('PATH')
15    if not paths:
16        return None
17    for p in paths.split(':'):
18        path = os.path.abspath(os.path.join(p, filename))
19        if os.path.isfile(path):
20            return p
21    return None
22
23# Run the given gcc binary, and parses its output to work out the gcc
24# version.
25def determine_gcc_version(gcc_binary):
26    stdout = os.popen('%s --version' % gcc_binary)
27    gcc_output = stdout.read().split()
28    stdout.close()
29    grab_next = False
30    for token in gcc_output:
31        if grab_next:
32            return token
33        elif token[-1] == ')':
34            grab_next = True
35    return None
36
37# Check that a given cross-compiler tool exists. If it does, the path is
38# added to the build environment, and the given environment variable is
39# set to the tool name.
40#
41# This is used to check for the presence of a working cross-compiler
42# toolchain, and to properly set up the environment to do it. See below
43# in the configuration section for details.
44def CheckTool(context, envname, toolname=None, hostprefix=None):
45    toolname = toolname or envname.lower()
46    if hostprefix is None:
47        hostprefix = '%s-' % context.env['CROSS_COMPILE_HOST']
48    toolname = '%s%s' % (hostprefix, toolname)
49    context.Message("Checking for %s..." % toolname)
50    toolpath = find_on_path(toolname)
51    if not toolpath:
52        context.Result('not found')
53        return False
54    else:
55        context.Result('ok')
56        context.env[envname] = toolname
57        context.env.AppendENVPath('PATH', toolpath)
58        return True
59
60# Find the correct variant and version of libgcc.a in the cross-compiler
61# toolchain.
62def CheckLibGcc(context, gccname):
63    context.Message("Locating a cross-compiled libgcc...")
64    toolpath = find_on_path(gccname)
65    if not toolpath:
66        context.Result("%s not found" % toolname)
67        return False
68    gcc_version = determine_gcc_version(gccname)
69    if not gcc_version:
70        context.Result("Could not determine gcc version")
71        return False
72    gcc_install_dir = os.path.split(os.path.normpath(toolpath))[0]
73    for libdir in ['interwork', 'thumb', '']:
74        libgcc_path = os.path.join(gcc_install_dir, 'lib', 'gcc',
75                               context.env['CROSS_COMPILE_HOST'],
76                               gcc_version, libdir, 'libgcc.a')
77        if os.path.isfile(libgcc_path):
78            break
79    if not os.path.isfile(libgcc_path):
80        context.Result("libgcc.a not found")
81        return False
82    context.Result("ok - " + libgcc_path)
83    context.env.Append(NXOS_LIBGCC=libgcc_path)
84    return True
85
86def CheckDoxygen(context):
87    context.Message("Looking for Doxygen...")
88    doxypath = find_on_path('doxygen')
89    if doxypath:
90        context.Result("ok")
91        context.env.AppendENVPath('PATH', doxypath)
92        context.env['WITH_DOXYGEN'] = True
93    else:
94        context.Result("not found")
95        context.env['WITH_DOXYGEN'] = False
96
97###############################################################
98# Tool that installs an application kernel helper.
99###############################################################
100
101def appkernel_tool(env):
102    def AppKernelVariant(env, kernel_name, variant_name, app_kernel,
103                         app_kernel_lds):
104        kvariant = '%s_%s' % (kernel_name, variant_name)
105
106        # ... Build the ELF kernel...
107        variant_kernel_elf = env.Command(
108            kvariant + '.elf',
109            [env['NXOS_BASEPLATE'], app_kernel, env['NXOS_LIBGCC']],
110            '$LINK -o $TARGET -T %s -Os --gc-sections --no-check-sections '
111            '$SOURCES' % app_kernel_lds)
112        env.Depends(variant_kernel_elf, app_kernel_lds)
113
114        # ... And make a binary image from that.
115        if variant_name != 'rxe':
116            variant_kernel = env.Command(
117                kvariant + '.bin', [variant_kernel_elf],
118                '$OBJCOPY -O binary $SOURCES $TARGET')
119        else:
120            variant_kernel = env.Command(
121                kernel_name + '.rxe', [variant_kernel_elf],
122                '$OBJCOPY -O binary $SOURCES $TARGET')
123
124    def AppKernel(self, kernel_name, sources, kernelsize='50k',
125                  romkernelsize=None, kernelisbuilt=False):
126        romkernelsize = romkernelsize or kernelsize # Sizes are never used
127        env = self.Clone()
128        env.Append(CPPPATH=['#systems'])
129
130        # Build a .a with all the application kernel code.
131        if kernelisbuilt:
132            app_kernel = sources
133        else:
134            app_kernel = []
135            for s in sources:
136                app_kernel.append(env.Object(s.split('.')[0], s))
137
138        # Build the SAM-BA and ROM kernel variants.
139        AppKernelVariant(env, kernel_name, 'samba', app_kernel,
140                         env.File('#systems/appkernel_samba.ld'))
141        AppKernelVariant(env, kernel_name, 'rom', app_kernel,
142                         env.File('#systems/appkernel_rom.ld'))
143        AppKernelVariant(env, kernel_name, 'rxe', app_kernel,
144                         env.File('#systems/appkernel_rxe.ld'))
145    appkernel_func = new.instancemethod(AppKernel, env, env.__class__)
146    env.AppKernel = appkernel_func
147
148###############################################################
149# Options that can be provided on the commandline
150###############################################################
151buildable_systems = []
152for root, dirs, files in os.walk('systems'):
153    if 'SConscript' in files:
154        buildable_systems.append(root.split('/', 1)[1])
155
156opts = Variables('scons.options', ARGUMENTS)
157opts.Add(ListVariable('appkernels',
158                    'List of application kernels to build '
159                    '(by default, only the tests kernel is compiled', 'tests',
160                    buildable_systems))
161opts.Add(PathVariable('gccprefix',
162                    'Prefix of the cross-gcc to use (by default arm-elf)',
163                    'arm-elf', PathVariable.PathAccept))
164
165Help('''
166Type: 'scons appkernels=... [gccprefix=...]' to build kernels.
167
168The application kernel names are the directory names in the systems/
169subdirectory. You can specify several application kernels, separated
170by commas.  If you specify no app kernel, only the tests kernel is
171built.
172
173Examples
174
175 - Build the Marvin application kernel:
176     scons appkernels=marvin
177
178 - Build both Marvin and the tests kernel:
179     scons appkernels=tests,marvin
180
181 - Build all available systems (may require extra external dependencies):
182     scons appkernels=all
183
184 - Build only the baseplate code:
185     scons appkernels=none
186
187 - To use another cross-gcc than arm-elf-gcc:
188     scons gccprefix=arm-softfloat-eabi
189
190Options are saved persistent in the file 'scons.options'. That means
191after you have called e.g. 'scons appkernels=tests,marvin' it's enough
192to call only 'scons' to build both Marvin and the tests kernel again.
193''')
194
195###############################################################
196# Construct and configure a cross-compiler environment
197###############################################################
198env = Environment(options = opts,
199                  tools = ['gcc', 'as', 'gnulink', 'ar',
200                           'doxygen', appkernel_tool],
201                  toolpath = ['scons_tools'],
202                  NXOS_LIBGCC = [], CPPPATH = '#',
203                  WITH_DOXYGEN = True)
204opts.Update(env)
205opts.Save('scons.options', env)
206
207if not env.GetOption('clean'):
208    conf = Configure(env, custom_tests = {'CheckTool': CheckTool,
209                                          'CheckLibGcc': CheckLibGcc,
210                                          'CheckDoxygen': CheckDoxygen})
211    conf.env['CROSS_COMPILE_HOST'] = env['gccprefix']
212    if not (conf.CheckTool('CC', 'gcc') and conf.CheckTool('AR') and
213            conf.CheckTool('OBJCOPY') and conf.CheckTool('LINK', 'ld') and
214            conf.CheckLibGcc(conf.env['CC'])):
215        print "Missing or incomplete arm-elf toolchain, cannot continue!"
216        Exit(1)
217    conf.CheckDoxygen()
218    env = conf.Finish()
219
220mycflags = ['-mcpu=arm7tdmi', '-Os', '-Wextra', '-Wall', '-Werror',
221                      '-Wno-div-by-zero', '-Wfloat-equal', '-Wshadow',
222                      '-Wpointer-arith', '-Wbad-function-cast',
223                      '-Wmissing-prototypes', '-ffreestanding',
224                      '-fsigned-char', '-ffunction-sections', '-std=gnu99',
225                      '-fdata-sections', '-fomit-frame-pointer', '-mhard-float']
226myasflags = ['-Wall', '-Werror', '-Os'];
227if str(env['NXOS_LIBGCC']).find('interwork') != -1:
228    mycflags.append('-mthumb-interwork')
229    myasflags.append('-Wa,-mcpu=arm7tdmi,-mfpu=fpa,-mthumb-interwork')
230elif str(env['NXOS_LIBGCC']).find('thumb') != -1:
231    mycflags.append('-mthumb')
232    myasflags.append('-Wa,-mcpu=arm7tdmi,-mfpu=fpa,-mthumb')
233else:
234    myasflags.append('-Wa,-mcpu=arm7tdmi,-mfpu=fpa')
235env.Replace(CCFLAGS = mycflags, ASFLAGS = myasflags )
236
237# Build the baseplate, and all selected application kernels.
238if env.GetOption('clean'):
239    appkernels = buildable_systems
240else:
241    appkernels = env['appkernels']
242systems_to_build = ['systems/%s/SConscript' % x for x in appkernels]
243SConscript(['base/SConscript'] + systems_to_build, 'env CheckTool')