mirror of
https://github.com/drasko/codezero.git
synced 2026-01-12 02:43:15 +01:00
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:
679
SConstruct
679
SConstruct
@@ -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.
|
||||
''')
|
||||
|
||||
Reference in New Issue
Block a user