From ace099220780e4968b74641c24b85c0ae880e670 Mon Sep 17 00:00:00 2001 From: GGG Date: Sat, 11 Jan 2025 21:04:04 -0300 Subject: [PATCH 1/3] patchcil: init at 0.2.2 --- pkgs/by-name/pa/patchcil/deps.json | 32 ++++++++++++ pkgs/by-name/pa/patchcil/package.nix | 76 ++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 pkgs/by-name/pa/patchcil/deps.json create mode 100644 pkgs/by-name/pa/patchcil/package.nix diff --git a/pkgs/by-name/pa/patchcil/deps.json b/pkgs/by-name/pa/patchcil/deps.json new file mode 100644 index 000000000000..175fd1c3cb4d --- /dev/null +++ b/pkgs/by-name/pa/patchcil/deps.json @@ -0,0 +1,32 @@ +[ + { + "pname": "AsmResolver", + "version": "6.0.0-beta.1", + "hash": "sha256-ZW61z6Qmztdy2NaiqxvNcP5RWBIiIO6CWNnqYq0MwoA=" + }, + { + "pname": "AsmResolver.DotNet", + "version": "6.0.0-beta.1", + "hash": "sha256-VoTiIr2/r2my6sg2AOEeiqz9vZhWtq5mGaW2Hx90Uo4=" + }, + { + "pname": "AsmResolver.PE", + "version": "6.0.0-beta.1", + "hash": "sha256-tTU/flTxRJaC4gkmI/gctqIriGIMntkgTs51TqzcQlg=" + }, + { + "pname": "AsmResolver.PE.File", + "version": "6.0.0-beta.1", + "hash": "sha256-hPuFrpcm2VMiYEirsL4kYmAhOzjwjNXUklIfYJEonLo=" + }, + { + "pname": "DotNet.Glob", + "version": "3.1.3", + "hash": "sha256-5uGSaGY1IqDjq4RCDLPJm0Lg9oyWmyR96OiNeGqSj84=" + }, + { + "pname": "System.CommandLine", + "version": "2.0.0-beta4.22272.1", + "hash": "sha256-zSO+CYnMH8deBHDI9DHhCPj79Ce3GOzHCyH1/TiHxcc=" + } +] diff --git a/pkgs/by-name/pa/patchcil/package.nix b/pkgs/by-name/pa/patchcil/package.nix new file mode 100644 index 000000000000..d0bdfb97c7b2 --- /dev/null +++ b/pkgs/by-name/pa/patchcil/package.nix @@ -0,0 +1,76 @@ +{ + lib, + fetchFromGitHub, + buildDotnetModule, + dotnetCorePackages, + stdenv, + nix-update-script, + aot ? dotnetCorePackages.sdk_9_0.hasILCompiler && !stdenv.hostPlatform.isDarwin, +}: + +buildDotnetModule rec { + pname = "patchcil"; + version = "0.2.2"; + + src = fetchFromGitHub { + owner = "GGG-KILLER"; + repo = "patchcil"; + tag = "v${version}"; + hash = "sha256-jqVXKp5ShWkIMAgmcwu9/QHy+Ey9d1Piv62wsO0Xm44="; + }; + + nativeBuildInputs = lib.optional aot stdenv.cc; + + projectFile = "src/PatchCil.csproj"; + nugetDeps = ./deps.json; + + dotnet-sdk = dotnetCorePackages.sdk_9_0; + dotnet-runtime = if aot then null else dotnetCorePackages.runtime_9_0; + + selfContainedBuild = aot; + dotnetFlags = lib.optionals (!aot) [ + # Disable AOT + "-p:PublishAot=false" + "-p:InvariantGlobalization=false" + "-p:EventSourceSupport=true" + "-p:HttpActivityPropagationSupport=true" + "-p:MetadataUpdaterSupport=true" + "-p:MetricsSupport=true" + "-p:UseNativeHttpHandler=false" + "-p:XmlResolverIsNetworkingEnabledByDefault=true" + "-p:EnableGeneratedComInterfaceComImportInterop=true" + "-p:_ComObjectDescriptorSupport=true" + "-p:_DataSetXmlSerializationSupport=true" + "-p:_DefaultValueAttributeSupport=true" + "-p:_DesignerHostSupport=true" + "-p:_EnableConsumingManagedCodeFromNativeHosting=true" + "-p:_UseManagedNtlm=true" + ]; + + preFixup = lib.optionalString aot '' + # Remove debug symbols as they shouldn't have anything in them. + rm $out/lib/patchcil/patchcil.dbg + ''; + + executables = [ "patchcil" ]; + + passthru = { + updateScript = nix-update-script { }; + }; + + meta = { + description = "A small utility to modify the library paths from PInvoke in .NET assemblies."; + homepage = "https://github.com/GGG-KILLER/patchcil"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ ggg ]; + mainProgram = "patchcil"; + platforms = [ + "x86_64-linux" + "aarch64-linux" + "x86_64-darwin" + "aarch64-darwin" + "x86_64-windows" + "i686-windows" + ]; + }; +} From 84aa2c4d8e51ce0e3d5080702cc3d538fb72930e Mon Sep 17 00:00:00 2001 From: GGG Date: Sat, 11 Jan 2025 21:34:44 -0300 Subject: [PATCH 2/3] dotnetCorePackages.autoPatchcilHook: init --- .../auto-patchcil-hook/auto-patchcil.sh | 118 ++++++++++++++++++ .../dotnet/auto-patchcil-hook/default.nix | 14 +++ pkgs/development/compilers/dotnet/default.nix | 1 + pkgs/top-level/all-packages.nix | 1 + 4 files changed, 134 insertions(+) create mode 100644 pkgs/build-support/dotnet/auto-patchcil-hook/auto-patchcil.sh create mode 100644 pkgs/build-support/dotnet/auto-patchcil-hook/default.nix diff --git a/pkgs/build-support/dotnet/auto-patchcil-hook/auto-patchcil.sh b/pkgs/build-support/dotnet/auto-patchcil-hook/auto-patchcil.sh new file mode 100644 index 000000000000..898ad68cb969 --- /dev/null +++ b/pkgs/build-support/dotnet/auto-patchcil-hook/auto-patchcil.sh @@ -0,0 +1,118 @@ +#!@shell@ +# shellcheck shell=bash + +declare -a autoPatchcilLibs +declare -a extraAutoPatchcilLibs + +gatherLibraries() { + if [ -d "$1/lib" ]; then + autoPatchcilLibs+=("$1/lib") + fi +} + +addEnvHooks "${targetOffset:?}" gatherLibraries + +# Can be used to manually add additional directories with shared object files +# to be included for the next autoPatchcil invocation. +addAutoPatchcilSearchPath() { + local -a findOpts=() + + while [ $# -gt 0 ]; do + case "$1" in + --) + shift + break + ;; + --no-recurse) + shift + findOpts+=("-maxdepth" 1) + ;; + --*) + echo "addAutoPatchcilSearchPath: ERROR: Invalid command line" \ + "argument: $1" >&2 + return 1 + ;; + *) break ;; + esac + done + + local dir= + while IFS= read -r -d '' dir; do + extraAutoPatchcilLibs+=("$dir") + done < <( + find "$@" "${findOpts[@]}" \! -type d \ + \( -name '*.so' -o -name '*.so.*' \) -print0 | + sed -z 's#/[^/]*$##' | + uniq -z + ) +} + +autoPatchcil() { + local rid= + local norecurse= + while [ $# -gt 0 ]; do + case "$1" in + --) + shift + break + ;; + --rid) + rid="$2" + shift 2 + ;; + --no-recurse) + shift + norecurse=1 + ;; + --*) + echo "autoPatchcil: ERROR: Invalid command line" \ + "argument: $1" >&2 + return 1 + ;; + *) break ;; + esac + done + + if [ -z "$rid" ]; then + echo "autoPatchcil: ERROR: No RID (Runtime ID) provided." >&2 + return 1 + fi + + local ignoreMissingDepsArray=("--ignore-missing") + concatTo ignoreMissingDepsArray autoPatchcilIgnoreMissingDeps + + if [ ${#ignoreMissingDepsArray[@]} -lt 2 ]; then + ignoreMissingDepsArray=() + fi + + local autoPatchcilFlags=( + ${norecurse:+--no-recurse} + --rid "$rid" + "${ignoreMissingDepsArray[@]}" + --paths "$@" + --libs "${autoPatchcilLibs[@]}" + ) + + # shellcheck disable=SC2016 + echoCmd 'patchcil auto flags' "${autoPatchcilFlags[@]}" + @patchcil@ auto "${autoPatchcilFlags[@]}" +} + +autoPatchcilFixupOutput() { + if [[ -z "${dontAutoPatchcil-}" ]]; then + if [ -n "${dotnetRuntimeIds+x}" ]; then + if [[ -n $__structuredAttrs ]]; then + local dotnetRuntimeIdsArray=("${dotnetRuntimeIds[@]}") + else + # shellcheck disable=SC2206 # Intentionally expanding it to preserve old behavior + local dotnetRuntimeIdsArray=($dotnetRuntimeIds) + fi + else + local dotnetRuntimeIdsArray=("") + fi + + autoPatchcil --rid "${autoPatchcilRuntimeId:-${dotnetRuntimeIdsArray[0]}}" -- "${prefix:?}" + fi +} + +fixupOutputHooks+=(autoPatchcilFixupOutput) diff --git a/pkgs/build-support/dotnet/auto-patchcil-hook/default.nix b/pkgs/build-support/dotnet/auto-patchcil-hook/default.nix new file mode 100644 index 000000000000..cbe9105e6115 --- /dev/null +++ b/pkgs/build-support/dotnet/auto-patchcil-hook/default.nix @@ -0,0 +1,14 @@ +{ + lib, + bash, + patchcil, + makeSetupHook, +}: + +makeSetupHook { + name = "auto-patchcil-hook"; + substitutions = { + shell = lib.getExe bash; + patchcil = lib.getExe patchcil; + }; +} ./auto-patchcil.sh diff --git a/pkgs/development/compilers/dotnet/default.nix b/pkgs/development/compilers/dotnet/default.nix index 923c31a7fcec..b40a3e9be4f2 100644 --- a/pkgs/development/compilers/dotnet/default.nix +++ b/pkgs/development/compilers/dotnet/default.nix @@ -78,6 +78,7 @@ let patchNupkgs = callPackage ./patch-nupkgs.nix { }; nugetPackageHook = callPackage ./nuget-package-hook.nix { }; + autoPatchcilHook = callPackage ../../../build-support/dotnet/auto-patchcil-hook { }; buildDotnetModule = callPackage ../../../build-support/dotnet/build-dotnet-module { }; buildDotnetGlobalTool = callPackage ../../../build-support/dotnet/build-dotnet-global-tool { }; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 598fee2bfcba..4035436e05a7 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -495,6 +495,7 @@ with pkgs; buildDotnetGlobalTool mkNugetSource mkNugetDeps + autoPatchcilHook ; dotnetenv = callPackage ../build-support/dotnet/dotnetenv { From e2d963b9f681f6068704c28ac8babb1ae8276427 Mon Sep 17 00:00:00 2001 From: GGG Date: Sat, 11 Jan 2025 21:58:48 -0300 Subject: [PATCH 3/3] docs/autoPatchcilHook: init --- doc/hooks/autopatchcil.section.md | 17 +++++++++++++++++ doc/hooks/index.md | 1 + doc/redirects.json | 3 +++ 3 files changed, 21 insertions(+) create mode 100644 doc/hooks/autopatchcil.section.md diff --git a/doc/hooks/autopatchcil.section.md b/doc/hooks/autopatchcil.section.md new file mode 100644 index 000000000000..f7ed8cb6835f --- /dev/null +++ b/doc/hooks/autopatchcil.section.md @@ -0,0 +1,17 @@ +# autoPatchcilHook {#setup-hook-autopatchcilhook} + +This is a special setup hook which helps in packaging .NET assemblies/programs in that it automatically tries to find missing shared library dependencies of .NET assemblies based on the given `buildInputs` and `nativeBuildInputs`. + +As the hook needs information for the host where the package will be run on, there's a required environment variable called `autoPatchcilRuntimeId` which should be filled in with the RID (Runtime Identifier) of the machine where the output will be run on. If you're using `buildDotnetModule`, it will fall back to `dotnetRuntimeIds` (which is set to `lib.singleton (if runtimeId != null then runtimeId else systemToDotnetRid stdenvNoCC.hostPlatform.system)`) for you if not provided. + +In certain situations you may want to run the main command (`autoPatchcil`) of the setup hook on a file or a set of directories instead of unconditionally patching all outputs. This can be done by setting the `dontAutoPatchcil` environment variable to a non-empty value. + +By default, `autoPatchcil` will fail as soon as any .NET assembly requires a dependency which cannot be resolved via the given build inputs. In some situations you might prefer to just leave missing dependencies unpatched and continue to patch the rest. This can be achieved by setting the `autoPatchcilIgnoreMissingDeps` environment variable to a non-empty value. `autoPatchcilIgnoreMissingDeps` can be set to a list like `autoPatchcilIgnoreMissingDeps = [ "libcuda.so.1" "libcudart.so.1" ];` or to `[ "*" ]` to ignore all missing dependencies. + +The `autoPatchcil` command requires the `--rid` command line flag, informing the RID (Runtime Identifier) it should assume the assemblies will be executed on, and also recognizes a `--no-recurse` command line flag, which prevents it from recursing into subdirectories. + +::: {.note} +Since, unlike most native binaries, .NET assemblies are compiled once to run on any platform, many assemblies may have PInvoke stubs for libraries that might not be available on the platform that the package will effectively run on. A few examples are assemblies that call native Windows APIs through PInvoke targeting `kernel32`, `gdi32`, `user32`, `shell32` or `ntdll`. + +`autoPatchcil` does its best to ignore dependencies from other platforms by checking the requested file extensions, however not all PInvoke stubs provide an extension so in those cases it will be necessary to list those in `autoPatchcilIgnoreMissingDeps` manually. +::: diff --git a/doc/hooks/index.md b/doc/hooks/index.md index e4b744056c5e..574b7eea8de3 100644 --- a/doc/hooks/index.md +++ b/doc/hooks/index.md @@ -7,6 +7,7 @@ The stdenv built-in hooks are documented in [](#ssec-setup-hooks). ```{=include=} sections autoconf.section.md automake.section.md +autopatchcil.section.md autopatchelf.section.md aws-c-common.section.md bmake.section.md diff --git a/doc/redirects.json b/doc/redirects.json index 2a8069a490bb..611d3974f46b 100644 --- a/doc/redirects.json +++ b/doc/redirects.json @@ -2064,6 +2064,9 @@ "setup-hook-automake": [ "index.html#setup-hook-automake" ], + "setup-hook-autopatchcilhook": [ + "index.html#setup-hook-autopatchcilhook" + ], "setup-hook-autopatchelfhook": [ "index.html#setup-hook-autopatchelfhook" ],