How to distribute a .NET Core app in Arch Linux
Tags: archlinux, linux, bash, dotnet
Published: 2020-08-27 11:08:50

In this post we'll build a netcore CLI app called [taskcore](https://github.com/tarikguney/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](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish):

```bash
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:

```bash
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](https://wiki.musl-libc.org/projects-using-musl.html) 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:

```bash
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`:

```bash
cp -r $pkgname "$pkgdir/usr/lib/"
```

Then, we should make the `taskcore` command accessible by locally logged in users with a symlink:

```bash
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:

```bash
makepkg -s
```

## More about .NET

If you want to start with .NET you could see this awesome video from Scott Hanselman

{% youtube bEfBfBQq7EE %}