autopilot: support arch-specific build directories

Replace the notion of board-specific platforms by clear definition
architecture and board.

Also added new command line switch for "targets"

  -t <target>         test target as triple of architecture-board-kernel,
                      e.g., arm_v7a-pbxa9-hw

Related to #3316
Fixes #3417
This commit is contained in:
Christian Helmuth 2019-06-14 16:18:58 +02:00
parent 7b1e3a9d63
commit 8d62f21b40
1 changed files with 143 additions and 85 deletions

View File

@ -5,9 +5,10 @@
# \author Norman Feske # \author Norman Feske
# \date 2011-07-2 # \date 2011-07-2
# #
# The autopilot utility automates the process of executing multiple run # The autopilot utility automates the process of executing multiple run scripts
# scripts on different platforms. For each executed run script, the exit # on different test targets. Each target is a combination of CPU architecture,
# value is checked and the result gets logged to a file. # hardware board, and kernel. For each executed run script, the exit value is
# checked and the result gets logged to a file.
# #
## ##
@ -43,10 +44,10 @@ proc default_test_dir { } { global env; return "/tmp/autopilot.$env(USER)" }
proc help { } { proc help { } {
set help_text { set help_text {
Automatically execute test cases on different platforms Automatically execute test cases on different test targets
usage: autopilot [-p <platform> ...] usage: autopilot [-t <target> ...]
[-k <kernel> ...] [-p <platform> -k <kernel> ...]
[-r <run-script> ...] [-r <run-script> ...]
[-d <test-dir>] [-j <make-jobs>] [-d <test-dir>] [-j <make-jobs>]
[-i <info-file-name>] [-i <info-file-name>]
@ -54,12 +55,20 @@ proc help { } {
[--stdout] [--skip-clean-rules] [--stdout] [--skip-clean-rules]
[--enable-ccache] [--enable-ccache]
-t <target> test target as triple of architecture-board-kernel,
e.g., arm_v7a-pbxa9-hw
-p <platform> platform as pair of architecture-board, e.g.,
x86_64-pc or arm_v8a-rpi3 (must specify -k too)
-k <kernel> kernel to use on all platforms configured via -p
-r <run-script> run scenario to test on all configured targets resp.
platform-kernel pairs
--cleanup remove test directory at exit
--enable-ccache use ccache instead of plain gcc
--force replace test directory if it already exists --force replace test directory if it already exists
--keep keep test directroy if it already exists --keep keep test directroy if it already exists
--cleanup remove test directory at exit
--stdout print test output instead of writing log files
--skip-clean-rules skip cleanall tests, keep build-directory content --skip-clean-rules skip cleanall tests, keep build-directory content
--enable-ccache use ccache instead of plain gcc --stdout print test output instead of writing log files
--time-stamp prepend log output lines with time stamps (requires ts utility) --time-stamp prepend log output lines with time stamps (requires ts utility)
} }
@ -111,83 +120,90 @@ proc fail { message error_code } {
## ##
# Return build directory used for the specified platform # Return build directory used for the specified architecture
# #
proc build_dir { platform } { proc build_dir { arch } {
global test_dir global test_dir
return [file join $test_dir $platform] return [file join $test_dir $arch]
}
##
# Return name of log file for test of 'run_script' on 'platform'
#
proc log_file_name { platform kernel run_script } {
return $platform.$kernel.$run_script.log
} }
## ##
# Return path to log file for test of 'run_script' on 'platform' # Return specific parts of target (arch-board-kernel)
# #
proc log_file { platform kernel run_script } { proc get_arch { target } { return [lindex [split $target "-"] 0] }
proc get_board { target } { return [lindex [split $target "-"] 1] }
proc get_kernel { target } { return [lindex [split $target "-"] 2] }
##
# Return name of log file for test of 'run_script' on target
#
proc log_file_name { arch board kernel run_script } {
return $arch.$board.$kernel.$run_script.log
}
##
# Return path to log file for test of 'run_script' on target
#
proc log_file { arch board kernel run_script } {
global test_dir global test_dir
return [file join $test_dir [log_file_name $platform $kernel $run_script]] return [file join $test_dir [log_file_name $arch $board $kernel $run_script]]
} }
## ##
# Return file descriptor for writing the log output of test case # Return file descriptor for writing the log output of test case
# #
proc log_fd { platform kernel run_script } { proc log_fd { arch board kernel run_script } {
# if '--stdout' was specified, don't write log output to files # if '--stdout' was specified, don't write log output to files
if {[get_cmd_switch --stdout]} { return stdout } if {[get_cmd_switch --stdout]} { return stdout }
# create file descriptor of log file on demand # create file descriptor of log file on demand
global _log_fds global _log_fds
if {![info exists _log_fds($platform,$kernel,$run_script)]} { if {![info exists _log_fds($arch,$board,$kernel,$run_script)]} {
set new_fd [open [log_file $platform $kernel $run_script] "WRONLY CREAT TRUNC"] set new_fd [open [log_file $arch $board $kernel $run_script] "WRONLY CREAT TRUNC"]
set _log_fds($platform,$kernel,$run_script) $new_fd set _log_fds($arch,$board,$kernel,$run_script) $new_fd
} }
return $_log_fds($platform,$kernel,$run_script) return $_log_fds($arch,$board,$kernel,$run_script)
} }
## ##
# Close file descriptor used for log output of test case # Close file descriptor used for log output of test case
# #
proc close_log_fd { platform kernel run_script } { proc close_log_fd { arch board kernel run_script } {
global _log_fds global _log_fds
if {[info exists _log_fds($platform,$kernel,$run_script)]} { if {[info exists _log_fds($arch,$board,$kernel,$run_script)]} {
close $_log_fds($platform,$kernel,$run_script) close $_log_fds($arch,$board,$kernel,$run_script)
unset _log_fds($platform,$kernel,$run_script) unset _log_fds($arch,$board,$kernel,$run_script)
} }
} }
## ##
# Execute single run script for specified platform # Execute single run script for specified target
# #
# \return true if run script succeeded # \return true if run script succeeded
# #
proc execute_run_script { platform kernel run_script } { proc execute_run_script { arch board kernel run_script } {
set return_value true set return_value true
set fd [log_fd $platform $kernel $run_script] set fd [log_fd $arch $board $kernel $run_script]
if {[catch { if {[catch {
if {[get_cmd_switch --time-stamp]} { if {[get_cmd_switch --time-stamp]} {
exec make -C [build_dir $platform] [file join run $run_script] KERNEL=$kernel \ exec make -C [build_dir $arch] [file join run $run_script] BOARD=$board KERNEL=$kernel \
|& ts "\[%F %H:%M:%S\]" >&@ $fd |& ts "\[%F %H:%M:%S\]" >&@ $fd
} else { } else {
exec make -C [build_dir $platform] [file join run $run_script] KERNEL=$kernel \ exec make -C [build_dir $arch] [file join run $run_script] BOARD=$board KERNEL=$kernel \
>&@ $fd >&@ $fd
} }
}]} { }]} {
set return_value false set return_value false
} }
close_log_fd $platform $kernel $run_script close_log_fd $arch $board $kernel $run_script
return $return_value return $return_value
} }
@ -197,27 +213,27 @@ proc execute_run_script { platform kernel run_script } {
# #
# \return list of unexpected files remaining after 'make cleanall' # \return list of unexpected files remaining after 'make cleanall'
# #
proc clean_build_dir { platform kernel } { proc clean_build_dir { arch board kernel } {
set fd [log_fd $platform $kernel cleanall] set fd [log_fd $arch $board $kernel cleanall]
# make returns the exit code 2 on error # make returns the exit code 2 on error
if {[catch { if {[catch {
exec make -C [build_dir $platform] cleanall KERNEL=$kernel >@ $fd exec make -C [build_dir $arch] cleanall BOARD=$board KERNEL=$kernel >@ $fd
}] == 2} { }] == 2} {
close_log_fd $platform $kernel cleanall close_log_fd $arch $board $kernel cleanall
return [list "clean rule terminated abnormally"] return [list "clean rule terminated abnormally"]
} }
close_log_fd $platform $kernel cleanall close_log_fd $arch $board $kernel cleanall
} }
## ##
# Obtain information about residual files in the build directory # Obtain information about residual files in the build directory
# #
proc build_dir_remainings { platform } { proc build_dir_remainings { arch } {
set remainings [split [exec sh -c "cd [build_dir $platform]; find . -mindepth 1"] "\n"] set remainings [split [exec sh -c "cd [build_dir $arch]; find . -mindepth 1"] "\n"]
set unexpected { } set unexpected { }
foreach r $remainings { foreach r $remainings {
@ -230,14 +246,14 @@ proc build_dir_remainings { platform } {
} }
proc build_failed_because_of_missing_run_script { platform kernel run_script } { proc build_failed_because_of_missing_run_script { arch board kernel run_script } {
# we cannot inspect any logfile when --stdout was used # we cannot inspect any logfile when --stdout was used
if {[get_cmd_switch --stdout]} { return 0 } if {[get_cmd_switch --stdout]} { return 0 }
# grep log output for the respective error message of the build system # grep log output for the respective error message of the build system
if {[catch { if {[catch {
exec grep {^\(\[....-..-.. ..:..:..] \)*Error: No run script for} [log_file $platform $kernel $run_script] exec grep {^\(\[....-..-.. ..:..:..] \)*Error: No run script for} [log_file $arch $board $kernel $run_script]
}]} { return 0 } }]} { return 0 }
return 1 return 1
} }
@ -247,11 +263,8 @@ proc build_failed_because_of_missing_run_script { platform kernel run_script } {
# Collect command-line arguments # Collect command-line arguments
# #
set platforms { } set targets { }
foreach_cmdline_arg p { global platforms; lappend platforms $p } foreach_cmdline_arg t { global targets; lappend targets $t }
set kernels { }
foreach_cmdline_arg k { global kernels; lappend kernels $k }
set run_scripts { } set run_scripts { }
foreach_cmdline_arg r { global run_scripts; lappend run_scripts $r } foreach_cmdline_arg r { global run_scripts; lappend run_scripts $r }
@ -265,25 +278,61 @@ foreach_cmdline_arg j { global make_jobs; set make_jobs $j }
# present help if explicitly requested # present help if explicitly requested
if {[get_cmd_switch --help]} { help; exit 0 } if {[get_cmd_switch --help]} { help; exit 0 }
#
# Handle platform-kernel combinations by adding to targets
#
set platforms { }
foreach_cmdline_arg p { global platforms; lappend platforms $p }
set kernels { }
foreach_cmdline_arg k { global kernels; lappend kernels $k }
if {![llength $platforms] != ![llength $kernels]} {
puts stderr "Error: -p and -k arguments only valid in combination"
help
exit -1
} elseif { [llength $platforms] } {
foreach p $platforms {
foreach k $kernels {
lappend targets $p-$k
}
}
}
unset platforms kernels
# present help if arguments do not suffice # present help if arguments do not suffice
if {![llength $platforms]} { if {![llength $run_scripts]} {
puts stderr "Error: invalid arguments" puts stderr "Error: no run script specified"
help help
exit -1 exit -1
} }
if {![llength $targets]} {
puts stderr "Error: no test targets specified"
help
exit -1
}
set targets [lsort -unique $targets]
# extract unique architectures from targets (for build-directory creation)
set architectures { }
foreach target $targets {
lappend architectures [get_arch $target]
}
set architectures [lsort -unique $architectures]
# print information about the parameters # print information about the parameters
puts "genode dir : [genode_dir]" puts "genode dir : [genode_dir]"
puts "platforms : $platforms" puts "targets : $targets"
puts "run scripts : $run_scripts" puts "architectures : $architectures"
puts "kernels : $kernels" puts "run scripts : $run_scripts"
puts "test dir : $test_dir" puts "test dir : $test_dir"
puts "make -j : $make_jobs" puts "make -j : $make_jobs"
# #
# We first create all build directory for all platforms to back out early if # We first create all build directory for all architectures to back out early
# any error occurs during the creation of build directories due to wrong # if any error occurs during the creation of build directories due to wrong
# command-line arguments. # command-line arguments.
# #
@ -298,21 +347,21 @@ if {[get_cmd_switch --force]} { wipe_test_dir }
# create build directories # create build directories
foreach platform $platforms { foreach arch $architectures {
set build_dir [build_dir $platform] set build_dir [build_dir $arch]
if {[get_cmd_switch --keep] && [file exists $build_dir]} { continue } if {[get_cmd_switch --keep] && [file exists $build_dir]} { continue }
if {[catch { if {[catch {
exec [genode_dir]/tool/create_builddir $platform BUILD_DIR=$build_dir exec [genode_dir]/tool/create_builddir $arch BUILD_DIR=$build_dir
}]} { }]} {
fail "create_builddir for platform $platform failed" -1 fail "create_builddir for architecture $arch failed" -1
} }
set build_conf [file join $build_dir etc build.conf] set build_conf [file join $build_dir etc build.conf]
if {![file exists $build_conf]} { if {![file exists $build_conf]} {
fail "build dir for $platform lacks 'etc/build.conf' file" -2 fail "build dir for $arch lacks 'etc/build.conf' file" -2
} }
# enable all repositories # enable all repositories
@ -345,26 +394,25 @@ set info_file "autopilot.log"
foreach_cmdline_arg i { global info_file; set info_file $i } foreach_cmdline_arg i { global info_file; set info_file $i }
set info_fd [open [file join $test_dir $info_file] "WRONLY CREAT TRUNC"] set info_fd [open [file join $test_dir $info_file] "WRONLY CREAT TRUNC"]
foreach platform $platforms { foreach t $targets {
foreach kernel $kernels { foreach run_script $run_scripts {
foreach run_script $run_scripts { puts $info_fd "[log_file_name [get_arch $t] [get_board $t] [get_kernel $t] $run_script]"
puts $info_fd "[log_file_name $platform $kernel $run_script]"
}
} }
} }
close $info_fd close $info_fd
# #
# Revisit each platform's build directory and execute all test cases # Revisit each architecture's build directory and execute all test cases for
# all specified targets
# #
## ##
# Print label identifying the specified test case to stderr # Print label identifying the specified test case to stderr
# #
proc print_step_label { platform kernel step } { proc print_step_label { board kernel step } {
puts -nonewline stderr "[format {%-30s} "$platform $kernel:"] [format {%-22s} $step] " puts -nonewline stderr "[format {%-30s} "$board $kernel:"] [format {%-22s} $step] "
} }
@ -385,24 +433,29 @@ set exit_value 0
# execute run scripts # execute run scripts
foreach platform $platforms { foreach arch $architectures {
puts stderr "\n--- platform $platform ---" puts stderr "\n--- architecture $arch ---"
foreach run_script $run_scripts { foreach run_script $run_scripts {
foreach kernel $kernels { foreach target $targets {
print_step_label $platform $kernel $run_script if { [get_arch $target] ne $arch } { continue }
set board [get_board $target]
set kernel [get_kernel $target]
print_step_label $board $kernel $run_script
set time_start [clock seconds] set time_start [clock seconds]
set result [execute_run_script $platform $kernel $run_script] set result [execute_run_script $arch $board $kernel $run_script]
set elapsed [elapsed_time $time_start [clock seconds]] set elapsed [elapsed_time $time_start [clock seconds]]
if {$result} { if {$result} {
puts stderr "-> OK ($elapsed)" puts stderr "-> OK ($elapsed)"
} else { } else {
if {[build_failed_because_of_missing_run_script $platform $kernel $run_script]} { if {[build_failed_because_of_missing_run_script $arch $board $kernel $run_script]} {
puts stderr "-> UNAVAILABLE" puts stderr "-> UNAVAILABLE"
} else { } else {
puts stderr "-> ERROR ($elapsed)" puts stderr "-> ERROR ($elapsed)"
@ -415,13 +468,18 @@ foreach platform $platforms {
if {[get_cmd_switch --skip-clean-rules]} continue if {[get_cmd_switch --skip-clean-rules]} continue
# execute and validate cleanall rule # execute and validate cleanall rule
foreach kernel $kernels { foreach target $targets {
print_step_label $platform $kernel cleanall if { [get_arch $target] ne $arch } { continue }
clean_build_dir $platform $kernel
set board [get_board $target]
set kernel [get_kernel $target]
print_step_label $board $kernel cleanall
clean_build_dir $arch $board $kernel
puts stderr "-> DONE" puts stderr "-> DONE"
} }
set pollution [build_dir_remainings $platform] set pollution [build_dir_remainings $arch]
if {[llength $pollution] == 0} { if {[llength $pollution] == 0} {
puts stderr "-> OK" puts stderr "-> OK"
} else { } else {