#!/bin/bash #Usage message function usage { echo 'ARM Emulator Cross Build Script' echo 'This script cross builds corefx source' echo '' echo 'Typical usage:' echo ' corefx source is at ~/cfx' echo '$ cd ~/cfx' echo '$ ./scripts/arm32_ci_script.sh' echo ' --buildConfig=Release' echo ' --armel' echo ' --verbose' echo '' echo 'Required Arguments:' echo ' --buildConfig= : The value of config should be either Debug or Release' echo ' Any other value is not accepted' echo 'Optional Arguments:' echo ' --mode= : docker (default) or emulator' echo ' --arm : Build as arm (default)' echo ' --armel : Build as armel' echo ' --linuxCodeName= : Code name for Linux: For arm, trusty (default) and xenial. For armel, tizen' echo ' --skipRootFS : Skip building rootfs' 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 ' -v --verbose : Build made verbose' echo ' -h --help : Prints this usage message and exits' 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 } #Unmount the emulator file systems function unmount_emulator { (set +x; echo 'Unmounting emulator...') #Unmount all the mounted emulator file systems unmount_rootfs "$__ARMRootfsMountPath/proc" unmount_rootfs "$__ARMRootfsMountPath/dev/pts" unmount_rootfs "$__ARMRootfsMountPath/dev" unmount_rootfs "$__ARMRootfsMountPath/run/shm" unmount_rootfs "$__ARMRootfsMountPath/sys" unmount_rootfs "$__ARMRootfsMountPath" } #Clean the changes made to the environment by the script function clean_env { #Check for revert of git changes check_git_head } #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..' clean_env } 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 -p "$__ARMRootfsMountPath" fi set +x mount_with_checking "" "$__ARMEmulPath/platform/$__ARMRootfsImageBase" "$__ARMRootfsMountPath" mount_with_checking "-t proc" "/proc" "$__ARMRootfsMountPath/proc" mount_with_checking "-o bind" "/dev/" "$__ARMRootfsMountPath/dev" mount_with_checking "-o bind" "/dev/pts" "$__ARMRootfsMountPath/dev/pts" mount_with_checking "-t tmpfs" "shm" "$__ARMRootfsMountPath/run/shm" mount_with_checking "-o bind" "/sys" "$__ARMRootfsMountPath/sys" } #Cross builds corefx function cross_build_corefx { #Apply fixes for armel if [ "$__buildArch" == "armel" ]; then #Export the needed environment variables (set +x; echo 'Exporting LINUX_ARM_* environment variable') source "$__ARMRootfsMountPath"/dotnet/setenv/setenv_incpath.sh "$__ARMRootfsMountPath" fi #Cross building for emulator rootfs ROOTFS_DIR="$__ARMRootfsMountPath" CPLUS_INCLUDE_PATH=$LINUX_ARM_INCPATH CXXFLAGS=$LINUX_ARM_CXXFLAGS ./build-native.sh -buildArch=$__buildArch -$__buildConfig -- cross $__verboseFlag ROOTFS_DIR="$__ARMRootfsMountPath" CPLUS_INCLUDE_PATH=$LINUX_ARM_INCPATH CXXFLAGS=$LINUX_ARM_CXXFLAGS ./build-managed.sh -$__buildConfig -skipTests -BuildPackages=false } # Cross builds corefx using Docker image function cross_build_corefx_with_docker { __currentWorkingDirectory=`pwd` # Check build configuration and choose Docker image 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:ubuntu1404_cross_prereqs_v3" __skipRootFS=1 __dockerEnvironmentVariables="-e ROOTFS_DIR=/crossrootfs/arm" __runtimeOS="ubuntu.14.04" ;; xenial) __dockerImage=" microsoft/dotnet-buildtools-prereqs:ubuntu1604_cross_prereqs_v3" __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=" t2wish/dotnetcore:ubuntu1404_cross_prereqs_v2" __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/corefx -w /opt/corefx $__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 # Cross building corefx with rootfs in Docker __buildNativeCmd="/opt/corefx/build-native.sh -buildArch=$__buildArch -$__buildConfig -- cross $__verboseFlag" if [ "$__buildArch" == "arm" ]; then __buildManagedCmd="./build-managed.sh -$__buildConfig -buildArch=$__buildArch -RuntimeOS=$__runtimeOS" else # TODO-armel: We can use same option to arm, i.e. -buildArch and -RuntimeOS options for armel, # when CoreCLR packages are also available at NuGet server for armel. __buildManagedCmd="./build-managed.sh -$__buildConfig -BuildPackages=false" fi $__dockerCmd $__buildNativeCmd $__dockerCmd $__buildManagedCmd sudo chown -R $(id -u -n) ./bin if [ "$__buildArch" == "armel" ]; then # TODO-armel: This is a workaround. We don't need this if we can build managed for armel in above. # Construct runtime directory if [[ "$__buildConfig" == "release" ]]; then __runtimePath="./bin/runtime/netcoreapp-Linux-Release-armel" __nativePath="./bin/Linux.armel.Release/native" __managedPath="./bin/runtime/netcoreapp-Linux-Release-x64" else __runtimePath="./bin/runtime/netcoreapp-Linux-Debug-armel" __nativePath="./bin/Linux.armel.Debug/native" __managedPath="./bin/runtime/netcoreapp-Linux-Debug-x64" fi mkdir $__runtimePath pushd $__managedPath rm apphost corerun dotnet rm *.so rm *.ni.dll popd cp $__nativePath/* $__runtimePath/ mv $__managedPath/* $__runtimePath/ rmdir $__managedPath fi } #Define script variables __ciMode="docker" __ARMEmulPath= __ARMRootfsImageBase="rootfs-u1404.ext4" __ARMRootfsMountPath= __buildConfig= __verboseFlag= __buildArch="arm" __linuxCodeName="trusty" __skipRootFS=0 __initialGitHead=`git rev-parse --verify HEAD` #Parse command line arguments for arg in "$@" do case $arg in --emulatorPath=*) __ARMEmulPath=${arg#*=} ;; --mountPath=*) __ARMRootfsMountPath=${arg#*=} ;; --buildConfig=*) __buildConfig="$(echo ${arg#*=} | awk '{print tolower($0)}')" if [[ "$__buildConfig" != "debug" && "$__buildConfig" != "release" ]]; then exit_with_error "--buildConfig can be only Debug or Release" true fi ;; --mode=*) __ciMode=${arg#*=} ;; --arm) __ARMRootfsImageBase="rootfs-u1404.ext4" __buildArch="arm" ;; --armel) __ARMRootfsImageBase="rootfs-t30.ext4" __buildArch="armel" __linuxCodeName="tizen" ;; --linuxCodeName=*) __linuxCodeName=${arg#*=} ;; --skipRootFS) __skipRootFS=1 ;; -v|--verbose) __verboseFlag="verbose" ;; -h|--help) usage ;; *) exit_with_error "$arg not a recognized argument" true ;; esac done #Check if there are any uncommited changes in the source directory as git adds and removes patches if [[ $(git status -s) != "" ]]; then echo 'ERROR: There are some uncommited changes. To avoid losing these changes commit them and try again.' echo '' git status exit 1 fi exit_if_empty "$__buildConfig" "--buildConfig is a mandatory argument, not provided" true if [ "$__ciMode" == "emulator" ]; then #Check if the compulsory arguments have been presented to the script and if the input paths exist exit_if_empty "$__ARMEmulPath" "--emulatorPath is a mandatory argument, not provided" true exit_if_empty "$__ARMRootfsMountPath" "--mountPath is a mandatory argument, not provided" true exit_if_path_absent "$__ARMEmulPath/platform/$__ARMRootfsImageBase" "Path specified in --emulatorPath does not have the rootfs" false __ARMRootfsMountPath="${__ARMRootfsMountPath}_${__buildArch}" fi set -x set -e ## Begin cross build (set +x; echo "Git HEAD @ $__initialGitHead") #Complete the cross build (set +x; echo 'Building corefx...') if [ "$__ciMode" == "docker" ]; then cross_build_corefx_with_docker else cross_build_corefx fi #Clean the environment (set +x; echo 'Cleaning environment...') clean_env (set +x; echo 'Build complete')