mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-22 06:53:01 +00:00
Merge pull request #313005 from tie/dotnet-cross
buildDotnetModule: fix structured attributes support
This commit is contained in:
commit
b5447275d2
@ -41,7 +41,7 @@ buildDotnetModule rec {
|
||||
doCheck = true;
|
||||
|
||||
preBuild = ''
|
||||
export projectFile=(ArchiSteamFarm)
|
||||
dotnetProjectFiles=(ArchiSteamFarm)
|
||||
'';
|
||||
|
||||
preInstall = ''
|
||||
|
@ -28,7 +28,7 @@ buildDotnetModule (args // {
|
||||
] ++ (nugetDeps fetchNuGet);
|
||||
};
|
||||
|
||||
projectFile = "";
|
||||
dotnetGlobalTool = true;
|
||||
|
||||
useDotnetFromEnv = true;
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
||||
, disabledTests ? [ ]
|
||||
# The project file to run unit tests against. This is usually referenced in the regular project file, but sometimes it needs to be manually set.
|
||||
# It gets restored and build, but not installed. You may need to regenerate your nuget lockfile after setting this.
|
||||
, testProjectFile ? ""
|
||||
, testProjectFile ? null
|
||||
|
||||
# The type of build to perform. This is passed to `dotnet` with the `--configuration` flag. Possible values are `Release`, `Debug`, etc.
|
||||
, buildType ? "Release"
|
||||
@ -88,17 +88,18 @@
|
||||
} @ args:
|
||||
|
||||
let
|
||||
projectFiles =
|
||||
lib.optionals (projectFile != null) (lib.toList projectFile);
|
||||
testProjectFiles =
|
||||
lib.optionals (testProjectFile != null) (lib.toList testProjectFile);
|
||||
|
||||
platforms =
|
||||
if args ? meta.platforms
|
||||
then lib.intersectLists args.meta.platforms dotnet-sdk.meta.platforms
|
||||
else dotnet-sdk.meta.platforms;
|
||||
|
||||
inherit (callPackage ./hooks {
|
||||
inherit dotnet-sdk disabledTests nuget-source dotnet-runtime runtimeDeps buildType;
|
||||
runtimeId =
|
||||
if runtimeId != null
|
||||
then runtimeId
|
||||
else dotnetCorePackages.systemToDotnetRid stdenvNoCC.targetPlatform.system;
|
||||
inherit dotnet-sdk dotnet-runtime;
|
||||
}) dotnetConfigureHook dotnetBuildHook dotnetCheckHook dotnetInstallHook dotnetFixupHook;
|
||||
|
||||
localDeps =
|
||||
@ -143,6 +144,19 @@ let
|
||||
nugetDepsFile = _nugetDeps.sourceFile;
|
||||
in
|
||||
stdenvNoCC.mkDerivation (args // {
|
||||
dotnetInstallPath = installPath;
|
||||
dotnetExecutables = executables;
|
||||
dotnetBuildType = buildType;
|
||||
dotnetProjectFiles = projectFiles;
|
||||
dotnetTestProjectFiles = testProjectFiles;
|
||||
dotnetDisabledTests = disabledTests;
|
||||
dotnetRuntimeId = runtimeId;
|
||||
nugetSource = nuget-source;
|
||||
dotnetRuntimeDeps = map lib.getLib runtimeDeps;
|
||||
dotnetSelfContainedBuild = selfContainedBuild;
|
||||
dotnetUseAppHost = useAppHost;
|
||||
inherit useDotnetFromEnv;
|
||||
|
||||
nativeBuildInputs = args.nativeBuildInputs or [ ] ++ [
|
||||
dotnetConfigureHook
|
||||
dotnetBuildHook
|
||||
@ -172,7 +186,7 @@ stdenvNoCC.mkDerivation (args // {
|
||||
else [ ]));
|
||||
|
||||
makeWrapperArgs = args.makeWrapperArgs or [ ] ++ [
|
||||
"--prefix LD_LIBRARY_PATH : ${dotnet-sdk.icu}/lib"
|
||||
"--prefix" "LD_LIBRARY_PATH" ":" "${dotnet-sdk.icu}/lib"
|
||||
];
|
||||
|
||||
# Stripping breaks the executable
|
||||
@ -181,8 +195,6 @@ stdenvNoCC.mkDerivation (args // {
|
||||
# gappsWrapperArgs gets included when wrapping for dotnet, as to avoid double wrapping
|
||||
dontWrapGApps = args.dontWrapGApps or true;
|
||||
|
||||
inherit selfContainedBuild useAppHost useDotnetFromEnv;
|
||||
|
||||
# propagate the runtime sandbox profile since the contents apply to published
|
||||
# executables
|
||||
propagatedSandboxProfile = toString dotnet-runtime.__propagatedSandboxProfile;
|
||||
@ -267,11 +279,11 @@ stdenvNoCC.mkDerivation (args // {
|
||||
--no-cache \
|
||||
--force \
|
||||
${lib.optionalString (!enableParallelBuilding) "--disable-parallel"} \
|
||||
${lib.optionalString (flags != []) (toString flags)}
|
||||
${lib.escapeShellArgs flags}
|
||||
}
|
||||
|
||||
declare -a projectFiles=( ${toString (lib.toList projectFile)} )
|
||||
declare -a testProjectFiles=( ${toString (lib.toList testProjectFile)} )
|
||||
declare -a projectFiles=( ${lib.escapeShellArgs projectFiles} )
|
||||
declare -a testProjectFiles=( ${lib.escapeShellArgs testProjectFiles} )
|
||||
|
||||
export DOTNET_NOLOGO=1
|
||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||
|
@ -4,28 +4,21 @@
|
||||
, coreutils
|
||||
, zlib
|
||||
, openssl
|
||||
, callPackage
|
||||
, makeSetupHook
|
||||
, makeWrapper
|
||||
, dotnetCorePackages
|
||||
# Passed from ../default.nix
|
||||
, dotnet-sdk
|
||||
, disabledTests
|
||||
, nuget-source
|
||||
, dotnet-runtime
|
||||
, runtimeDeps
|
||||
, buildType
|
||||
, runtimeId
|
||||
}:
|
||||
assert (builtins.isString runtimeId);
|
||||
|
||||
let
|
||||
libraryPath = lib.makeLibraryPath runtimeDeps;
|
||||
runtimeId = dotnetCorePackages.systemToDotnetRid stdenv.hostPlatform.system;
|
||||
in
|
||||
{
|
||||
dotnetConfigureHook = makeSetupHook
|
||||
{
|
||||
name = "dotnet-configure-hook";
|
||||
substitutions = {
|
||||
nugetSource = nuget-source;
|
||||
runtimeId = lib.escapeShellArg runtimeId;
|
||||
dynamicLinker = "${stdenv.cc}/nix-support/dynamic-linker";
|
||||
libPath = lib.makeLibraryPath [
|
||||
stdenv.cc.cc.lib
|
||||
@ -34,7 +27,6 @@ in
|
||||
zlib
|
||||
openssl
|
||||
];
|
||||
inherit runtimeId;
|
||||
};
|
||||
}
|
||||
./dotnet-configure-hook.sh;
|
||||
@ -43,7 +35,7 @@ in
|
||||
{
|
||||
name = "dotnet-build-hook";
|
||||
substitutions = {
|
||||
inherit buildType runtimeId;
|
||||
runtimeId = lib.escapeShellArg runtimeId;
|
||||
};
|
||||
}
|
||||
./dotnet-build-hook.sh;
|
||||
@ -52,15 +44,7 @@ in
|
||||
{
|
||||
name = "dotnet-check-hook";
|
||||
substitutions = {
|
||||
inherit buildType runtimeId libraryPath;
|
||||
disabledTests = lib.optionalString (disabledTests != [ ])
|
||||
(
|
||||
let
|
||||
escapedNames = lib.lists.map (n: lib.replaceStrings [ "," ] [ "%2C" ] n) disabledTests;
|
||||
filters = lib.lists.map (n: "FullyQualifiedName!=${n}") escapedNames;
|
||||
in
|
||||
"${lib.concatStringsSep "&" filters}"
|
||||
);
|
||||
runtimeId = lib.escapeShellArg runtimeId;
|
||||
};
|
||||
}
|
||||
./dotnet-check-hook.sh;
|
||||
@ -69,7 +53,7 @@ in
|
||||
{
|
||||
name = "dotnet-install-hook";
|
||||
substitutions = {
|
||||
inherit buildType runtimeId;
|
||||
runtimeId = lib.escapeShellArg runtimeId;
|
||||
};
|
||||
}
|
||||
./dotnet-install-hook.sh;
|
||||
@ -79,11 +63,7 @@ in
|
||||
name = "dotnet-fixup-hook";
|
||||
substitutions = {
|
||||
dotnetRuntime = dotnet-runtime;
|
||||
runtimeDeps = libraryPath;
|
||||
shell = stdenv.shell;
|
||||
which = "${which}/bin/which";
|
||||
dirname = "${coreutils}/bin/dirname";
|
||||
realpath = "${coreutils}/bin/realpath";
|
||||
wrapperPath = lib.makeBinPath [ which coreutils ];
|
||||
};
|
||||
}
|
||||
./dotnet-fixup-hook.sh;
|
||||
|
@ -1,12 +1,25 @@
|
||||
# inherit arguments from derivation
|
||||
dotnetBuildFlags=( ${dotnetBuildFlags[@]-} )
|
||||
|
||||
dotnetBuildHook() {
|
||||
echo "Executing dotnetBuildHook"
|
||||
|
||||
runHook preBuild
|
||||
|
||||
if [ "${enableParallelBuilding-}" ]; then
|
||||
local -r hostRuntimeId=@runtimeId@
|
||||
local -r dotnetBuildType="${dotnetBuildType-Release}"
|
||||
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
|
||||
|
||||
if [[ -n $__structuredAttrs ]]; then
|
||||
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
|
||||
local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
|
||||
local dotnetFlagsArray=( "${dotnetFlags[@]}" )
|
||||
local dotnetBuildFlagsArray=( "${dotnetBuildFlags[@]}" )
|
||||
else
|
||||
local dotnetProjectFilesArray=($dotnetProjectFiles)
|
||||
local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
|
||||
local dotnetFlagsArray=($dotnetFlags)
|
||||
local dotnetBuildFlagsArray=($dotnetBuildFlags)
|
||||
fi
|
||||
|
||||
if [[ -n "${enableParallelBuilding-}" ]]; then
|
||||
local -r maxCpuFlag="$NIX_BUILD_CORES"
|
||||
local -r parallelBuildFlag="true"
|
||||
else
|
||||
@ -14,50 +27,53 @@ dotnetBuildHook() {
|
||||
local -r parallelBuildFlag="false"
|
||||
fi
|
||||
|
||||
if [ "${selfContainedBuild-}" ]; then
|
||||
dotnetBuildFlags+=("-p:SelfContained=true")
|
||||
if [[ -n ${dotnetSelfContainedBuild-} ]]; then
|
||||
dotnetBuildFlagsArray+=("-p:SelfContained=true")
|
||||
else
|
||||
dotnetBuildFlags+=("-p:SelfContained=false")
|
||||
dotnetBuildFlagsArray+=("-p:SelfContained=false")
|
||||
fi
|
||||
|
||||
if [ "${useAppHost-}" ]; then
|
||||
dotnetBuildFlags+=("-p:UseAppHost=true")
|
||||
if [[ -n ${dotnetUseAppHost-} ]]; then
|
||||
dotnetBuildFlagsArray+=("-p:UseAppHost=true")
|
||||
fi
|
||||
|
||||
local versionFlags=()
|
||||
if [ "${version-}" ]; then
|
||||
versionFlags+=("-p:InformationalVersion=${version-}")
|
||||
local versionFlagsArray=()
|
||||
if [[ -n ${version-} ]]; then
|
||||
versionFlagsArray+=("-p:InformationalVersion=$version")
|
||||
fi
|
||||
|
||||
if [ "${versionForDotnet-}" ]; then
|
||||
versionFlags+=("-p:Version=${versionForDotnet-}")
|
||||
if [[ -n ${versionForDotnet-} ]]; then
|
||||
versionFlagsArray+=("-p:Version=$versionForDotnet")
|
||||
fi
|
||||
|
||||
dotnetBuild() {
|
||||
local -r project="${1-}"
|
||||
local -r projectFile="${1-}"
|
||||
|
||||
runtimeIdFlags=()
|
||||
if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then
|
||||
runtimeIdFlags+=("--runtime @runtimeId@")
|
||||
local runtimeIdFlagsArray=()
|
||||
if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
|
||||
runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId")
|
||||
fi
|
||||
|
||||
dotnet build ${project-} \
|
||||
-maxcpucount:$maxCpuFlag \
|
||||
-p:BuildInParallel=$parallelBuildFlag \
|
||||
dotnet build ${1+"$projectFile"} \
|
||||
-maxcpucount:"$maxCpuFlag" \
|
||||
-p:BuildInParallel="$parallelBuildFlag" \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--configuration "@buildType@" \
|
||||
--configuration "$dotnetBuildType" \
|
||||
--no-restore \
|
||||
${versionFlags[@]} \
|
||||
${runtimeIdFlags[@]} \
|
||||
${dotnetBuildFlags[@]} \
|
||||
${dotnetFlags[@]}
|
||||
"${versionFlagsArray[@]}" \
|
||||
"${runtimeIdFlagsArray[@]}" \
|
||||
"${dotnetBuildFlagsArray[@]}" \
|
||||
"${dotnetFlagsArray[@]}"
|
||||
}
|
||||
|
||||
(( "${#projectFile[@]}" == 0 )) && dotnetBuild
|
||||
if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
|
||||
dotnetBuild
|
||||
fi
|
||||
|
||||
for project in ${projectFile[@]} ${testProjectFile[@]-}; do
|
||||
dotnetBuild "$project"
|
||||
local projectFile
|
||||
for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do
|
||||
dotnetBuild "$projectFile"
|
||||
done
|
||||
|
||||
runHook postBuild
|
||||
@ -65,6 +81,6 @@ dotnetBuildHook() {
|
||||
echo "Finished dotnetBuildHook"
|
||||
}
|
||||
|
||||
if [[ -z "${dontDotnetBuild-}" && -z "${buildPhase-}" ]]; then
|
||||
if [[ -z ${dontDotnetBuild-} && -z ${buildPhase-} ]]; then
|
||||
buildPhase=dotnetBuildHook
|
||||
fi
|
||||
|
@ -1,39 +1,65 @@
|
||||
# inherit arguments from derivation
|
||||
dotnetTestFlags=( ${dotnetTestFlags[@]-} )
|
||||
|
||||
dotnetCheckHook() {
|
||||
echo "Executing dotnetCheckHook"
|
||||
|
||||
runHook preCheck
|
||||
|
||||
if [ "${disabledTests-}" ]; then
|
||||
local -r disabledTestsFlag="--filter @disabledTests@"
|
||||
local -r hostRuntimeId=@runtimeId@
|
||||
local -r dotnetBuildType="${dotnetBuildType-Release}"
|
||||
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
|
||||
|
||||
if [[ -n $__structuredAttrs ]]; then
|
||||
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
|
||||
local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
|
||||
local dotnetTestFlagsArray=( "${dotnetTestFlags[@]}" )
|
||||
local dotnetDisabledTestsArray=( "${dotnetDisabledTests[@]}" )
|
||||
local dotnetRuntimeDepsArray=( "${dotnetRuntimeDeps[@]}" )
|
||||
else
|
||||
local dotnetProjectFilesArray=($dotnetProjectFiles)
|
||||
local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
|
||||
local dotnetTestFlagsArray=($dotnetTestFlags)
|
||||
local dotnetDisabledTestsArray=($dotnetDisabledTests)
|
||||
local dotnetRuntimeDepsArray=($dotnetRuntimeDeps)
|
||||
fi
|
||||
|
||||
if [ "${enableParallelBuilding-}" ]; then
|
||||
if (( ${#dotnetDisabledTestsArray[@]} > 0 )); then
|
||||
local disabledTestsFilters=("${dotnetDisabledTestsArray[@]/#/FullyQualifiedName!=}")
|
||||
local OLDIFS="$IFS" IFS='&'
|
||||
dotnetTestFlagsArray+=("--filter:${disabledTestsFilters[*]//,/%2C}")
|
||||
IFS="$OLDIFS"
|
||||
fi
|
||||
|
||||
local libraryPath="${LD_LIBRARY_PATH-}"
|
||||
if (( ${#dotnetRuntimeDepsArray[@]} > 0 )); then
|
||||
local libraryPathArray=("${dotnetRuntimeDepsArray[@]/%//lib}")
|
||||
local OLDIFS="$IFS" IFS=':'
|
||||
libraryPath="${libraryPathArray[*]}${libraryPath:+':'}$libraryPath"
|
||||
IFS="$OLDIFS"
|
||||
fi
|
||||
|
||||
if [[ -n ${enableParallelBuilding-} ]]; then
|
||||
local -r maxCpuFlag="$NIX_BUILD_CORES"
|
||||
else
|
||||
local -r maxCpuFlag="1"
|
||||
fi
|
||||
|
||||
for project in ${testProjectFile[@]-${projectFile[@]}}; do
|
||||
runtimeIdFlags=()
|
||||
if [[ "$project" == *.csproj ]]; then
|
||||
runtimeIdFlags=("--runtime @runtimeId@")
|
||||
local projectFile
|
||||
for projectFile in "${dotnetTestProjectFilesArray[@]-${dotnetProjectFilesArray[@]}}"; do
|
||||
local runtimeIdFlagsArray=()
|
||||
if [[ $projectFile == *.csproj ]]; then
|
||||
runtimeIdFlagsArray=("--runtime" "$dotnetRuntimeId")
|
||||
fi
|
||||
|
||||
LD_LIBRARY_PATH="@libraryPath@" \
|
||||
dotnet test "$project" \
|
||||
-maxcpucount:$maxCpuFlag \
|
||||
LD_LIBRARY_PATH=$libraryPath \
|
||||
dotnet test "$projectFile" \
|
||||
-maxcpucount:"$maxCpuFlag" \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--configuration "@buildType@" \
|
||||
--configuration "$dotnetBuildType" \
|
||||
--no-build \
|
||||
--logger "console;verbosity=normal" \
|
||||
${disabledTestsFlag-} \
|
||||
${runtimeIdFlags[@]} \
|
||||
"${dotnetTestFlags[@]}" \
|
||||
"${dotnetFlags[@]}"
|
||||
"${runtimeIdFlagsArray[@]}" \
|
||||
"${dotnetTestFlagsArray[@]}" \
|
||||
"${dotnetFlagsArray[@]}"
|
||||
done
|
||||
|
||||
runHook postCheck
|
||||
|
@ -1,63 +1,103 @@
|
||||
declare -a projectFile testProjectFile
|
||||
|
||||
# Inherit arguments from derivation
|
||||
dotnetFlags=( ${dotnetFlags[@]-} )
|
||||
dotnetRestoreFlags=( ${dotnetRestoreFlags[@]-} )
|
||||
|
||||
dotnetConfigureHook() {
|
||||
echo "Executing dotnetConfigureHook"
|
||||
|
||||
runHook preConfigure
|
||||
|
||||
if [ -z "${enableParallelBuilding-}" ]; then
|
||||
if [[ -z ${nugetSource-} ]]; then
|
||||
echo
|
||||
echo "ERROR: no dependencies were specified"
|
||||
echo 'Hint: set `nugetSource` if using these hooks individually. If this is happening with `buildDotnetModule`, please open an issue.'
|
||||
echo
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local nugetSourceSedQuoted="${nugetSource//[\/\\&$'\n']/\\&}"
|
||||
local nugetSourceXMLQuoted="$nugetSource"
|
||||
nugetSourceXMLQuoted="${nugetSource//&/\&}"
|
||||
nugetSourceXMLQuoted="${nugetSourceXMLQuoted//\"/\"}"
|
||||
|
||||
local -r hostRuntimeId=@runtimeId@
|
||||
local -r dynamicLinker=@dynamicLinker@
|
||||
local -r libPath=@libPath@
|
||||
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
|
||||
|
||||
if [[ -n $__structuredAttrs ]]; then
|
||||
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
|
||||
local dotnetTestProjectFilesArray=( "${dotnetTestProjectFiles[@]}" )
|
||||
local dotnetFlagsArray=( "${dotnetFlags[@]}" )
|
||||
local dotnetRestoreFlagsArray=( "${dotnetRestoreFlags[@]}" )
|
||||
else
|
||||
local dotnetProjectFilesArray=($dotnetProjectFiles)
|
||||
local dotnetTestProjectFilesArray=($dotnetTestProjectFiles)
|
||||
local dotnetFlagsArray=($dotnetFlags)
|
||||
local dotnetRestoreFlagsArray=($dotnetRestoreFlags)
|
||||
fi
|
||||
|
||||
if [[ -z ${enableParallelBuilding-} ]]; then
|
||||
local -r parallelFlag="--disable-parallel"
|
||||
fi
|
||||
|
||||
dotnetRestore() {
|
||||
local -r project="${1-}"
|
||||
dotnet restore ${project-} \
|
||||
local -r projectFile="${1-}"
|
||||
dotnet restore ${1+"$projectFile"} \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--runtime "@runtimeId@" \
|
||||
--source "@nugetSource@/lib" \
|
||||
--runtime "$dotnetRuntimeId" \
|
||||
--source "$nugetSource/lib" \
|
||||
${parallelFlag-} \
|
||||
${dotnetRestoreFlags[@]} \
|
||||
${dotnetFlags[@]}
|
||||
"${dotnetRestoreFlagsArray[@]}" \
|
||||
"${dotnetFlagsArray[@]}"
|
||||
}
|
||||
|
||||
# Generate a NuGet.config file to make sure everything,
|
||||
# including things like <Sdk /> dependencies, is restored from the proper source
|
||||
cat <<EOF > "./NuGet.config"
|
||||
cat >NuGet.config <<EOF
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<clear />
|
||||
<add key="nugetSource" value="@nugetSource@/lib" />
|
||||
<add key="nugetSource" value="$nugetSourceXMLQuoted/lib" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
EOF
|
||||
|
||||
# Patch paket.dependencies and paket.lock (if found) to use the proper source. This ensures
|
||||
# paket restore works correctly
|
||||
# We use + instead of / in sed to avoid problems with slashes
|
||||
find -name paket.dependencies -exec sed -i 's+source .*+source @nugetSource@/lib+g' {} \;
|
||||
find -name paket.lock -exec sed -i 's+remote:.*+remote: @nugetSource@/lib+g' {} \;
|
||||
# Patch paket.dependencies and paket.lock (if found) to use the proper
|
||||
# source. This ensures paket restore works correctly. Note that the
|
||||
# nugetSourceSedQuoted abomination below safely escapes nugetSource string
|
||||
# for use as a sed replacement string to avoid issues with slashes and other
|
||||
# special characters ('&', '\\' and '\n').
|
||||
find -name paket.dependencies -exec sed -i "s/source .*/source $nugetSourceSedQuoted\/lib/g" {} \;
|
||||
find -name paket.lock -exec sed -i "s/remote:.*/remote: $nugetSourceSedQuoted\/lib/g" {} \;
|
||||
|
||||
dotnet tool restore --add-source "@nugetSource@/lib"
|
||||
dotnet tool restore --add-source "$nugetSource/lib"
|
||||
|
||||
(( "${#projectFile[@]}" == 0 )) && dotnetRestore
|
||||
# dotnetGlobalTool is set in buildDotnetGlobalTool to patch dependencies but
|
||||
# avoid other project-specific logic. This is a hack, but the old behavior
|
||||
# is worse as it relied on a bug: setting projectFile to an empty string
|
||||
# made the hooks actually skip all project-specific logic. It’s hard to keep
|
||||
# backwards compatibility with this odd behavior now since we are using
|
||||
# arrays, so instead we just pass a variable to indicate that we don’t have
|
||||
# projects.
|
||||
if [[ -z ${dotnetGlobalTool-} ]]; then
|
||||
if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
|
||||
dotnetRestore
|
||||
fi
|
||||
|
||||
for project in ${projectFile[@]} ${testProjectFile[@]-}; do
|
||||
dotnetRestore "$project"
|
||||
done
|
||||
local projectFile
|
||||
for projectFile in "${dotnetProjectFilesArray[@]}" "${dotnetTestProjectFilesArray[@]}"; do
|
||||
dotnetRestore "$projectFile"
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Fixing up native binaries..."
|
||||
# Find all native binaries and nuget libraries, and fix them up,
|
||||
# by setting the proper interpreter and rpath to some commonly used libraries
|
||||
local binary
|
||||
for binary in $(find "$HOME/.nuget/packages/" -type f -executable); do
|
||||
if patchelf --print-interpreter "$binary" >/dev/null 2>/dev/null; then
|
||||
echo "Found binary: $binary, fixing it up..."
|
||||
patchelf --set-interpreter "$(cat "@dynamicLinker@")" "$binary"
|
||||
patchelf --set-interpreter "$(cat "$dynamicLinker")" "$binary"
|
||||
|
||||
# This makes sure that if the binary requires some specific runtime dependencies, it can find it.
|
||||
# This fixes dotnet-built binaries like crossgen2
|
||||
@ -68,7 +108,7 @@ EOF
|
||||
--add-needed libssl.so \
|
||||
"$binary"
|
||||
|
||||
patchelf --set-rpath "@libPath@" "$binary"
|
||||
patchelf --set-rpath "$libPath" "$binary"
|
||||
fi
|
||||
done
|
||||
|
||||
|
@ -1,28 +1,55 @@
|
||||
# Inherit arguments from the derivation
|
||||
declare -a derivationMakeWrapperArgs="( ${makeWrapperArgs-} )"
|
||||
makeWrapperArgs=( "${derivationMakeWrapperArgs[@]}" )
|
||||
|
||||
# First argument is the executable you want to wrap,
|
||||
# the second is the destination for the wrapper.
|
||||
wrapDotnetProgram() {
|
||||
local dotnetRootFlags=()
|
||||
local -r dotnetRuntime=@dotnetRuntime@
|
||||
local -r wrapperPath=@wrapperPath@
|
||||
|
||||
if [ ! "${selfContainedBuild-}" ]; then
|
||||
if [ "${useDotnetFromEnv-}" ]; then
|
||||
local -r dotnetFromEnvScript='dotnetFromEnv() {
|
||||
local dotnetPath
|
||||
if command -v dotnet 2>&1 >/dev/null; then
|
||||
dotnetPath=$(which dotnet) && \
|
||||
dotnetPath=$(realpath "$dotnetPath") && \
|
||||
dotnetPath=$(dirname "$dotnetPath") && \
|
||||
export DOTNET_ROOT="$dotnetPath"
|
||||
fi
|
||||
}
|
||||
dotnetFromEnv'
|
||||
|
||||
if [[ -n $__structuredAttrs ]]; then
|
||||
local -r dotnetRuntimeDepsArray=( "${dotnetRuntimeDeps[@]}" )
|
||||
local -r makeWrapperArgsArray=( "${makeWrapperArgs[@]}" )
|
||||
else
|
||||
local -r dotnetRuntimeDepsArray=($dotnetRuntimeDeps)
|
||||
local -r makeWrapperArgsArray=($makeWrapperArgs)
|
||||
fi
|
||||
|
||||
local dotnetRuntimeDepsFlags=()
|
||||
if (( ${#dotnetRuntimeDepsArray[@]} > 0 )); then
|
||||
local libraryPathArray=("${dotnetRuntimeDepsArray[@]/%//lib}")
|
||||
local OLDIFS="$IFS" IFS=':'
|
||||
dotnetRuntimeDepsFlags+=("--suffix" "LD_LIBRARY_PATH" ":" "${libraryPathArray[*]}")
|
||||
IFS="$OLDIFS"
|
||||
fi
|
||||
|
||||
local dotnetRootFlagsArray=()
|
||||
if [[ -z ${dotnetSelfContainedBuild-} ]]; then
|
||||
if [[ -n ${useDotnetFromEnv-} ]]; then
|
||||
# if dotnet CLI is available, set DOTNET_ROOT based on it. Otherwise set to default .NET runtime
|
||||
dotnetRootFlags+=("--run" 'command -v dotnet &>/dev/null && export DOTNET_ROOT="$(@dirname@ "$(@realpath@ "$(@which@ dotnet)")")" || export DOTNET_ROOT="@dotnetRuntime@"')
|
||||
dotnetRootFlags+=("--suffix" "PATH" ":" "@dotnetRuntime@/bin")
|
||||
dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$wrapperPath")
|
||||
dotnetRootFlagsArray+=("--run" "$dotnetFromEnvScript")
|
||||
dotnetRootFlagsArray+=("--set-default" "DOTNET_ROOT" "$dotnetRuntime")
|
||||
dotnetRootFlagsArray+=("--suffix" "PATH" ":" "$dotnetRuntime/bin")
|
||||
else
|
||||
dotnetRootFlags+=("--set" "DOTNET_ROOT" "@dotnetRuntime@")
|
||||
dotnetRootFlags+=("--prefix" "PATH" ":" "@dotnetRuntime@/bin")
|
||||
dotnetRootFlagsArray+=("--set" "DOTNET_ROOT" "$dotnetRuntime")
|
||||
dotnetRootFlagsArray+=("--prefix" "PATH" ":" "$dotnetRuntime/bin")
|
||||
fi
|
||||
fi
|
||||
|
||||
makeWrapper "$1" "$2" \
|
||||
--suffix "LD_LIBRARY_PATH" : "@runtimeDeps@" \
|
||||
"${dotnetRootFlags[@]}" \
|
||||
"${dotnetRuntimeDepsFlags[@]}" \
|
||||
"${dotnetRootFlagsArray[@]}" \
|
||||
"${gappsWrapperArgs[@]}" \
|
||||
"${makeWrapperArgs[@]}"
|
||||
"${makeWrapperArgsArray[@]}"
|
||||
|
||||
echo "installed wrapper to "$2""
|
||||
}
|
||||
@ -30,13 +57,24 @@ wrapDotnetProgram() {
|
||||
dotnetFixupHook() {
|
||||
echo "Executing dotnetFixupPhase"
|
||||
|
||||
# check if executables is declared (including empty values, in which case we generate no executables)
|
||||
if declare -p executables &>/dev/null; then
|
||||
for executable in ${executables[@]}; do
|
||||
path="${installPath-$out/lib/$pname}/$executable"
|
||||
local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
|
||||
|
||||
local executable executableBasename
|
||||
|
||||
# check if dotnetExecutables is declared (including empty values, in which case we generate no executables)
|
||||
if declare -p dotnetExecutables &>/dev/null; then
|
||||
if [[ -n $__structuredAttrs ]]; then
|
||||
local dotnetExecutablesArray=( "${dotnetExecutables[@]}" )
|
||||
else
|
||||
local dotnetExecutablesArray=($dotnetExecutables)
|
||||
fi
|
||||
for executable in "${dotnetExecutablesArray[@]}"; do
|
||||
executableBasename=$(basename "$executable")
|
||||
|
||||
local path="$dotnetInstallPath/$executable"
|
||||
|
||||
if test -x "$path"; then
|
||||
wrapDotnetProgram "$path" "$out/bin/$(basename "$executable")"
|
||||
wrapDotnetProgram "$path" "$out/bin/$executableBasename"
|
||||
else
|
||||
echo "Specified binary \"$executable\" is either not an executable or does not exist!"
|
||||
echo "Looked in $path"
|
||||
@ -45,8 +83,9 @@ dotnetFixupHook() {
|
||||
done
|
||||
else
|
||||
while IFS= read -d '' executable; do
|
||||
wrapDotnetProgram "$executable" "$out/bin/$(basename "$executable")" \;
|
||||
done < <(find "${installPath-$out/lib/$pname}" ! -name "*.dll" -executable -type f -print0)
|
||||
executableBasename=$(basename "$executable")
|
||||
wrapDotnetProgram "$executable" "$out/bin/$executableBasename" \;
|
||||
done < <(find "$dotnetInstallPath" ! -name "*.dll" -executable -type f -print0)
|
||||
fi
|
||||
|
||||
echo "Finished dotnetFixupPhase"
|
||||
|
@ -1,70 +1,86 @@
|
||||
# inherit arguments from derivation
|
||||
dotnetInstallFlags=( ${dotnetInstallFlags[@]-} )
|
||||
|
||||
dotnetInstallHook() {
|
||||
echo "Executing dotnetInstallHook"
|
||||
|
||||
runHook preInstall
|
||||
|
||||
if [ "${selfContainedBuild-}" ]; then
|
||||
dotnetInstallFlags+=("--self-contained")
|
||||
local -r hostRuntimeId=@runtimeId@
|
||||
local -r dotnetInstallPath="${dotnetInstallPath-$out/lib/$pname}"
|
||||
local -r dotnetBuildType="${dotnetBuildType-Release}"
|
||||
local -r dotnetRuntimeId="${dotnetRuntimeId-$hostRuntimeId}"
|
||||
|
||||
if [[ -n $__structuredAttrs ]]; then
|
||||
local dotnetProjectFilesArray=( "${dotnetProjectFiles[@]}" )
|
||||
local dotnetFlagsArray=( "${dotnetFlags[@]}" )
|
||||
local dotnetInstallFlagsArray=( "${dotnetInstallFlags[@]}" )
|
||||
local dotnetPackFlagsArray=( "${dotnetPackFlags[@]}" )
|
||||
else
|
||||
dotnetInstallFlags+=("--no-self-contained")
|
||||
# https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained
|
||||
# Trimming is only available for self-contained build, so force disable it here
|
||||
dotnetInstallFlags+=("-p:PublishTrimmed=false")
|
||||
local dotnetProjectFilesArray=($dotnetProjectFiles)
|
||||
local dotnetFlagsArray=($dotnetFlags)
|
||||
local dotnetInstallFlagsArray=($dotnetInstallFlags)
|
||||
local dotnetPackFlagsArray=($dotnetPackFlags)
|
||||
fi
|
||||
|
||||
if [ "${useAppHost-}" ]; then
|
||||
dotnetInstallFlags+=("-p:UseAppHost=true")
|
||||
if [[ -n ${dotnetSelfContainedBuild-} ]]; then
|
||||
dotnetInstallFlagsArray+=("--self-contained")
|
||||
else
|
||||
dotnetInstallFlagsArray+=("--no-self-contained")
|
||||
# https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-self-contained
|
||||
# Trimming is only available for self-contained build, so force disable it here
|
||||
dotnetInstallFlagsArray+=("-p:PublishTrimmed=false")
|
||||
fi
|
||||
|
||||
if [[ -n ${dotnetUseAppHost-} ]]; then
|
||||
dotnetInstallFlagsArray+=("-p:UseAppHost=true")
|
||||
fi
|
||||
|
||||
dotnetPublish() {
|
||||
local -r project="${1-}"
|
||||
local -r projectFile="${1-}"
|
||||
|
||||
runtimeIdFlags=()
|
||||
if [[ "$project" == *.csproj ]] || [ "${selfContainedBuild-}" ]; then
|
||||
runtimeIdFlags+=("--runtime @runtimeId@")
|
||||
runtimeIdFlagsArray=()
|
||||
if [[ $projectFile == *.csproj || -n ${dotnetSelfContainedBuild-} ]]; then
|
||||
runtimeIdFlagsArray+=("--runtime" "$dotnetRuntimeId")
|
||||
fi
|
||||
|
||||
dotnet publish ${project-} \
|
||||
dotnet publish ${1+"$projectFile"} \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--output "${installPath-$out/lib/$pname}" \
|
||||
--configuration "@buildType@" \
|
||||
--output "$dotnetInstallPath" \
|
||||
--configuration "$dotnetBuildType" \
|
||||
--no-build \
|
||||
${runtimeIdFlags[@]} \
|
||||
${dotnetInstallFlags[@]} \
|
||||
${dotnetFlags[@]}
|
||||
"${runtimeIdFlagsArray[@]}" \
|
||||
"${dotnetInstallFlagsArray[@]}" \
|
||||
"${dotnetFlagsArray[@]}"
|
||||
}
|
||||
|
||||
dotnetPack() {
|
||||
local -r project="${1-}"
|
||||
dotnet pack ${project-} \
|
||||
local -r projectFile="${1-}"
|
||||
dotnet pack ${1+"$projectFile"} \
|
||||
-p:ContinuousIntegrationBuild=true \
|
||||
-p:Deterministic=true \
|
||||
--output "$out/share" \
|
||||
--configuration "@buildType@" \
|
||||
--configuration "$dotnetBuildType" \
|
||||
--no-build \
|
||||
--runtime "@runtimeId@" \
|
||||
${dotnetPackFlags[@]} \
|
||||
${dotnetFlags[@]}
|
||||
--runtime "$dotnetRuntimeId" \
|
||||
"${dotnetPackFlagsArray[@]}" \
|
||||
"${dotnetFlagsArray[@]}"
|
||||
}
|
||||
|
||||
if (( "${#projectFile[@]}" == 0 )); then
|
||||
if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
|
||||
dotnetPublish
|
||||
else
|
||||
for project in ${projectFile[@]}; do
|
||||
dotnetPublish "$project"
|
||||
local projectFile
|
||||
for projectFile in "${dotnetProjectFilesArray[@]}"; do
|
||||
dotnetPublish "$projectFile"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ "${packNupkg-}" ]]; then
|
||||
if (( "${#projectFile[@]}" == 0 )); then
|
||||
if [[ -n ${packNupkg-} ]]; then
|
||||
if (( ${#dotnetProjectFilesArray[@]} == 0 )); then
|
||||
dotnetPack
|
||||
else
|
||||
for project in ${projectFile[@]}; do
|
||||
dotnetPack "$project"
|
||||
local projectFile
|
||||
for projectFile in "${dotnetProjectFilesArray[@]}"; do
|
||||
dotnetPack "$projectFile"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
@ -1,5 +1,7 @@
|
||||
{ callPackage }:
|
||||
{ lib, callPackage }:
|
||||
|
||||
{
|
||||
project-references = callPackage ./project-references { };
|
||||
use-dotnet-from-env = lib.recurseIntoAttrs (callPackage ./use-dotnet-from-env { });
|
||||
structured-attrs = lib.recurseIntoAttrs (callPackage ./structured-attrs { });
|
||||
}
|
||||
|
@ -4,11 +4,13 @@
|
||||
|
||||
{ lib
|
||||
, dotnet-sdk
|
||||
, buildDotnetModule
|
||||
, buildPackages # buildDotnetModule
|
||||
, runCommand
|
||||
}:
|
||||
|
||||
let
|
||||
inherit (buildPackages) buildDotnetModule;
|
||||
|
||||
nugetDeps = ./nuget-deps.nix;
|
||||
|
||||
# Specify the TargetFramework via an environment variable so that we don't
|
||||
@ -18,7 +20,8 @@ let
|
||||
library = buildDotnetModule {
|
||||
name = "project-references-test-library";
|
||||
src = ./library;
|
||||
inherit nugetDeps TargetFramework;
|
||||
inherit nugetDeps;
|
||||
env.TargetFramework = TargetFramework;
|
||||
|
||||
packNupkg = true;
|
||||
};
|
||||
@ -26,7 +29,8 @@ let
|
||||
application = buildDotnetModule {
|
||||
name = "project-references-test-application";
|
||||
src = ./application;
|
||||
inherit nugetDeps TargetFramework;
|
||||
inherit nugetDeps;
|
||||
env.TargetFramework = TargetFramework;
|
||||
|
||||
projectReferences = [ library ];
|
||||
};
|
||||
|
36
pkgs/test/dotnet/structured-attrs/default.nix
Normal file
36
pkgs/test/dotnet/structured-attrs/default.nix
Normal file
@ -0,0 +1,36 @@
|
||||
{ lib
|
||||
, dotnet-sdk
|
||||
, buildPackages # buildDotnetModule
|
||||
, testers
|
||||
, runCommand
|
||||
}:
|
||||
let
|
||||
# Note: without structured attributes, we can’t use derivation arguments that
|
||||
# contain spaces unambiguously because arguments are passed as space-separated
|
||||
# environment variables.
|
||||
copyrightString = "Public domain 🅮";
|
||||
|
||||
inherit (buildPackages) buildDotnetModule;
|
||||
|
||||
app = buildDotnetModule {
|
||||
name = "structured-attrs-test-application";
|
||||
src = ./src;
|
||||
nugetDeps = ./nuget-deps.nix;
|
||||
dotnetFlags = [ "--property:Copyright=${copyrightString}" ];
|
||||
env.TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}";
|
||||
__structuredAttrs = true;
|
||||
};
|
||||
in
|
||||
{
|
||||
no-structured-attrs = testers.testBuildFailure (app.overrideAttrs {
|
||||
__structuredAttrs = false;
|
||||
});
|
||||
|
||||
check-output = testers.testEqualContents {
|
||||
assertion = "buildDotnetModule sets AssemblyCopyrightAttribute with structured attributes";
|
||||
expected = builtins.toFile "expected-copyright.txt" copyrightString;
|
||||
actual = runCommand "dotnet-structured-attrs-test" { } ''
|
||||
${app}/bin/Application >"$out"
|
||||
'';
|
||||
};
|
||||
}
|
5
pkgs/test/dotnet/structured-attrs/nuget-deps.nix
Normal file
5
pkgs/test/dotnet/structured-attrs/nuget-deps.nix
Normal file
@ -0,0 +1,5 @@
|
||||
# This file was automatically generated by passthru.fetch-deps.
|
||||
# Please dont edit it manually, your changes might get overwritten!
|
||||
|
||||
{ fetchNuGet }: [
|
||||
]
|
10
pkgs/test/dotnet/structured-attrs/src/Application.cs
Normal file
10
pkgs/test/dotnet/structured-attrs/src/Application.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
Console.Write(
|
||||
(
|
||||
(AssemblyCopyrightAttribute)Assembly
|
||||
.GetExecutingAssembly()
|
||||
.GetCustomAttributes(typeof(AssemblyCopyrightAttribute), true)[0]
|
||||
).Copyright
|
||||
);
|
5
pkgs/test/dotnet/structured-attrs/src/Application.csproj
Normal file
5
pkgs/test/dotnet/structured-attrs/src/Application.csproj
Normal file
@ -0,0 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>exe</OutputType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
60
pkgs/test/dotnet/use-dotnet-from-env/default.nix
Normal file
60
pkgs/test/dotnet/use-dotnet-from-env/default.nix
Normal file
@ -0,0 +1,60 @@
|
||||
{ lib
|
||||
, dotnet-sdk
|
||||
, buildPackages # buildDotnetModule, dotnet-runtime
|
||||
, testers
|
||||
, runCommand
|
||||
, removeReferencesTo
|
||||
}:
|
||||
let
|
||||
inherit (buildPackages) buildDotnetModule dotnet-runtime;
|
||||
|
||||
app = buildDotnetModule {
|
||||
name = "use-dotnet-from-env-test-application";
|
||||
src = ./src;
|
||||
nugetDeps = ./nuget-deps.nix;
|
||||
useDotnetFromEnv = true;
|
||||
env.TargetFramework = "net${lib.versions.majorMinor (lib.getVersion dotnet-sdk)}";
|
||||
};
|
||||
|
||||
appWithoutFallback = app.overrideAttrs (oldAttrs: {
|
||||
nativeBuildInputs = (oldAttrs.nativeBuildInputs or [ ]) ++ [
|
||||
removeReferencesTo
|
||||
];
|
||||
postFixup = (oldAttrs.postFixup or "") + ''
|
||||
remove-references-to -t ${dotnet-runtime} "$out/bin/Application"
|
||||
'';
|
||||
});
|
||||
|
||||
runtimeVersion = lib.getVersion dotnet-runtime;
|
||||
runtimeVersionFile = builtins.toFile "dotnet-version.txt" runtimeVersion;
|
||||
in
|
||||
{
|
||||
fallback = testers.testEqualContents {
|
||||
assertion = "buildDotnetModule sets fallback DOTNET_ROOT in wrapper";
|
||||
expected = runtimeVersionFile;
|
||||
actual = runCommand "use-dotnet-from-env-fallback-test" { } ''
|
||||
${app}/bin/Application >"$out"
|
||||
'';
|
||||
};
|
||||
|
||||
# Check that appWithoutFallback does not use fallback .NET runtime.
|
||||
without-fallback = testers.testBuildFailure (runCommand "use-dotnet-from-env-without-fallback-test" { } ''
|
||||
${appWithoutFallback}/bin/Application >"$out"
|
||||
'');
|
||||
|
||||
# NB assumes that without-fallback above to passes.
|
||||
use-dotnet-root-env = testers.testEqualContents {
|
||||
assertion = "buildDotnetModule uses DOTNET_ROOT from environment in wrapper";
|
||||
expected = runtimeVersionFile;
|
||||
actual = runCommand "use-dotnet-from-env-root-test" { env.DOTNET_ROOT = dotnet-runtime; } ''
|
||||
${appWithoutFallback}/bin/Application >"$out"
|
||||
'';
|
||||
};
|
||||
use-dotnet-path-env = testers.testEqualContents {
|
||||
assertion = "buildDotnetModule uses DOTNET_ROOT from dotnet in PATH in wrapper";
|
||||
expected = runtimeVersionFile;
|
||||
actual = runCommand "use-dotnet-from-env-path-test" { dotnetRuntime = dotnet-runtime; } ''
|
||||
PATH=$dotnetRuntime''${PATH+:}$PATH ${appWithoutFallback}/bin/Application >"$out"
|
||||
'';
|
||||
};
|
||||
}
|
5
pkgs/test/dotnet/use-dotnet-from-env/nuget-deps.nix
Normal file
5
pkgs/test/dotnet/use-dotnet-from-env/nuget-deps.nix
Normal file
@ -0,0 +1,5 @@
|
||||
# This file was automatically generated by passthru.fetch-deps.
|
||||
# Please dont edit it manually, your changes might get overwritten!
|
||||
|
||||
{ fetchNuGet }: [
|
||||
]
|
3
pkgs/test/dotnet/use-dotnet-from-env/src/Application.cs
Normal file
3
pkgs/test/dotnet/use-dotnet-from-env/src/Application.cs
Normal file
@ -0,0 +1,3 @@
|
||||
using System;
|
||||
|
||||
Console.Write(Environment.Version.ToString());
|
@ -0,0 +1,5 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>exe</OutputType>
|
||||
</PropertyGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user