Making native library builds more consistent with GitHub Actions

When we first started building C# libraries that relied on native libraries to do their function, we tried to make this goal stand still: maintaining cross platform support across all popular architectures, such as AMD64 and ARM64. For consistency, we wanted to bundle the native libraries that we built to a single NuGet package, just like all other NuGet packages that packaged their native libraries.

However, our method of relying on different mechanisms, such as using GitHub Actions for building on one platform and using our virtual machines for building on others was wrong, because we didn’t create isolated build environments to be able to successfully reproduce builds of the native libraries. As a result, we had to often mix and match dependencies to get things to go smoothly, which resulted in more wasted time.

It was at the time that GitHub Actions didn’t support Windows ARM64. However, we’re relieved to see that GitHub have added a new GitHub-hosted runner for Windows 11 ARM64 and Ubuntu 24.04 ARM64, alongside the macOS ARM64 runners!

So, we’ve decided to re-evaluate our approach of building those native libraries so that we’d save time, considering the resource constraints that we currently have, by taking those runners into account. As a result, six workflow files were added to the library repository. Here, we’ll be talking about BassBoom.Basolia and how it managed to use mpg123 v1.33.3.

The following workflow files were added:

We’ve adjusted the workflow files to point to the correct commands to build the native library. For example, on the Ubuntu workflow, we’ve added commands that installed build dependencies of mpg123 according to the Debian package control file.

- name: Setting up environment run: | sudo apt update sudo apt install libasound2-dev libaudio-dev libjack-dev libltdl-dev libopenal-dev libpulse-dev pkgconf portaudio19-dev

Then, we’ve changed the link to the archive file of the MPG123 source code to point to version v1.33.3 as follows:

- name: Setting up MPG123 library run: | curl -o mpg123.tar.bz2 https://www.mpg123.de/download/mpg123-1.33.3.tar.bz2 tar xvf mpg123.tar.bz2

Afterwards, we’ve added the parameters to the ./configure command to disable building the application, while only building the relevant libraries that BassBoom uses, to save time. We’ve also added a new command, make install DESTDIR=$PWD/outlib, to group the artifacts together in one folder that is to be uploaded to the action workflow artifacts.

- name: MPG123 compilation run: | cd mpg123-1.33.3 ; ./configure --disable-components --enable-libmpg123 --enable-libout123 --enable-libout123-modules ; make ; make install DESTDIR=$PWD/outlib

Once done, the resulting ZIP file is then manually downloaded, and the resulting library files are placed to the appropriate directories. For example, we extract libmpg123.so and libout123.so files from /usr/local/lib in the archive to the public/BassBoom.Native/runtimes/linux-x64/native folder. Afterwards, the out123 modules are then copied from /usr/local/lib/mpg123 to the plugins folder underneath the abovementioned native folder.

We’ve done something almost similar to the macOS workflows, except that the dynamic link libraries are under the .dylib extension instead of the .so extension that FreeBSD uses.

However, in Windows, we had to do something different to build the library. In order to do this, we had leveraged the MinGW development environment with CLANGARM64 for ARM64 builds (in the ARM64 workflow) and MINGW64 for AMD64 builds (in the AMD64 workflow) under the MSYS2 environment. This was done to avoid cross-compiling the ARM64 version in the AMD64 environment, thus reducing the failure rate.

The following was done for AMD64:

- name: Setting up environment uses: msys2/setup-msys2@v2 with: msystem: MINGW64 update: true install: | base-devel mingw-w64-x86_64-gcc mingw-w64-x86_64-make autoconf automake libtool tar bzip2

The following was done for ARM64:

- name: Setting up environment uses: msys2/setup-msys2@v2 with: msystem: CLANGARM64 update: true install: | base-devel mingw-w64-clang-aarch64-clang mingw-w64-clang-aarch64-make autoconf automake libtool tar bzip2

Then, after downloading the archive file for the source code of the MPG123 libraries and extracting it under the MSYS2 shell, we’ve run the compilation of the library as follows:

- name: MPG123 compilation shell: msys2 {0} run: | cd mpg123-1.33.3 ; ./configure --prefix=/mingw64 --disable-components --enable-libmpg123 --enable-libout123 --enable-libout123-modules --with-default-audio=win32 CFLAGS="-O2 -D__USE_MINGW_ANSI_STDIO=0" ; make ; make install DESTDIR=$PWD/outlib cp /mingw64/bin/libgcc_s_seh-1.dll outlib/ || true cp /mingw64/bin/libwinpthread-1.dll outlib/ || true

We’ve selected the win32 audio driver as the default, enabled the optimization level 2 to generate optimized code, and disabled ANSI for stdio, as per the C compiler flags. Afterwards, the two dependencies, libgcc_s_seh-1.dll and libwinpthread-1.dll, have been copied to the artifacts folder, as specified above.

We had to do the same thing for ARM64:

- name: MPG123 compilation shell: msys2 {0} run: | export lt_cv_deplibs_check_method=pass_all cd mpg123-1.33.3 ; ./configure --prefix=/clangarm64 --disable-components --enable-libmpg123 --enable-libout123 --enable-libout123-modules --with-default-audio=win32 CFLAGS="-O2 -D__USE_MINGW_ANSI_STDIO=0" ; make ; make install DESTDIR=$PWD/outlib cp /clangarm64/bin/libunwind.dll outlib/ || true cp /clangarm64/bin/libc++.dll outlib/ || true

The difference was that we had added an export for the environment variable, called lt_cv_deplibs_check_method, with the value of pass_all, because building MPG123 in a CLANGARM64 environment within the ARM64 runner would yield messages like this:

*** Warning: linker path does not have real file for library -lwinmm.*** I have the capability to make that library automatically link in when*** you link to this library. But I can only do this if you have a*** shared version of the library, which you do not appear to have*** because I did check the linker path looking for a file starting*** with libwinmm and none of the candidates passed a file format test*** using a file magic. Last file checked: C:/a/_temp/msys64/clangarm64/lib/libwinmm.a*** Warning: libtool could not satisfy all declared inter-library*** dependencies of module output_win32. Therefore, libtool will create*** a static module, that should work as long as the dlopening*** application is linked with the -dlopen flag.

This environment variable tells libtool to trust all system libraries, such as winmm and ole32, to mitigate this issue and to let the build system generate DLL files for the ARM64 installation. As a result, we now have DLLs in the /clangarm64/bin folder (/mingw64/bin for AMD64), and the out123 modules are located in the /clangarm64/lib/mpg123 folder (/mingw64/lib/mpg123 for AMD64). After that, they’re extracted to their appropriate places in the runtime folder of the managed library, BassBoom.Native.

For Windows, we have to rename the generated library DLL file name to mpg123.dll and out123.dll, as per the initializer source code. Afterwards, BassBoom.Native can load the updated libraries and make use of them in BassBoom.Basolia.

As a result, we have managed to achieve consistency in building the native library for BassBoom, Magico, and SpecProbe, all of which have been updated in order to update those libraries to their latest versions.

Photo by Nicole Wolf on Unsplash

#C #github #GitHubActions #NativeLibraries #NativeLibrary #news #PInvoke #Tech #Technology #update

UAC shield icon in WPF #code #.net #c# #wpf #uac #pinvoke How to add the correct UAC shield icon for each Windows version in .NET WPF using Win32 interop.

https://fed.brid.gy/r/https://ithoughthecamewithyou.com/post/uac-shield-icon-in-wpf

От сервера к десктопу: эксперимент с ASP.NET AOT и WebKitGTK

В этой статье я расскажу об экспериментальной связке технологий, которую я раньше не встречал нигде и основным мотиватором была проверка жизнеспособности этой идеи. Цель этого эксперимента — проверка жизнеспособности стека ASP .NET + WebKitGTK + frontend на JS/TS. Этот стек предназначен для работы на ОС Linux. В проекте весь код написан на C# за исключением клиентской части, которая реализована при помощи TypeScript и Angular 19.1. Этот концепт представляет из себя приложение, которое получает данные из Github API и для выбранного языка программирования проводит анализ количества новых репозиториев github по годам, строя линейную диаграмму, показывая тем самым тренд языка программирования. Этот анализ изначально не слишком объективен, но для тестового приложения вполне нормальный сценарий использования. В приложении на том же WebKitGTK реализована OAuth аутентификация Github. Полученные токены хранятся в системе при помощи библиотеки libsecret в зашифрованном (AES) виде. После первого входа пользователю необходимо установить пин-код, который является частью ключа. Немного заморочился с безопасностью токенов, да =)

https://habr.com/ru/articles/880908/

#ASPNET #NET #AOT #WebKitGTK #Angular #TypeScript #C# #SkiaSharp #GraphQL #PInvoke

От сервера к десктопу: эксперимент с ASP.NET AOT и WebKitGTK

Введение Пару лет назад для .NET в Linux не было не то чтобы production-ready фреймворков для создания native desktop приложений, но и экспериментальных. На тот момент существовали Uno Platform и...

Хабр

Please explain to me, why a #PInvoke call would be able to heap corrupt a managed #DotNet #CSharp application, when invoking that method repeatedly (<5 times).
If - and only if - that application is running in a packaged MSIX environment (with full trust enabled) since 24H2.

Running that application standalone doesn’t cause a crash.

#Microsoft #Windows

P/Invoke is a way to allow unmanaged libraries from managed code. In the old days, it was a way to access Windows API that was not yet available in the .NET framework.

Since .NET became multi-platform, being present on Mac and Linux, P/Invoke has also been available on those platforms.

To import a native method that we would like to use, we need to create a static extern function and mark it with the DllImport attribute.

The DllImport attribute allows us to specify the native library from which the function should be loaded, as well as some extra parameters to cover more complex scenarios.

Docs 📑: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/pinvoke

#dotnet #platforminvoke #pinvoke
---
If you find this useful, consider giving a like & share ♻

Platform Invoke (P/Invoke) - .NET

Learn how to call native functions via P/Invoke in .NET.

I'm happy to announce that my NDC Porto talk "Introduction to unsafe C#: Calling native code and crashing in entirely new ways" is now available on YouTube. Thanks NDC for having me and for making all recordings available for free!
#NDC #NDCPorto #NDCConferences #dotnet #CSharp #PInvoke #UnsafeCSharp
https://www.youtube.com/watch?v=aSm8o2EIyvM
Introduction to unsafe C#: Calling native code and crashing in entirely new ways - Dennis Dietrich

YouTube

Fresh new PowerShell module called ctypes https://powershellgallery.com/packages/Ctypes/0.1.0. This makes it easier to prototype PInvoke calls in PowerShell. As an example, to call `GetCurrentProcess()`, it's simply:

$k32 = New-CtypesLib Kernel32.dll
$k32.GetCurrentProcess[IntPtr]()

#powershell #pinvoke

Ctypes 0.1.0

PowerShell implemtation of the Python's ctypes library for PInvoke methods

heise+ | Mit PowerShell Windows-Funktionen steuern: Fenster verstecken, Klänge erzeugen

Mit PowerShell und der Win32-API können Sie Funktionen aufrufen, für die es weder Cmdlets noch Aufrufe in .NET gibt. Ein Einstieg mit Praxisbeispielen. Mit PowerShell Windows-Funktionen steuern: Fenster verstecken, Klänge erzeugen
Mit PowerShell Windows-Funktionen steuern: Fenster verstecken, Klänge erzeugen

Mit PowerShell und der Win32-API können Sie Funktionen aufrufen, für die es weder Cmdlets noch Aufrufe in .NET gibt. Ein Einstieg mit Praxisbeispielen.