#!/usr/bin/env bash set -u set -e ROOT_PATH="$(cd "$(dirname $0)"; echo $PWD)" export ROOT_PATH OUTPUT_DIRECTORY="$ROOT_PATH/output" EXPORT_DIRECTORY="" UPDATE_SUBMODULES=false CONFIGURATION="Release" BUILD_PLATFORM="Any CPU" CLEAN=false PACKAGES=false NIGHTLY=false PORTABLE=false HEADLESS=false SKIP_FETCH=false TLIB_ONLY=false TLIB_EXPORT_COMPILE_COMMANDS=false TLIB_ARCH="" NET=false TFM="net462" GENERATE_DOTNET_BUILD_TARGET=true PARAMS=() CUSTOM_PROP= NET_FRAMEWORK_VER= RID="linux-x64" HOST_ARCH="i386" # Common cmake flags CMAKE_COMMON="" function print_help() { echo "Usage: $0 [-cdvspnt] [-b properties-file.csproj] [--no-gui] [--skip-fetch] [--profile-build] [--tlib-only] [--tlib-export-compile-commands] [--tlib-arch ] [--host-arch i386|aarch64] [-- ]" echo echo "-c clean instead of building" echo "-d build Debug configuration" echo "-v verbose output" echo "-p create packages after building" echo "-n create nightly packages after building" echo "-t create a portable package (experimental, Linux only)" echo "-s update submodules" echo "-b custom build properties file" echo "-o custom output directory" echo "--skip-fetch skip fetching submodules and additional resources" echo "--no-gui build with GUI disabled" echo "--force-net-framework-version build against different version of .NET Framework than specified in the solution" echo "--net build with dotnet" echo "-B bundle target runtime (default value: $RID, requires --net, -t)" echo "-F select the target framework for which Renode should be built (default value: $TFM)" echo "--profile-build build optimized for profiling" echo "--tlib-only only build tlib" echo "--tlib-arch build only single arch (implies --tlib-only)" echo "--tlib-export-compile-commands build tlibs with 'compile_commands.json' (requires --tlib-arch)" echo "--host-arch build with a specific tcg host architecture (default: i386)" echo "--skip-dotnet-target-generation don't generate 'Directory.Build.targets' file, useful when experimenting with different build settings" echo " arguments to pass to the build system" } while getopts "cdvpnstb:o:B:F:-:" opt do case $opt in c) CLEAN=true ;; d) CONFIGURATION="Debug" ;; v) PARAMS+=(verbosity:detailed) ;; p) PACKAGES=true ;; n) NIGHTLY=true PACKAGES=true ;; t) PORTABLE=true ;; s) UPDATE_SUBMODULES=true ;; b) CUSTOM_PROP=$OPTARG ;; o) EXPORT_DIRECTORY=$OPTARG echo "Setting the output directory to $EXPORT_DIRECTORY" ;; B) RID=$OPTARG ;; F) if ! $NET; then echo "-F requires --net being set" exit 1 fi TFM=$OPTARG ;; -) case $OPTARG in "no-gui") HEADLESS=true ;; "skip-fetch") SKIP_FETCH=true ;; "force-net-framework-version") shift $((OPTIND-1)) NET_FRAMEWORK_VER="p:TargetFrameworkVersion=v$1" PARAMS+=("$NET_FRAMEWORK_VER") OPTIND=2 ;; "net") NET=true TFM="net8.0" PARAMS+=(p:NET=true) ;; "profile-build") CMAKE_COMMON="-DPROFILING_BUILD=ON" ;; "tlib-only") TLIB_ONLY=true ;; "tlib-arch") # This only makes sense with '--tlib-only' set; it might as well imply it TLIB_ONLY=true shift $((OPTIND-1)) TLIB_ARCH=$1 OPTIND=2 ;; "tlib-export-compile-commands") if [ -z $TLIB_ARCH ]; then echo "--tlib-export-compile-commands requires --tlib-arch begin set" exit 1 fi TLIB_EXPORT_COMPILE_COMMANDS=true ;; "host-arch") shift $((OPTIND-1)) if [ $1 == "aarch64" ] || [ $1 == "arm64" ]; then HOST_ARCH="aarch64" elif [ $1 == "i386" ] || [ $1 == "x86" ] || [ $1 == "x86_64" ]; then HOST_ARCH="i386" else echo "host architecture $1 not supported. Supported architectures are i386 and aarch64" exit 1 fi OPTIND=2 ;; "skip-dotnet-target-generation") GENERATE_DOTNET_BUILD_TARGET=false ;; *) print_help exit 1 ;; esac ;; \?) print_help exit 1 ;; esac done shift "$((OPTIND-1))" PARAMS+=( # By default use CC as Compiler- and LinkerPath, and AR as ArPath ${CC:+"p:CompilerPath=$CC"} ${CC:+"p:LinkerPath=$CC"} ${AR:+"p:ArPath=$AR"} # But allow users to override it "$@" ) if [ -n "${PLATFORM:-}" ] then echo "PLATFORM environment variable is currently set to: >>$PLATFORM<<" echo "This might cause problems during the build." echo "Please clear it with:" echo "" echo " unset PLATFORM" echo "" echo " and run the build script again." exit 1 fi # We can only update parts of this repository if Renode is built from within the git tree if [ ! -e .git ] then SKIP_FETCH=true UPDATE_SUBMODULES=false fi if $SKIP_FETCH then echo "Skipping init/update of submodules" else # Update submodules if not initialized or if requested by the user # Warn if not updating, but unclean # Disabling -e to allow grep to fail set +e git submodule status --recursive | grep -q "^-" SUBMODULES_NOT_INITED=$? git submodule status --recursive | grep -q "^+" SUBMODULES_NOT_CLEAN=$? set -e if $UPDATE_SUBMODULES || [ $SUBMODULES_NOT_INITED -eq 0 ] then echo "Updating submodules..." git submodule update --init --recursive elif [ $SUBMODULES_NOT_CLEAN -eq 0 ] then echo "Submodules are not updated. Use -s to force update." fi fi . "${ROOT_PATH}/tools/common.sh" if $SKIP_FETCH then echo "Skipping library fetch" else "${ROOT_PATH}"/tools/building/fetch_libraries.sh fi if $HEADLESS then BUILD_TARGET=Headless PARAMS+=(p:GUI_DISABLED=true) elif $ON_WINDOWS then BUILD_TARGET=Windows TFM="$TFM-windows10.0.17763.0" else BUILD_TARGET=Mono fi # Set correct RID if $ON_LINUX; then RID="linux-x64" if [[ $HOST_ARCH == "aarch64" ]]; then RID="linux-arm64" fi elif $ON_OSX; then RID="osx-x64" if [[ $HOST_ARCH == "aarch64" ]]; then RID="osx-arm64" fi elif $ON_WINDOWS; then RID="win-x64" fi if [[ $GENERATE_DOTNET_BUILD_TARGET = true ]]; then if $ON_WINDOWS; then # CsWinRTAotOptimizerEnabled is disabled due to a bug in dotnet-sdk. # See: https://github.com/dotnet/sdk/issues/44026 OS_SPECIFIC_TARGET_OPTS='false' fi cat < "$(get_path "$PWD/Directory.Build.targets")" $TFM ${OS_SPECIFIC_TARGET_OPTS:+${OS_SPECIFIC_TARGET_OPTS}} EOF fi if $NET then export DOTNET_CLI_TELEMETRY_OPTOUT=1 CS_COMPILER="dotnet build" TARGET="`get_path \"$PWD/Renode_NET.sln\"`" BUILD_TYPE="dotnet" else TARGET="`get_path \"$PWD/Renode.sln\"`" BUILD_TYPE="mono" fi OUT_BIN_DIR="$(get_path "output/bin/${CONFIGURATION}")" BUILD_TYPE_FILE=$(get_path "${OUT_BIN_DIR}/build_type") # Verify Mono and mcs version on Linux and macOS if ! $ON_WINDOWS && ! $NET then if ! [ -x "$(command -v mcs)" ] then MINIMUM_MONO=`get_min_mono_version` echo "mcs not found. Renode requires Mono $MINIMUM_MONO or newer. Please refer to documentation for installation instructions. Exiting!" exit 1 fi verify_mono_version fi # Copy properties file according to the running OS mkdir -p "$OUTPUT_DIRECTORY" if [ -n "${CUSTOM_PROP}" ]; then PROP_FILE=$CUSTOM_PROP else if $ON_OSX then PROP_FILE="${CURRENT_PATH:=.}/src/Infrastructure/src/Emulator/Cores/osx-properties.csproj" elif $ON_LINUX then PROP_FILE="${CURRENT_PATH:=.}/src/Infrastructure/src/Emulator/Cores/linux-properties.csproj" else PROP_FILE="${CURRENT_PATH:=.}/src/Infrastructure/src/Emulator/Cores/windows-properties.csproj" fi fi cp "$PROP_FILE" "$OUTPUT_DIRECTORY/properties.csproj" if ! $NET then # Assets files are not deleted during `dotnet clean`, as it would confuse intellisense per comment in https://github.com/NuGet/Home/issues/7368#issuecomment-457411014, # but we need to delete them to build Renode again for .NETFramework since `project.assets.json` doesn't play well if project files share the same directory. # If `Renode_NET.sln` is picked for OmniSharp, it will trigger reanalysis of the project after removing assets files. # We don't remove these files as part of `clean` target, because other intermediate files are well separated between .NET and .NETFramework # and enforcing `clean` every time before rebuilding would slow down the build process on both frameworks. find $ROOT_PATH -type f -name 'project.assets.json' -delete fi CORES_PATH="$ROOT_PATH/src/Infrastructure/src/Emulator/Cores" # clean instead of building if $CLEAN then for project_dir in $(find "$(get_path "${ROOT_PATH}/src")" -iname '*.csproj' -exec dirname '{}' \;) do for dir in {bin,obj}/{Debug,Release} do output_dir="$(get_path "${project_dir}/${dir}")" if [[ -d "${output_dir}" ]] then echo "Removing: ${output_dir}" rm -rf "${output_dir}" fi done done # Manually clean the main output directory as it's location is non-standard main_output_dir="$(get_path "${OUTPUT_DIRECTORY}/bin")" if [[ -d "${main_output_dir}" ]] then echo "Removing: ${main_output_dir}" rm -rf "${main_output_dir}" fi exit 0 fi # Check if a full rebuild is needed if [[ -f "$BUILD_TYPE_FILE" ]] then if [[ "$(cat "$BUILD_TYPE_FILE")" != "$BUILD_TYPE" ]] then echo "Attempted to build Renode in a different configuration than the previous build" echo "Please run '$0 -c' to clean the previous build before continuing" exit 1 fi fi # check weak implementations of core libraries pushd "$ROOT_PATH/tools/building" > /dev/null ./check_weak_implementations.sh popd > /dev/null PARAMS+=(p:Configuration="${CONFIGURATION}${BUILD_TARGET}" p:GenerateFullPaths=true p:Platform="\"$BUILD_PLATFORM\"") # Paths for tlib CORES_BUILD_PATH="$CORES_PATH/obj/$CONFIGURATION" CORES_BIN_PATH="$CORES_PATH/bin/$CONFIGURATION" # Cmake generator, handled in their own variable since the names contain spaces if $ON_WINDOWS then CMAKE_GEN="-GMinGW Makefiles" else CMAKE_GEN="-GUnix Makefiles" fi # Macos architecture flags, to make rosetta work properly if $ON_OSX then CMAKE_COMMON+=" -DCMAKE_OSX_ARCHITECTURES=x86_64" if [ $HOST_ARCH == "aarch64" ]; then CMAKE_COMMON+=" -DCMAKE_OSX_ARCHITECTURES=arm64" fi fi # This list contains all cores that will be built. # If you are adding a new core or endianness add it here to have the correct tlib built CORES=(arm.le arm.be arm64.le arm-m.le arm-m.be ppc.le ppc.be ppc64.le ppc64.be i386.le x86_64.le riscv.le riscv64.le sparc.le sparc.be xtensa.le) # if '--tlib-arch' was used - pick the first matching one if [[ ! -z $TLIB_ARCH ]]; then NONE_MATCHED=true for potential_match in "${CORES[@]}"; do if [[ $potential_match == "$TLIB_ARCH"* ]]; then CORES=("$potential_match") echo "Compiling tlib for $potential_match" NONE_MATCHED=false break fi done if $NONE_MATCHED ; then echo "Failed to match any tlib arch" exit 1 fi fi # build tlib for core_config in "${CORES[@]}" do CORE="$(echo $core_config | cut -d '.' -f 1)" ENDIAN="$(echo $core_config | cut -d '.' -f 2)" BITS=32 # Check if core is 64-bit if [[ $CORE =~ "64" ]]; then BITS=64 fi # Core specific flags to cmake CMAKE_CONF_FLAGS="-DTARGET_ARCH=$CORE -DTARGET_WORD_SIZE=$BITS -DCMAKE_BUILD_TYPE=$CONFIGURATION" CORE_DIR=$CORES_BUILD_PATH/$CORE/$ENDIAN mkdir -p $CORE_DIR pushd "$CORE_DIR" > /dev/null if [[ $ENDIAN == "be" ]]; then CMAKE_CONF_FLAGS+=" -DTARGET_WORDS_BIGENDIAN=1" fi if [[ "$TLIB_EXPORT_COMPILE_COMMANDS" = true ]]; then CMAKE_CONF_FLAGS+=" -DCMAKE_EXPORT_COMPILE_COMMANDS=1" fi cmake "$CMAKE_GEN" $CMAKE_COMMON $CMAKE_CONF_FLAGS -DHOST_ARCH=$HOST_ARCH $CORES_PATH cmake --build . -j"$(nproc)" CORE_BIN_DIR=$CORES_BIN_PATH/lib mkdir -p $CORE_BIN_DIR if $ON_OSX; then # macos `cp` does not have the -u flag cp -v tlib/*.so $CORE_BIN_DIR/ else cp -u -v tlib/*.so $CORE_BIN_DIR/ fi # copy compile_commands.json to tlib directory if [[ "$TLIB_EXPORT_COMPILE_COMMANDS" = true ]]; then command cp -v -f $CORE_DIR/compile_commands.json $CORES_PATH/tlib/ fi popd > /dev/null done if $TLIB_ONLY then exit 0 fi # build eval "$CS_COMPILER $(build_args_helper "${PARAMS[@]}") $TARGET" echo -n "$BUILD_TYPE" > "$BUILD_TYPE_FILE" # copy llvm library LLVM_LIB="libllvm-disas" if [[ $HOST_ARCH == "aarch64" ]]; then # aarch64 host binaries have a different name LLVM_LIB="libllvm-disas-aarch64" fi if [[ "${DETECTED_OS}" == "windows" ]]; then LIB_EXT="dll" elif [[ "${DETECTED_OS}" == "osx" ]]; then LIB_EXT="dylib" else LIB_EXT="so" fi cp lib/resources/llvm/$LLVM_LIB.$LIB_EXT $OUT_BIN_DIR/libllvm-disas.$LIB_EXT # build packages after successful compilation params="" if [ $CONFIGURATION == "Debug" ] then params="$params -d" fi if [ -n "$EXPORT_DIRECTORY" ] then if [ "${DETECTED_OS}" != "linux" ] then echo "Custom output directory is currently available on Linux only" exit 1 fi $ROOT_PATH/tools/packaging/export_${DETECTED_OS}_workdir.sh $EXPORT_DIRECTORY $params echo "Renode built to $EXPORT_DIRECTORY" fi if $PACKAGES && $NIGHTLY then params="$params -n" fi if $PACKAGES then if $NET then # dotnet package on linux uses a separate script if $ON_LINUX then # maxcpucount:1 to avoid an error with multithreaded publish eval "dotnet publish -maxcpucount:1 -f $TFM --self-contained false $(build_args_helper "${PARAMS[@]}") $TARGET" export RID TFM $ROOT_PATH/tools/packaging/make_linux_dotnet_package.sh $params # Source package bundles nuget dependencies required for building the dotnet version of Renode # so it can only be built when using dotnet. The generated package can also be used with Mono/.NETFramework $ROOT_PATH/tools/packaging/make_source_package.sh $params elif $ON_WINDOWS && ! $PORTABLE then # No Non portable dotnet package on windows yet echo "Only portable dotnet packages are supported on windows. Rerun build.sh with -t flag to build portable" exit 1 elif $ON_OSX then eval "dotnet publish -maxcpucount:1 -f $TFM --self-contained false $(build_args_helper "${PARAMS[@]}") $TARGET" export RID TFM $ROOT_PATH/tools/packaging/make_osx_dotnet_package.sh $params fi else $ROOT_PATH/tools/packaging/make_${DETECTED_OS}_packages.sh $params fi fi if $PORTABLE then PARAMS+=(p:PORTABLE=true) if $NET then # maxcpucount:1 to avoid an error with multithreaded publish echo "RID = $RID" eval "dotnet publish -maxcpucount:1 -r $RID -f $TFM --self-contained true $(build_args_helper "${PARAMS[@]}") $TARGET" export RID TFM $ROOT_PATH/tools/packaging/make_${DETECTED_OS}_portable_dotnet.sh $params else if $ON_LINUX then $ROOT_PATH/tools/packaging/make_linux_portable.sh $params else echo "Portable packages for Mono are only available on Linux. Exiting!" exit 1 fi fi fi