genode/tool/run/run
Norman Feske 5ae0dab6c5 mk: remove implicit build of shared libraries
This patch removes the implicit build of all shared libraries a target
depends on. Targets only depend on the respective ABIs instead. This
alleviates the need to locally build complex shared libraries (think of
Qt) when developing applications. Instead, application developers can
use binary depot archives.

The implementation splits the mk/lib.mk file into three files:
- mk/a.mk   for building one static library (.lib.a)
- mk/so.mk  for building one shared object  (.lib.so)
- mk/abi.mk for building one ABI stub       (.abi.so)

Furthermore, the commit moves messages and the collection of build
artifacts to var/libdeps, triggers the build of kernel-specific
ld-<kernel>.lib.so, and prunes the lib-dependency tree at ABIs.

Fixes #5061
2023-11-28 14:44:29 +01:00

1238 lines
29 KiB
Plaintext
Executable File

#!/usr/bin/expect
#
# \brief Framework for running automated tests
# \author Norman Feske
# \date 2010-03-16
#
# Usage: run --name <run_name> --include <run_script> ...
#
# The '--name' argument is used for as name for the boot-image and
# temporary directories. The files includes via the '--include'
# argument provide platform-specific additions/refinements to the
# test framework as well as the actual test steps.
#
##
# Remove leading and trailing whitespace from string
#
proc strip_whitespace {string} {
regsub -all {^\s+} $string "" string
regsub -all {\s+$} $string "" string
return $string
}
##
# Check if the specified spec requirement is satisfied
#
proc assert_spec {spec} {
global specs
if {[lsearch $specs $spec] == -1} {
puts stderr "Test requires '$spec'"
exit 0
}
}
##
# Build genode targets specified as space-separated strings
#
# If the build process fails, this procedure will exit the program with
# the error code -4.
#
proc build {targets} {
global _collected_build_artifacts
if {[llength $targets] == 0} return
if {[get_cmd_switch --skip-build]} return
regsub -all {\s\s+} $targets " " targets
puts "building targets: $targets"
set timeout 10000
set pid [eval "spawn make $targets"]
expect { eof { } }
if {[lindex [wait $pid] end] != 0} {
puts "Error: Genode build failed"
exit -4
}
# record the build artifacts created during the build process
set artifacts_log [exec sed -n "/BUILD_ARTIFACTS/s/^.*= //p" progress.log]
lappend _collected_build_artifacts {*}[split $artifacts_log "\n"]
puts "genode build completed"
}
##
# Return list of build artifacts created during 'build'
#
# The returned artifacts can be used as boot-modules arguments for
# the 'build_boot_image' step.
#
proc build_artifacts { } {
global _collected_build_artifacts
if {![info exists _collected_build_artifacts]} {
return { } }
# use lsort to remove duplicates
return [lsort -unique $_collected_build_artifacts]
}
##
# Create a fresh boot directory
#
proc create_boot_directory { } {
exec rm -rf [run_dir]
exec mkdir -p [run_dir]
exec mkdir -p [run_dir]/genode
}
##
# Append string to variable only if 'condition' is satisfied
#
proc append_if {condition var args} {
upvar $var up_var
if {$condition} { append up_var [join $args ""] }
}
##
# Append element to list only if 'condition' is satisfied
#
proc lappend_if {condition var string} {
upvar $var up_var
if {$condition} { lappend up_var $string }
}
##
# Check syntax of specified XML file using xmllint
#
proc check_xml_syntax {xml_file} {
if {![have_installed xmllint]} {
puts "Warning: Cannot validate config syntax (please install xmllint)"
return;
}
if {[catch {exec xmllint --noout $xml_file} result]} {
puts stderr $result
puts stderr "Error: Invalid XML syntax in $xml_file"
exit 1
}
}
##
# Validate configuration file to match an XML schema using xmllint
#
# \param bin binary name of the component behind the config
# \param xml_file configuration file
# \param xsd_file configuration schema file
# \param avail_xsd_files list of xsd files that might be used for
# configurations of children of the component
# \param nesting_level level of recursive calls of this procedure
#
proc check_config {bin xml_file xsd_file label avail_xsd_files xsd_inc nesting_level} {
# check prerequisites if this is not a recursive call
if {$nesting_level == 0} {
if {![have_installed xmllint]} {
puts ""
puts "Warning: cannot validate config syntax\
(please install xmllint)"
puts ""
exit 1
}
}
# check the given component configuration itself
puts " CHECK $label"
if {[catch {exec xmllint --noout --path $xsd_inc -schema $xsd_file $xml_file} result]} {
if {$result != "$xml_file validates"} {
puts stderr "$result"
puts stderr "Error: Invalid XML syntax"
exit 1
}
}
#
# If this is no instance of Genodes Init component, we can skip checking
# for potential child configurations.
#
if {$bin != "init"} {
return
}
#
# The names of the available XSD files tell us for which binaries we
# can do a check. So iterate through the XSD files and try to find
# corresponding child instances.
#
foreach child_xsd_file $avail_xsd_files {
set child_bin [file tail [file rootname $child_xsd_file]]
for {set child_instance 1} {1} {incr child_instance} {
#
# Try to find a child instance that uses this binary. We start
# with selecting the first matching start node, than the
# second, and so on. As soon as xmlscarlet returns an empty
# string, we know that there is no further matching start node.
#
set xpath_start_cond "child::binary\[@name=\'$child_bin\'\] or \
not(child::binary) and @name=\'$child_bin\'"
set xpath "string(/config/start\[$xpath_start_cond\]\[$child_instance\]/@name)"
set select_xpath "xmllint --xpath \"$xpath\" $xml_file"
if {[catch {exec {*}$select_xpath} child_name]} {
#
# No further child instance that uses this binary.
# Proceed with next binary.
#
break
}
#
# If the child has a config, check it with a recursive
# call to this procedure.
#
set xpath "/config/start\[$xpath_start_cond\]\[$child_instance\]/config"
set select_xpath "xmllint --xpath \"$xpath\" $xml_file"
if {[catch {exec {*}$select_xpath} child_xml]} {
# the child has no config
break
}
# write child config to temporary file
set child_xml_file ".config_$nesting_level.xml.tmp"
set child_xml_fd [open $child_xml_file w]
puts $child_xml_fd $child_xml
close $child_xml_fd
# call this procedure again on the child config file
set child_label "$label -> $child_name"
check_config $child_bin $child_xml_file $child_xsd_file \
$child_label $avail_xsd_files $xsd_inc \
[expr $nesting_level+1]
# clean up
exec rm -f $child_xml_file
}
}
}
##
# Install boot module with the given 'name' and content
#
proc install_boot_module { name args } {
set fh [open [file join [run_dir] genode $name] "WRONLY CREAT TRUNC"]
puts $fh [join $args {}]
close $fh
}
##
# Install content of specified variable as init config file
#
proc install_config { args } {
install_boot_module "config" [join $args {}]
}
##
# Check consistency between [build_artifacts] and build_boot_image arguments
#
proc check_for_missing_lib_build { binaries } {
set artifacts [build_artifacts]
set missing_lib_builds { }
set proposed_build_targets { }
foreach binary $binaries {
if {[lsearch $artifacts $binary] == -1} {
if {[regexp {(.*)\.lib\.so$} $binary dummy libname]} {
lappend missing_lib_builds $binary
lappend proposed_build_targets "lib/$libname" } } }
if {[llength $missing_lib_builds] == 0} {
return }
set plural ""
if {[llength $missing_lib_builds] > 1} { set plural "s" }
puts stderr ""
puts stderr "Error: missing build argument$plural for: $missing_lib_builds\n"
puts stderr " The build_boot_image argument$plural may be superfluous or"
puts stderr " the build step lacks the argument$plural: $proposed_build_targets\n"
puts stderr " Consider using \[build_artifacts\] as build_boot_image argument to"
puts stderr " maintain consistency beween the build and build_boot_image steps."
puts stderr ""
exit 1
}
##
# Integrate specified binaries into boot image
#
# \param binaries space-separated list of file names located within the
# '<build-dir>/bin/' directory
#
proc build_boot_image { binaries } {
check_for_missing_depot_archives
check_for_missing_lib_build $binaries
# lookup XSD files in the run-script-specific folder
set xsd_files ""
if {[catch {exec find [run_dir]/genode -name *.xsd} find_stdout] == 0} {
set xsd_files [split "$find_stdout" "\n"]
}
# lookup XSD files in the binaries folder
foreach binary $binaries {
if {[file exists "bin/$binary.xsd"]} {
lappend xsd_files "bin/$binary.xsd"
}
}
# determine which XSD file to use for the init config
set init_xsd_file ""
foreach xsd_file $xsd_files {
set filename [file tail $xsd_file]
if {$filename == "init.xsd"} {
set init_xsd_file $xsd_file
}
}
# determine include directories that can be used by the XSD files
global repositories;
set xsd_inc ""
foreach repo $repositories {
if {[file exists $repo/xsd]} {
append xsd_inc "$repo/xsd " }
}
# check configurations of init and its children
puts "checking configuration syntax"
check_config init [run_dir]/genode/config $init_xsd_file init $xsd_files $xsd_inc 0
run_boot_dir $binaries
}
# set expect match-buffer size
match_max -d 40000
##
# Execute Genode
#
# \param wait_for_re regular expression that matches the test completion
# \param timeout_value timeout in seconds
# \param spawn_id spawn_id of a already running and spawned process
# spawn_id may be a list of spawned processes if needed
# \global output contains the core output (modified)
#
# If the function is called without any argument, Genode is executed in
# interactive mode.
#
# If the test execution times out, this procedure will exit the program with
# the error code -2.
#
proc run_genode_until {{wait_for_re forever} {timeout_value 0} {running_spawn_id -1}} {
#
# If a running_spawn_id is specified, wait for the expected output
#
if {$running_spawn_id != -1} {
wait_for_output $wait_for_re $timeout_value $running_spawn_id
return;
}
set retry 3
while { $retry != 0 } {
#
# Depending an the used run module, a reset can include
# shutting the power off and turning it on again.
#
if (![run_power_cycle]) {
puts "Power cycle step failed, retry."
sleep 3
incr retry -1;
continue
}
if {![run_load]} {
puts "Load step failed, retry."
# kill the spawned load process if there is one
if {[load_spawn_id] != -1} {
kill_spawned [load_spawn_id]
}
incr retry -1;
continue;
}
if {![run_log $wait_for_re $timeout_value]} {
puts "Log step failed, retry."
incr retry -1;
continue;
}
return;
}
puts stderr "Boot process failed 3 times in series. I give up!";
exit -1;
}
##
# "Safely" kill spawned process
#
proc kill_spawned {spawn_id} {
set pid [exp_pid -i $spawn_id]
close -i $spawn_id
exec kill -9 $pid
wait -i $spawn_id
}
##
# Exeute command line with retry and optional timeout per try
#
# \param retry number of attempts before giving up
# \param cmd commane line to execute
# \param success_re output denoting successful attempt
# \param to timeout per attempt (optional)
#
# \return list { output of last attempt, spawn id }
#
# The output of each attempt is checked with the regular expression
# 'success_re' and presumed successful on a match. The proc can also be used to
# just execute one attempt with empty 'success_re' but a timeout.
#
proc retry { retry cmd success_re {to -1} } {
set success false
set output ""
while {$retry > 0} {
spawn {*}$cmd
sleep $to
expect {
-timeout 0
default { close; wait }
-re $success_re {
set success true
set output $expect_out(buffer)
}
}
if {$success} { break }
incr retry -1
}
return [list $output $spawn_id]
}
##
# Remove color information from output
#
proc filter_out_color_escape_sequences { } {
global output
regsub -all {\e\[.*?m} $output "" output
}
##
# Remove superfluous empty lines and unify line endings from output
#
proc trim_lines { } {
global output
regsub -all {[\r\n]+} $output "\n" output
}
##
# Filter output based on the specified pattern
#
# Only those lines that match the pattern are preserved.
#
proc grep_output {pattern} {
global output
filter_out_color_escape_sequences
trim_lines
set output_list [split $output "\n"]
set filtered ""
foreach line $output_list {
if {[regexp $pattern $line]} {
append filtered "$line\n"
}
}
set output $filtered
}
##
# Unify known variations that appear in the test output
#
# \global output test output (modified)
#
proc unify_output {pattern replacement} {
global output
regsub -all $pattern $output $replacement output
}
##
# Compare output against expected output line by line
#
# \param good expected test output
# \global output test output
#
# This procedure will exit the program with the error code -1 if the
# comparison fails.
#
proc compare_output_to { good } {
global output
set output_list [split [strip_whitespace $output] "\n"]
set good_list [split [strip_whitespace $good] "\n"]
set i 0
set mismatch_cnt 0
foreach good_line $good_list {
set output_line [strip_whitespace [lindex $output_list $i]]
set good_line [strip_whitespace $good_line]
if {$output_line != $good_line} {
puts ""
puts stderr "Line $i of output is unexpected"
puts stderr " expected: '$good_line'"
puts stderr " got: '$output_line'"
incr mismatch_cnt
}
incr i
}
#
# if $good is empty the foreach-loop isn't entered
# so we've to check for it separately
#
if {![llength $good_list] && [llength $output_list]} {
foreach output_line $output_list {
set output_line [strip_whitespace $output_line]
puts ""
puts stderr "Line $i of output is unexpected"
puts stderr " got: '$output_line'"
incr mismatch_cnt
incr i
}
}
if {$mismatch_cnt > 0} {
puts "Error: Test failed, $mismatch_cnt unexpected lines of output"
exit -1
}
}
##
# Return true if command-line switch was specified
#
proc get_cmd_switch { arg_name } {
global argv
return [expr [lsearch $argv $arg_name] >= 0]
}
##
# Return command-line argument value
#
# If a argument name is specified multiple times, a
# list of argument values is returned.
#
proc get_cmd_arg { arg_name default_value } {
global argv
# find argument name in argv list
set arg_idx_list [lsearch -all $argv $arg_name]
if {[llength $arg_idx_list] == 0} { return $default_value }
set result {}
foreach arg_idx $arg_idx_list {
set next_idx [expr $arg_idx + 1]
# stop if argv ends with the argument name
if {$next_idx >= [llength $argv]} continue
# return list element following the argument name
lappend result [lindex $argv $next_idx]
}
# if argument occurred only once, return its value
if {[llength $result] == 1} { return [lindex $result 0] }
# if argument occurred multiple times, contain list of arguments
return $result
}
##
# Return command-line argument value
#
# If a argument name is specified multiple times, return only the
# first match.
#
proc get_cmd_arg_first { arg_name default_value } {
global argv
set arg_idx [lsearch $argv $arg_name]
if {$arg_idx == -1} { return $default_value }
return [lindex $argv [expr $arg_idx + 1]]
}
#
# Read command-line arguments
#
set run_name [get_cmd_arg --name "noname"]
set genode_dir [get_cmd_arg --genode-dir ""]
set cross_dev_prefix [get_cmd_arg --cross-dev-prefix ""]
set specs [get_cmd_arg --specs ""]
set board_var [get_cmd_arg --board ""]
set repositories [get_cmd_arg --repositories ""]
# accessor functions for command-line arguments
proc run_name { } { global run_name; return $run_name }
proc run_dir { } { global run_name; return var/run/$run_name }
proc genode_dir { } { global genode_dir; return $genode_dir }
proc cross_dev_prefix { } { global cross_dev_prefix; return $cross_dev_prefix }
##
# Return true if spec value is set for the build
#
proc have_spec {spec} { global specs; return [expr [lsearch $specs $spec] != -1] }
##
# Return true if specified program is installed
#
proc have_installed {program} {
if {[auto_execok "$program"] != ""} { return true; }
return false;
}
##
# Check if a shell command is installed
#
# \param command name of the command to search
#
# \return absolute path of command if found, or exists if not
#
proc installed_command {command} {
set path [auto_execok $command]
if {$path != ""} {
return $path }
set dir { /sbin /usr/sbin /usr/local/bin }
foreach location $dir {
append location / $command
if { [file exists $location] == 1} {
return $location
}
}
puts stderr "Error: '$command' command could be not found. Please make sure to install the"
puts stderr " packet containing '$command', or make it available in your PATH variable.\n"
exit 1
}
##
# Return first repository containing the given path
#
proc repository_contains { rep_rel_path } {
global repositories;
foreach rep $repositories {
if {[file exists [file join $rep $rep_rel_path]]} {
return $rep }
}
}
##
# Return path to first file found in the available repositories
#
proc select_from_repositories { rep_rel_path } {
set rep_dir [repository_contains $rep_rel_path]
if {[llength $rep_dir]} {
return [file join $rep_dir $rep_rel_path] }
puts stderr "Error: $rep_rel_path not present in any repository"
exit -8
}
##
## Utilities for performing steps that are the same on several platforms
##
##
# Read kernel location from build-directory configuration
#
# If config file does not exist or if there is no 'KERNEL' declaration in the
# config file, the function returns 'default_location'. If the config file
# points to a non-existing kernel image, the function aborts with the exit
# value -6.
#
proc kernel_location_from_config_file { config_file default_location } {
global _kernel
if {![info exists _kernel]} {
if {[file exists $config_file]} {
set _kernel [exec sed -n "/^KERNEL/s/^.*=\\s*//p" $config_file]
# check if the regular expression matched
if {$_kernel != ""} {
if {[file exists $_kernel]} {
return $_kernel
} else {
puts stderr "Error: kernel specified in '$config_file' does not exist"
exit -6
}
}
}
# try to fall back to version hosted with the Genode build directory
set _kernel $default_location
}
return $_kernel
}
##
# Return name of kernel-specific binary for a given generic name
#
# The boot_dir plugin may provide functions named 'binary_name_<binary>'
# where '<binary>' stands for a generic name like "timer" or "nic". The
# function returns the name of the binary to integrate into the boot
# directory under the name '<binary>'.
#
# If no such function exists, it returns the argument as is. This is the
# case for regular binaries that appear in the boot directory under their
# original name.
#
proc kernel_specific_binary { binary {silent ""} } {
regsub -all {\.} $binary "_" function_suffix
set function_name "binary_name_$function_suffix"
if {[info procs $function_name] == $function_name} {
set binary_name [$function_name]
if {$silent != "silent"} {
puts "using '$binary_name' as '$binary'"
}
return [$function_name]
}
return $binary
}
proc copy_file {src dst} {
file copy -force $src $dst
}
proc copy_genode_binaries_to_run_dir { binaries } {
foreach binary $binaries {
#
# Normalize kernel-specific file names, needed when a run script passes
# [build_artifacts] to the build_boot_image command. The build artfact
# fetched from the progress.log has the kernel-specific name.
#
foreach name { "timer" "core" "ld.lib.so" } {
if {$binary == [kernel_specific_binary $name silent]} {
set binary $name } }
copy_file bin/[kernel_specific_binary $binary] [run_dir]/genode/$binary
}
}
##
# Wait for a specific output of a already running spawned process
#
proc wait_for_output { wait_for_re timeout_value running_spawn_id } {
global output
if {$wait_for_re == "forever"} {
set timeout -1
interact {
\003 {
send_user "Expect: 'interact' received 'strg+c' and was cancelled\n";
exit
}
-i $running_spawn_id
}
} else {
set timeout $timeout_value
}
set platform_msg [run_boot_string]
if {$platform_msg eq ""} {
set platform_msg "undefined platform command startup string sequence"
}
expect {
-i $running_spawn_id $platform_msg { puts stderr "Error: platform rebooted unexpectedly"; exit -4 }
-i $running_spawn_id -re $wait_for_re { }
eof { puts stderr "Error: Spawned process died unexpectedly"; exit -3 }
timeout { puts stderr "Error: Test execution timed out"; exit -2 }
}
append output $expect_out(buffer)
}
##
# Remove 'genode' directory in 'run_dir' unless --preserve-genode-dir is present
# in RUN_OPT
#
proc remove_genode_dir { } {
global env
if {![get_cmd_switch --preserve-genode-dir]} {
exec rm -rf [run_dir]/genode
}
}
##
## Fall-back implementations of all run module procedures
##
##
# Dummy boot_string procedure
#
proc run_boot_string { } { return ""; }
##
# Fall-back boot_dir module
#
# If this function is called someone forgot to include an appropriate boot_dir
# module. So, we exit with an error.
#
proc run_boot_dir { binaries } {
puts stderr "Error: boot_dir module missing, e.g., '--include boot_dir/hw'"
exit 1
}
##
# Bring the boot-directory content into a form that can actually be booted
#
# The form suitable for booting depends on the platform and the used boot
# loader. By convention, this function can expect the presence of the boot
# image in the form of an [run_dir]/boot/image.elf file.
#
proc run_image { } { return true; }
##
# Dummy load module
#
proc run_load { } { return true; }
##
# Dummy output module
#
# XXX explain why exit 0 is appropiate
#
proc run_log { wait_for_re timeout_value } { exit 0 }
##
# Dummy power_on module
#
proc run_power_on { } { return true; }
##
# Dummy power_off module
#
proc run_power_off { } { return true; }
##
# Default power cycle fallback procedure
#
proc run_power_cycle { } {
#
# On targets that are directly connected to a socket,
# turn the socket off and on again. On targets that
# use a module were that is not required, e.g. softreset,
# power_off is a NOP. Note, we give the target some time
# to effectively drain energy before switching it on again.
#
run_power_off
sleep 1
return [run_power_on]
}
##
# Default core linker options
#
proc core_ld_opts { } {
set ret { -Wl,-T }
lappend ret "-Wl,[genode_dir]/repos/base/src/ld/genode.ld"
return $ret
}
##
# Default core link address
#
proc core_link_address { } { return "0x01000000" }
##
# Check if a specific file is included
#
proc have_include { name } {
global include_list
foreach element $include_list {
if {[string equal $element $name]} {
return true
}
}
return false
}
##
# Override the exit procedure
#
# We have to override the exit procedure to make sure that a loaded
# run_power_off procedure is in any case execute when stopping the
# execution of the run tool.
#
rename exit real_exit
proc exit {{status 0}} {
run_power_off
real_exit $status
}
##
# Determine terminal program
#
proc terminal { } {
global env
return x-terminal-emulator
}
##
# Return the board to build for
#
proc board { } {
global board_var
if {$board_var eq ""} {
puts "Unknown platform no BOARD variable set"
exit 1
}
return $board_var
}
##
# Return whether the board equals to the given string
#
proc have_board {board} { global board_var; return [expr {$board_var} eq {$board}] }
##
# Determine GDB executable installed at the host
#
proc gdb { } {
if {[have_installed "[cross_dev_prefix]gdb"]} {
return "[cross_dev_prefix]gdb" }
return [installed_command gdb]
}
##
# Generate assembly code aggregating boot-module data from specified files.
#
proc generate_boot_modules_asm {path modules} {
# architecture dependent definitions
if {[have_spec "64bit"]} { set address_type ".quad"
} else { set address_type ".long" }
# header
set asm_src {}
append asm_src ".set MIN_PAGE_SIZE_LOG2, 12\n"
append asm_src ".set DATA_ACCESS_ALIGNM_LOG2, 3\n"
append asm_src "\n"
append asm_src ".section .data\n"
append asm_src "\n"
append asm_src ".p2align DATA_ACCESS_ALIGNM_LOG2\n"
append asm_src ".global _boot_modules_headers_begin\n"
append asm_src "_boot_modules_headers_begin:\n"
append asm_src "\n"
# module list
set i 0
foreach module $modules {
incr i
append asm_src "${address_type} _boot_module_${i}_name\n"
append asm_src "${address_type} _boot_module_${i}_begin\n"
append asm_src "${address_type} _boot_module_${i}_end -"
append asm_src " _boot_module_${i}_begin\n"
append asm_src "\n"
}
append asm_src ".global _boot_modules_headers_end\n"
append asm_src "_boot_modules_headers_end:\n"
append asm_src "\n"
# module names
set i 0
foreach module $modules {
incr i
append asm_src ".p2align DATA_ACCESS_ALIGNM_LOG2\n"
append asm_src "_boot_module_${i}_name:\n"
append asm_src ".string \"${module}\"\n"
append asm_src ".byte 0\n"
append asm_src "\n"
}
# header end
append asm_src ".section .data.boot_modules_binaries\n"
append asm_src "\n"
append asm_src ".global _boot_modules_binaries_begin\n"
append asm_src "_boot_modules_binaries_begin:\n"
append asm_src "\n"
# module data
set i 0
foreach module $modules {
incr i
append asm_src ".p2align MIN_PAGE_SIZE_LOG2\n"
append asm_src "_boot_module_${i}_begin:\n"
append asm_src ".incbin \"${path}/${module}\"\n"
append asm_src "_boot_module_${i}_end:\n"
append asm_src "\n"
}
append asm_src ".p2align MIN_PAGE_SIZE_LOG2\n"
append asm_src ".global _boot_modules_binaries_end\n"
append asm_src "_boot_modules_binaries_end:\n"
return $asm_src
}
##
# Link core image containing given modules
#
proc build_core {lib modules target link_address} {
# generate assembly code aggregating the modules data
set asm_src [generate_boot_modules_asm [run_dir]/genode $modules]
# architecture dependent definitions
set arch {}
if {[have_spec "x86_64"]} { set arch { -m64 -mcmodel=large } }
if {[have_spec "x86_32"]} { set arch -m32 }
# determine the libgcc
set libgcc [exec [cross_dev_prefix]gcc -print-libgcc-file-name {*}$arch]
# compile the boot modules into one object file
exec [cross_dev_prefix]gcc {*}$arch -c -x assembler -o [run_dir].boot_modules.o - << $asm_src
# link final image
exec [cross_dev_prefix]g++ {*}$arch -nostdlib {*}[core_ld_opts] \
-Wl,-z -Wl,max-page-size=0x1000 \
-Wl,-Ttext=$link_address -Wl,-gc-sections \
-Wl,-nostdlib -Wl,--whole-archive -Wl,--start-group \
$lib [run_dir].boot_modules.o -Wl,--no-whole-archive \
-Wl,--end-group $libgcc -o $target
}
##
# Return kernel-specific files to be excluded from the core image
#
proc kernel_files { } { return { } }
##
# Generate bootable core image containing all boot-modules
#
proc build_core_image { modules } {
set core_obj [kernel_specific_binary core.a]
# replace 'core' with actual core-object name in 'modules' list
if {[lsearch $modules "core"] != -1} {
set idx [lsearch $modules "core"]
set modules [lreplace $modules $idx $idx]
lappend modules $core_obj
}
copy_genode_binaries_to_run_dir $modules
# create core binary without modules for debugging
if {[file exists debug/$core_obj]} {
build_core debug/$core_obj {} [run_dir].core [core_link_address]
}
# determine modules to be incorporated into the core image
set modules [glob -nocomplain -tails -directory [run_dir]/genode/ *]
set excluded_modules [kernel_files]
lappend excluded_modules $core_obj
foreach excluded $excluded_modules {
set modules [lsearch -inline -not -all $modules $excluded] }
# check syntax of all boot modules named *.config
foreach file [glob -nocomplain [run_dir]/genode/*.config] {
check_xml_syntax $file }
# create core binary containing the boot modules
build_core [run_dir]/genode/$core_obj $modules [run_dir]/image.elf [core_link_address]
exec [cross_dev_prefix]strip [run_dir]/image.elf
# Save config part of the image.elf for easy inspection
exec cp -f [run_dir]/genode/config [run_dir].config
}
proc build_initrd { modules } {
copy_genode_binaries_to_run_dir $modules
set modules [glob -nocomplain -tails -directory [run_dir]/genode/ *]
set excluded_modules [kernel_files]
foreach file [glob -nocomplain [run_dir]/genode/*.config] {
check_xml_syntax $file }
exec cp -f [run_dir]/genode/config [run_dir].config
set here [pwd]
cd [run_dir]
puts "generating initrd"
exec cp genode/initramfs init
exec mkdir tmp
exec mkdir dev
set files "init\ntmp\ndev\ngenode\n"
append files [exec find genode -type f,l -printf "genode/%f\n"]
exec -ignorestderr echo $files | [installed_command cpio] -o -L -H newc > initrd
#workaround because cpio fails to compress broken links sometimes
exec touch dev/platform_info
cd genode
exec ln -s ../dev/platform_info platform_info
cd ..
exec -ignorestderr echo "genode/platform_info" | [installed_command cpio] -o -A -H newc -O initrd
cd ${here}
}
source [genode_dir]/tool/run/depot.inc
source [genode_dir]/tool/run/qemu.inc
##
## Execution of run scripts
##
set include_list { }
##
# Read and execute files specified as '--include' arguments
#
# Out of convenience run modules may be included by using a relative path.
#
foreach include_name [get_cmd_arg --include ""] {
# first check if the include name is absolute
if {[string first "/" $include_name] == 0} {
puts "including $include_name"
lappend include_list $include_name
source $include_name
continue
}
# if it is relative, check repositories and tool directory for run modules
set search_dirs {}
foreach rep_dir $repositories {
lappend search_dirs [file join $rep_dir tool run] }
lappend search_dirs [genode_dir]/tool/run
set found 0
foreach dir $search_dirs {
set include_file $dir/$include_name
if {[file exists $include_file] == 1} {
puts "including $include_file"
lappend include_list $include_name
source $include_file
set found 1
break
}
}
if {!$found} {
puts stderr "Aborting, could not load '$include_file'"
exit -1;
}
}
puts "\nRun script execution successful."
exit 0