From df0e309335b7ad2c86ae4b4fc17efd88f92c3cfc Mon Sep 17 00:00:00 2001 From: Brad Wilson Date: Sat, 3 Feb 2024 19:48:33 -0800 Subject: [PATCH 1/2] Update to use the common build system --- .github/workflows/pull-request.yaml | 10 +- .github/workflows/push-v2.yaml | 12 +- build.ps1 | 278 +++-------------------- src/Directory.Build.targets | 13 +- src/xunit.assert.nuspec | 9 +- src/xunit.assert.source.nuspec | 4 +- src/xunit.console.nuspec | 7 +- src/xunit.core.nuspec | 9 +- src/xunit.extensibility.core.nuspec | 15 +- src/xunit.extensibility.execution.nuspec | 9 +- src/xunit.runner.console.nuspec | 135 ++++++++--- src/xunit.runner.msbuild.nuspec | 17 +- src/xunit.runner.reporters.nuspec | 9 +- src/xunit.runner.utility.nuspec | 21 +- tools/builder/Program.cs | 11 + tools/builder/build.csproj | 18 ++ tools/builder/models/BuildContext.cs | 63 +++++ tools/builder/models/BuildTarget.cs | 7 + tools/builder/targets/Build.cs | 15 ++ tools/builder/targets/Packages.cs | 30 +++ tools/builder/targets/SignAssemblies.cs | 82 +++++++ tools/builder/targets/Test.cs | 8 + tools/builder/targets/TestCore.cs | 40 ++++ tools/builder/targets/TestFx.cs | 46 ++++ 24 files changed, 526 insertions(+), 342 deletions(-) create mode 100644 tools/builder/Program.cs create mode 100644 tools/builder/build.csproj create mode 100644 tools/builder/models/BuildContext.cs create mode 100644 tools/builder/models/BuildTarget.cs create mode 100644 tools/builder/targets/Build.cs create mode 100644 tools/builder/targets/Packages.cs create mode 100644 tools/builder/targets/SignAssemblies.cs create mode 100644 tools/builder/targets/Test.cs create mode 100644 tools/builder/targets/TestCore.cs create mode 100644 tools/builder/targets/TestFx.cs diff --git a/.github/workflows/pull-request.yaml b/.github/workflows/pull-request.yaml index 77d786c54..2355e4c85 100644 --- a/.github/workflows/pull-request.yaml +++ b/.github/workflows/pull-request.yaml @@ -7,7 +7,7 @@ on: jobs: build: - name: "Build for PR (v2)" + name: "Build" runs-on: windows-2022 env: DOTNET_NOLOGO: true @@ -18,7 +18,7 @@ jobs: fetch-depth: 0 submodules: true - - name: Add msbuild to PATH + - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 - name: Replace global.json @@ -37,11 +37,11 @@ jobs: run: dotnet --info - name: "Build target: BuildAll" - run: .\build.ps1 -target BuildAll + run: dotnet run --project tools/builder --no-launch-profile -- BuildAll --timing - - name: "Upload artifact: test-results" + - name: "Upload artifact: test" uses: actions/upload-artifact@v3 with: - name: test-results + name: test path: artifacts/test if: always() diff --git a/.github/workflows/push-v2.yaml b/.github/workflows/push-v2.yaml index 41a0d17d0..d70723c5f 100644 --- a/.github/workflows/push-v2.yaml +++ b/.github/workflows/push-v2.yaml @@ -7,7 +7,7 @@ on: jobs: build: - name: "CI Build" + name: "Build" runs-on: windows-2022 env: DOTNET_NOLOGO: true @@ -18,7 +18,7 @@ jobs: fetch-depth: 0 submodules: true - - name: Add msbuild to PATH + - name: Add MSBuild to PATH uses: microsoft/setup-msbuild@v1 - name: Replace global.json @@ -36,7 +36,7 @@ jobs: - name: Get .NET information run: dotnet --info - - name: "Build target: CI" + - name: "Build target: BuildAll & PublishPackages" env: PUSH_APIKEY: ${{ secrets.PUSH_APIKEY }} PUSH_URI: ${{ secrets.PUSH_URI }} @@ -47,12 +47,12 @@ jobs: SIGN_TENANT: ${{ secrets.SIGN_TENANT }} SIGN_TIMESTAMP_URI: ${{ secrets.SIGN_TIMESTAMP_URI }} SIGN_VAULT_URI: ${{ secrets.SIGN_VAULT_URI }} - run: .\build.ps1 -target CI + run: dotnet run --project tools/builder --no-launch-profile -- BuildAll PublishPackages --timing - - name: "Upload artifact: test-results" + - name: "Upload artifact: test" uses: actions/upload-artifact@v3 with: - name: test-results + name: test path: artifacts/test if: always() diff --git a/build.ps1 b/build.ps1 index 044f4e497..0da04533f 100644 --- a/build.ps1 +++ b/build.ps1 @@ -1,271 +1,47 @@ +#!/usr/bin/env pwsh #Requires -Version 5.1 -param( - [ValidateSet('Build','BuildAll','CI','FormatSource','PackageRestore','Packages','Restore','Test', - '_AnalyzeSource', '_Packages','_Publish','_PushPackages','_SignPackages','_Test32','_Test64','_TestCore')] - [string]$target = "BuildAll", - [string]$configuration = "Release" -) - Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" -if ($null -eq $PSScriptRoot) { - write-host "This build script requires PowerShell 3 or later." -ForegroundColor Red - exit -1 -} - -Set-Location $PSScriptRoot - -$packageOutputFolder = (join-path (Get-Location) "artifacts\packages") -$parallelFlags = "-parallel all" -$nonparallelFlags = "-parallel collections" -$testOutputFolder = (join-path (Get-Location) "artifacts\test") -$dotnetFormatCommand = "& dotnet dotnet-format --folder --exclude src/common/AssemblyResolution/Microsoft.DotNet.PlatformAbstractions --exclude src/common/AssemblyResolution/Microsoft.Extensions.DependencyModel --exclude src/xunit.assert/Asserts" - -# Helper functions - -function _build_step([string] $message) { - Write-Host -ForegroundColor White $("==> " + $message + " <==") - Write-Host "" -} - -function _exec([string] $command, [string] $message = "") { - if ($message -eq "") { - $message = $command - } - Write-Host -ForegroundColor DarkGray ("EXEC: " + $message) - Write-Host "" - Invoke-Expression $command - Write-Host "" +function GuardBin { + param ( + [string]$binary, + [string]$message + ) - if ($LASTEXITCODE -ne 0) { - exit 1 + if ($null -eq (Get-Command $binary -ErrorAction Ignore)) { + throw "Could not find '$binary'; $message" } } -function _fatal([string] $message) { - Write-Host -ForegroundColor Red ("Error: " + $message) - exit -1 -} - -function _mkdir([string] $path) { - if ((test-path $path) -eq $false) { - New-Item -Type directory -Path $path | out-null - } -} +GuardBin git "please install the Git CLI from https://git-scm.com/" +GuardBin dotnet "please install the .NET SDK from https://dot.net/" -function _msbuild([string] $project, [string] $configuration, [string] $target = "build", [string] $verbosity = "minimal", [string] $message = "", [string] $binlogFile = "") { - $cmd = "msbuild " + $project + " /t:" + $target + " /p:Configuration=" + $configuration + " /v:" + $verbosity + " /m" - if ($binlogFile -ne "") { - $cmd = $cmd + " /bl:" + $binlogFile - } - _exec $cmd $message +if ((get-content variable:IsLinux -ErrorAction Ignore) -or (get-content variable:IsMacOS -ErrorAction Ignore)) { + GuardBin mono "please install Mono from https://www.mono-project.com/" +} else { + GuardBin msbuild.exe "please run this from a Visual Studio developer shell" } -function _require([string] $command, [string] $message) { - if ($null -eq (get-command $command -ErrorAction SilentlyContinue)) { - _fatal $message - } +$version = [Version]$([regex]::matches((&dotnet --version), '^(\d+\.)?(\d+\.)?(\*|\d+)').value) +if ($version.Major -lt 8) { + throw ".NET SDK version ($version) is too low; please install version 8.0 or later from https://dot.net/" } -function _verify_version([string]$version, [string]$minVersion, [string]$appName) { - $dashIndex = $version.IndexOf('-') - if ($dashIndex -gt -1) { - $version = $version.Substring(0, $dashIndex) - } - - if ([version]$version -lt [version]$minVersion) { - _fatal ("Unsupported " + $appName + " version '$version' (must be '$minVersion' or later).") +& git submodule status | ForEach-Object { + if ($_[0] -eq '-') { + $pieces = $_.Split(' ') + & git submodule update --init "$($pieces[1])" + Write-Host "" } } -function _verify_dotnetsdk_version([string]$minVersion) { - _verify_version (& dotnet --version) $minVersion ".NET SDK" -} +Push-Location (Split-Path $MyInvocation.MyCommand.Definition) -function _verify_msbuild_version([string]$minVersion) { - _verify_version (& msbuild /nologo /ver) $minVersion "MSBuild" -} -function _xunit_x64([string]$command) { - _exec ("src\xunit.console\bin\" + $configuration + "\net452\xunit.console.exe " + $command) +try { + & dotnet run --project tools/builder --no-launch-profile -- $args } - -function _xunit_x86([string]$command) { - _exec ("src\xunit.console\bin\" + $configuration + "_x86\net452\xunit.console.x86.exe " + $command) -} - -function _xunit_netcore([string]$targetFramework, [string]$command) { - _exec ("dotnet src\xunit.console\bin\" + $configuration + "\" + $targetFramework + "\xunit.console.dll " + $command) -} - -# Top-level targets - -function __target_build() { - __target_restore - __target__analyzesource - - _build_step "Compiling binaries" - _msbuild "xunit.sln" $configuration - _msbuild "src\xunit.console\xunit.console.csproj" ($configuration + "_x86") -} - -function __target_buildall() { - if ($null -ne $env:CI) { - $script:parallelFlags = "-parallel none -maxthreads 1" - $script:nonparallelFlags = "-parallel none -maxthreads 1" - } - - __target_test - __target__publish - __target__packages -} - -function __target_ci() { - __target_buildall - __target__signpackages - __target__pushpackages -} - -function __target_formatsource() { - _build_step "Formatting source" - _exec "& dotnet tool restore" - _exec $dotnetFormatCommand +finally { + Pop-Location } - -function __target_packagerestore() { - __target_restore -} - -function __target_packages() { - __target_build - __target__publish - __target__packages -} - -function __target_restore() { - _build_step "Restoring NuGet packages" - _msbuild "xunit.sln" $configuration "restore" -} - -function __target_test() { - __target_build - __target__test32 - __target__test64 - __target__testcore -} - -# Dependent targets - -function __target__analyzesource() { - _build_step "Analyzing source (if this fails, run './build FormatSource' to fix)" - _exec "& dotnet tool restore" - _exec ($dotnetFormatCommand + " --check") -} - -function __target__packages() { - _build_step "Creating NuGet packages" - Get-ChildItem -Path $packageOutputFolder -Recurse -Filter *.nupkg | Remove-Item - Get-ChildItem -Recurse -Filter *.nuspec | ForEach-Object { - _exec ('& dotnet pack --nologo --no-build --configuration ' + $configuration + ' --verbosity minimal --output "' + $packageOutputFolder + '" src/xunit.core -p:NuspecFile="' + $_.FullName + '"') - } -} - -function __target__publish() { - _build_step "Publishing projects for packaging" - _msbuild "src\xunit.console\xunit.console.csproj /p:TargetFramework=netcoreapp1.0" $configuration "publish" - _msbuild "src\xunit.console\xunit.console.csproj /p:TargetFramework=netcoreapp2.0" $configuration "publish" - _msbuild "src\xunit.console\xunit.console.csproj /p:TargetFramework=net6.0" $configuration "publish" -} - -function __target__pushpackages() { - _build_step "Pushing NuGet packages" - if (($null -eq $env:PUSH_APIKEY) -or - ($null -eq $env:PUSH_URI)) - { - Write-Host -ForegroundColor Yellow "Skipping package push because of one or more missing environment variables: 'PUSH_APIKEY', 'PUSH_URI'" - Write-Host "" - } else { - Get-ChildItem -Filter *.nupkg $packageOutputFolder | ForEach-Object { - $cmd = '& dotnet nuget push --source ' + $env:PUSH_URI + ' --api-key ' + $env:PUSH_APIKEY + ' "' + $_.FullName + '"' - $message = $cmd.Replace($env:PUSH_APIKEY, "[redacted]") - _exec $cmd $message - } - } -} - -function __target__signpackages() { - _build_step "Signing NuGet packages" - if (($null -eq $env:SIGN_APP_ID) -or - ($null -eq $env:SIGN_APP_SECRET) -or - ($null -eq $env:SIGN_CERT_NAME) -or - ($null -eq $env:SIGN_TENANT) -or - ($null -eq $env:SIGN_TIMESTAMP_URI) -or - ($null -eq $env:SIGN_VAULT_URI)) - { - Write-Host -ForegroundColor Yellow "Skipping package sign because of one or more missing environment variables: 'SIGN_APP_ID', 'SIGN_APP_SECRET', 'SIGN_CERT_NAME', 'SIGN_TENANT', 'SIGN_TIMESTAMP_URI', 'SIGN_VAULT_URI'" - Write-Host "" - } else { - _exec "& dotnet tool restore" - - $cmd = ` - '& dotnet sign code azure-key-vault **/*.nupkg' + ` - ' --base-directory "' + $packageOutputFolder + '"' + ` - ' --description "xUnit.net"' + ` - ' --description-url https://github.com/xunit' + ` - ' --timestamp-url ' + $env:SIGN_TIMESTAMP_URI + ` - ' --azure-key-vault-url ' + $env:SIGN_VAULT_URI + ` - ' --azure-key-vault-client-id ' + $env:SIGN_APP_ID + ` - ' --azure-key-vault-client-secret "' + $env:SIGN_APP_SECRET + '"' + ` - ' --azure-key-vault-tenant-id ' + $env:SIGN_TENANT + ` - ' --azure-key-vault-certificate ' + $env:SIGN_CERT_NAME - - $msg = $cmd.Replace($env:SIGN_VAULT_URI, '[redacted]') - $msg = $msg.Replace($env:SIGN_APP_ID, '[redacted]') - $msg = $msg.Replace($env:SIGN_APP_SECRET, '[redacted]') - $msg = $msg.Replace($env:SIGN_TENANT, '[redacted]') - $msg = $msg.Replace($env:SIGN_CERT_NAME, '[redacted]') - _exec $cmd $msg - } -} - -function __target__test32() { - _build_step "Running tests: 32-bit .NET 4.x" - $v2_assemblies = [System.String]::Join(" ", (Get-ChildItem -Recurse -Include test.xunit.*.dll | Where-Object { $_.FullName -match "bin\\" + $configuration + "\\net452" } | ForEach-Object { $_.FullName })) - _xunit_x86 ("test\test.xunit1\bin\" + $configuration + "\net40\test.xunit1.dll -xml artifacts\test\v1-x86.xml -html artifacts\test\v1-x86.html " + $nonparallelFlags) - _xunit_x86 ($v2_assemblies + " -xml artifacts\test\v2-x86.xml -html artifacts\test\v2-x86.html -serialize " + $parallelFlags) -} - -function __target__test64() { - _build_step "Running tests: 64-bit .NET 4.x" - $v2_assemblies = [System.String]::Join(" ", (Get-ChildItem -Recurse -Include test.xunit.*.dll | Where-Object { $_.FullName -match "bin\\" + $configuration + "\\net452" } | ForEach-Object { $_.FullName })) - _xunit_x64 ("test\test.xunit1\bin\" + $configuration + "\net40\test.xunit1.dll -xml artifacts\test\v1-x64.xml -html artifacts\test\v1-x64.html " + $nonparallelFlags) - _xunit_x64 ($v2_assemblies + " -xml artifacts\test\v2-x64.xml -html artifacts\test\v2-x64.html -serialize " + $parallelFlags) -} - -function __target__testcore() { - _build_step "Running tests: .NET Core 2.0" - $netcore_assemblies = [System.String]::Join(" ", (Get-ChildItem -Recurse -Include test.xunit.*.dll | Where-Object { $_.FullName -match "bin\\" + $configuration + "\\netcoreapp2.0" } | ForEach-Object { $_.FullName })) - _xunit_netcore "netcoreapp2.0" ($netcore_assemblies + " -xml artifacts\test\v2-netcore.xml -html artifacts\test\v2-netcore.html -serialize " + $nonparallelFlags) - - _build_step "Running tests: .NET 6" - $net6_assemblies = [System.String]::Join(" ", (Get-ChildItem -Recurse -Include test.xunit.*.dll | Where-Object { $_.FullName -match "bin\\" + $configuration + "\\net6.0" } | ForEach-Object { $_.FullName })) - _xunit_netcore "net6.0" ($net6_assemblies + " -xml artifacts\test\v2-net6.xml -html artifacts\test\v2-net6.html -serialize " + $nonparallelFlags) -} - -# Dispatch - -$targetFunction = (Get-ChildItem ("Function:__target_" + $target.ToLowerInvariant()) -ErrorAction SilentlyContinue) -if ($null -eq $targetFunction) { - _fatal "Unknown target '$target'" -} - -_build_step "Performing pre-build verifications" - _require dotnet "Could not find 'dotnet'. Please ensure .NET SDK 8.0 or later is installed." - _verify_dotnetsdk_version "8.0" - _require msbuild.exe "Could not find 'msbuild'. Please ensure MSBUILD.EXE v17.0 is on the path." - _verify_msbuild_version "17.0.0" - -_mkdir $packageOutputFolder -_mkdir $testOutputFolder -& $targetFunction diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 87c4ea80a..819c97c38 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -21,10 +21,19 @@ + + signed\ + + -dev - $(BuildVersionSimple)$(PrereleaseVersion) + $(BuildVersionSimple)$(PrereleaseVersion)$(PrereleaseSuffix) - PackageVersion=$(PackageVersion);GitCommitId=$(GitCommitId);Configuration=$(Configuration) + + Configuration=$(Configuration); + GitCommitId=$(GitCommitId); + PackageVersion=$(PackageVersion); + SignedPath=$(SignedPath); + diff --git a/src/xunit.assert.nuspec b/src/xunit.assert.nuspec index c92dcba2e..445f2954c 100644 --- a/src/xunit.assert.nuspec +++ b/src/xunit.assert.nuspec @@ -23,14 +23,15 @@ + - - + + - - + + diff --git a/src/xunit.assert.source.nuspec b/src/xunit.assert.source.nuspec index 0e7f8004b..1e646a33d 100644 --- a/src/xunit.assert.source.nuspec +++ b/src/xunit.assert.source.nuspec @@ -22,7 +22,7 @@ - - + + diff --git a/src/xunit.console.nuspec b/src/xunit.console.nuspec index 8f73ebd9f..7ed7f015a 100644 --- a/src/xunit.console.nuspec +++ b/src/xunit.console.nuspec @@ -23,12 +23,15 @@ + - + - + + + diff --git a/src/xunit.core.nuspec b/src/xunit.core.nuspec index 58a09104d..c18a9be69 100644 --- a/src/xunit.core.nuspec +++ b/src/xunit.core.nuspec @@ -23,9 +23,10 @@ - - - - + + + + + diff --git a/src/xunit.extensibility.core.nuspec b/src/xunit.extensibility.core.nuspec index 1590670db..6dc1e917c 100644 --- a/src/xunit.extensibility.core.nuspec +++ b/src/xunit.extensibility.core.nuspec @@ -30,17 +30,18 @@ + - - - - - + + + + + - - + + diff --git a/src/xunit.extensibility.execution.nuspec b/src/xunit.extensibility.execution.nuspec index e0fdbba3c..208dcc6d7 100644 --- a/src/xunit.extensibility.execution.nuspec +++ b/src/xunit.extensibility.execution.nuspec @@ -27,14 +27,15 @@ + - - + + - - + + diff --git a/src/xunit.runner.console.nuspec b/src/xunit.runner.console.nuspec index 88397986c..c7444a509 100644 --- a/src/xunit.runner.console.nuspec +++ b/src/xunit.runner.console.nuspec @@ -16,39 +16,108 @@ true + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/xunit.runner.msbuild.nuspec b/src/xunit.runner.msbuild.nuspec index 6e71f7b48..349ecf374 100644 --- a/src/xunit.runner.msbuild.nuspec +++ b/src/xunit.runner.msbuild.nuspec @@ -19,18 +19,19 @@ + - - - - - - - + + + + + + + - + diff --git a/src/xunit.runner.reporters.nuspec b/src/xunit.runner.reporters.nuspec index 0cc73eb53..2b7e42871 100644 --- a/src/xunit.runner.reporters.nuspec +++ b/src/xunit.runner.reporters.nuspec @@ -38,16 +38,17 @@ + - + - + - + - + diff --git a/src/xunit.runner.utility.nuspec b/src/xunit.runner.utility.nuspec index 7503f3808..dc3609342 100644 --- a/src/xunit.runner.utility.nuspec +++ b/src/xunit.runner.utility.nuspec @@ -44,23 +44,24 @@ + - - + + - - + + - - + + - - + + - - + + diff --git a/tools/builder/Program.cs b/tools/builder/Program.cs new file mode 100644 index 000000000..3727e67b9 --- /dev/null +++ b/tools/builder/Program.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using McMaster.Extensions.CommandLineUtils; +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools; + +public class Program +{ + public static Task Main(string[] args) + => CommandLineApplication.ExecuteAsync(args); +} diff --git a/tools/builder/build.csproj b/tools/builder/build.csproj new file mode 100644 index 000000000..85b36ba92 --- /dev/null +++ b/tools/builder/build.csproj @@ -0,0 +1,18 @@ + + + + enable + Exe + net8.0 + true + + + + + + + + + + + diff --git a/tools/builder/models/BuildContext.cs b/tools/builder/models/BuildContext.cs new file mode 100644 index 000000000..87dfd1d6b --- /dev/null +++ b/tools/builder/models/BuildContext.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.IO; +using McMaster.Extensions.CommandLineUtils; + +namespace Xunit.BuildTools.Models; + +public partial class BuildContext +{ + string? consoleRunnerExe; + string? consoleRunner32Exe; + string? testFlagsNonParallel; + string? testFlagsParallel; + + // Calculated properties + + public string ConsoleRunnerExe + { + get => consoleRunnerExe ?? throw new InvalidOperationException($"Tried to retrieve unset {nameof(BuildContext)}.{nameof(ConsoleRunnerExe)}"); + private set => consoleRunnerExe = value ?? throw new ArgumentNullException(nameof(ConsoleRunnerExe)); + } + + public string ConsoleRunner32Exe + { + get => consoleRunner32Exe ?? throw new InvalidOperationException($"Tried to retrieve unset {nameof(BuildContext)}.{nameof(ConsoleRunner32Exe)}"); + private set => consoleRunner32Exe = value ?? throw new ArgumentNullException(nameof(ConsoleRunner32Exe)); + } + + public string TestFlagsNonParallel + { + get => testFlagsNonParallel ?? throw new InvalidOperationException($"Tried to retrieve unset {nameof(BuildContext)}.{nameof(TestFlagsNonParallel)}"); + private set => testFlagsNonParallel = value ?? throw new ArgumentNullException(nameof(TestFlagsNonParallel)); + } + + public string TestFlagsParallel + { + get => testFlagsParallel ?? throw new InvalidOperationException($"Tried to retrieve unset {nameof(BuildContext)}.{nameof(TestFlagsParallel)}"); + private set => testFlagsParallel = value ?? throw new ArgumentNullException(nameof(TestFlagsParallel)); + } + + public partial IReadOnlyList GetSkippedAnalysisFolders() => + new[] + { + "src/common/AssemblyResolution/Microsoft.DotNet.PlatformAbstractions", + "src/common/AssemblyResolution/Microsoft.Extensions.DependencyModel", + }; + + partial void Initialize() + { + ConsoleRunnerExe = Path.Combine(BaseFolder, "src", "xunit.console", "bin", ConfigurationText, "net452", "xunit.console.exe"); + ConsoleRunner32Exe = Path.Combine(BaseFolder, "src", "xunit.console", "bin", ConfigurationText + "_x86", "net452", "xunit.console.x86.exe"); + + TestFlagsNonParallel = "-parallel collections"; + TestFlagsParallel = "-parallel all"; + + // Run parallelizable tests with a single thread in CI to help catch Task-related deadlocks + if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("CI"))) + { + TestFlagsNonParallel = "-parallel none -maxthreads 1"; + TestFlagsParallel = "-parallel none -maxthreads 1"; + } + } +} diff --git a/tools/builder/models/BuildTarget.cs b/tools/builder/models/BuildTarget.cs new file mode 100644 index 000000000..0b9010431 --- /dev/null +++ b/tools/builder/models/BuildTarget.cs @@ -0,0 +1,7 @@ +namespace Xunit.BuildTools.Models; + +public partial class BuildTarget +{ + public const string TestCore = nameof(TestCore); + public const string TestFx = nameof(TestFx); +} diff --git a/tools/builder/targets/Build.cs b/tools/builder/targets/Build.cs new file mode 100644 index 000000000..e4f849dd9 --- /dev/null +++ b/tools/builder/targets/Build.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools.Targets; + +public static partial class Build +{ + public static partial async Task PerformBuild(BuildContext context) + { + context.BuildStep("Compiling binaries"); + + await context.Exec("msbuild", $"xunit.sln /t:build /p:Configuration={context.ConfigurationText} /v:{context.Verbosity} /m"); + await context.Exec("msbuild", $"src/xunit.console/xunit.console.csproj /t:build /p:Configuration={context.ConfigurationText}_x86 /v:{context.Verbosity} /m"); + } +} diff --git a/tools/builder/targets/Packages.cs b/tools/builder/targets/Packages.cs new file mode 100644 index 000000000..c590f0baf --- /dev/null +++ b/tools/builder/targets/Packages.cs @@ -0,0 +1,30 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools.Targets; + +public static partial class Packages +{ + public static async Task OnExecute(BuildContext context) + { + context.BuildStep("Creating NuGet packages"); + + // Clean up any existing packages to force re-packing + var packageFiles = Directory.GetFiles(context.PackageOutputFolder, "*.nupkg"); + foreach (var packageFile in packageFiles) + File.Delete(packageFile); + + // Enumerate the .nuspec files and pack those + var srcFolder = Path.Join(context.BaseFolder, "src"); + var nuspecFiles = + Directory + .GetFiles(srcFolder, "*.nuspec", SearchOption.AllDirectories) + .ToList(); + + // Pack the .nuspec file(s) + foreach (var nuspecFile in nuspecFiles.OrderBy(x => x)) + await context.Exec("dotnet", $"pack --nologo --no-build --configuration {context.ConfigurationText} --output {context.PackageOutputFolder} --verbosity {context.Verbosity} src/xunit.core -p:NuspecFile=\"{nuspecFile}\""); + } +} diff --git a/tools/builder/targets/SignAssemblies.cs b/tools/builder/targets/SignAssemblies.cs new file mode 100644 index 000000000..ece4e9f27 --- /dev/null +++ b/tools/builder/targets/SignAssemblies.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools.Targets; + +public static partial class SignAssemblies +{ + public static async Task OnExecute(BuildContext context) + { + // We need to run the dotnet publish before we sign anything, since we use the published console runner binaries + context.BuildStep("Publishing projects for packaging"); + + foreach (var targetFramework in new[] { "netcoreapp1.0", "netcoreapp2.0", "net6.0" }) + await context.Exec("msbuild", @$"src\xunit.console\xunit.console.csproj /p:TargetFramework={targetFramework} /p:Configuration={context.ConfigurationText} /t:publish /v:minimal /m"); + + // Check early because we don't need to make copies or show the banner for non-signed scenarios + if (!context.CanSign) + return; + + context.BuildStep("Signing binaries"); + + // Note that any changes to .nuspec files means this list needs to be updated, and nuspec files should + // always reference the original signed paths, and not dependency copies (i.e., xunit.runner.utility.*.dll) + var binaries = + new[] { + Path.Combine(context.BaseFolder, "src", "xunit.assert", "bin", context.ConfigurationText, "netstandard1.1", "xunit.assert.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.assert", "bin", context.ConfigurationText, "net6.0", "xunit.assert.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net452", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net452", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net46", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net46", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net461", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net461", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net462", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net462", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net47", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net47", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net471", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net471", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net472", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net472", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net48", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net48", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net481", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText + "_x86", "net481", "xunit.console.x86.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "netcoreapp1.0", "publish", "xunit.console.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "netcoreapp2.0", "publish", "xunit.console.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net6.0", "publish", "xunit.console.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, "net6.0", "publish", "xunit.console.exe"), + Path.Combine(context.BaseFolder, "src", "xunit.core", "bin", context.ConfigurationText, "netstandard1.1", "xunit.core.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.core", "bin", context.ConfigurationText, "netstandard1.1", "xunit.core.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.execution", "bin", context.ConfigurationText, "net452", "xunit.execution.desktop.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.execution", "bin", context.ConfigurationText, "netstandard1.1", "xunit.execution.dotnet.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.msbuild", "bin", context.ConfigurationText, "net452", "xunit.runner.msbuild.net452.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.reporters", "bin", context.ConfigurationText, "net452", "xunit.runner.reporters.net452.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.reporters", "bin", context.ConfigurationText, "netcoreapp1.0", "xunit.runner.reporters.netcoreapp10.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.reporters", "bin", context.ConfigurationText, "netstandard1.1", "xunit.runner.reporters.netstandard11.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.reporters", "bin", context.ConfigurationText, "netstandard1.5", "xunit.runner.reporters.netstandard15.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.tdnet", "bin", context.ConfigurationText, "net452", "xunit.runner.tdnet.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.utility", "bin", context.ConfigurationText, "net35", "xunit.runner.utility.net35.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.utility", "bin", context.ConfigurationText, "net452", "xunit.runner.utility.net452.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.utility", "bin", context.ConfigurationText, "netcoreapp1.0", "xunit.runner.utility.netcoreapp10.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.utility", "bin", context.ConfigurationText, "netstandard1.1", "xunit.runner.utility.netstandard11.dll"), + Path.Combine(context.BaseFolder, "src", "xunit.runner.utility", "bin", context.ConfigurationText, "netstandard1.5", "xunit.runner.utility.netstandard15.dll"), + }.Select(unsignedPath => + { + var unsignedFolder = Path.GetDirectoryName(unsignedPath) ?? throw new InvalidOperationException($"Path '{unsignedPath}' did not have a folder"); + var signedFolder = Path.Combine(unsignedFolder, "signed"); + Directory.CreateDirectory(signedFolder); + + var signedPath = Path.Combine(signedFolder, Path.GetFileName(unsignedPath)); + File.Copy(unsignedPath, signedPath, overwrite: true); + + return signedPath; + }).ToArray(); + + await context.SignFiles(context.BaseFolder, binaries); + } +} diff --git a/tools/builder/targets/Test.cs b/tools/builder/targets/Test.cs new file mode 100644 index 000000000..a408a45c6 --- /dev/null +++ b/tools/builder/targets/Test.cs @@ -0,0 +1,8 @@ +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools.Targets; +[Target( + BuildTarget.Test, + BuildTarget.TestCore, BuildTarget.TestFx +)] +public class Test { } diff --git a/tools/builder/targets/TestCore.cs b/tools/builder/targets/TestCore.cs new file mode 100644 index 000000000..269b21b0f --- /dev/null +++ b/tools/builder/targets/TestCore.cs @@ -0,0 +1,40 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools.Targets; + +[Target( + BuildTarget.TestCore, + BuildTarget.Build +)] +public static class TestCore +{ + public static async Task OnExecute(BuildContext context) + { + context.BuildStep("Running .NET Core 2.0 tests"); + await RunTests(context, "netcoreapp2.0", "-netcore"); + + context.BuildStep("Running .NET 6 tests"); + await RunTests(context, "net6.0", "-net6"); + } + + static Task RunTests( + BuildContext context, + string targetFramework, + string reportSuffix) + { + var runner = Path.Combine(context.BaseFolder, "src", "xunit.console", "bin", context.ConfigurationText, targetFramework, "xunit.console.dll"); + var netFxSubpath = Path.Combine("bin", context.ConfigurationText, targetFramework); + var v2TestDlls = + Directory + .GetFiles(context.BaseFolder, "test.xunit.*.dll", SearchOption.AllDirectories) + .Where(x => x.Contains(netFxSubpath)) + .OrderBy(x => x) + .Select(x => '"' + x + '"'); + var v2OutputFileName = Path.Combine(context.TestOutputFolder, $"v2{reportSuffix}"); + + return context.Exec("dotnet", $"\"{runner}\" {string.Join(' ', v2TestDlls)} {context.TestFlagsNonParallel} -xml \"{v2OutputFileName}.xml\" -html \"{v2OutputFileName}.html\""); + } +} diff --git a/tools/builder/targets/TestFx.cs b/tools/builder/targets/TestFx.cs new file mode 100644 index 000000000..c7f037e8f --- /dev/null +++ b/tools/builder/targets/TestFx.cs @@ -0,0 +1,46 @@ +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Xunit.BuildTools.Models; + +namespace Xunit.BuildTools.Targets; + +[Target( + BuildTarget.TestFx, + BuildTarget.Build +)] +public static class TestFx +{ + public static async Task OnExecute(BuildContext context) + { + context.BuildStep("Running .NET Framework tests (AnyCPU)"); + await RunTests(context, context.ConsoleRunnerExe, "-x64"); + + context.BuildStep("Running .NET Framework tests (x86)"); + await RunTests(context, context.ConsoleRunner32Exe, "-x86"); + } + + static async Task RunTests( + BuildContext context, + string runner, + string reportSuffix) + { + // v2 + var netFxSubpath = Path.Combine("bin", context.ConfigurationText, "net4"); + var v2TestDlls = + Directory + .GetFiles(context.BaseFolder, "test.xunit.*.dll", SearchOption.AllDirectories) + .Where(x => x.Contains(netFxSubpath)) + .OrderBy(x => x) + .Select(x => '"' + x + '"'); + var v2OutputFileName = Path.Combine(context.TestOutputFolder, $"v2-netfx{reportSuffix}"); + + await context.Exec(runner, $"{string.Join(' ', v2TestDlls)} {context.TestFlagsParallel} -serialize -xml \"{v2OutputFileName}.xml\" -html \"{v2OutputFileName}.html"); + + // v1 + var v1TestDll = Path.Combine(context.BaseFolder, "test", "test.xunit1", "bin", context.ConfigurationText, "net40", "test.xunit1.dll"); + var v1OutputFileName = Path.Combine(context.TestOutputFolder, $"v1-netfx{reportSuffix}"); + + await context.Exec(runner, $"\"{v1TestDll}\" {context.TestFlagsNonParallel} -xml \"{v1OutputFileName}.xml\" -html \"{v1OutputFileName}.html"); + } +} From cf210358ee233278f684ace417ca41b20bcd1e2e Mon Sep 17 00:00:00 2001 From: Brad Wilson Date: Sat, 3 Feb 2024 19:55:11 -0800 Subject: [PATCH 2/2] Missed one SignedPath in xunit.extensibility.core.nuspec --- src/xunit.extensibility.core.nuspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/xunit.extensibility.core.nuspec b/src/xunit.extensibility.core.nuspec index 6dc1e917c..8b3fac36e 100644 --- a/src/xunit.extensibility.core.nuspec +++ b/src/xunit.extensibility.core.nuspec @@ -39,7 +39,7 @@ - +