mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 10:53:16 +01:00
266 lines
13 KiB
Python
266 lines
13 KiB
Python
# -*- 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
|
|
# <http://www.gnu.org/licenses/>.
|
|
#
|
|
# 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'
|
|
posixServicesDirectory = containersDirectory + '/posix'
|
|
toolsDirectory = 'tools'
|
|
cml2ToolsDirectory = toolsDirectory + '/cml2-tools'
|
|
buildDirectory = 'build'
|
|
|
|
cml2CompileRulesFile = buildDirectory + '/cml2Rules.out'
|
|
cml2ConfigPropertiesFile = buildDirectory + '/cml2Config.out'
|
|
cml2ConfigHeaderFile = buildDirectory + '/cml2Config.h'
|
|
|
|
# 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 part of 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)
|
|
|
|
else :
|
|
|
|
if not os.path.exists(cml2ConfigPropertiesFile):
|
|
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,
|
|
posixServicesDirectory = posixServicesDirectory,
|
|
toolsDirectory = toolsDirectory,
|
|
cml2ToolsDirectory = cml2ToolsDirectory,
|
|
buildDirectory = buildDirectory,
|
|
)
|
|
|
|
# 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])
|
|
|
|
########## Build the kernel ########################
|
|
|
|
startAxf = SConscript('src/SConscript' , variant_dir = buildDirectory + '/kernel' , duplicate = 0, exports = {'environment': baseEnvironment})
|
|
|
|
Alias('kernel', startAxf)
|
|
|
|
########## Build the task libraries ########################
|
|
|
|
taskSupportLibraryEnvironment = baseEnvironment.Clone()
|
|
taskSupportLibraryEnvironment.Append(CPPPATH = ['#' + buildDirectory, '#' + buildDirectory + '/l4', '#' + includeDirectory])
|
|
|
|
taskLibraryNames = [f.name for f in Glob(posixServicesDirectory + '/lib*')]
|
|
|
|
taskLibraries = []
|
|
for library in taskLibraryNames:
|
|
taskLibraries.append(SConscript(posixServicesDirectory + '/' + library + '/SConscript', variant_dir = buildDirectory + '/' + posixServicesDirectory + '/' + library,
|
|
duplicate = 0, exports = {'environment': taskSupportLibraryEnvironment}))
|
|
|
|
Alias ('tasklibs', taskLibraries)
|
|
|
|
########## Build the tasks ########################
|
|
|
|
def buildTask(programName, sources, environment, previousImage, extraCppPath=None):
|
|
e = environment.Clone()
|
|
e.Append(LINKFLAGS=['-T' + e['posixServicesDirectory'] + '/' + programName + '/include/linker.lds'])
|
|
e.Append(LIBPATH=['#build/' + e['posixServicesDirectory'] + '/' + programName])
|
|
if extraCppPath: e.Append(CPPPATH=extraCppPath)
|
|
objects = e.StaticObject(sources)
|
|
Depends(objects, e['configFiles'])
|
|
program = e.Program(programName, objects)
|
|
environment['physicalBaseLinkerScript'] = Command('include/physical_base.lds', previousImage, 'tools/pyelf/readelf.py --first-free-page ' + previousImage[0].path + ' >> $TARGET')
|
|
Depends(program, [environment['physicalBaseLinkerScript']])
|
|
return program
|
|
|
|
tasksEnvironment = baseEnvironment.Clone()
|
|
tasksEnvironment.Append(LIBS = taskLibraries + ['gcc'] + taskLibraries)
|
|
tasksEnvironment.Append(CPPDEFINES = ['__USERSPACE__'])
|
|
tasksEnvironment.Append(CPPPATH = ['#' + buildDirectory, '#' + buildDirectory + '/l4', '#' + includeDirectory, 'include', \
|
|
'#' + posixServicesDirectory + '/libl4/include', '#' + posixServicesDirectory + '/libc/include', \
|
|
'#' + posixServicesDirectory + '/libmem', '#' + posixServicesDirectory + '/libposix/include'])
|
|
tasksEnvironment.Append(buildTask = buildTask)
|
|
|
|
####
|
|
#### TODO: Why does the linker require crt0.o to be in the current directory and named as such. Is it
|
|
#### because of the text in the linker script?
|
|
####
|
|
|
|
#### taskNameList = [ f.name for f in Glob(posixServicesDirectory + '*') if f.name not in taskLibraryNames + ['bootdesc'] ]
|
|
#### imageOrderData = [(taskName, []) for taskName in taskNameList]
|
|
execfile(posixServicesDirectory + '/taskOrder.py')
|
|
imageOrderData = [(taskName, []) for taskName in taskOrder]
|
|
imageOrderData[0][1].append(startAxf)
|
|
tasks = []
|
|
for i in range(len(imageOrderData)):
|
|
taskName = imageOrderData[i][0]
|
|
dependency = imageOrderData[i][1]
|
|
program = SConscript(posixServicesDirectory + '/' + taskName + '/SConscript', variant_dir = buildDirectory + '/' + posixServicesDirectory + '/' + taskName,
|
|
duplicate = 0, exports = {'environment': tasksEnvironment, 'previousImage': dependency[0]})
|
|
tasks.append(program)
|
|
if i < len(imageOrderData) - 1:
|
|
imageOrderData[i+1][1].append(program)
|
|
|
|
Alias ('tasks', tasks)
|
|
|
|
########## Create the boot description ########################
|
|
|
|
bootdesc = SConscript(posixServicesDirectory + '/bootdesc/SConscript', variant_dir = buildDirectory + '/' + posixServicesDirectory + '/bootdesc',
|
|
duplicate = 0, exports = {'environment': baseEnvironment, 'images': [startAxf] + tasks})
|
|
|
|
Alias('bootdesc', bootdesc)
|
|
|
|
########## Do the packing / create loadable ########################
|
|
|
|
loader = SConscript('loader/SConscript', variant_dir = buildDirectory + '/loader', duplicate = 0,
|
|
exports = {'environment': baseEnvironment, 'images': [startAxf, bootdesc] + tasks, 'libsInOrder': [libelf, libs['baremetal'], 'gcc', libs['baremetal']]})
|
|
|
|
Alias('final', loader)
|
|
|
|
# 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(crts.values() + libs.values() + [libelf, startAxf] + tasks + bootdesc + loader)
|
|
|
|
Clean('.', [buildDirectory])
|
|
|
|
########## Be helpful ########################
|
|
|
|
Help('''
|
|
Explicit targets are:
|
|
|
|
configure -- configure the build area ready for a build.
|
|
|
|
libs -- build the support library.
|
|
kernel -- build the kernel.
|
|
tasklibs -- build all the support libraries for the tasks.
|
|
tasks -- build all the tasks.
|
|
bootdesc -- build the tasks and the boot descriptor.
|
|
final -- build the loadable.
|
|
|
|
The default target is to compile everything and to do a final link.
|
|
|
|
Compilation can only be undertaken after a configuration.
|
|
''')
|