# -*- mode: python; coding: utf-8; -*- # Codezero -- a microkernel for embedded systems. # # Copyright © 2009 B Labs Ltd # # This program is free software: you can redistribute it and/or modify it under the terms of the GNU # General Public License as published by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even # the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public # License for more details. # # You should have received a copy of the GNU General Public License along with this program. If not, see # . # # Author: Russel Winder # To support Python 2.5 we need the following, which seems to do no harm in Python 2.6. Only if Python 2.6 # is the floor version supported can this be dispensed with. from __future__ import with_statement import os includeDirectory = 'include' containersDirectory = 'containers' toolsDirectory = 'tools' cml2ToolsDirectory = toolsDirectory + '/cml2-tools' buildDirectory = 'build' cml2CompileRulesFile = buildDirectory + '/cml2Rules.out' cml2ConfigPropertiesFile = buildDirectory + '/cml2Config.out' cml2ConfigHeaderFile = buildDirectory + '/cml2Config.h' configureHelpEntry = 'configure the build. This must be done before any other action is possible.' # The choice of which parts of the kernel to compile and include in the build depends on the configuration # which is managed using CML2. CML2 uses a base configuration file (currently #configs/arm.cml) to drive # an interaction with the user which results in a trio of files that specify the user choice. # # cml2RulesFile is the pickled version of the source configuration driver. # # cml2Config.out is a properties type representation of the user selected configuration data. # # cml2Config.h is a C include file representation of the user selected configuration data derived from # cml2Config.out, it is essential for the compilation of the C code of the kernel and the tasks. # # Since the DAG for building the kernel relies on the information from the configuration split the build # into two distinct phases as Autotools and Waf do, configure then build. Achieve this by partitioning the # SCons DAG building in two depending on the command line. if 'configure' in COMMAND_LINE_TARGETS : def performCML2Configuration(target, source, env): if not os.path.isdir(buildDirectory) : os.mkdir(buildDirectory) os.system(cml2ToolsDirectory + '/cmlcompile.py -o ' + cml2CompileRulesFile + ' ' + source[0].path) os.system(cml2ToolsDirectory + '/cmlconfigure.py -c -o ' + cml2ConfigPropertiesFile + ' ' + cml2CompileRulesFile) os.system(toolsDirectory + '/cml2header.py -o ' + cml2ConfigHeaderFile + ' -i ' + cml2ConfigPropertiesFile) if len(COMMAND_LINE_TARGETS) != 1: print '#### Warning ####: configure is a target on the command line, all the other targets are being ignored as this is a configure step.' Command('configure', ['#configs/arm.cml'], performCML2Configuration) Clean('configure', buildDirectory) baseEnvironment = Environment(tools=[], targetHelpEntries = {'configure': configureHelpEntry}, ) else : if not os.path.exists(cml2ConfigPropertiesFile): if GetOption('help'): print ''' The project has to be configured even to get the help on the project. Running 'scons configure' starts the configuration system. Typing x causes the configuration system to terminate and write all the configuration data. 'scons -h' will then print the project help. ''' else: print "####\n#### Configuration has not been undertaken, please run 'scons configure'.\n####" Exit() ########## Create the base environment and process the configuration ######################## def processCML2Config(): configItems = {} with file(cml2ConfigPropertiesFile) as configFile: for line in configFile: item = line.split('=') if len(item) == 2: configItems[item[0].strip()] = (item[1].strip() == 'y') return configItems baseEnvironment = Environment(tools = ['gnulink', 'gcc', 'gas', 'ar'], ENV = {'PATH': os.environ['PATH']}, configFiles = ('#' + cml2CompileRulesFile, '#' + cml2ConfigPropertiesFile, '#' + cml2ConfigHeaderFile), CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'], ASFLAGS = ['-D__ASSEMBLY__'], LINKFLAGS = ['-nostdlib'], PROGSUFFIX = '.axf', toolChains = { 'arm926': { 'mainCompiler': 'arm-none-linux-gnueabi-gcc', 'kernelCompiler': 'arm-none-eabi-gcc', 'cpuOption': 'arm926ej-s'} }, includeDirectory = includeDirectory, containersDirectory = containersDirectory, toolsDirectory = toolsDirectory, cml2ToolsDirectory = cml2ToolsDirectory, buildDirectory = buildDirectory, targetHelpEntries = {'configure': configureHelpEntry}, ) # It is assumed that the C code is assuming that the configuration file will be found at l4/config.h so create it there. # # Kernel code include config.h in a different way to all the other bits of code. # # TODO: Decide if this is an issue or not. configHPath = buildDirectory + '/l4/config.h' configuration = Configure(baseEnvironment, config_h = configHPath) configData = processCML2Config() for key, value in configData.items(): if value: items = key.split('_') if items[0] == 'ARCH': configuration.env['ARCH'] = items[1].lower() for key, value in configData.items(): if value: items = key.split('_') if items[0] == 'ARCH': continue if items[0] == configuration.env['ARCH'].upper(): configuration.env[items[1]] = items[2].lower() else: path = items[1].lower() + '/' + items[2].lower() try: configuration.env[items[0]] except KeyError: configuration.env[items[0]] = [] configuration.env[items[0]].append(path) configuration.Define('__ARCH__', configuration.env['ARCH']) configuration.Define('__PLATFORM__', configuration.env['PLATFORM']) configuration.Define('__SUBARCH__', configuration.env['SUBARCH']) baseEnvironment = configuration.Finish() baseEnvironment.Append(configFiles = ('#' + configHPath,)) baseEnvironment['CC'] = baseEnvironment['toolChains'][configuration.env['CPU']]['mainCompiler'] ## ## Using the cpu option changes the behaviour of the test execution, it generates an illegal instruction exception :-( ## #baseEnvironment.Append(CCFLAGS = ['-mcpu=' + baseEnvironment['toolChains'][baseEnvironment['CPU']]['cpuOption']]) ########## Build the libraries ######################## libs = {} crts = {} for variant in ['baremetal']: (libs[variant], crts[variant]) = SConscript('libs/c/SConscript', variant_dir = buildDirectory + '/lib/c/' + variant, duplicate = 0, exports = {'environment': baseEnvironment, 'variant': variant}) baseEnvironment['baremetal_libc'] = libs['baremetal'] baseEnvironment['baremetal_crt0'] = crts['baremetal'] libelf = SConscript('libs/elf/SConscript', variant_dir = buildDirectory + '/lib/elf', duplicate = 0, exports = {'environment': baseEnvironment}) Alias('libs', crts.values() + libs.values() + [libelf]) baseEnvironment['targetHelpEntries']['libs'] = 'build the support libraries.' ########## Build the kernel ######################## startAxf = SConscript('src/SConscript', variant_dir = buildDirectory + '/kernel', duplicate = 0, exports = {'environment': baseEnvironment}) Alias('kernel', startAxf) baseEnvironment['targetHelpEntries']['kernel'] = 'build the kernel itself.' ########## Handle all the container creation ####################### containers = SConscript('containers/SConscript', variant_dir = buildDirectory + '/containers', duplicate = 0, exports = {'environment': baseEnvironment, 'startAxf': startAxf}) Alias('containers', containers) baseEnvironment['targetHelpEntries']['containers'] = 'build all the containers.' ########## Do the packing / create loadable ######################## loader = SConscript('loader/SConscript', variant_dir = buildDirectory + '/loader', duplicate = 0, exports = {'environment': baseEnvironment, 'images': [startAxf] + containers, 'libsInOrder': [libelf, libs['baremetal'], 'gcc', libs['baremetal']]}) Alias('final', loader) baseEnvironment['targetHelpEntries']['final'] = 'build all components and the final loadable.' # The test does not terminate and Ctrl-C and Ctrl-Z have no effect. Run the job in the background so # the initiating terminal retains control and allows the process to be killed from this terminal. Add # the sleep to force SCons to wait until the test has run before it decides all targets are built and # return to the prompt. Remind the user they have a running process in the background. Command('runTest', loader, "qemu-system-arm -kernel $SOURCE -nographic -m 128 -M versatilepb & sleep 10 ; echo '####\\n#### You will need to kill the qemu-system-arm process\\n#### that is running in the background.\\n####\\n'") ########## Other rules. ######################## Default(loader) Clean('.', [buildDirectory]) ########## Be helpful ######################## if len(COMMAND_LINE_TARGETS) != 0: Help('\n') for item in COMMAND_LINE_TARGETS: if baseEnvironment['targetHelpEntries'].has_key(item): Help(' ' + item + ' -- ' + baseEnvironment['targetHelpEntries'][item] + '\n') elif FindFile(item, '.'): Help(' ' + item + ' is a file that exists or can be created.\n') else: Help(' ' + item + ' is not a possible target.\n') else: Help(''' Possible targets are: ''') targetList = baseEnvironment['targetHelpEntries'].keys() targetList.sort() for item in targetList: Help(' ' + item + ' -- ' + baseEnvironment['targetHelpEntries'][item] + '\n') Help(''' The default target is 'final'. A configure must have been executed before any other target is possible. ''')