Merge branch 'newBuild' of /opt/c0-trees/codezero-russel into devel

Conflicts:

	SConstruct
	src/api/SConscript
	src/arch/arm/SConscript
	src/generic/SConscript
	tasks/bootdesc/SConstruct
This commit is contained in:
Bahadir Balban
2009-08-14 17:09:58 +03:00
47 changed files with 998 additions and 1573 deletions

View File

@@ -1,459 +1,304 @@
# -*- mode: python; coding: utf-8; -*-
# Codezero -- a microkernel for embedded systems.
#
# Almost enough mechanism to build a toy OS
# Copyright © 2009 B Labs Ltd
#
# Copyright (C) 2007 Bahadir Balban
# 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 be dispensed with.
from __future__ import with_statement
import os
import sys
import shutil
from string import split
from os.path import join
from os import sys
import glob
# The root directory of the repository where this file resides:
project_root = os.getcwd()
source_root = os.path.join(project_root, 'src')
headers_root = os.path.join(project_root, 'include')
project_tools_root = os.path.join(project_root, 'tools')
cml2_tools_root = os.path.join(project_tools_root, 'cml2-tools')
config_h = os.path.join(headers_root, "l4/config.h")
# Make sure python modules can be imported from tools and cml-tools
sys.path.append(project_tools_root)
sys.path.append(cml2_tools_root)
import cml2header
import cmlconfigure
# The linker script to link the final executable
linker_script = join(headers_root, 'l4/arch/arm/linker.lds')
# Environment to build sources
#env = None
###########################################
# #
# ARM kernel build environment #
# #
###########################################
kernel_env = Environment(CC = 'arm-none-eabi-gcc',
# We don't use -nostdinc because sometimes we need standard headers,
# such as stdarg.h e.g. for variable args, as in printk().
CCFLAGS = ['-g', '-mcpu=arm926ej-s', '-nostdlib', '-ffreestanding', \
'-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib', '-T' + linker_script],
ASFLAGS = ['-D__ASSEMBLY__'],
PROGSUFFIX = '.axf', # The suffix to use for final executable
ENV = {'PATH' : os.environ['PATH']}, # Inherit shell path
LIBS = 'gcc', # libgcc.a - This is required for division routines.
CPPPATH = headers_root,
CPPFLAGS = '-include l4/config.h -include l4/macros.h -include l4/types.h -D__KERNEL__')
#########################################
# #
# TEST BUILDING ENVIRONMENT #
# #
#########################################
# The purpose of test build is somewhat similar to regression/unit
# testing. There are individual tests for each interesting
# function/module (e.g. memory allocator layers). For each individual
# test only the relevant sources' SConscript files are picked up.
# So we don't build a whole kernel, but many little individual tests.
tests_env = Environment(CC = 'gcc -m32',
CCFLAGS = ['-g', '-std=gnu99', '-Wall', '-Werror'],
ENV = {'PATH' : os.environ['PATH']},
LIBS = 'gcc',
CPPPATH = '#include',
CPPFLAGS = '-include l4/config.h -include l4/macros.h -include l4/types.h -D__KERNEL__')
# Dictionary mappings for configuration symbols to directories of
# sources that relate to those symbols.
arch_to_dir = { 'ARCH_ARM' : 'arm', \
'ARCH_TEST' : 'tests' }
plat_to_dir = { 'ARM_PLATFORM_PB926' : 'pb926', \
'TEST_PLATFORM' : 'tests' }
subarch_to_dir = { 'ARM_SUBARCH_V5' : 'v5', \
'TEST_SUBARCH' : 'tests' }
driver_to_dir = { 'DRIVER_UART_PL011' : 'uart/pl011', \
'DRIVER_TIMER_SP804' : 'timer/sp804', \
'DRIVER_IRQCTRL_PL190' : 'irq/pl190' }
arch_to_env = { 'arm' : kernel_env, \
'tests' : tests_env }
def kprocess_config_symbols(config_symbols):
'''
Checks configuration symbols and discovers the directories with
SConscripts that needs to be compiled for those symbols.
Also derives symbols and adds them to CPPFLAGS.
'''
archdir = None
subarchdir = None
platdir = None
driversdirs = []
for sym in config_symbols:
for arch in arch_to_dir:
if sym == arch:
archdir = arch_to_dir[arch]
env = arch_to_env[arch]
Export('env')
if arch == 'ARCH_TEST':
env.Append(CPPFLAGS = '-DSUBARCH_TEST ')
env.Append(CPPFLAGS = '-DPLATFORM_TEST ')
subarchdir = "tests"
platdir = "tests"
for subarch in subarch_to_dir:
if sym == subarch:
subarchdir = subarch_to_dir[subarch]
for plat in plat_to_dir:
if sym == plat:
platdir = plat_to_dir[plat]
for driver in driver_to_dir:
if sym == driver:
# There can me multiple drivers, so making a list.
driversdirs.append(driver_to_dir[driver])
if archdir == None:
print "Error: No config symbol found for architecture"
sys.exit()
if subarchdir == None:
print "Error: No config symbol found for subarchitecture"
sys.exit()
if platdir == None:
print "Error: No config symbol found for platform"
sys.exit()
# Update config.h with derived values. Unfortunately CML2 does not handle
# derived symbols well yet. This can be fixed in the future.
f = open(config_h, "a")
f.seek(0, 2)
f.write("/* Symbols derived from this file by SConstruct\process_config_symbols() */")
f.write("\n#define __ARCH__\t\t%s\n" % archdir)
f.write("#define __PLATFORM__\t\t%s\n" % platdir)
f.write("#define __SUBARCH__\t\t%s\n\n" % subarchdir)
f.close()
return archdir, subarchdir, platdir, driversdirs
def process_config_symbols(config_symbols):
'''
Checks configuration symbols and discovers the directories with
SConscripts that needs to be compiled for those symbols.
'''
archdir = None
subarchdir = None
platdir = None
driversdirs = []
for sym,val in config_symbols:
if sym == "__ARCH__":
archdir = val
env = arch_to_env[val]
Export('env')
if sym == "__PLATFORM__":
platdir = val
if sym == "__SUBARCH__":
subarchdir = val
for driver in driver_to_dir:
if sym == driver:
# There can me multiple drivers, so making a list.
driversdirs.append(driver_to_dir[driver])
if archdir == None:
print "Error: No config symbol found for architecture"
sys.exit()
if subarchdir == None:
print "Error: No config symbol found for subarchitecture"
sys.exit()
if platdir == None:
print "Error: No config symbol found for platform"
sys.exit()
includeDirectory = 'include'
toolsDirectory = 'tools'
cml2ToolsDirectory = toolsDirectory + '/cml2-tools'
buildDirectory = 'build'
return archdir, subarchdir, platdir, driversdirs
cml2CompileRulesFile = buildDirectory + '/cml2Rules.out'
cml2ConfigPropertiesFile = buildDirectory + '/cml2Config.out'
cml2ConfigHeaderFile = buildDirectory + '/cml2Config.h'
def gather_config_symbols(header_file):
'''
Gathers configuration symbols from config.h to be used in the build. This
is particularly used for determining what sources to build. Each Sconscript
has the option to select sources to build based on the symbols imported to it.
'''
if not os.path.exists(header_file):
print "\n\%s does not exist. "\
"Please run: `scons configure' first\n\n" % header_file
sys.exit()
f = open(header_file)
config_symbols = []
while True:
line = f.readline()
if line == "":
break
str_parts = split(line)
if len(str_parts) > 0:
if str_parts[0] == "#define":
config_symbols.append((str_parts[1], str_parts[2]))
f.close()
Export('config_symbols')
return config_symbols
# 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.
def gather_sconscripts(config_symbols):
"Gather all SConscripts to be compiled depending on configuration symbols"
arch, subarch, plat, drivers = process_config_symbols(config_symbols)
dirpath = []
allpaths = []
if 'configure' in COMMAND_LINE_TARGETS :
# Paths for the SConscripts: sc_<path> etc. glob.glob is useful in that
# it returns `None' for non-existing paths.
sc_generic = glob.glob("src/generic/SConscript")
sc_api = glob.glob("src/api/SConscript")
sc_lib = glob.glob("src/lib/SConscript")
sc_platform = glob.glob("src/platform/" + plat + "/SConscript")
sc_arch = glob.glob("src/arch/" + arch + "/SConscript")
sc_glue = glob.glob("src/glue/" + arch + "/SConscript")
sc_subarch = glob.glob("src/arch/" + arch + "/" + subarch + "/SConscript")
sc_drivers = []
for driver in drivers:
sc_drivers.append("src/drivers/" + driver + "/SConscript")
#print "\nSConscripts collected for this build:"
for dirpath in [sc_generic, sc_api, sc_lib, sc_platform, sc_arch, sc_subarch, sc_glue]:
for path in dirpath:
# print path
allpaths.append(join(os.getcwd(), path[:-11]))
for drvpath in sc_drivers:
allpaths.append(join(os.getcwd(), drvpath[:-11]))
return allpaths
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)
def gather_kernel_build_paths(scons_script_path_list):
'''
Takes the list of all SConscript files, and returns the list of
corresponding build directories for each SConscript file.
'''
build_path_list = []
for path in scons_script_path_list:
build_path_list.append('build' + path)
return build_path_list
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)
def declare_kernel_build_paths(script_paths):
'''
Gathers all SConscript files and their corresponding
distinct build directories. Declares the association
to the scons build system.
'''
# Get all the script and corresponding build paths
build_paths = gather_kernel_build_paths(script_paths)
script_paths.sort()
build_paths.sort()
else :
# Declare build paths
for script_path, build_path in zip(script_paths, build_paths):
# print "Sources in " + script_path + " will be built in: " + build_path
BuildDir(build_path, script_path)
if not os.path.exists(cml2ConfigPropertiesFile):
print "####\n#### Configuration has not been undertaken, please run 'scons configure'.\n####"
Exit()
return build_paths
########## Create the base environment and process the configuration ########################
def build_and_gather(bpath_list):
'''
Given a SConscript build path list, declares all
SConscript files and builds them, gathers and
returns the resulting object files.
'''
obj_list = []
for path in bpath_list:
# print "Building files in: " + path
o = SConscript(path + '/' + 'SConscript')
obj_list.append(o)
return obj_list
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))
def determine_target(config_symbols):
if "ARCH_TEST" in config_symbols:
target = "tests"
else:
target = "kernel"
return target
kernelSConscriptPaths = ['generic', 'api', 'lib']
def build_kernel_objs(config_symbols):
"Builds the final kernel executable."
# Declare builddir <--> SConscript association
script_paths = gather_sconscripts(config_symbols)
build_paths = declare_kernel_build_paths(script_paths)
# Declare Sconscript files, build and gather objects.
objs = build_and_gather(build_paths)
return objs
# 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.
#################################################################
# Kernel and Test Targets #
# Executes python functions and compiles the kernel. #
#################################################################
configuration = Configure(baseEnvironment, config_h = buildDirectory + '/l4/config.h')
configData = processCML2Config()
arch = None
platform = None
subarch = None
for key, value in configData.items():
if value:
items = key.split('_')
if items[0] == 'ARCH':
arch = items[1].lower()
for key, value in configData.items():
if value:
items = key.split('_')
if items[0] == arch.upper():
if items[1] == 'PLATFORM':
platform = items[2].lower()
if items[1] == 'SUBARCH':
subarch = items[2].lower()
if items[0] == 'DRIVER':
kernelSConscriptPaths.append('drivers/' + ('irq' if items[1] == 'IRQCTRL' else items[1].lower()) + '/' + items[2].lower())
configuration.Define('__ARCH__', arch)
configuration.Define('__PLATFORM__', platform)
configuration.Define('__SUBARCH__', subarch)
kernelSConscriptPaths += [
'arch/' + arch,
'glue/' + arch,
'platform/' + platform,
'arch/' + arch + '/' + subarch]
configuration.env['ARCH'] = arch
configuration.env['PLATFORM'] = platform
configuration.env['SUBARCH'] = subarch
baseEnvironment = configuration.Finish()
# Unfortunately the build system is imperative rather than declarative,
# and this is the entry point. This inconvenience is due to the fact that
# it is not easily possible in SCons to produce a target list dynamically,
# which, is generated by the `configure' target.
########## Build the libraries ########################
if "build" in COMMAND_LINE_TARGETS:
config_symbols = gather_config_symbols(config_h)
target = determine_target(config_symbols)
if target == "kernel":
built_objs = build_kernel_objs(config_symbols)
kernel_target = kernel_env.Program('build/start', built_objs)
kernel_env.Alias('build', kernel_target)
elif target == "tests":
built_objs = build_kernel_objs(config_symbols)
tests_target = tests_env.Program('build/tests', built_objs)
tests_env.Alias('build', tests_target)
else:
print "Invalid or unknown target.\n"
libraryEnvironment = baseEnvironment.Clone(
CC = 'arm-none-linux-gnueabi-gcc',
CCFLAGS = ['-g', '-nostdinc', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib'],
LIBS = ['gcc'],
ARCH = arch,
PLATFORM = platform)
#################################################################
# The Configuration Targets: #
# Executes python functions/commands to configure the kernel. #
#################################################################
# Configuration Build Details: #
# ---------------------------- #
# 1) Compiles the ruleset for given architecture/platform. #
# 2) Invokes the CML2 kernel configurator using the given #
# ruleset. #
# 3) Configurator output is translated into a header file. #
# #
# For more information on this see the cml2 manual. Recap: #
# The cml file defines the rules (i.e. option a and b #
# implies c etc.) The compiler converts it to a pickled #
# form. The configurator reads it and brings up a UI menu. #
# Saving the selections results in a configuration file, #
# which in turn is converted to a header file includable #
# by the C sources. #
#################################################################
libs = {}
crts = {}
for variant in ['baremetal', 'userspace']:
(libs[variant], crts[variant]) = SConscript('libs/c/SConscript', variant_dir = buildDirectory + '/lib/c/' + variant, duplicate = 0, exports = {'environment': libraryEnvironment, 'variant': variant})
Depends((libs[variant], crts[variant]), libraryEnvironment['configFiles'])
def cml_configure(target, source, env):
baseEnvironment['baremetal_libc'] = libs['baremetal']
baseEnvironment['baremetal_crt0'] = crts['baremetal']
baseEnvironment['userspace_libc'] = libs['userspace']
baseEnvironment['userspace_crt0'] = crts['userspace']
default_target = 'config.out'
default_source = 'rules.out'
libelf = SConscript('libs/elf/SConscript', variant_dir = buildDirectory + '/lib/elf', duplicate = 0, exports = {'environment': libraryEnvironment})
Depends(libelf, libraryEnvironment['configFiles'])
target = str(target[0])
source = str(source[0])
# Strangely, cmlconfigure.py's command line option parsing is fairly broken
# if you specify an input or output file a combination of things can happen,
# so we supply no options, and move the defaults ifile= ./rules.out
# ofile=./config.out around to match with supplied target/source arguments.
Alias('libs', crts.values() + libs.values() + [libelf])
# Move source to where cmlconfigure.py expects to find it.
print "Moving", source, "to", default_source
shutil.move(source, default_source)
cml2_configure_cmd = cml2_tools_root + '/cmlconfigure.py -c'
os.system(cml2_configure_cmd)
########## Build the kernel ########################
# Move target file to where the build system expects to find it.
print "Moving", default_target, "to", target
shutil.move(default_target, target)
print "Moving", default_source, "back to", source
shutil.move(default_source, source)
return None
kernelEnvironment = baseEnvironment.Clone(
CC = 'arm-none-eabi-gcc',
# We don't use -nostdinc because sometimes we need standard headers, such as stdarg.h e.g. for variable
# args, as in printk().
CCFLAGS = ['-mcpu=arm926ej-s', '-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib', '-T' + includeDirectory + '/l4/arch/' + arch + '/mylink.lds'],
ASFLAGS = ['-D__ASSEMBLY__'],
PROGSUFFIX = '.axf',
LIBS = ['gcc'],
CPPPATH = ['#' + buildDirectory, '#' + buildDirectory + '/l4', '#' + includeDirectory, '#' + includeDirectory + '/l4'],
def cml_compile(target, source, env):
# Same here, this always produces a default file.
default_target = "rules.out"
####
#### TODO: Why are these files forcibly included, why not just leave it up to the C code to include things?
####
target = str(target[0])
source = str(source[0])
CPPFLAGS = ['-include', 'config.h', '-include', 'cml2Config.h', '-include', 'macros.h', '-include', 'types.h', '-D__KERNEL__'])
kernelComponents = []
for scriptPath in ['src/' + path for path in kernelSConscriptPaths]:
kernelComponents.append(SConscript(scriptPath + '/SConscript', variant_dir = buildDirectory + '/' + scriptPath, duplicate = 0, exports = {'environment': kernelEnvironment}))
startAxf = kernelEnvironment.Program(buildDirectory + '/start.axf', kernelComponents)
Depends(kernelComponents + [startAxf], kernelEnvironment['configFiles'])
Alias('kernel', startAxf)
cml2_compile_cmd = cml2_tools_root + '/cmlcompile.py < ' + source + ' > ' + target
os.system(cml2_compile_cmd)
########## Build the task libraries ########################
print "Moving " + default_target + " to " + target
shutil.move(default_target, target)
return None
taskSupportLibraryEnvironment = baseEnvironment.Clone(
CC = 'arm-none-linux-gnueabi-gcc',
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib'],
ASFLAGS = ['-D__ASSEMBLY__'],
LIBS = ['gcc'],
CPPPATH = ['#' + buildDirectory, '#' + buildDirectory + '/l4', '#' + includeDirectory])
def derive_symbols(config_header):
'''
Reads target header (config.h), derives symbols, and appends them to the same file.
Normally CML2 should do this on-the-fly but, doesn't so this function explicitly
does it here.
'''
archdir = None
subarchdir = None
platdir = None
driversdirs = []
taskLibraryNames = [f.name for f in Glob('tasks/lib*')]
config_symbols = gather_config_symbols(config_header)
for sym,val in config_symbols:
for arch in arch_to_dir:
if sym == arch:
archdir = arch_to_dir[arch]
if arch == "ARCH_TEST":
# These are derived from ARCH_TEST, but don't need to be written,
# because they are unused. Instead, __ARCH__ test etc. are derived
# from these and written to config.h because those are used by the
# build system. IOW these are transitional symbols.
config_symbols.append(("TEST_SUBARCH","1"))
config_symbols.append(("TEST_PLATFORM","1"))
for subarch in subarch_to_dir:
if sym == subarch:
subarchdir = subarch_to_dir[subarch]
for plat in plat_to_dir:
if sym == plat:
platdir = plat_to_dir[plat]
for driver in driver_to_dir:
if sym == driver:
# There can me multiple drivers, so making a list.
driversdirs.append(driver_to_dir[driver])
if archdir == None:
print "Error: No config symbol found for architecture"
sys.exit()
if subarchdir == None:
print "Error: No config symbol found for subarchitecture"
sys.exit()
if platdir == None:
print "Error: No config symbol found for platform"
sys.exit()
taskLibraries = []
for library in taskLibraryNames:
taskLibraries.append(SConscript('tasks/' + library + '/SConscript', variant_dir = buildDirectory + '/tasks/' + library, duplicate = 0, exports = {'environment': taskSupportLibraryEnvironment}))
# Update config.h with derived values. Unfortunately CML2 does not handle
# derived symbols well yet. This can be fixed in the future.
f = open(config_h, "a")
f.seek(0, 2)
f.write("/* Symbols derived from this file by SConstruct\derive_symbols() */")
f.write("\n#define __ARCH__\t\t%s\n" % archdir)
f.write("#define __PLATFORM__\t\t%s\n" % platdir)
f.write("#define __SUBARCH__\t\t%s\n\n" % subarchdir)
Depends(taskLibraries, taskSupportLibraryEnvironment['configFiles'])
f.close()
Alias ('tasklibs', taskLibraries)
########## Build the tasks ########################
def cml_config2header(target, source, env):
'''
Translate the output of cml2 configurator to a C header file,
to be included by the kernel.
'''
target = str(target[0])
source = str(source[0])
config_translator = cml2header.cml2header_translator()
if config_translator.translate(source, target) < 0:
print 'Configuration translation failed.'
##
## CML2 is incapable of deriving symbols. Hence, derived
## symbols are handled here.
derive_symbols(target)
def buildTask(programName, sources, environment, previousImage, extraCppPath=None):
e = environment.Clone()
e.Append(LINKFLAGS=['-Ttasks/' + programName + '/include/linker.lds'])
e.Append(LIBPATH=['#build/tasks/' + programName, '#build/lib/c/userspace/crt/sys-userspace/arch-arm'])
if extraCppPath: e.Append(CPPPATH=extraCppPath)
objects = e.StaticObject(sources)
Depends(objects, e['configFiles'])
program = e.Program(programName, objects + ['#' + e['userspace_crt0'][0].name])
physicalBaseLinkerScript = Command('include/physical_base.lds', previousImage, 'tools/pyelf/readelf.py --first-free-page ' + previousImage[0].path + ' >> $TARGET')
Depends(program, [physicalBaseLinkerScript, e['userspace_crt0']])
return program
# `hash' ('#') is a shorthand meaning `relative to
# the root directory'. But this only works for
# well-defined variables, not all paths, and only
# if we use a relative build directory for sources.
# Individual Build commands for configuration targets
tasksEnvironment = baseEnvironment.Clone(
CC = 'arm-none-linux-gnueabi-gcc',
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib'],
ASFLAGS = ['-D__ASSEMBLY__'],
LIBS = [libs['userspace']] + taskLibraries + ['gcc', libs['userspace']], #### TODO: Why have the userspace C library twice?
PROGSUFFIX = '.axf',
CPPDEFINES = ['__USERSPACE__'],
CPPPATH = ['#' + buildDirectory, '#' + buildDirectory + '/l4', '#' + includeDirectory, 'include', '#tasks/libl4/include', '#tasks/libmem', '#tasks/libposix/include'],
buildTask = buildTask)
cml_compiled_target = kernel_env.Command('#build/rules.out', '#configs/arm.cml', cml_compile)
cml_configured_target = kernel_env.Command('#build/config.out', '#build/rules.out', cml_configure)
cml_translated_target = kernel_env.Command(config_h, '#build/config.out', cml_config2header)
####
#### TODO: Why doe 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?
####
if "configure" in COMMAND_LINE_TARGETS:
# This ensures each time `scons configure' is typed, a configuration occurs.
# Otherwise the build system would think target is up-to-date and do nothing.
kernel_env.AlwaysBuild(cml_compiled_target)
kernel_env.AlwaysBuild(cml_configured_target)
kernel_env.AlwaysBuild(cml_translated_target)
userspaceRuntime = Command(crts['userspace'][0].name, crts['userspace'][0], 'ln -s $SOURCE.path $TARGET')
# Commandline aliases for each individual configuration targets
kernel_env.Alias('cmlcompile', cml_compiled_target)
kernel_env.Alias('cmlconfigure', cml_configured_target)
kernel_env.Alias('cmltranslate', cml_translated_target)
execfile('tasks/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('tasks/' + taskName + '/SConscript', variant_dir = buildDirectory + '/tasks/' + taskName, duplicate = 0, exports = {'environment': tasksEnvironment, 'previousImage': dependency[0]})
Depends(program, userspaceRuntime)
tasks.append(program)
if i < len(imageOrderData) - 1:
imageOrderData[i+1][1].append(program)
Depends(tasks, tasksEnvironment['configFiles'])
# Below alias is enough to trigger all config dependency tree.
kernel_env.Alias('configure', cml_translated_target)
Alias ('tasks', tasks)
########## Create the boot description ########################
taskName = 'bootdesc'
bootdescEnvironment = baseEnvironment.Clone(
CC = 'arm-none-linux-gnueabi-gcc',
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib', '-Ttasks/' + taskName + '/linker.lds'],
ASFLAGS = ['-D__ASSEMBLY__'],
PROGSUFFIX = '.axf',
LIBS = ['gcc'],
CPPPATH = ['#' + includeDirectory])
bootdesc = SConscript('tasks/' + taskName + '/SConscript', variant_dir = buildDirectory + '/tasks/' + taskName, duplicate = 0, exports = {'environment': bootdescEnvironment, 'images': [startAxf] + tasks})
Alias('bootdesc', bootdesc)
########## Do the packing / create loadable ########################
loaderEnvironment = baseEnvironment.Clone(
CC = 'arm-none-linux-gnueabi-gcc',
CCFLAGS = ['-g', '-nostdlib', '-ffreestanding', '-std=gnu99', '-Wall', '-Werror'],
LINKFLAGS = ['-nostdlib', '-T' + buildDirectory + '/loader/linker.lds'],
PROGSUFFIX = '.axf',
LIBS = [libelf, libs['baremetal'], 'gcc', libs['baremetal']],
CPPPATH = ['#libs/elf/include', '#' + buildDirectory + '/loader'])
#### TODO: Fix the tasks data structure so as to avoid all the assumptions.
loader = SConscript('loader/SConscript', variant_dir = buildDirectory + '/loader', duplicate = 0, exports = {'environment': loaderEnvironment, 'images':[startAxf, bootdesc] + tasks})
Alias('final', loader)
########## 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.
''')