#!/bin/bash #Usage message function usage { echo 'ARM Emulator Cross Build and Test Script' echo 'This script cross builds coreclr source and tests the binaries generated' echo '' echo 'Typical usage:' echo ' coreclr source is at ~/clr' echo ' corefx source is at ~/cfx' echo ' --testRootDir and --mscorlibDir have been built on Windows/downloaded from dotnet-ci.cloudapp.net' echo ' --coreFxNativeBinDir has been built using cross build' echo ' --coreFxBinDir has been built on Linux' echo '$ cd ~/clr' echo '$ ./tests/scripts/arm32_ci_script.sh' echo ' --emulatorPath=/opt/linux-arm-emulator' echo ' --mountPath=/opt/linux-arm-emulator-root' echo ' --buildConfig=Release' echo ' --testRootDir=~/Downloads/Windows_NT.x64.Release' echo ' --mscorlibDir=~/clr/bin/Product/Linux.armel.Release' echo ' --coreFxNativeBinDir=~/cfx/bin/Linux.armel.Release' echo ' --coreFxBinDir="~/cfx/bin/Linux.AnyCPU.Release;~/cfx/bin/Unix.AnyCPU.Release;~/cfx/bin/AnyOS.AnyCPU.Release"' echo ' --testDirFile=~/clr/tests/testsRunningInsideARM.txt' echo '' echo 'Required Arguments:' echo ' --emulatorPath= : Path of the emulator folder (without ending /)' echo ' /platform/rootfs-t30.ext4 should exist' echo ' --mountPath= : The desired path for mounting the emulator rootfs (without ending /)' echo ' This path is created if not already present' echo ' --buildConfig= : The value of config should be either Debug, Checked or Release' echo ' Any other value is not accepted' echo 'Optional Arguments:' echo ' --mode= : docker or emulator (default)' echo ' --arm : Build using hard ABI' echo ' --armel : Build using softfp ABI (default)' echo ' --linuxCodeName= : Code name for Linux: For arm, trusty (default) and xenial. For armel, tizen' echo ' --skipRootFS : Skip building rootfs' echo ' --skipTests : Presenting this option skips testing the generated binaries' echo ' If this option is not presented, then tests are run by default' echo ' using the other test related options' echo ' --skipmscorlib : Skips generating mscorlib.dll on Linux' echo ' If tests are run and this option is not used,' echo ' then --mscorlibDir option to this script is mandatory' echo ' -v --verbose : Build made verbose' echo ' -h --help : Prints this usage message and exits' echo '' echo 'Test related Arguments (mandatory if --skipTests is not used):' echo ' --testRootDir= : The root directory of the test build' echo ' --mscorlibDir= : The directory containing the mscorlib.dll binary' echo ' If provided, then the mscorlib.dll in this directory is' echo ' used for tests instead of the built mscorlib.dll' echo ' --coreFxNativeBinDir= : The directory of the CoreFX native build' echo ' --coreFxBinDir="[;]" : List one or more directories with CoreFX managed build binaries' echo ' --testDirFile= : Runs tests only in the directories specified by the file at ' echo ' The directories are listed in lines in the file at ' echo '' echo 'Any other argument triggers an error and this usage message is displayed' exit 1 } #Display error message and exit function exit_with_error { set +x local errorMessage="$1" local printUsage=$2 echo "ERROR: $errorMessage" if [ "$printUsage" == "true" ]; then echo '' usage fi exit 1 } #Exit if input string is empty function exit_if_empty { local inputString="$1" local errorMessage="$2" local printUsage=$3 if [ -z "$inputString" ]; then exit_with_error "$errorMessage" $printUsage fi } #Exit if the input path does not exist function exit_if_path_absent { local path="$1" local errorMessage="$2" local printUsage=$3 if [ ! -f "$path" -a ! -d "$path" ]; then exit_with_error "$errorMessage" $printUsage fi } #Check if the git changes were reverted completely function check_git_head { local currentGitHead=`git rev-parse --verify HEAD` if [[ "$__initialGitHead" != "$currentGitHead" ]]; then exit_with_error "Some changes made to the code history were not completely reverted. Intial Git HEAD: $__initialGitHead, current Git HEAD: $currentGitHead" false fi } function unmount_rootfs { local rootfsFolder="$1" #Check if there are any open files in this directory. if [ -d $rootfsFolder ]; then #If we find information about the file if sudo lsof +D $rootfsFolder; then (set +x; echo 'See above for lsof information. Continuing with the build.') fi fi if mountpoint -q -- "$rootfsFolder"; then sudo umount "$rootfsFolder" fi } #Clean the previous build files inside the emulator function clean_emulator { #Remove any previous copies of the coreclr and the corefx directories in the emulator sudo rm -rf "$__ARMRootfsCoreclrPath" "$__ARMRootfsCorefxPath" } #Clean the changes made to the environment by the script function clean_env { #Clean the emulator clean_emulator #Check for revert of git changes check_git_head sudo rm -rf "/mnt/arm32_ci_temp" } #Trap Ctrl-C and handle it function handle_ctrl_c { set +x echo 'ERROR: Ctrl-C handled. Script aborted before complete execution.' exit 1 } trap handle_ctrl_c INT #Trap Exit and handle it function handle_exit { set +x echo 'The script is exited. Cleaning environment..' if [ "$__ciMode" == "emulator" ]; then clean_env fi } trap handle_exit EXIT #Mount with checking to be already existed function mount_with_checking { set +x local options="$1" local from="$2" local rootfsFolder="$3" if mountpoint -q -- "$rootfsFolder"; then (set +x; echo "$rootfsFolder is already mounted.") else { (set -x; sudo mount $options "$from" "$rootfsFolder") } fi } #Mount emulator to the target mount path function mount_emulator { #Check if the mount path exists and create if neccessary if [ ! -d "$__ARMRootfsMountPath" ]; then sudo mkdir "$__ARMRootfsMountPath" fi if [ ! -d "$__ARMEmulRootfs" ]; then sudo mkdir "$__ARMEmulRootfs" fi if [ ! -f "$__ARMEmulRootfs/arm-emulator-rootfs.tar" ]; then if mountpoint -q -- "$__ARMRootfsMountPath"; then sudo umount -l $__ARMRootfsMountPath fi mount_with_checking "" "$__ARMEmulPath/platform/rootfs-t30.ext4" "$__ARMRootfsMountPath" cd $__ARMRootfsMountPath sudo tar -cf "$__ARMEmulRootfs/arm-emulator-rootfs.tar" * cd - fi sudo tar -xf "$__ARMEmulRootfs/arm-emulator-rootfs.tar" -C "$__ARMEmulRootfs" mount_with_checking "-t proc" "/proc" "$__ARMEmulRootfs/proc" mount_with_checking "-o bind" "/dev/" "$__ARMEmulRootfs/dev" mount_with_checking "-o bind" "/dev/pts" "$__ARMEmulRootfs/dev/pts" mount_with_checking "-t tmpfs" "shm" "$__ARMEmulRootfs/run/shm" mount_with_checking "-o bind" "/sys" "$__ARMEmulRootfs/sys" if [ ! -d "$__ARMEmulRootfs/bindings/tmp" ]; then sudo mkdir -p "$__ARMEmulRootfs/bindings/tmp" fi mount_with_checking "-o bind" "/mnt" "$__ARMEmulRootfs/bindings/tmp" if [ ! -d "$__ARMEmulRootfs/$__TempFolder" ]; then sudo mkdir "$__ARMEmulRootfs/$__TempFolder" fi } #Cross builds coreclr function cross_build_coreclr { #Export the needed environment variables (set +x; echo 'Exporting LINUX_ARM_* environment variable') source "$__ARMEmulRootfs"/dotnet/setenv/setenv_incpath.sh "$__ARMEmulRootfs" #Apply release optimization patch if needed if [[ "$__buildConfig" == "Release" ]]; then (set +x; echo 'Applying release optimization patch to build in Release mode') git am < "$__ARMEmulRootfs"/dotnet/setenv/coreclr_release.patch fi #Cross building for emulator rootfs ROOTFS_DIR="$__ARMEmulRootfs" CPLUS_INCLUDE_PATH=$LINUX_ARM_INCPATH CXXFLAGS=$LINUX_ARM_CXXFLAGS ./build.sh $__buildArch cross $__verboseFlag $__skipMscorlib clang3.5 $__buildConfig -rebuild #Reset the code to the upstream version (set +x; echo 'Rewinding HEAD to master code') if [[ "$__buildConfig" == "Release" ]]; then git reset --hard HEAD^ fi } #Cross builds coreclr using Docker function cross_build_coreclr_with_docker { __currentWorkingDirectory=`pwd` # Check build configuration and choose Docker image __dockerEnvironmentVariables="" if [ "$__buildArch" == "arm" ]; then # TODO: For arm, we are going to embed RootFS inside Docker image. case $__linuxCodeName in trusty) __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu-14.04-cross-0cd4667-20172211042239" __skipRootFS=1 __dockerEnvironmentVariables+=" -e ROOTFS_DIR=/crossrootfs/arm" __runtimeOS="ubuntu.14.04" ;; xenial) __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu-16.04-cross-ef0ac75-20175511035548" __skipRootFS=1 __dockerEnvironmentVariables+=" -e ROOTFS_DIR=/crossrootfs/arm" __runtimeOS="ubuntu.16.04" ;; *) exit_with_error "ERROR: $__linuxCodeName is not a supported linux name for $__buildArch" false ;; esac elif [ "$__buildArch" == "armel" ]; then # For armel Tizen, we are going to construct RootFS on the fly. case $__linuxCodeName in tizen) __dockerImage=" hqueue/dotnetcore:ubuntu1404_cross_prereqs_v4-tizen_rootfs" __skipRootFS=1 __dockerEnvironmentVariables+=" -e ROOTFS_DIR=/crossrootfs/armel.tizen.build" __runtimeOS="tizen.4.0.0" ;; *) echo "ERROR: $__linuxCodeName is not a supported linux name for $__buildArch" exit_with_error "ERROR: $__linuxCodeName is not a supported linux name for $__buildArch" false ;; esac else exit_with_error "ERROR: unknown buildArch $__buildArch" false fi __dockerCmd="sudo docker run ${__dockerEnvironmentVariables} --privileged -i --rm -v $__currentWorkingDirectory:/opt/code -w /opt/code $__dockerImage" if [ $__skipRootFS == 0 ]; then # Build rootfs __buildRootfsCmd="./cross/build-rootfs.sh $__buildArch $__linuxCodeName --skipunmount" (set +x; echo "Build RootFS for $__buildArch $__linuxCodeName") $__dockerCmd $__buildRootfsCmd sudo chown -R $(id -u -n) cross/rootfs fi __extraArgs="" if [[ "$__buildArch" == "armel" && "$__linuxCodeName" == "tizen" ]]; then __extraArgs="cmakeargs -DFEATURE_GDBJIT=TRUE -PortableBuild=false" fi # Cross building coreclr with rootfs in Docker (set +x; echo "Start cross build coreclr for $__buildArch $__linuxCodeName") __buildCmd="./build.sh $__buildArch cross $__verboseFlag $__skipMscorlib $__buildConfig $__extraArgs -rebuild" $__dockerCmd $__buildCmd sudo chown -R $(id -u -n) ./bin } #Copy the needed files to the emulator to run tests function copy_to_emulator { #Create the coreclr and corefx directories in the emulator sudo mkdir -p "$__ARMRootfsCoreclrPath/bin/obj/$__buildDirName" sudo mkdir -p "$__ARMRootfsCoreclrPath/bin/Product" sudo mkdir "$__ARMRootfsCorefxPath" #Copy all coreclr files to the coreclr root in the emulator and set the paths accordingly local testRootDirBase=`basename "$__testRootDir"` sudo cp -R "$__testRootDir" "$__ARMRootfsCoreclrPath/$testRootDirBase" __testRootDirBase="$__ARMEmulCoreclr/$testRootDirBase" sudo cp -R "./$__testNativeBinDirBase" "$__ARMRootfsCoreclrPath/$__testNativeBinDirBase" __testNativeBinDirBase="$__ARMEmulCoreclr/$__testNativeBinDirBase" sudo cp -R "./$__coreClrBinDirBase" "$__ARMRootfsCoreclrPath/$__coreClrBinDirBase" __coreClrBinDirBase="$__ARMEmulCoreclr/$__coreClrBinDirBase" __mscorlibDirBase="$__coreClrBinDirBase" local testDirFileBase=`basename "$__testDirFile"` sudo cp "$__testDirFile" "$__ARMRootfsCoreclrPath/$testDirFileBase" __testDirFileBase="$__ARMEmulCoreclr/$testDirFileBase" sudo cp -R ./tests "$__ARMRootfsCoreclrPath/" sudo cp -R ./packages "$__ARMRootfsCoreclrPath/" sudo cp -R ./Tools "$__ARMRootfsCoreclrPath/" #Copy corefx binary directories to the corefx root in the emulator (first native and then managed) local coreFxNativeBinDirBase=`basename "$__coreFxNativeBinDir"` sudo cp -R "$__coreFxNativeBinDir" "$__ARMRootfsCorefxPath/$coreFxNativeBinDirBase" __coreFxNativeBinDirBase="$__ARMEmulCorefx/$coreFxNativeBinDirBase" __coreFxBinDirBase= while IFS=';' read -ra coreFxBinDirectories; do for currDir in "${coreFxBinDirectories[@]}"; do local currDirBase=`basename "$currDir"` sudo cp -R "$currDir" "$__ARMRootfsCorefxPath/$currDirBase" if [ -z "$__coreFxBinDirBase" ]; then __coreFxBinDirBase="$__ARMEmulCorefx/$currDirBase" else __coreFxBinDirBase="$__coreFxBinDirBase;$__ARMEmulCorefx/$currDirBase" fi done done <<< "$__coreFxBinDir" } #Runs tests in an emulated mode function run_tests { sudo chroot $__ARMEmulRootfs /bin/bash -x <