1#!/usr/bin/env bash 2 3set -u 4set -e 5 6ROOT_PATH="$(cd "$(dirname $0)"; echo $PWD)" 7export ROOT_PATH 8OUTPUT_DIRECTORY="$ROOT_PATH/output" 9EXPORT_DIRECTORY="" 10 11UPDATE_SUBMODULES=false 12CONFIGURATION="Release" 13BUILD_PLATFORM="Any CPU" 14CLEAN=false 15PACKAGES=false 16NIGHTLY=false 17PORTABLE=false 18HEADLESS=false 19SKIP_FETCH=false 20TLIB_ONLY=false 21TLIB_EXPORT_COMPILE_COMMANDS=false 22TLIB_ARCH="" 23NET=false 24TFM="net462" 25GENERATE_DOTNET_BUILD_TARGET=true 26PARAMS=() 27CUSTOM_PROP= 28NET_FRAMEWORK_VER= 29RID="linux-x64" 30HOST_ARCH="i386" 31# Common cmake flags 32CMAKE_COMMON="" 33 34function print_help() { 35 echo "Usage: $0 [-cdvspnt] [-b properties-file.csproj] [--no-gui] [--skip-fetch] [--profile-build] [--tlib-only] [--tlib-export-compile-commands] [--tlib-arch <arch>] [--host-arch i386|aarch64] [-- <ARGS>]" 36 echo 37 echo "-c clean instead of building" 38 echo "-d build Debug configuration" 39 echo "-v verbose output" 40 echo "-p create packages after building" 41 echo "-n create nightly packages after building" 42 echo "-t create a portable package (experimental, Linux only)" 43 echo "-s update submodules" 44 echo "-b custom build properties file" 45 echo "-o custom output directory" 46 echo "--skip-fetch skip fetching submodules and additional resources" 47 echo "--no-gui build with GUI disabled" 48 echo "--force-net-framework-version build against different version of .NET Framework than specified in the solution" 49 echo "--net build with dotnet" 50 echo "-B bundle target runtime (default value: $RID, requires --net, -t)" 51 echo "-F select the target framework for which Renode should be built (default value: $TFM)" 52 echo "--profile-build build optimized for profiling" 53 echo "--tlib-only only build tlib" 54 echo "--tlib-arch build only single arch (implies --tlib-only)" 55 echo "--tlib-export-compile-commands build tlibs with 'compile_commands.json' (requires --tlib-arch)" 56 echo "--host-arch build with a specific tcg host architecture (default: i386)" 57 echo "--skip-dotnet-target-generation don't generate 'Directory.Build.targets' file, useful when experimenting with different build settings" 58 echo "<ARGS> arguments to pass to the build system" 59} 60 61while getopts "cdvpnstb:o:B:F:-:" opt 62do 63 case $opt in 64 c) 65 CLEAN=true 66 ;; 67 d) 68 CONFIGURATION="Debug" 69 ;; 70 v) 71 PARAMS+=(verbosity:detailed) 72 ;; 73 p) 74 PACKAGES=true 75 ;; 76 n) 77 NIGHTLY=true 78 PACKAGES=true 79 ;; 80 t) 81 PORTABLE=true 82 ;; 83 s) 84 UPDATE_SUBMODULES=true 85 ;; 86 b) 87 CUSTOM_PROP=$OPTARG 88 ;; 89 o) 90 EXPORT_DIRECTORY=$OPTARG 91 echo "Setting the output directory to $EXPORT_DIRECTORY" 92 ;; 93 B) 94 RID=$OPTARG 95 ;; 96 F) 97 if ! $NET; then 98 echo "-F requires --net being set" 99 exit 1 100 fi 101 TFM=$OPTARG 102 ;; 103 -) 104 case $OPTARG in 105 "no-gui") 106 HEADLESS=true 107 ;; 108 "skip-fetch") 109 SKIP_FETCH=true 110 ;; 111 "force-net-framework-version") 112 shift $((OPTIND-1)) 113 NET_FRAMEWORK_VER="p:TargetFrameworkVersion=v$1" 114 PARAMS+=("$NET_FRAMEWORK_VER") 115 OPTIND=2 116 ;; 117 "net") 118 NET=true 119 TFM="net8.0" 120 PARAMS+=(p:NET=true) 121 ;; 122 "profile-build") 123 CMAKE_COMMON="-DPROFILING_BUILD=ON" 124 ;; 125 "tlib-only") 126 TLIB_ONLY=true 127 ;; 128 "tlib-arch") 129 # This only makes sense with '--tlib-only' set; it might as well imply it 130 TLIB_ONLY=true 131 shift $((OPTIND-1)) 132 TLIB_ARCH=$1 133 OPTIND=2 134 ;; 135 "tlib-export-compile-commands") 136 if [ -z $TLIB_ARCH ]; then 137 echo "--tlib-export-compile-commands requires --tlib-arch begin set" 138 exit 1 139 fi 140 TLIB_EXPORT_COMPILE_COMMANDS=true 141 ;; 142 "host-arch") 143 shift $((OPTIND-1)) 144 if [ $1 == "aarch64" ] || [ $1 == "arm64" ]; then 145 HOST_ARCH="aarch64" 146 elif [ $1 == "i386" ] || [ $1 == "x86" ] || [ $1 == "x86_64" ]; then 147 HOST_ARCH="i386" 148 else 149 echo "host architecture $1 not supported. Supported architectures are i386 and aarch64" 150 exit 1 151 fi 152 OPTIND=2 153 ;; 154 "skip-dotnet-target-generation") 155 GENERATE_DOTNET_BUILD_TARGET=false 156 ;; 157 *) 158 print_help 159 exit 1 160 ;; 161 esac 162 ;; 163 \?) 164 print_help 165 exit 1 166 ;; 167 esac 168done 169shift "$((OPTIND-1))" 170PARAMS+=( 171 # By default use CC as Compiler- and LinkerPath, and AR as ArPath 172 ${CC:+"p:CompilerPath=$CC"} 173 ${CC:+"p:LinkerPath=$CC"} 174 ${AR:+"p:ArPath=$AR"} 175 # But allow users to override it 176 "$@" 177) 178 179if [ -n "${PLATFORM:-}" ] 180then 181 echo "PLATFORM environment variable is currently set to: >>$PLATFORM<<" 182 echo "This might cause problems during the build." 183 echo "Please clear it with:" 184 echo "" 185 echo " unset PLATFORM" 186 echo "" 187 echo " and run the build script again." 188 189 exit 1 190fi 191 192# We can only update parts of this repository if Renode is built from within the git tree 193if [ ! -e .git ] 194then 195 SKIP_FETCH=true 196 UPDATE_SUBMODULES=false 197fi 198 199if $SKIP_FETCH 200then 201 echo "Skipping init/update of submodules" 202else 203 # Update submodules if not initialized or if requested by the user 204 # Warn if not updating, but unclean 205 # Disabling -e to allow grep to fail 206 set +e 207 git submodule status --recursive | grep -q "^-" 208 SUBMODULES_NOT_INITED=$? 209 210 git submodule status --recursive | grep -q "^+" 211 SUBMODULES_NOT_CLEAN=$? 212 set -e 213 if $UPDATE_SUBMODULES || [ $SUBMODULES_NOT_INITED -eq 0 ] 214 then 215 echo "Updating submodules..." 216 git submodule update --init --recursive 217 elif [ $SUBMODULES_NOT_CLEAN -eq 0 ] 218 then 219 echo "Submodules are not updated. Use -s to force update." 220 fi 221fi 222 223. "${ROOT_PATH}/tools/common.sh" 224 225if $SKIP_FETCH 226then 227 echo "Skipping library fetch" 228else 229 "${ROOT_PATH}"/tools/building/fetch_libraries.sh 230fi 231 232if $HEADLESS 233then 234 BUILD_TARGET=Headless 235 PARAMS+=(p:GUI_DISABLED=true) 236elif $ON_WINDOWS 237then 238 BUILD_TARGET=Windows 239 TFM="$TFM-windows10.0.17763.0" 240else 241 BUILD_TARGET=Mono 242fi 243 244# Set correct RID 245if $ON_LINUX; then 246 RID="linux-x64" 247 if [[ $HOST_ARCH == "aarch64" ]]; then 248 RID="linux-arm64" 249 fi 250elif $ON_OSX; then 251 RID="osx-x64" 252 if [[ $HOST_ARCH == "aarch64" ]]; then 253 RID="osx-arm64" 254 fi 255elif $ON_WINDOWS; then 256 RID="win-x64" 257fi 258 259if [[ $GENERATE_DOTNET_BUILD_TARGET = true ]]; then 260 if $ON_WINDOWS; then 261 # CsWinRTAotOptimizerEnabled is disabled due to a bug in dotnet-sdk. 262 # See: https://github.com/dotnet/sdk/issues/44026 263 OS_SPECIFIC_TARGET_OPTS='<CsWinRTAotOptimizerEnabled>false</CsWinRTAotOptimizerEnabled>' 264 fi 265 266cat <<EOF > "$(get_path "$PWD/Directory.Build.targets")" 267<Project> 268 <PropertyGroup> 269 <TargetFrameworks>$TFM</TargetFrameworks> 270 ${OS_SPECIFIC_TARGET_OPTS:+${OS_SPECIFIC_TARGET_OPTS}} 271 </PropertyGroup> 272</Project> 273EOF 274 275fi 276 277if $NET 278then 279 export DOTNET_CLI_TELEMETRY_OPTOUT=1 280 CS_COMPILER="dotnet build" 281 TARGET="`get_path \"$PWD/Renode_NET.sln\"`" 282 BUILD_TYPE="dotnet" 283else 284 TARGET="`get_path \"$PWD/Renode.sln\"`" 285 BUILD_TYPE="mono" 286fi 287 288OUT_BIN_DIR="$(get_path "output/bin/${CONFIGURATION}")" 289BUILD_TYPE_FILE=$(get_path "${OUT_BIN_DIR}/build_type") 290 291# Verify Mono and mcs version on Linux and macOS 292if ! $ON_WINDOWS && ! $NET 293then 294 if ! [ -x "$(command -v mcs)" ] 295 then 296 MINIMUM_MONO=`get_min_mono_version` 297 echo "mcs not found. Renode requires Mono $MINIMUM_MONO or newer. Please refer to documentation for installation instructions. Exiting!" 298 exit 1 299 fi 300 301 verify_mono_version 302fi 303 304# Copy properties file according to the running OS 305mkdir -p "$OUTPUT_DIRECTORY" 306if [ -n "${CUSTOM_PROP}" ]; then 307 PROP_FILE=$CUSTOM_PROP 308else 309 if $ON_OSX 310 then 311 PROP_FILE="${CURRENT_PATH:=.}/src/Infrastructure/src/Emulator/Cores/osx-properties.csproj" 312 elif $ON_LINUX 313 then 314 PROP_FILE="${CURRENT_PATH:=.}/src/Infrastructure/src/Emulator/Cores/linux-properties.csproj" 315 else 316 PROP_FILE="${CURRENT_PATH:=.}/src/Infrastructure/src/Emulator/Cores/windows-properties.csproj" 317 fi 318fi 319cp "$PROP_FILE" "$OUTPUT_DIRECTORY/properties.csproj" 320 321if ! $NET 322then 323 # 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, 324 # 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. 325 # If `Renode_NET.sln` is picked for OmniSharp, it will trigger reanalysis of the project after removing assets files. 326 # We don't remove these files as part of `clean` target, because other intermediate files are well separated between .NET and .NETFramework 327 # and enforcing `clean` every time before rebuilding would slow down the build process on both frameworks. 328 find $ROOT_PATH -type f -name 'project.assets.json' -delete 329fi 330 331CORES_PATH="$ROOT_PATH/src/Infrastructure/src/Emulator/Cores" 332 333# clean instead of building 334if $CLEAN 335then 336 for project_dir in $(find "$(get_path "${ROOT_PATH}/src")" -iname '*.csproj' -exec dirname '{}' \;) 337 do 338 for dir in {bin,obj}/{Debug,Release} 339 do 340 output_dir="$(get_path "${project_dir}/${dir}")" 341 if [[ -d "${output_dir}" ]] 342 then 343 echo "Removing: ${output_dir}" 344 rm -rf "${output_dir}" 345 fi 346 done 347 done 348 349 # Manually clean the main output directory as it's location is non-standard 350 main_output_dir="$(get_path "${OUTPUT_DIRECTORY}/bin")" 351 if [[ -d "${main_output_dir}" ]] 352 then 353 echo "Removing: ${main_output_dir}" 354 rm -rf "${main_output_dir}" 355 fi 356 exit 0 357fi 358 359# Check if a full rebuild is needed 360if [[ -f "$BUILD_TYPE_FILE" ]] 361then 362 if [[ "$(cat "$BUILD_TYPE_FILE")" != "$BUILD_TYPE" ]] 363 then 364 echo "Attempted to build Renode in a different configuration than the previous build" 365 echo "Please run '$0 -c' to clean the previous build before continuing" 366 exit 1 367 fi 368fi 369 370# check weak implementations of core libraries 371pushd "$ROOT_PATH/tools/building" > /dev/null 372./check_weak_implementations.sh 373popd > /dev/null 374 375PARAMS+=(p:Configuration="${CONFIGURATION}${BUILD_TARGET}" p:GenerateFullPaths=true p:Platform="\"$BUILD_PLATFORM\"") 376 377# Paths for tlib 378CORES_BUILD_PATH="$CORES_PATH/obj/$CONFIGURATION" 379CORES_BIN_PATH="$CORES_PATH/bin/$CONFIGURATION" 380 381# Cmake generator, handled in their own variable since the names contain spaces 382if $ON_WINDOWS 383then 384 CMAKE_GEN="-GMinGW Makefiles" 385else 386 CMAKE_GEN="-GUnix Makefiles" 387fi 388 389# Macos architecture flags, to make rosetta work properly 390if $ON_OSX 391then 392 CMAKE_COMMON+=" -DCMAKE_OSX_ARCHITECTURES=x86_64" 393 if [ $HOST_ARCH == "aarch64" ]; then 394 CMAKE_COMMON+=" -DCMAKE_OSX_ARCHITECTURES=arm64" 395 fi 396fi 397 398# This list contains all cores that will be built. 399# If you are adding a new core or endianness add it here to have the correct tlib built 400CORES=(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) 401 402# if '--tlib-arch' was used - pick the first matching one 403if [[ ! -z $TLIB_ARCH ]]; then 404 NONE_MATCHED=true 405 for potential_match in "${CORES[@]}"; do 406 if [[ $potential_match == "$TLIB_ARCH"* ]]; then 407 CORES=("$potential_match") 408 echo "Compiling tlib for $potential_match" 409 NONE_MATCHED=false 410 break 411 fi 412 done 413 if $NONE_MATCHED ; then 414 echo "Failed to match any tlib arch" 415 exit 1 416 fi 417fi 418 419# build tlib 420for core_config in "${CORES[@]}" 421do 422 CORE="$(echo $core_config | cut -d '.' -f 1)" 423 ENDIAN="$(echo $core_config | cut -d '.' -f 2)" 424 BITS=32 425 # Check if core is 64-bit 426 if [[ $CORE =~ "64" ]]; then 427 BITS=64 428 fi 429 # Core specific flags to cmake 430 CMAKE_CONF_FLAGS="-DTARGET_ARCH=$CORE -DTARGET_WORD_SIZE=$BITS -DCMAKE_BUILD_TYPE=$CONFIGURATION" 431 CORE_DIR=$CORES_BUILD_PATH/$CORE/$ENDIAN 432 mkdir -p $CORE_DIR 433 pushd "$CORE_DIR" > /dev/null 434 if [[ $ENDIAN == "be" ]]; then 435 CMAKE_CONF_FLAGS+=" -DTARGET_WORDS_BIGENDIAN=1" 436 fi 437 if [[ "$TLIB_EXPORT_COMPILE_COMMANDS" = true ]]; then 438 CMAKE_CONF_FLAGS+=" -DCMAKE_EXPORT_COMPILE_COMMANDS=1" 439 fi 440 cmake "$CMAKE_GEN" $CMAKE_COMMON $CMAKE_CONF_FLAGS -DHOST_ARCH=$HOST_ARCH $CORES_PATH 441 cmake --build . -j"$(nproc)" 442 CORE_BIN_DIR=$CORES_BIN_PATH/lib 443 mkdir -p $CORE_BIN_DIR 444 if $ON_OSX; then 445 # macos `cp` does not have the -u flag 446 cp -v tlib/*.so $CORE_BIN_DIR/ 447 else 448 cp -u -v tlib/*.so $CORE_BIN_DIR/ 449 fi 450 # copy compile_commands.json to tlib directory 451 if [[ "$TLIB_EXPORT_COMPILE_COMMANDS" = true ]]; then 452 command cp -v -f $CORE_DIR/compile_commands.json $CORES_PATH/tlib/ 453 fi 454 popd > /dev/null 455done 456 457if $TLIB_ONLY 458then 459 exit 0 460fi 461 462# build 463eval "$CS_COMPILER $(build_args_helper "${PARAMS[@]}") $TARGET" 464echo -n "$BUILD_TYPE" > "$BUILD_TYPE_FILE" 465 466# copy llvm library 467LLVM_LIB="libllvm-disas" 468if [[ $HOST_ARCH == "aarch64" ]]; then 469 # aarch64 host binaries have a different name 470 LLVM_LIB="libllvm-disas-aarch64" 471fi 472if [[ "${DETECTED_OS}" == "windows" ]]; then 473 LIB_EXT="dll" 474elif [[ "${DETECTED_OS}" == "osx" ]]; then 475 LIB_EXT="dylib" 476else 477 LIB_EXT="so" 478fi 479cp lib/resources/llvm/$LLVM_LIB.$LIB_EXT $OUT_BIN_DIR/libllvm-disas.$LIB_EXT 480 481# build packages after successful compilation 482params="" 483 484if [ $CONFIGURATION == "Debug" ] 485then 486 params="$params -d" 487fi 488 489if [ -n "$EXPORT_DIRECTORY" ] 490then 491 if [ "${DETECTED_OS}" != "linux" ] 492 then 493 echo "Custom output directory is currently available on Linux only" 494 exit 1 495 fi 496 497 $ROOT_PATH/tools/packaging/export_${DETECTED_OS}_workdir.sh $EXPORT_DIRECTORY $params 498 echo "Renode built to $EXPORT_DIRECTORY" 499fi 500 501if $PACKAGES && $NIGHTLY 502then 503 params="$params -n" 504fi 505 506if $PACKAGES 507then 508 if $NET 509 then 510 # dotnet package on linux uses a separate script 511 if $ON_LINUX 512 then 513 # maxcpucount:1 to avoid an error with multithreaded publish 514 eval "dotnet publish -maxcpucount:1 -f $TFM --self-contained false $(build_args_helper "${PARAMS[@]}") $TARGET" 515 export RID TFM 516 $ROOT_PATH/tools/packaging/make_linux_dotnet_package.sh $params 517 # Source package bundles nuget dependencies required for building the dotnet version of Renode 518 # so it can only be built when using dotnet. The generated package can also be used with Mono/.NETFramework 519 $ROOT_PATH/tools/packaging/make_source_package.sh $params 520 elif $ON_WINDOWS && ! $PORTABLE 521 then 522 # No Non portable dotnet package on windows yet 523 echo "Only portable dotnet packages are supported on windows. Rerun build.sh with -t flag to build portable" 524 exit 1 525 elif $ON_OSX 526 then 527 eval "dotnet publish -maxcpucount:1 -f $TFM --self-contained false $(build_args_helper "${PARAMS[@]}") $TARGET" 528 export RID TFM 529 $ROOT_PATH/tools/packaging/make_osx_dotnet_package.sh $params 530 fi 531 else 532 $ROOT_PATH/tools/packaging/make_${DETECTED_OS}_packages.sh $params 533 fi 534fi 535 536if $PORTABLE 537then 538 PARAMS+=(p:PORTABLE=true) 539 if $NET 540 then 541 # maxcpucount:1 to avoid an error with multithreaded publish 542 echo "RID = $RID" 543 eval "dotnet publish -maxcpucount:1 -r $RID -f $TFM --self-contained true $(build_args_helper "${PARAMS[@]}") $TARGET" 544 export RID TFM 545 $ROOT_PATH/tools/packaging/make_${DETECTED_OS}_portable_dotnet.sh $params 546 else 547 if $ON_LINUX 548 then 549 $ROOT_PATH/tools/packaging/make_linux_portable.sh $params 550 else 551 echo "Portable packages for Mono are only available on Linux. Exiting!" 552 exit 1 553 fi 554 fi 555fi 556