From d347b0400c3ee88747e104f098d1960ece55117a Mon Sep 17 00:00:00 2001 From: Raphael Robatsch Date: Thu, 4 May 2023 08:19:25 +0200 Subject: [PATCH] mkNugetSource: remove mono from build closure A directory full of *.nupkg files is a valid nuget source. We do not need mono and the Nuget command line tool to create this structure. This has two advantages: - Nuget is currently broken due to a kernel bug affecting mono (#229476). Replacing the mkNugetSource implementation allows affected users on 6.1+ kernels compile .NET core packages again. - It removes mono from the build closure of .NET core packages. .NET core builds should not depend on .NET framework tools like mono. There is no equivalent of the `nuget init` command in .NET core. The closest command is `dotnet nuget push`, which just copies the *.nupkg files around anyway, just like this PR does with `cp`. `nuget init` used to extract the *.nuspec files from the nupkgs, this new implementation doesn't. .NET core doesn't care, but it makes the license extraction more difficult. What was previously done with find/grep/xml2 is now a python script (extract-licenses-from-nupkgs.py). --- .../dotnet/make-nuget-source/default.nix | 18 +++++------ .../extract-licenses-from-nupkgs.py | 30 +++++++++++++++++++ 2 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 pkgs/build-support/dotnet/make-nuget-source/extract-licenses-from-nupkgs.py diff --git a/pkgs/build-support/dotnet/make-nuget-source/default.nix b/pkgs/build-support/dotnet/make-nuget-source/default.nix index ea1c5328082..6bda65f18d5 100644 --- a/pkgs/build-support/dotnet/make-nuget-source/default.nix +++ b/pkgs/build-support/dotnet/make-nuget-source/default.nix @@ -1,4 +1,4 @@ -{ dotnetPackages, lib, xml2, stdenvNoCC }: +{ lib, python3, stdenvNoCC }: { name , description ? "" @@ -10,21 +10,21 @@ let inherit name; meta.description = description; - nativeBuildInputs = [ dotnetPackages.Nuget xml2 ]; + nativeBuildInputs = [ python3 ]; buildCommand = '' - export HOME=$(mktemp -d) mkdir -p $out/{lib,share} - ${lib.concatMapStringsSep "\n" (dep: '' - nuget init "${dep}" "$out/lib" - '') deps} + ( + shopt -s nullglob + for nupkg in ${lib.concatMapStringsSep " " (dep: "\"${dep}\"/*.nupkg") deps}; do + cp --no-clobber "$nupkg" $out/lib + done + ) # Generates a list of all licenses' spdx ids, if available. # Note that this currently ignores any license provided in plain text (e.g. "LICENSE.txt") - find "$out/lib" -name "*.nuspec" -exec sh -c \ - "NUSPEC=\$(xml2 < {}) && echo "\$NUSPEC" | grep license/@type=expression | tr -s \ '\n' | grep "license=" | cut -d'=' -f2" \ - \; | sort -u > $out/share/licenses + python ${./extract-licenses-from-nupkgs.py} $out/lib > $out/share/licenses ''; } // { # We need data from `$out` for `meta`, so we have to use overrides as to not hit infinite recursion. meta.licence = let diff --git a/pkgs/build-support/dotnet/make-nuget-source/extract-licenses-from-nupkgs.py b/pkgs/build-support/dotnet/make-nuget-source/extract-licenses-from-nupkgs.py new file mode 100644 index 00000000000..22564b0bb2b --- /dev/null +++ b/pkgs/build-support/dotnet/make-nuget-source/extract-licenses-from-nupkgs.py @@ -0,0 +1,30 @@ +#!/usr/bin/env python3 +""" +Opens each .nupkg file in a directory, and extracts the SPDX license identifiers +from them if they exist. The SPDX license identifier is stored in the +'...' tag in the .nuspec file. +All found license identifiers will be printed to stdout. +""" + +from glob import glob +from pathlib import Path +import sys +import xml.etree.ElementTree as ET +import zipfile + +all_licenses = set() + +if len(sys.argv) < 2: + print(f"Usage: {sys.argv[0]} DIRECTORY") + sys.exit(1) + +nupkg_dir = Path(sys.argv[1]) +for nupkg_name in glob("*.nupkg", root_dir=nupkg_dir): + with zipfile.ZipFile(nupkg_dir / nupkg_name) as nupkg: + for nuspec_name in [name for name in nupkg.namelist() if name.endswith(".nuspec")]: + with nupkg.open(nuspec_name) as nuspec_stream: + nuspec = ET.parse(nuspec_stream) + licenses = nuspec.findall(".//{*}license[@type='expression']") + all_licenses.update([license.text for license in licenses]) + +print("\n".join(sorted(all_licenses)))