In this post we'll build a netcore CLI app called taskcore and learn about the interaction between makepkg
and a self-contained
.NET app. We start from a PKGBUILD
file and examine what it does with this self-contained
app.
What is a self-contained app?
In the .NET world this means that the published artifacts of the app will include everything to run without requiring .NET installed in the user's system.
Building the app
This is the PKGBUILD
for build the taskcore app in Arch Linux using the publish command:
pkgname=taskcore
pkgver=1.0.0beta3
_pkgver=1.0.0-beta.3
pkgrel=1
arch=('x86_64')
url='https://github.com/tarikguney/taskcore'
license=("APACHE")
depends=("icu")
makedepends=("dotnet-sdk")
options=("staticlibs")
source=("$url/archive/v$_pkgver.tar.gz")
sha256sums=('8472c31bb7cff8b6f543a46288753747d9c47fee8f32b3e198f8da5bcea3fca6')
build() {
cd "$pkgname-$_pkgver/TaskCore.App"
MSBUILDDISABLENODEREUSE=1 dotnet publish \
--configuration Release \
--self-contained true \
--runtime linux-x64 \
-p:PublishTrimmed=true \
--output ../$pkgname \
./TaskCore.App.csproj
}
package() {
cd "$pkgname-$_pkgver"
install -d $pkgdir/usr/{bin,lib}
cp -r $pkgname "$pkgdir/usr/lib/"
ln -s "/usr/lib/$pkgname/$pkgname" "$pkgdir/usr/bin/$pkgname"
}
The dependencies
The installation of .NET Core in Arch Linux is so easy:
depends=("icu")
makedepends=("dotnet-sdk")
We need the icu package (International Components for Unicode library) to run this app, otherwise we'll get this error:
Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support.
The makepkg options
We should use the staticlibs
option in the PKGBUILD
to tell Arch to leave static library files in the package, otherwise the makepkg
will assume the libraries will be installed as a dependency package to avoid duplicated files in the OS.
The publish arguments
The -p:PublishTrimmed=true
will remove unused libraries in the build output if they aren't statically linked to our executable file.
The --self-contained true
produces a platform-specific executable, so we should include the target platform with --runtime linux-x64
, other options are linux-arm64
, or linux-musl-x64
for lightweight distributions using musl like Alpine Linux.
We can publish the libraries inside the executable with -p:PublishSingleFile=true
, but makepkg
will strip all the libraries from the executable file. This optimization is useful to remove symbols (commonly used for debug) and get a lighter package, but for .NET we should use:
options=("!strip")
What about the MSBUILDDISABLENODEREUSE=1
? I found this error when I run the build several times:
MSBUILD : error MSB4166: Child node "2" exited prematurely. Shutting down...
Looks like the failed builds leave MSBuild processes lying around.
How to package the app
We should move all the build output to the /usr/lib/taskcore
:
cp -r $pkgname "$pkgdir/usr/lib/"
Then, we should make the taskcore
command accessible by locally logged in users with a symlink:
ln -s "/usr/lib/$pkgname/$pkgname" "$pkgdir/usr/bin/$pkgname"
# /usr/bin/taskcore -> /usr/lib/taskcore/taskcore
And, it's done! We've our first .NET self-contained app for Linux. Just build it:
makepkg -s
More about .NET
If you want to start with .NET you could see this awesome video from Scott Hanselman
Top comments (1)
package() fails at
cp -r $pkgname "$pkgdir/usr/lib/"
:if i remove $pkgdir from the output path i get Permission denied for trying to move to /usr/lib
also getting
from doing
ln -s bin/Release/net6.0/linux-arm64/publish/app /usr/bin/
but even if i do
sudo ln -s..
when the link is created in /usr/bin/ i still cant executeapp
, the link appears red when i dols /usr/bin/app