Co-authored by Swapnali Pawar
Have you ever attempted to generate a .NET 8 tarball directly from the source code? Wondering why you cannot compile it on IBM Power to create the SDK? The reason lies in the compiler code itself, written in C#. To overcome this, you must first cross-build all required repositories on an x86 machine to generate the .NET SDK tarball for Power architecture. This tarball can then be installed on a Power virtual machine (VM), enabling you to leverage .NET for development. .NET 8 boasts numerous enhancements in performance, stability, security, platforms, and tools, ultimately leading to increased developer productivity and innovation velocity.
Let's delve into a step-by-step guide on cross-building .NET 8 on x86 Ubuntu for IBM Power, followed by source building on the Power VM.
Prerequisites
- x86 Ubuntu VM: This VM will be used to create the cross build tarball.
- Docker installed on the x86 Ubuntu VM: The scripts require Docker to run.
Refer to the Install Docker Engine on Ubuntu documentation for the Docker installation steps.
Steps
1. Cross-build .NET repositories to generate tarball for IBM Power
- Clone the dotnet-s390x repository: This repository contains scripts for building the .NET SDK inside a Docker container. Clone the repository using the following command.
git clone --recurse-submodules https://github.com/IBM/dotnet-s390x.git
git checkout c6b6d47ab2b3ed4c165b7299526fb0229b47f624
Note: This repository was originally created for building s390x and then extended to support building ppc64le (Power architecture).
- Navigate to the dotnet-s390x folder: Navigate to the -s390x directory using the following command.
cd dotnet-s390x
- Export the architecture for Power: Create a target SDKs for Power on the cross x86 architecture using the following command.
export ARCH=ppc64le
- Clean up stale files: Remove any stale and outdated files using the following command.
./docker/run ./dotnet-cleanup
- Checkout source and create local tag: Check out the corresponding source code in the repository and create a local tag for the version you want to cross-build using the following command.
./docker/run ./dotnet-bump "<installer_version>"
Example:
./docker/run ./dotnet-bump "8.0.100"
Note: Refer to Microsoft .Net page for valid <installer_version>
values.
- Prepare repositories and patches: Clone the required .NET repositories and apply a few non-release patches using the following command.
./docker/run ./dotnet-prepare
Note: Ensure all repositories and patches are in place before proceeding.
- Build the tarball: Finally, build the tarball using the following command.
./docker/run ./dotnet-build
Upon successful build completion, you can find a file named dotnet-sdk-8.0.100-linux-ppc64le.tar.gz along with runtime-specific packages in the output folder, as follows:
dotnet-runtime-8.0.0-linux-ppc64le.tar.gz
runtime.linux-ppc64le.Microsoft.NETCore.DotNetHost.8.0.0.nupkg
dotnet-runtime-symbols-linux-ppc64le-8.0.0.tar.gz
runtime.linux-ppc64le.Microsoft.NETCore.DotNetHostPolicy.8.0.0.nupkg
dotnet-sdk-8.0.100-linux-ppc64le.tar.gz
runtime.linux-ppc64le.Microsoft.NETCore.DotNetHostResolver.8.0.0.nupkg
Microsoft.AspNetCore.App.Runtime.linux-ppc64le.8.0.0.nupkg
runtime.linux-ppc64le.Microsoft.NETCore.ILAsm.8.0.0.nupkg
Microsoft.NETCore.App.Host.linux-ppc64le.8.0.0.nupkg
runtime.linux-ppc64le.Microsoft.NETCore.ILDAsm.8.0.0.nupkg
Microsoft.NETCore.App.Runtime.linux-ppc64le.8.0.0.nupkg
- Copy the output folder: Copy the entire output folder to the Power VM where you intend to install the .NET SDK.
2. Install tarball on Power VM with Ubuntu 18.04
- Navigate to the output directory: On the Power VM, navigate to the directory where you copied the tarball generated on the x86 machine.
- Create a sub folder and extract tarball: Create a sub-folder and extract tarball using the following command.
DOTNET_FILE=dotnet-sdk-8.0.100-linux-ppc64le.tar.gz
mkdir -p .dotnet
export DOTNET_ROOT=$(pwd)/.dotnet
tar zxf "$DOTNET_FILE" -C "$DOTNET_ROOT"
- Add .NET root folder: Add .NET root folder in path using the following command.
export PATH=$PATH:$DOTNET_ROOT
- Verify installation: Verify the successful installation of the .NET SDK using the following command.
dotnet --info
This command should display information about the installed .NET SDK version, including details about the .NET runtime and workloads.
Upon successful completion, you should see the output displayed as follows:
# dotnet --info
.NET SDK:
Version: 8.0.100
Commit: 57efcf1350
Workload version: 8.0.100-manifests.e15fffd0
Runtime Environment:
OS Name: rhel
OS Version: 9
OS Platform: Linux
RID: linux-ppc64le
Base Path: /root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/.dotnet/sdk/8.0.100/
.NET workloads installed:
Workload version: 8.0.100-manifests.e15fffd0
There are no installed workloads to display.
Host:
Version: 8.0.0
Architecture: ppc64le
Commit: b3daf0fa09
.NET SDKs installed:
8.0.100 [/root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/.dotnet/sdk]
.NET runtimes installed:
Microsoft.AspNetCore.App 8.0.0 [/root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/.dotnet/shared/Microsoft.AspNetCore.App]
Microsoft.NETCore.App 8.0.0 [/root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/.dotnet/shared/Microsoft.NETCore.App]
Other architectures found:
None
Environment variables:
DOTNET_ROOT [/root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/.dotnet]
global.json file:
Not found
Learn more:
https://aka.ms/dotnet/info
Download .NET:
https://aka.ms/dotnet/download
3. Run Hello World program on Power VM with Ubuntu version 18.04
- Generate the console application: Create a new directory named HelloWorld and navigate to the HelloWorld directory. Generate the console application using the following command.
mkdir HelloWorld
cd HelloWorld
dotnet new console
Upon successful completion, you should see the output displayed as follows:
$ mkdir HelloWorld
$ cd HelloWorld
$/HelloWorld$ dotnet new console
Welcome to .NET 8.0!
---------------------
SDK Version: 8.0.100
----------------
Installed an ASP.NET Core HTTPS development certificate.
To trust the certificate, view the instructions: https://aka.ms/dotnet-https-linux
----------------
Write your first app: https://aka.ms/dotnet-hello-world
Find out what's new: https://aka.ms/dotnet-whats-new
Explore documentation: https://aka.ms/dotnet-docs
Report issues and find source on GitHub: https://github.com/dotnet/core
Use 'dotnet --help' to see available commands or visit: https://aka.ms/dotnet-cli
--------------------------------------------------------------------------------------
The template "Console App" was created successfully.
Processing post-creation actions...
Restoring /root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/HelloWorld/HelloWorld.csproj:
Determining projects to restore...
Restored /root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/HelloWorld/HelloWorld.csproj (in 1.2 sec).
- Build and run the application: Build the application using
dotnet build
command and then run the application using dotnet run
command.
dotnet build
dotnet run
Upon successful completion, you should see the output displayed as follows:
~/HelloWorld$ dotnet build
MSBuild version 17.8.3+195e7f5a3 for .NET
Determining projects to restore...
All projects are up-to-date for restore.
HelloWorld -> /root/dotnet-sdk-upstream-8.0.100-ga.ppc64le/HelloWorld/bin/Debug/net8.0/HelloWorld.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:08.93
~/HelloWorld$ dotnet run
Hello, World!
Note: These scripts have been tested on Ubuntu version 18.04, but they should also function correctly on Ubuntu version 20.04.
The next step focuses on building the source code using the cross-build tarball generated earlier.
4. Source build .NET repositories to generate tarball for Power on Ubuntu
- Create the first level source build: Copy the following script to the Power VM and set the path of the cross-build SDK to the OUTPUT_DIR variable, as shown in line 15 of the script.
#!/bin/bash
# Performs a build and runs tests of https://github.com/dotnet/dotnet
# to detect regressions early
set -euxo pipefail
REPO=https://github.com/dotnet/dotnet
REF=release/8.0.1xx
CONFIGURATION="Release"
HOST_RID_OVERRIDE=""
SDK_VERSION=""
OUTPUT_DIR=/root/dotnet-sdk-upstream-8.0.105-ga.ppc64le
while [ $# -ne 0 ]
do
name="$1"
case "$name" in
# --configuration: build configuration (Release/Debug)
--configuration)
shift
CONFIGURATION="$1"
;;
--ref)
shift
REF="$1"
;;
--use-mono-runtime)
build_arguments+=(--use-mono-runtime)
;;
--sdk_version)
shift
SDK_VERSION="$1"
;;
*)
echo "Unknown argument \`$name\`"
exit 1
;;
esac
shift
done
architecture=$(uname -m)
# install dependencies
sudo apt-get install clang cmake findutils git libc-dev hostname krb5-multidev libicu-dev liblttng-ust-dev llvm make libssl-dev python3 zlib1g-dev
sudo update-locale LANG=en_US.UTF-8
# function to rename nupkg
cp_nupkg_as_version()
{
local file="$1"
local targetversion="$2"
local tmpdir=$(mktemp -d)
unzip "$file" -d "$tmpdir"
local nuspecfile=$(ls $tmpdir/*.nuspec)
local id_line=$(grep -m 1 "<id>" "$nuspecfile")
id_regex="<id>(.*)</id>"
if [[ "$id_line" =~ $id_regex ]]; then
local id="${BASH_REMATCH[1]}"
else
echo "failed to parse id from: '$id_line'"
exit 1
fi
local version_line=$(grep -m 1 "<version>" "$nuspecfile")
version_regex="<version>(.*)</version>"
if [[ "$version_line" =~ $version_regex ]]; then
local version="${BASH_REMATCH[1]}"
else
echo "failed to parse version from: '$version_line'"
exit 1
fi
sed -i "s|$version_line|<version>$targetversion</version>|g" "$nuspecfile"
local targetfile="$(dirname "$file")/$id.$targetversion.nupkg"
targetfile=$(echo "$targetfile" | tr '[:upper:]' '[:lower:]')
pushd "$tmpdir"
zip -r "$targetfile" .
popd
}
git clone "$REPO"
cd "$(basename "$REPO" .git)"
git checkout "$REF"
COMMIT=$(git rev-parse HEAD)
echo "$REPO is at $COMMIT"
BUILD_DIR="$(pwd)"
EXIT_CODE=256
BUILD_EXIT_CODE=256
if [[ "$architecture" = s390x ]] || [[ "$architecture" = "ppc64le" ]]; then
# The dotnet SDK artifact uploaded will be in this form: ${NAME}-${VERSION}-${RELEASE}.${ARCH}.rpm.
# Construct a ".rpm" like string after parsing the version from "global.json" file.
mkdir dotnet-sdk-$architecture
cp -r ${OUTPUT_DIR}/. dotnet-sdk-$architecture/.
nuget_dir=$(readlink -f "dotnet-sdk-$architecture")
pushd dotnet-sdk-$architecture
if [ ! -d .dotnet ]; then
mkdir .dotnet
tar xf "dotnet-sdk-"*"${architecture}.tar.gz" -C .dotnet
fi
sdk_versions=( .dotnet/sdk/* )
sdk_version=$(basename "${sdk_versions[0]}")
popd
cp -a "dotnet-sdk-${architecture}"/.dotnet .dotnet
DOTNET_ROOT=$(pwd)/.dotnet
export DOTNET_ROOT
# replace version but only when it appears on the line after the "sdk" key
# See https://stackoverflow.com/questions/18620153
sed -i -E '/"sdk": \{/!b;n;s/"version": "[^"]+"/"version": "'"$sdk_version"'"/' global.json
# replace dotnet but only when it appears on the line after the "tools" key
sed -i -E '/"tools": \{/!b;n;s/"dotnet": "[^"]+"/"dotnet": "'"$sdk_version"'"/' global.json
# Remove installation of runtimes, these will install invalid binaries, or,
# worse, overwrite our architecture-specific binaries with x86_64 binaries
sed -i -E '/"runtimes"/,+4d' global.json
sed -i -zE 's/,\n *\}/\n\}/' global.json
# Make the pre-built s390x/ppc64le nuget packages available
find . -iname 'nuget.config' -exec sed -i -zE 's|(<packageSources>.*<clear ?/>)|\1\n<add key="'"$architecture"'" value="'"$nuget_dir"'" />|' {} \;
fi
if [[ "$architecture" = ppc64le ]]; then
# Disable apphost for ppc64le as do not support .net6
sed -i -E 's|</OutputType>|</OutputType><UseAppHost>false</UseAppHost>|' src/roslyn-analyzers/src/PerformanceTests/Tests/PerformanceTests.csproj
sed -i -E 's|</OutputType>|</OutputType><UseAppHost>false</UseAppHost>|' src/vstest/test/Intent/Intent.csproj
fi
#prep
./prep.sh
# allow fetching additional prebuilds while building non-release branches.
if [[ "$REF" != release/* ]]; then
build_arguments+=(--online)
fi
if [[ "$architecture" == "s390x" ]] || [[ "$architecture" == "ppc64le" ]]; then
# Update ilasm nupkg version as per PackageVersions.prop
mkdir -p packages-customized-local
pushd packages-customized-local
tar xf ../prereqs/packages/archive/Private.SourceBuilt.Artifacts.*.tar.gz
ilasm_new_version=$(sed -n '/<MicrosoftNETCoreILAsmVersion>/,/<\/MicrosoftNETCoreILAsmVersion>/ s/.*<MicrosoftNETCoreILAsmVersion>\(.*\)<\/MicrosoftNETCoreILAsmVersion>.*/\1/p' PackageVersions.props | sed -n '1p')
popd
ilasm_version=$(ls $nuget_dir | grep -i ilasm | tr 'A-Z' 'a-z' | sed -E 's|runtime.linux-'"$architecture"'.microsoft.netcore.ilasm.||' | sed -E 's|.nupkg$||')
echo $ilasm_version
cp_nupkg_as_version "$nuget_dir/runtime.linux-$architecture.Microsoft.NETCore.ILAsm.$ilasm_version.nupkg" "$ilasm_new_version"
cp_nupkg_as_version "$nuget_dir/runtime.linux-$architecture.Microsoft.NETCore.ILDAsm.$ilasm_version.nupkg" "$ilasm_new_version"
cp -r "$nuget_dir"/*.nupkg prereqs/packages/prebuilt/
build_arguments+=(--use-mono-runtime)
fi
BUILD_EXIT_CODE=0
./build.sh ${common_arguments[0]+"${common_arguments[@]}"} ${build_arguments[0]+"${build_arguments[@]}"} || BUILD_EXIT_CODE=$?
EXIT_CODE=$BUILD_EXIT_CODE
exit $EXIT_CODE
This script is designed for the dotnet/dotnet repository, a virtual monolithic repository which includes all the source code and infrastructure required to build the .NET SDK (dotnet/dotnet). The script manages essential dependencies required for compiling .NET from its source code. Additionally, it prepares the source code for building and then proceeds to build the .NET SDK.
- Run the script: Run the script using the following command.
./dotnet-dotnet.sh --ref release/8.0.1xx
Here –ref
parameter is to specify the check out
For example, ./dotnet-dotnet.sh --ref release/8.0.1xx
Refer to the version specified in the global.json file to determine the tarball version to be used for the source build.
Note: If you're building from a specific tag, utilize the cross-built tarball from the previous tag. On the other hand, if you're working with a branch, use the most recent version available.
- Locate the built tarball: Locate the resulting SDK at the following path.
artifacts/ppc64le/Release/dotnet-sdk-8.0.100-your-RID.tar.gz.
- Specify the path: Provide the path of
OUTPUT_DIR
in the script where you have set up your cross-build untar
folder.
Conclusion
That brings us to the end of this blog! We hope you feel more confident now that you have learned how to cross-build .NET 8 on x86 for the IBM Power architecture and perform a source build using the cross-built tarball. With numerous enhancements and improvements, .NET 8 offers a richer experience for developers. By installing the .NET 8 tarball on a Power VM, you can leverage .NET for your development needs. If you follow these steps and successfully build .NET on your Power VM, do tell us about your experience!