root/nxos/SConstruct

Revision 623:b4b839410185, 8.6 kB (checked in by David Anderson <dave@…>, 5 months ago)

Add a help message to the SConstruct file.

This help explains to new users how to build application kernels, which is
not obvious from reading the SConstruct.

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