Huge build project refactoring
This commit is contained in:
Родитель
144f5c5552
Коммит
6291a7e724
|
@ -4,16 +4,58 @@ on:
|
||||||
pull_request:
|
pull_request:
|
||||||
push:
|
push:
|
||||||
|
|
||||||
|
permissions: write-all
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
|
||||||
|
build-windows-core:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
steps:
|
steps:
|
||||||
|
- name: Disable Windows Defender
|
||||||
|
run: Set-MpPreference -DisableRealtimeMonitoring $true
|
||||||
|
shell: powershell
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Run
|
- name: Run task 'Build'
|
||||||
shell: cmd
|
shell: cmd
|
||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
./build.bat
|
./build.cmd Build
|
||||||
|
- name: Run task 'InTestsCore'
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
|
./build.cmd InTestsCore -e
|
||||||
|
- name: Upload test results
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: build-windows-core-trx
|
||||||
|
path: "**/*.trx"
|
||||||
|
|
||||||
|
build-windows-full:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- name: Disable Windows Defender
|
||||||
|
run: Set-MpPreference -DisableRealtimeMonitoring $true
|
||||||
|
shell: powershell
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Run task 'Build'
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
|
./build.cmd Build
|
||||||
|
- name: Run task 'InTestsFull'
|
||||||
|
shell: cmd
|
||||||
|
run: |
|
||||||
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||||
|
./build.cmd InTestsFull -e
|
||||||
|
- name: Upload test results
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: build-windows-full-trx
|
||||||
|
path: "**/*.trx"
|
||||||
|
|
||||||
build-linux:
|
build-linux:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
@ -25,11 +67,52 @@ jobs:
|
||||||
platform: x64
|
platform: x64
|
||||||
- name: Set up zlib-static
|
- name: Set up zlib-static
|
||||||
run: sudo apt-get install -y libkrb5-dev
|
run: sudo apt-get install -y libkrb5-dev
|
||||||
- name: Run
|
- name: Run task 'Build'
|
||||||
run: ./build.sh
|
run: ./build.cmd Build
|
||||||
|
- name: Run task 'UnitTests'
|
||||||
|
run: ./build.cmd UnitTests -e
|
||||||
|
- name: Run task 'InTestsCore'
|
||||||
|
run: ./build.cmd InTestsCore -e
|
||||||
|
- name: Upload test results
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: build-linux-trx
|
||||||
|
path: "**/*.trx"
|
||||||
|
|
||||||
build-macos:
|
build-macos:
|
||||||
runs-on: macOS-latest
|
runs-on: macos-13
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Run
|
- name: Run task 'Build'
|
||||||
run: ./build.sh
|
run: ./build.cmd Build
|
||||||
|
- name: Run task 'UnitTests'
|
||||||
|
run: ./build.cmd UnitTests -e
|
||||||
|
- name: Run task 'InTestsCore'
|
||||||
|
run: ./build.cmd InTestsCore -e
|
||||||
|
- name: Upload test results
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: build-macos-trx
|
||||||
|
path: "**/*.trx"
|
||||||
|
|
||||||
|
report:
|
||||||
|
concurrency: ci-${{ github.ref }}
|
||||||
|
needs: [build-windows-full, build-windows-core, build-linux, build-macos]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: always()
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Download Artifacts
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
- name: Display structure of downloaded files
|
||||||
|
run: ls -R
|
||||||
|
- name: Report tests results
|
||||||
|
uses: dorny/test-reporter@v1
|
||||||
|
if: always()
|
||||||
|
with:
|
||||||
|
name: test-results
|
||||||
|
path: "**/*.trx"
|
||||||
|
reporter: dotnet-trx
|
||||||
|
fail-on-error: true
|
||||||
|
|
|
@ -19,7 +19,7 @@ jobs:
|
||||||
ref: master
|
ref: master
|
||||||
|
|
||||||
- name: Download changelog
|
- name: Download changelog
|
||||||
run: ./build.sh --target DocFX_Changelog_Download --VersionCount 1
|
run: ./build.cmd DocsUpdate /p:Depth=1
|
||||||
env:
|
env:
|
||||||
GITHUB_PRODUCT: ChangelogBuilder
|
GITHUB_PRODUCT: ChangelogBuilder
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
|
@ -19,16 +19,16 @@ jobs:
|
||||||
ref: docs-stable
|
ref: docs-stable
|
||||||
|
|
||||||
- name: Build BenchmarkDotNet
|
- name: Build BenchmarkDotNet
|
||||||
run: ./build.bat --target Build
|
run: ./build.cmd Build
|
||||||
|
|
||||||
- name: Download changelog
|
- name: Download changelog
|
||||||
run: ./build.bat --target DocFX_Changelog_Download --VersionCount 1
|
run: ./build.cmd DocsUpdate /p:Depth=1
|
||||||
env:
|
env:
|
||||||
GITHUB_PRODUCT: ChangelogBuilder
|
GITHUB_PRODUCT: ChangelogBuilder
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: ./build.bat --target DocFX_Build
|
run: ./build.cmd DocsBuild
|
||||||
|
|
||||||
- name: Upload Artifacts
|
- name: Upload Artifacts
|
||||||
uses: actions/upload-artifact@v1
|
uses: actions/upload-artifact@v1
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
name: publish-nightly
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
main:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Set date
|
||||||
|
run: echo "DATE=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||||
|
- name: Pack
|
||||||
|
run: ./build.cmd pack /p:VersionSuffix=nightly.$DATE.$GITHUB_RUN_NUMBER
|
||||||
|
- name: Publish nupkg
|
||||||
|
env:
|
||||||
|
MYGET_API_KEY: ${{ secrets.MYGET_API_KEY }}
|
||||||
|
run: .dotnet/dotnet nuget push **/*.nupkg --source https://www.myget.org/F/benchmarkdotnet/api/v3/index.json --api-key $MYGET_API_KEY --timeout 600
|
||||||
|
- name: Publish snupkg
|
||||||
|
env:
|
||||||
|
MYGET_API_KEY: ${{ secrets.MYGET_API_KEY }}
|
||||||
|
run: .dotnet/dotnet nuget push **/*.snupkg --source https://www.myget.org/F/benchmarkdotnet/api/v3/index.json --api-key $MYGET_API_KEY --timeout 600
|
|
@ -37,7 +37,7 @@ before_build:
|
||||||
#---------------------------------#
|
#---------------------------------#
|
||||||
|
|
||||||
build_script:
|
build_script:
|
||||||
- ps: .\build.ps1
|
- ps: .\build.cmd CI
|
||||||
|
|
||||||
test: off
|
test: off
|
||||||
deploy: off
|
deploy: off
|
||||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
||||||
parameters:
|
parameters:
|
||||||
name: Ubuntu
|
name: Ubuntu
|
||||||
vmImage: 'ubuntu-20.04'
|
vmImage: 'ubuntu-20.04'
|
||||||
scriptFileName: ./build.sh
|
scriptFileName: ./build.cmd CI
|
||||||
initialization:
|
initialization:
|
||||||
- bash: |
|
- bash: |
|
||||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||||
|
|
|
@ -13,4 +13,4 @@ jobs:
|
||||||
parameters:
|
parameters:
|
||||||
name: Windows
|
name: Windows
|
||||||
vmImage: 'windows-2019'
|
vmImage: 'windows-2019'
|
||||||
scriptFileName: .\build.ps1
|
scriptFileName: .\build.cmd CI
|
|
@ -13,4 +13,4 @@ jobs:
|
||||||
parameters:
|
parameters:
|
||||||
name: macOS
|
name: macOS
|
||||||
vmImage: 'macOS-latest'
|
vmImage: 'macOS-latest'
|
||||||
scriptFileName: ./build.sh
|
scriptFileName: ./build.cmd CI
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
:<<"::CMDLITERAL"
|
||||||
|
@CALL build\build.bat %*
|
||||||
|
@GOTO :EOF
|
||||||
|
::CMDLITERAL
|
||||||
|
"$(cd "$(dirname "$0")"; pwd)/build/build.sh" "$@"
|
|
@ -3,13 +3,13 @@
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>net7.0</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Cake.Frosting" Version="3.0.0" />
|
<PackageReference Include="Cake.Frosting" Version="3.0.0" />
|
||||||
<PackageReference Include="Cake.FileHelpers" Version="6.1.3" />
|
<PackageReference Include="Cake.FileHelpers" Version="6.1.3" />
|
||||||
<PackageReference Include="Cake.Git" Version="3.0.0" />
|
<PackageReference Include="Cake.Git" Version="3.0.0" />
|
||||||
<PackageReference Include="Microsoft.DocAsCode.App" Version="2.67.5" />
|
<PackageReference Include="Microsoft.DocAsCode.App" Version="2.67.5" />
|
||||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" />
|
|
||||||
<PackageReference Include="Octokit" Version="7.0.0" />
|
<PackageReference Include="Octokit" Version="7.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -0,0 +1,228 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using BenchmarkDotNet.Build.Helpers;
|
||||||
|
using BenchmarkDotNet.Build.Meta;
|
||||||
|
using BenchmarkDotNet.Build.Runners;
|
||||||
|
using Cake.Common;
|
||||||
|
using Cake.Common.Build;
|
||||||
|
using Cake.Common.Build.AppVeyor;
|
||||||
|
using Cake.Common.Diagnostics;
|
||||||
|
using Cake.Common.IO;
|
||||||
|
using Cake.Common.Tools.DotNet;
|
||||||
|
using Cake.Common.Tools.DotNet.MSBuild;
|
||||||
|
using Cake.Core;
|
||||||
|
using Cake.Core.IO;
|
||||||
|
using Cake.FileHelpers;
|
||||||
|
using Cake.Frosting;
|
||||||
|
using Cake.Git;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build;
|
||||||
|
|
||||||
|
public class BuildContext : FrostingContext
|
||||||
|
{
|
||||||
|
public string BuildConfiguration { get; set; } = "Release";
|
||||||
|
public DotNetVerbosity BuildVerbosity { get; set; } = DotNetVerbosity.Minimal;
|
||||||
|
public int Depth { get; set; }
|
||||||
|
|
||||||
|
public DirectoryPath RootDirectory { get; }
|
||||||
|
public DirectoryPath ArtifactsDirectory { get; }
|
||||||
|
public DirectoryPath DocsDirectory { get; }
|
||||||
|
public FilePath DocfxJsonFile { get; }
|
||||||
|
|
||||||
|
public DirectoryPath ChangeLogDirectory { get; }
|
||||||
|
public DirectoryPath ChangeLogGenDirectory { get; }
|
||||||
|
|
||||||
|
public DirectoryPath RedirectRootDirectory { get; }
|
||||||
|
public DirectoryPath RedirectTargetDirectory { get; }
|
||||||
|
|
||||||
|
public FilePath SolutionFile { get; }
|
||||||
|
public FilePath TemplatesTestsProjectFile { get; }
|
||||||
|
public FilePathCollection AllPackableSrcProjects { get; }
|
||||||
|
|
||||||
|
public DotNetMSBuildSettings MsBuildSettingsRestore { get; }
|
||||||
|
public DotNetMSBuildSettings MsBuildSettingsBuild { get; }
|
||||||
|
public DotNetMSBuildSettings MsBuildSettingsPack { get; }
|
||||||
|
|
||||||
|
private IAppVeyorProvider AppVeyor => this.BuildSystem().AppVeyor;
|
||||||
|
public bool IsRunningOnAppVeyor => AppVeyor.IsRunningOnAppVeyor;
|
||||||
|
public bool IsOnAppVeyorAndNotPr => IsRunningOnAppVeyor && !AppVeyor.Environment.PullRequest.IsPullRequest;
|
||||||
|
|
||||||
|
public bool IsOnAppVeyorAndBdnNightlyCiCd => IsOnAppVeyorAndNotPr &&
|
||||||
|
AppVeyor.Environment.Repository.Branch == "master" &&
|
||||||
|
this.IsRunningOnWindows();
|
||||||
|
|
||||||
|
public bool IsLocalBuild => this.BuildSystem().IsLocalBuild;
|
||||||
|
public bool IsCiBuild => !this.BuildSystem().IsLocalBuild;
|
||||||
|
|
||||||
|
public UnitTestRunner UnitTestRunner { get; }
|
||||||
|
public DocumentationRunner DocumentationRunner { get; }
|
||||||
|
public BuildRunner BuildRunner { get; }
|
||||||
|
|
||||||
|
public BuildContext(ICakeContext context)
|
||||||
|
: base(context)
|
||||||
|
{
|
||||||
|
RootDirectory = new DirectoryPath(new DirectoryInfo(Directory.GetCurrentDirectory()).Parent?.Parent?.FullName);
|
||||||
|
ArtifactsDirectory = RootDirectory.Combine("artifacts");
|
||||||
|
DocsDirectory = RootDirectory.Combine("docs");
|
||||||
|
DocfxJsonFile = DocsDirectory.CombineWithFilePath("docfx.json");
|
||||||
|
|
||||||
|
ChangeLogDirectory = RootDirectory.Combine("docs").Combine("changelog");
|
||||||
|
ChangeLogGenDirectory = RootDirectory.Combine("docs").Combine("_changelog");
|
||||||
|
|
||||||
|
RedirectRootDirectory = RootDirectory.Combine("docs").Combine("_redirects");
|
||||||
|
RedirectTargetDirectory = RootDirectory.Combine("docs").Combine("_site");
|
||||||
|
|
||||||
|
SolutionFile = RootDirectory.CombineWithFilePath("BenchmarkDotNet.sln");
|
||||||
|
|
||||||
|
TemplatesTestsProjectFile = RootDirectory.Combine("templates")
|
||||||
|
.CombineWithFilePath("BenchmarkDotNet.Templates.csproj");
|
||||||
|
AllPackableSrcProjects = new FilePathCollection(context.GetFiles(RootDirectory.FullPath + "/src/**/*.csproj")
|
||||||
|
.Where(p => !p.FullPath.Contains("Disassembler")));
|
||||||
|
|
||||||
|
MsBuildSettingsRestore = new DotNetMSBuildSettings();
|
||||||
|
MsBuildSettingsBuild = new DotNetMSBuildSettings();
|
||||||
|
MsBuildSettingsPack = new DotNetMSBuildSettings();
|
||||||
|
|
||||||
|
if (IsCiBuild)
|
||||||
|
{
|
||||||
|
System.Environment.SetEnvironmentVariable("BDN_CI_BUILD", "true");
|
||||||
|
|
||||||
|
MsBuildSettingsBuild.MaxCpuCount = 1;
|
||||||
|
MsBuildSettingsBuild.WithProperty("UseSharedCompilation", "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
Depth = -1;
|
||||||
|
if (context.Arguments.HasArgument("msbuild"))
|
||||||
|
{
|
||||||
|
var msBuildParameters = context.Arguments.GetArguments().First(it => it.Key == "msbuild").Value;
|
||||||
|
foreach (var msBuildParameter in msBuildParameters)
|
||||||
|
{
|
||||||
|
var split = msBuildParameter.Split(new[] { '=' }, 2);
|
||||||
|
if (split.Length == 2)
|
||||||
|
{
|
||||||
|
var name = split[0];
|
||||||
|
var value = split[1];
|
||||||
|
|
||||||
|
MsBuildSettingsRestore.WithProperty(name, value);
|
||||||
|
MsBuildSettingsBuild.WithProperty(name, value);
|
||||||
|
MsBuildSettingsPack.WithProperty(name, value);
|
||||||
|
|
||||||
|
if (name.Equals("configuration", StringComparison.OrdinalIgnoreCase)) BuildConfiguration = value;
|
||||||
|
|
||||||
|
if (name.Equals("verbosity", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
var parsedVerbosity = Utils.ParseVerbosity(value);
|
||||||
|
if (parsedVerbosity != null)
|
||||||
|
BuildVerbosity = parsedVerbosity.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (name.Equals("depth", StringComparison.OrdinalIgnoreCase))
|
||||||
|
Depth = int.Parse(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NativeAOT build requires VS C++ tools to be added to $path via vcvars64.bat
|
||||||
|
// but once we do that, dotnet restore fails with:
|
||||||
|
// "Please specify a valid solution configuration using the Configuration and Platform properties"
|
||||||
|
if (context.IsRunningOnWindows())
|
||||||
|
{
|
||||||
|
MsBuildSettingsRestore.WithProperty("Platform", "Any CPU");
|
||||||
|
MsBuildSettingsBuild.WithProperty("Platform", "Any CPU");
|
||||||
|
}
|
||||||
|
|
||||||
|
UnitTestRunner = new UnitTestRunner(this);
|
||||||
|
DocumentationRunner = new DocumentationRunner(this);
|
||||||
|
BuildRunner = new BuildRunner(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EnsureChangelogDetailsExist(bool forceClean = false)
|
||||||
|
{
|
||||||
|
var path = ChangeLogGenDirectory.Combine("details");
|
||||||
|
if (this.DirectoryExists(path) && forceClean)
|
||||||
|
this.DeleteDirectory(path, new DeleteDirectorySettings() { Force = true, Recursive = true });
|
||||||
|
|
||||||
|
if (!this.DirectoryExists(path))
|
||||||
|
{
|
||||||
|
var repo = Repo.HttpsGitUrl;
|
||||||
|
var branchName = Repo.ChangelogDetailsBranch;
|
||||||
|
var settings = new GitCloneSettings { Checkout = true, BranchName = branchName };
|
||||||
|
this.Information($"Trying to clone {repo} to {path} (branch: '{branchName})");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.GitClone(repo, path, settings);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
this.Error($"Failed to clone {repo} to {path} (branch: '{branchName}), Exception: {e.GetType().Name}'");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var gitArgs = $"clone -b {branchName} {repo} {path}";
|
||||||
|
this.Information($"Trying to clone manually: 'git {gitArgs}'");
|
||||||
|
this.StartProcess("git", gitArgs);
|
||||||
|
}
|
||||||
|
catch (Exception e2)
|
||||||
|
{
|
||||||
|
throw new Exception($"Failed to clone {repo} to {path} (branch: '{branchName})'", e2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.Information("Clone is successfully finished");
|
||||||
|
this.Information("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DocfxChangelogDownload(string version, string versionPrevious, string lastCommit = "")
|
||||||
|
{
|
||||||
|
EnsureChangelogDetailsExist();
|
||||||
|
this.Information("DocfxChangelogDownload: " + version);
|
||||||
|
var path = ChangeLogGenDirectory.Combine("details");
|
||||||
|
ChangeLogBuilder.Run(path, version, versionPrevious, lastCommit).Wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void DocfxChangelogGenerate(string version)
|
||||||
|
{
|
||||||
|
EnsureChangelogDetailsExist();
|
||||||
|
this.Information("DocfxChangelogGenerate: " + version);
|
||||||
|
var header = ChangeLogGenDirectory.Combine("header").CombineWithFilePath(version + ".md");
|
||||||
|
var footer = ChangeLogGenDirectory.Combine("footer").CombineWithFilePath(version + ".md");
|
||||||
|
var details = ChangeLogGenDirectory.Combine("details").CombineWithFilePath(version + ".md");
|
||||||
|
var release = ChangeLogDirectory.CombineWithFilePath(version + ".md");
|
||||||
|
|
||||||
|
var content = new StringBuilder();
|
||||||
|
content.AppendLine("---");
|
||||||
|
content.AppendLine("uid: changelog." + version);
|
||||||
|
content.AppendLine("---");
|
||||||
|
content.AppendLine("");
|
||||||
|
content.AppendLine("# BenchmarkDotNet " + version);
|
||||||
|
content.AppendLine("");
|
||||||
|
content.AppendLine("");
|
||||||
|
|
||||||
|
if (this.FileExists(header))
|
||||||
|
{
|
||||||
|
content.AppendLine(this.FileReadText(header));
|
||||||
|
content.AppendLine("");
|
||||||
|
content.AppendLine("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.FileExists(details))
|
||||||
|
{
|
||||||
|
content.AppendLine(this.FileReadText(details));
|
||||||
|
content.AppendLine("");
|
||||||
|
content.AppendLine("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.FileExists(footer))
|
||||||
|
{
|
||||||
|
content.AppendLine("## Additional details");
|
||||||
|
content.AppendLine("");
|
||||||
|
content.AppendLine(this.FileReadText(footer));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.FileWriteText(release, content.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -5,78 +5,28 @@ using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using BenchmarkDotNet.Build.Helpers;
|
||||||
|
using BenchmarkDotNet.Build.Meta;
|
||||||
using Cake.Core.IO;
|
using Cake.Core.IO;
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Octokit;
|
using Octokit;
|
||||||
|
|
||||||
namespace Build;
|
namespace BenchmarkDotNet.Build;
|
||||||
|
|
||||||
public static class OctokitExtensions
|
public static class ChangeLogBuilder
|
||||||
{
|
{
|
||||||
public static string ToStr(this User user, string prefix) => user != null
|
private class Config
|
||||||
? $" ({prefix} [@{user.Login}]({user.HtmlUrl}))"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
private static string ToStr(this Author user, string prefix) => user != null
|
|
||||||
? $" ({prefix} {user.ToLink()})"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
private static string ToStr(this Committer user, string prefix) => user != null
|
|
||||||
? $" ({prefix} {user.Name})"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
public static string ToLink(this Author user) => $"[@{user.Login}]({user.HtmlUrl})";
|
|
||||||
|
|
||||||
public static string ToLinkWithName(this Author user, string name) => $"[@{user.Login} ({name})]({user.HtmlUrl})";
|
|
||||||
|
|
||||||
public static string ToCommitMessage(this Commit commit)
|
|
||||||
{
|
{
|
||||||
var message = commit.Message.Trim().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
public string CurrentMilestone { get; }
|
||||||
.FirstOrDefault() ?? "";
|
public string PreviousMilestone { get; }
|
||||||
return message.Length > 80 ? message.Substring(0, 77) + "..." : message;
|
public string LastCommit { get; }
|
||||||
}
|
|
||||||
|
|
||||||
public static string ToLink(this GitHubCommit commit) => $"[{commit.Sha.Substring(0, 6)}]({commit.HtmlUrl})";
|
public void Deconstruct(out string currentMilestone, out string previousMilestone, out string lastCommit)
|
||||||
|
|
||||||
public static string ToByStr(this GitHubCommit commit)
|
|
||||||
{
|
{
|
||||||
if (commit.Author != null)
|
|
||||||
return commit.Author.ToStr("by");
|
|
||||||
return commit.Commit.Author != null ? commit.Commit.Author.ToStr("by") : "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ChangeLogBuilder
|
|
||||||
{
|
|
||||||
public class Config
|
|
||||||
{
|
|
||||||
[PublicAPI] public string ProductHeader => Environment.GetEnvironmentVariable("GITHUB_PRODUCT");
|
|
||||||
[PublicAPI] public string Token => Environment.GetEnvironmentVariable("GITHUB_TOKEN");
|
|
||||||
|
|
||||||
[PublicAPI] public string RepoOwner => "dotnet";
|
|
||||||
[PublicAPI] public string RepoName => "BenchmarkDotNet";
|
|
||||||
[PublicAPI] public string CurrentMilestone { get; }
|
|
||||||
|
|
||||||
[PublicAPI] public string PreviousMilestone { get; }
|
|
||||||
[PublicAPI] public string LastCommit { get; }
|
|
||||||
|
|
||||||
public void Deconstruct(out string repoOwner, out string repoName, out string currentMilestone,
|
|
||||||
out string previousMilestone, out string lastCommit)
|
|
||||||
{
|
|
||||||
repoOwner = RepoOwner;
|
|
||||||
repoName = RepoName;
|
|
||||||
currentMilestone = CurrentMilestone;
|
currentMilestone = CurrentMilestone;
|
||||||
previousMilestone = PreviousMilestone;
|
previousMilestone = PreviousMilestone;
|
||||||
lastCommit = LastCommit;
|
lastCommit = LastCommit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Config(string[] args)
|
|
||||||
{
|
|
||||||
CurrentMilestone = args[0];
|
|
||||||
PreviousMilestone = args[1];
|
|
||||||
LastCommit = args.Length <= 2 ? CurrentMilestone : args[2];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Config(string currentMilestone, string previousMilestone, string lastCommit)
|
public Config(string currentMilestone, string previousMilestone, string lastCommit)
|
||||||
{
|
{
|
||||||
CurrentMilestone = currentMilestone;
|
CurrentMilestone = currentMilestone;
|
||||||
|
@ -85,18 +35,9 @@ public class ChangeLogBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AuthorEqualityComparer : IEqualityComparer<Author>
|
private class MarkdownBuilder
|
||||||
{
|
{
|
||||||
public static readonly IEqualityComparer<Author> Default = new AuthorEqualityComparer();
|
private static IReadOnlyList<Milestone>? allMilestones;
|
||||||
|
|
||||||
public bool Equals(Author x, Author y) => x.Login == y.Login;
|
|
||||||
|
|
||||||
public int GetHashCode(Author author) => author.Login.GetHashCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MarkdownBuilder
|
|
||||||
{
|
|
||||||
private static IReadOnlyList<Milestone> AllMilestones = null;
|
|
||||||
private static readonly Dictionary<string, string> AuthorNames = new();
|
private static readonly Dictionary<string, string> AuthorNames = new();
|
||||||
|
|
||||||
private readonly Config config;
|
private readonly Config config;
|
||||||
|
@ -115,17 +56,17 @@ public class ChangeLogBuilder
|
||||||
|
|
||||||
private async Task<string> Build()
|
private async Task<string> Build()
|
||||||
{
|
{
|
||||||
var (repoOwner, repoName, milestone, previousMilestone, lastCommit) = config;
|
var (milestone, previousMilestone, lastCommit) = config;
|
||||||
if (string.IsNullOrEmpty(lastCommit))
|
if (string.IsNullOrEmpty(lastCommit))
|
||||||
lastCommit = milestone;
|
lastCommit = milestone;
|
||||||
|
|
||||||
var client = new GitHubClient(new ProductHeaderValue(config.ProductHeader));
|
var client = new GitHubClient(new ProductHeaderValue(Repo.ProductHeader));
|
||||||
var tokenAuth = new Credentials(config.Token);
|
var tokenAuth = new Credentials(Repo.Token);
|
||||||
client.Credentials = tokenAuth;
|
client.Credentials = tokenAuth;
|
||||||
|
|
||||||
if (milestone == "_")
|
if (milestone == "_")
|
||||||
{
|
{
|
||||||
var allContributors = await client.Repository.GetAllContributors(repoOwner, repoName);
|
var allContributors = await client.Repository.GetAllContributors(Repo.Owner, Repo.Name);
|
||||||
builder.AppendLine("# All contributors");
|
builder.AppendLine("# All contributors");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
foreach (var contributor in allContributors)
|
foreach (var contributor in allContributors)
|
||||||
|
@ -140,17 +81,17 @@ public class ChangeLogBuilder
|
||||||
return builder.ToString();
|
return builder.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AllMilestones == null)
|
if (allMilestones == null)
|
||||||
{
|
{
|
||||||
var milestoneRequest = new MilestoneRequest
|
var milestoneRequest = new MilestoneRequest
|
||||||
{
|
{
|
||||||
State = ItemStateFilter.All
|
State = ItemStateFilter.All
|
||||||
};
|
};
|
||||||
AllMilestones = await client.Issue.Milestone.GetAllForRepository(repoOwner, repoName, milestoneRequest);
|
allMilestones = await client.Issue.Milestone.GetAllForRepository(Repo.Owner, Repo.Name, milestoneRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
IReadOnlyList<Issue> allIssues = Array.Empty<Issue>();
|
IReadOnlyList<Issue> allIssues = Array.Empty<Issue>();
|
||||||
var targetMilestone = AllMilestones.FirstOrDefault(m => m.Title == milestone);
|
var targetMilestone = allMilestones.FirstOrDefault(m => m.Title == milestone);
|
||||||
if (targetMilestone != null)
|
if (targetMilestone != null)
|
||||||
{
|
{
|
||||||
var issueRequest = new RepositoryIssueRequest
|
var issueRequest = new RepositoryIssueRequest
|
||||||
|
@ -159,7 +100,7 @@ public class ChangeLogBuilder
|
||||||
Milestone = targetMilestone.Number.ToString()
|
Milestone = targetMilestone.Number.ToString()
|
||||||
};
|
};
|
||||||
|
|
||||||
allIssues = await client.Issue.GetAllForRepository(repoOwner, repoName, issueRequest);
|
allIssues = await client.Issue.GetAllForRepository(Repo.Owner, Repo.Name, issueRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
var issues = allIssues
|
var issues = allIssues
|
||||||
|
@ -171,7 +112,7 @@ public class ChangeLogBuilder
|
||||||
.OrderBy(issue => issue.Number)
|
.OrderBy(issue => issue.Number)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var compare = await client.Repository.Commit.Compare(repoOwner, repoName, previousMilestone, lastCommit);
|
var compare = await client.Repository.Commit.Compare(Repo.Owner, Repo.Name, previousMilestone, lastCommit);
|
||||||
var commits = compare.Commits;
|
var commits = compare.Commits;
|
||||||
|
|
||||||
|
|
||||||
|
@ -196,7 +137,7 @@ public class ChangeLogBuilder
|
||||||
.Distinct()
|
.Distinct()
|
||||||
.ToImmutableList();
|
.ToImmutableList();
|
||||||
|
|
||||||
var milestoneHtmlUlr = $"https://github.com/{repoOwner}/{repoName}/issues?q=milestone:{milestone}";
|
var milestoneHtmlUlr = $"https://github.com/{Repo.Owner}/{Repo.Name}/issues?q=milestone:{milestone}";
|
||||||
|
|
||||||
builder.AppendLine("## Milestone details");
|
builder.AppendLine("## Milestone details");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
|
@ -218,7 +159,7 @@ public class ChangeLogBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AppendList<T>(string title, IReadOnlyList<T> items, Func<T, string> format,
|
private void AppendList<T>(string title, IReadOnlyList<T> items, Func<T, string> format,
|
||||||
string conclusion = null)
|
string? conclusion = null)
|
||||||
{
|
{
|
||||||
builder.AppendLine($"## {title} ({items.Count})");
|
builder.AppendLine($"## {title} ({items.Count})");
|
||||||
builder.AppendLine();
|
builder.AppendLine();
|
||||||
|
@ -234,7 +175,8 @@ public class ChangeLogBuilder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task Run(DirectoryPath path, string currentMilestone, string previousMilestone, string lastCommit)
|
public static async Task Run(DirectoryPath path, string currentMilestone, string previousMilestone,
|
||||||
|
string lastCommit)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
|
@ -0,0 +1,291 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Cake.Frosting;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build;
|
||||||
|
|
||||||
|
public class CommandLineParser
|
||||||
|
{
|
||||||
|
public static readonly CommandLineParser Instance = new();
|
||||||
|
|
||||||
|
private record Option(string ShortName, string FullName, string Arg, string Description, string CakeOption);
|
||||||
|
|
||||||
|
private readonly Option[] options =
|
||||||
|
{
|
||||||
|
new("-v",
|
||||||
|
"--verbosity",
|
||||||
|
"<LEVEL>",
|
||||||
|
"Specifies the amount of information to be displayed\n(Quiet, Minimal, Normal, Verbose, Diagnostic)",
|
||||||
|
"--verbosity"),
|
||||||
|
new("-e",
|
||||||
|
"--exclusive",
|
||||||
|
"",
|
||||||
|
"Executes the target task without any dependencies",
|
||||||
|
"--exclusive")
|
||||||
|
};
|
||||||
|
|
||||||
|
private void PrintHelp(bool skipWelcome = false)
|
||||||
|
{
|
||||||
|
const string scriptName = "build.cmd";
|
||||||
|
if (!skipWelcome)
|
||||||
|
{
|
||||||
|
WriteHeader("Welcome to the BenchmarkDotNet build script!");
|
||||||
|
WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteHeader("USAGE:");
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
Write(scriptName + " ");
|
||||||
|
WriteTask("<TASK> ");
|
||||||
|
WriteOption("[OPTIONS]");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WriteHeader("EXAMPLES:");
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
Write(scriptName + " ");
|
||||||
|
WriteTask("restore");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
Write(scriptName + " ");
|
||||||
|
WriteTask("build ");
|
||||||
|
WriteOption("/p:");
|
||||||
|
WriteArg("Configuration");
|
||||||
|
WriteOption("=");
|
||||||
|
WriteArg("Debug");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
Write(scriptName + " ");
|
||||||
|
WriteTask("pack ");
|
||||||
|
WriteOption("/p:");
|
||||||
|
WriteArg("Version");
|
||||||
|
WriteOption("=");
|
||||||
|
WriteArg("0.1.1729-preview");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
Write(scriptName + " ");
|
||||||
|
WriteTask("unittests ");
|
||||||
|
WriteOption("--exclusive --verbosity ");
|
||||||
|
WriteArg("Diagnostic");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
Write(scriptName + " ");
|
||||||
|
WriteTask("docsupdate ");
|
||||||
|
WriteOption("/p:");
|
||||||
|
WriteArg("Depth");
|
||||||
|
WriteOption("=");
|
||||||
|
WriteArg("3");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WriteLine("OPTIONS:", ConsoleColor.DarkCyan);
|
||||||
|
|
||||||
|
var shortNameWidth = options.Max(it => it.ShortName.Length);
|
||||||
|
var targetWidth = options.Max(it => it.FullName.Length + it.Arg.Length);
|
||||||
|
|
||||||
|
foreach (var (shortName, fullName, arg, description, _) in options)
|
||||||
|
{
|
||||||
|
WritePrefix();
|
||||||
|
WriteOption(shortName.PadRight(shortNameWidth));
|
||||||
|
WriteOption(shortName != "" ? "," : " ");
|
||||||
|
WriteOption(fullName);
|
||||||
|
Write(" ");
|
||||||
|
WriteArg(arg);
|
||||||
|
Write(new string(' ', targetWidth - fullName.Length - arg.Length + 3));
|
||||||
|
var descriptionLines = description.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
Write(descriptionLines.FirstOrDefault() ?? "");
|
||||||
|
for (int i = 1; i < descriptionLines.Length; i++)
|
||||||
|
{
|
||||||
|
WriteLine();
|
||||||
|
WritePrefix();
|
||||||
|
Write(new string(' ', shortNameWidth + 2 + targetWidth + 3));
|
||||||
|
Write(descriptionLines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
WritePrefix();
|
||||||
|
WriteOption("/p:");
|
||||||
|
WriteArg("<KEY>");
|
||||||
|
WriteOption("=");
|
||||||
|
WriteArg("<VALUE>");
|
||||||
|
Write(new string(' ', targetWidth + shortNameWidth - 11));
|
||||||
|
Write("Passes custom properties to MSBuild");
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WriteLine();
|
||||||
|
|
||||||
|
WriteHeader("TASKS:");
|
||||||
|
var taskWidth = GetTaskNames().Max(name => name.Length) + 3;
|
||||||
|
foreach (var (taskName, taskDescription) in GetTasks())
|
||||||
|
{
|
||||||
|
if (taskName.Equals("Default", StringComparison.OrdinalIgnoreCase))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (taskDescription.StartsWith("OBSOLETE", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
WriteObsolete(" " + taskName.PadRight(taskWidth));
|
||||||
|
WriteObsolete(taskDescription);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WriteTask(" " + taskName.PadRight(taskWidth));
|
||||||
|
Write(taskDescription);
|
||||||
|
}
|
||||||
|
|
||||||
|
WriteLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
void WritePrefix() => Write(" ");
|
||||||
|
void WriteTask(string message) => Write(message, ConsoleColor.Green);
|
||||||
|
void WriteOption(string message) => Write(message, ConsoleColor.Blue);
|
||||||
|
void WriteArg(string message) => Write(message, ConsoleColor.DarkYellow);
|
||||||
|
void WriteObsolete(string message) => Write(message, ConsoleColor.Gray);
|
||||||
|
|
||||||
|
void WriteHeader(string message)
|
||||||
|
{
|
||||||
|
WriteLine(message, ConsoleColor.DarkCyan);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write(string message, ConsoleColor? color = null)
|
||||||
|
{
|
||||||
|
if (color != null)
|
||||||
|
Console.ForegroundColor = color.Value;
|
||||||
|
Console.Write(message);
|
||||||
|
if (color != null)
|
||||||
|
Console.ResetColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteLine(string message = "", ConsoleColor? color = null)
|
||||||
|
{
|
||||||
|
Write(message, color);
|
||||||
|
Console.WriteLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static HashSet<string> GetTaskNames()
|
||||||
|
{
|
||||||
|
return GetTasks().Select(task => task.Name).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<(string Name, string Description)> GetTasks()
|
||||||
|
{
|
||||||
|
return typeof(BuildContext).Assembly
|
||||||
|
.GetTypes()
|
||||||
|
.Where(type => type.IsSubclassOf(typeof(FrostingTask<BuildContext>)) && !type.IsAbstract)
|
||||||
|
.Select(type => (
|
||||||
|
Name: type.GetCustomAttribute<TaskNameAttribute>()?.Name ?? "",
|
||||||
|
Description: type.GetCustomAttribute<TaskDescriptionAttribute>()?.Description ?? ""
|
||||||
|
))
|
||||||
|
.Where(task => task.Name != "")
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public string[]? Parse(string[]? args)
|
||||||
|
{
|
||||||
|
if (args == null || args.Length == 0)
|
||||||
|
{
|
||||||
|
PrintHelp();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.Length == 1)
|
||||||
|
{
|
||||||
|
if (IsOneOf(args[0], "help"))
|
||||||
|
{
|
||||||
|
PrintHelp();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsOneOf(args[0], "help-cake"))
|
||||||
|
{
|
||||||
|
new CakeHost().UseContext<BuildContext>().Run(new[] { "--help" });
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var argsToProcess = new Queue<string>(args);
|
||||||
|
|
||||||
|
var taskName = argsToProcess.Dequeue();
|
||||||
|
if (IsOneOf(taskName, "-t", "--target") && argsToProcess.Any())
|
||||||
|
taskName = argsToProcess.Dequeue();
|
||||||
|
|
||||||
|
var taskNames = GetTaskNames();
|
||||||
|
if (!taskNames.Contains(taskName))
|
||||||
|
{
|
||||||
|
PrintError($"'{taskName}' is not a task");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cakeArgs = new List<string>
|
||||||
|
{
|
||||||
|
"--target",
|
||||||
|
taskName
|
||||||
|
};
|
||||||
|
while (argsToProcess.Any())
|
||||||
|
{
|
||||||
|
var arg = argsToProcess.Dequeue();
|
||||||
|
|
||||||
|
var matched = false;
|
||||||
|
foreach (var option in options)
|
||||||
|
{
|
||||||
|
if (IsOneOf(arg, option.ShortName, option.FullName))
|
||||||
|
{
|
||||||
|
matched = true;
|
||||||
|
cakeArgs.Add(option.CakeOption);
|
||||||
|
if (option.Arg != "")
|
||||||
|
{
|
||||||
|
if (!argsToProcess.Any())
|
||||||
|
{
|
||||||
|
PrintError(option.FullName + " is not specified");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
cakeArgs.Add(argsToProcess.Dequeue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg.StartsWith("/p:"))
|
||||||
|
{
|
||||||
|
matched = true;
|
||||||
|
cakeArgs.Add("--msbuild");
|
||||||
|
cakeArgs.Add(arg[3..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matched)
|
||||||
|
{
|
||||||
|
PrintError("Unknown option: " + arg);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cakeArgs.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsOneOf(string arg, params string[] values) =>
|
||||||
|
values.Any(value => value.Equals(arg, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
void PrintError(string text)
|
||||||
|
{
|
||||||
|
Console.ForegroundColor = ConsoleColor.Red;
|
||||||
|
Console.Error.WriteLine("ERROR: " + text);
|
||||||
|
Console.WriteLine();
|
||||||
|
Console.ResetColor();
|
||||||
|
PrintHelp(true);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticFields/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Nupkg/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
|
@ -0,0 +1,40 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Octokit;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Helpers;
|
||||||
|
|
||||||
|
public static class OctokitExtensions
|
||||||
|
{
|
||||||
|
public static string ToStr(this User? user, string prefix) => user != null
|
||||||
|
? $" ({prefix} [@{user.Login}]({user.HtmlUrl}))"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
private static string ToStr(this Author? user, string prefix) => user != null
|
||||||
|
? $" ({prefix} {user.ToLink()})"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
private static string ToStr(this Committer? user, string prefix) => user != null
|
||||||
|
? $" ({prefix} {user.Name})"
|
||||||
|
: "";
|
||||||
|
|
||||||
|
public static string ToLink(this Author user) => $"[@{user.Login}]({user.HtmlUrl})";
|
||||||
|
|
||||||
|
public static string ToLinkWithName(this Author user, string name) => $"[@{user.Login} ({name})]({user.HtmlUrl})";
|
||||||
|
|
||||||
|
public static string ToCommitMessage(this Commit commit)
|
||||||
|
{
|
||||||
|
var message = commit.Message.Trim().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||||
|
.FirstOrDefault() ?? "";
|
||||||
|
return message.Length > 80 ? message.Substring(0, 77) + "..." : message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string ToLink(this GitHubCommit commit) => $"[{commit.Sha.Substring(0, 6)}]({commit.HtmlUrl})";
|
||||||
|
|
||||||
|
public static string ToByStr(this GitHubCommit commit)
|
||||||
|
{
|
||||||
|
if (commit.Author != null)
|
||||||
|
return commit.Author.ToStr("by");
|
||||||
|
return commit.Commit.Author != null ? commit.Commit.Author.ToStr("by") : "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using Cake.Common.Tools.DotNet;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Helpers;
|
||||||
|
|
||||||
|
public static class Utils
|
||||||
|
{
|
||||||
|
public static string GetOs()
|
||||||
|
{
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||||
|
return "linux";
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||||
|
return "windows";
|
||||||
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||||
|
return "macos";
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
public static DotNetVerbosity? ParseVerbosity(string verbosity)
|
||||||
|
{
|
||||||
|
var lookup = new Dictionary<string, DotNetVerbosity>(StringComparer.OrdinalIgnoreCase)
|
||||||
|
{
|
||||||
|
{ "q", DotNetVerbosity.Quiet },
|
||||||
|
{ "quiet", DotNetVerbosity.Quiet },
|
||||||
|
{ "m", DotNetVerbosity.Minimal },
|
||||||
|
{ "minimal", DotNetVerbosity.Minimal },
|
||||||
|
{ "n", DotNetVerbosity.Normal },
|
||||||
|
{ "normal", DotNetVerbosity.Normal },
|
||||||
|
{ "d", DotNetVerbosity.Detailed },
|
||||||
|
{ "detailed", DotNetVerbosity.Detailed },
|
||||||
|
{ "diag", DotNetVerbosity.Diagnostic },
|
||||||
|
{ "diagnostic", DotNetVerbosity.Diagnostic }
|
||||||
|
};
|
||||||
|
return lookup.TryGetValue(verbosity, out var value) ? value : null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
namespace BenchmarkDotNet.Build.Meta;
|
||||||
|
|
||||||
|
public static class DocumentationHelper
|
||||||
|
{
|
||||||
|
public static readonly string[] BdnAllVersions =
|
||||||
|
{
|
||||||
|
"v0.7.0",
|
||||||
|
"v0.7.1",
|
||||||
|
"v0.7.2",
|
||||||
|
"v0.7.3",
|
||||||
|
"v0.7.4",
|
||||||
|
"v0.7.5",
|
||||||
|
"v0.7.6",
|
||||||
|
"v0.7.7",
|
||||||
|
"v0.7.8",
|
||||||
|
"v0.8.0",
|
||||||
|
"v0.8.1",
|
||||||
|
"v0.8.2",
|
||||||
|
"v0.9.0",
|
||||||
|
"v0.9.1",
|
||||||
|
"v0.9.2",
|
||||||
|
"v0.9.3",
|
||||||
|
"v0.9.4",
|
||||||
|
"v0.9.5",
|
||||||
|
"v0.9.6",
|
||||||
|
"v0.9.7",
|
||||||
|
"v0.9.8",
|
||||||
|
"v0.9.9",
|
||||||
|
"v0.10.0",
|
||||||
|
"v0.10.1",
|
||||||
|
"v0.10.2",
|
||||||
|
"v0.10.3",
|
||||||
|
"v0.10.4",
|
||||||
|
"v0.10.5",
|
||||||
|
"v0.10.6",
|
||||||
|
"v0.10.7",
|
||||||
|
"v0.10.8",
|
||||||
|
"v0.10.9",
|
||||||
|
"v0.10.10",
|
||||||
|
"v0.10.11",
|
||||||
|
"v0.10.12",
|
||||||
|
"v0.10.13",
|
||||||
|
"v0.10.14",
|
||||||
|
"v0.11.0",
|
||||||
|
"v0.11.1",
|
||||||
|
"v0.11.2",
|
||||||
|
"v0.11.3",
|
||||||
|
"v0.11.4",
|
||||||
|
"v0.11.5",
|
||||||
|
"v0.12.0",
|
||||||
|
"v0.12.1",
|
||||||
|
"v0.13.0",
|
||||||
|
"v0.13.1",
|
||||||
|
"v0.13.2",
|
||||||
|
"v0.13.3",
|
||||||
|
"v0.13.4",
|
||||||
|
"v0.13.5"
|
||||||
|
};
|
||||||
|
|
||||||
|
public const string BdnNextVersion = "v0.13.6";
|
||||||
|
public const string BdnFirstCommit = "6eda98ab1e83a0d185d09ff8b24c795711af8db1";
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Meta;
|
||||||
|
|
||||||
|
public static class Repo
|
||||||
|
{
|
||||||
|
public const string Owner = "dotnet";
|
||||||
|
public const string Name = "BenchmarkDotNet";
|
||||||
|
public const string HttpsUrlBase = $"https://github.com/{Owner}/{Name}";
|
||||||
|
public const string HttpsGitUrl = $"{HttpsUrlBase}.git";
|
||||||
|
public const string ChangelogDetailsBranch = "docs-changelog-details";
|
||||||
|
|
||||||
|
public const string ProductHeaderVar = "GITHUB_PRODUCT";
|
||||||
|
public const string TokenVar = "GITHUB_TOKEN";
|
||||||
|
|
||||||
|
public static string? ProductHeader => Environment.GetEnvironmentVariable(ProductHeaderVar);
|
||||||
|
public static string? Token => Environment.GetEnvironmentVariable(TokenVar);
|
||||||
|
}
|
|
@ -0,0 +1,158 @@
|
||||||
|
using Cake.Common;
|
||||||
|
using Cake.Frosting;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build;
|
||||||
|
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static int Main(string[] args)
|
||||||
|
{
|
||||||
|
var cakeArgs = CommandLineParser.Instance.Parse(args);
|
||||||
|
return cakeArgs == null
|
||||||
|
? 0
|
||||||
|
: new CakeHost().UseContext<BuildContext>().Run(cakeArgs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("Restore")]
|
||||||
|
[TaskDescription("Restore NuGet packages")]
|
||||||
|
public class RestoreTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.BuildRunner.Restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("Build")]
|
||||||
|
[TaskDescription("Build BenchmarkDotNet.sln solution")]
|
||||||
|
[IsDependentOn(typeof(RestoreTask))]
|
||||||
|
public class BuildTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.BuildRunner.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("UnitTests")]
|
||||||
|
[TaskDescription("Run unit tests (fast)")]
|
||||||
|
[IsDependentOn(typeof(BuildTask))]
|
||||||
|
public class UnitTestsTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.UnitTestRunner.RunUnitTests();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("InTestsFull")]
|
||||||
|
[TaskDescription("Run integration tests using .NET Framework 4.6.2+ (slow)")]
|
||||||
|
[IsDependentOn(typeof(BuildTask))]
|
||||||
|
public class InTestsFullTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override bool ShouldRun(BuildContext context) =>
|
||||||
|
context.IsRunningOnWindows() && !context.IsRunningOnAppVeyor;
|
||||||
|
|
||||||
|
public override void Run(BuildContext context) => context.UnitTestRunner.RunInTests("net462");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("InTestsCore")]
|
||||||
|
[TaskDescription("Run integration tests using .NET 7 (slow)")]
|
||||||
|
[IsDependentOn(typeof(BuildTask))]
|
||||||
|
public class InTestsCoreTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.UnitTestRunner.RunInTests("net7.0");
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("AllTests")]
|
||||||
|
[TaskDescription("Run all unit and integration tests (slow)")]
|
||||||
|
[IsDependentOn(typeof(UnitTestsTask))]
|
||||||
|
[IsDependentOn(typeof(InTestsFullTask))]
|
||||||
|
[IsDependentOn(typeof(InTestsCoreTask))]
|
||||||
|
public class AllTestsTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("Pack")]
|
||||||
|
[TaskDescription("Pack Nupkg packages")]
|
||||||
|
[IsDependentOn(typeof(BuildTask))]
|
||||||
|
public class PackTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.BuildRunner.Pack();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("CI")]
|
||||||
|
[TaskDescription("Perform all CI-related tasks: Restore, Build, AllTests, Pack")]
|
||||||
|
[IsDependentOn(typeof(BuildTask))]
|
||||||
|
[IsDependentOn(typeof(AllTestsTask))]
|
||||||
|
[IsDependentOn(typeof(PackTask))]
|
||||||
|
public class CiTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("DocsUpdate")]
|
||||||
|
[TaskDescription("Update generated documentation files")]
|
||||||
|
public class DocsUpdateTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.DocumentationRunner.Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("DocsPrepare")]
|
||||||
|
[TaskDescription("Prepare auxiliary documentation files")]
|
||||||
|
public class DocsPrepareTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.DocumentationRunner.Prepare();
|
||||||
|
}
|
||||||
|
|
||||||
|
// In order to work around xref issues in DocFx, BenchmarkDotNet and BenchmarkDotNet.Annotations must be build
|
||||||
|
// before running the DocFX_Build target. However, including a dependency on BuildTask here may have unwanted
|
||||||
|
// side effects (CleanTask).
|
||||||
|
// TODO: Define dependencies when a CI workflow scenario for using the "DocFX_Build" target exists.
|
||||||
|
[TaskName("DocsBuild")]
|
||||||
|
[TaskDescription("Build final documentation")]
|
||||||
|
[IsDependentOn(typeof(DocsPrepareTask))]
|
||||||
|
public class DocsBuildTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
public override void Run(BuildContext context) => context.DocumentationRunner.Build();
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("FastTests")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'UnitTests'")]
|
||||||
|
[IsDependentOn(typeof(UnitTestsTask))]
|
||||||
|
public class FastTestsTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("SlowFullFrameworkTests")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'InTestsFull'")]
|
||||||
|
[IsDependentOn(typeof(InTestsFullTask))]
|
||||||
|
public class SlowFullFrameworkTestsTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("SlowTestsNetCore")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'InTestsCore'")]
|
||||||
|
[IsDependentOn(typeof(InTestsCoreTask))]
|
||||||
|
public class SlowTestsNetCoreTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("DocFX_Changelog_Download")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'DocsUpdate'")]
|
||||||
|
[IsDependentOn(typeof(DocsUpdateTask))]
|
||||||
|
public class DocFxChangelogDownloadTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("DocFX_Changelog_Generate")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'DocsPrepare'")]
|
||||||
|
[IsDependentOn(typeof(DocsPrepareTask))]
|
||||||
|
public class DocfxChangelogGenerateTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("DocFX_Generate_Redirects")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'DocsBuild'")]
|
||||||
|
[IsDependentOn(typeof(DocsBuildTask))]
|
||||||
|
public class DocfxGenerateRedirectsTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
[TaskName("DocFX_Build")]
|
||||||
|
[TaskDescription("OBSOLETE: use 'DocsBuild'")]
|
||||||
|
[IsDependentOn(typeof(DocsBuildTask))]
|
||||||
|
public class DocfxBuildTask : FrostingTask<BuildContext>
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using Cake.Common.Build;
|
||||||
|
using Cake.Common.Diagnostics;
|
||||||
|
using Cake.Common.IO;
|
||||||
|
using Cake.Common.Tools.DotNet;
|
||||||
|
using Cake.Common.Tools.DotNet.Build;
|
||||||
|
using Cake.Common.Tools.DotNet.Pack;
|
||||||
|
using Cake.Common.Tools.DotNet.Restore;
|
||||||
|
using Cake.Core;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Runners;
|
||||||
|
|
||||||
|
public class BuildRunner
|
||||||
|
{
|
||||||
|
private readonly BuildContext context;
|
||||||
|
|
||||||
|
public BuildRunner(BuildContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Restore()
|
||||||
|
{
|
||||||
|
context.DotNetRestore(context.SolutionFile.FullPath,
|
||||||
|
new DotNetRestoreSettings
|
||||||
|
{
|
||||||
|
MSBuildSettings = context.MsBuildSettingsRestore
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Build()
|
||||||
|
{
|
||||||
|
context.Information("BuildSystemProvider: " + context.BuildSystem().Provider);
|
||||||
|
context.DotNetBuild(context.SolutionFile.FullPath, new DotNetBuildSettings
|
||||||
|
{
|
||||||
|
NoRestore = true,
|
||||||
|
DiagnosticOutput = true,
|
||||||
|
MSBuildSettings = context.MsBuildSettingsBuild,
|
||||||
|
Configuration = context.BuildConfiguration,
|
||||||
|
Verbosity = context.BuildVerbosity
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Pack()
|
||||||
|
{
|
||||||
|
context.CleanDirectory(context.ArtifactsDirectory);
|
||||||
|
|
||||||
|
var settingsSrc = new DotNetPackSettings
|
||||||
|
{
|
||||||
|
OutputDirectory = context.ArtifactsDirectory,
|
||||||
|
ArgumentCustomization = args => args.Append("--include-symbols").Append("-p:SymbolPackageFormat=snupkg"),
|
||||||
|
MSBuildSettings = context.MsBuildSettingsPack,
|
||||||
|
Configuration = context.BuildConfiguration
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var project in context.AllPackableSrcProjects)
|
||||||
|
context.DotNetPack(project.FullPath, settingsSrc);
|
||||||
|
|
||||||
|
var settingsTemplate = new DotNetPackSettings
|
||||||
|
{
|
||||||
|
OutputDirectory = context.ArtifactsDirectory,
|
||||||
|
MSBuildSettings = context.MsBuildSettingsPack,
|
||||||
|
Configuration = context.BuildConfiguration
|
||||||
|
};
|
||||||
|
context.DotNetPack(context.TemplatesTestsProjectFile.FullPath, settingsTemplate);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,168 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using BenchmarkDotNet.Build.Meta;
|
||||||
|
using Cake.Common.Diagnostics;
|
||||||
|
using Cake.Common.IO;
|
||||||
|
using Cake.Core.IO;
|
||||||
|
using Cake.FileHelpers;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Runners;
|
||||||
|
|
||||||
|
public class DocumentationRunner
|
||||||
|
{
|
||||||
|
private readonly BuildContext context;
|
||||||
|
|
||||||
|
public DocumentationRunner(BuildContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateRedirects()
|
||||||
|
{
|
||||||
|
var redirectFile = context.RedirectRootDirectory.CombineWithFilePath("_redirects");
|
||||||
|
if (!context.FileExists(redirectFile))
|
||||||
|
{
|
||||||
|
context.Error($"Redirect file '{redirectFile}' does not exist");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.EnsureDirectoryExists(context.RedirectTargetDirectory);
|
||||||
|
|
||||||
|
var redirects = context.FileReadLines(redirectFile)
|
||||||
|
.Select(line => line.Split(' '))
|
||||||
|
.Select(parts => (source: parts[0], target: parts[1]))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var (source, target) in redirects)
|
||||||
|
{
|
||||||
|
var fileName = source.StartsWith("/") || source.StartsWith("\\") ? source[1..] : source;
|
||||||
|
var fullFileName = context.RedirectTargetDirectory.CombineWithFilePath(fileName);
|
||||||
|
var content =
|
||||||
|
$"<!doctype html>" +
|
||||||
|
$"<html lang=en-us>" +
|
||||||
|
$"<head>" +
|
||||||
|
$"<title>{target}</title>" +
|
||||||
|
$"<link rel=canonical href='{target}'>" +
|
||||||
|
$"<meta name=robots content=\"noindex\">" +
|
||||||
|
$"<meta charset=utf-8><meta http-equiv=refresh content=\"0; url={target}\">" +
|
||||||
|
$"</head>" +
|
||||||
|
$"</html>";
|
||||||
|
context.EnsureDirectoryExists(fullFileName.GetDirectory());
|
||||||
|
context.FileWriteText(fullFileName, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunDocfx(FilePath docfxJson)
|
||||||
|
{
|
||||||
|
context.Information($"Running docfx for '{docfxJson}'");
|
||||||
|
|
||||||
|
var currentDirectory = Directory.GetCurrentDirectory();
|
||||||
|
Directory.SetCurrentDirectory(docfxJson.GetDirectory().FullPath);
|
||||||
|
Microsoft.DocAsCode.Dotnet.DotnetApiCatalog.GenerateManagedReferenceYamlFiles(docfxJson.FullPath).Wait();
|
||||||
|
Microsoft.DocAsCode.Docset.Build(docfxJson.FullPath).Wait();
|
||||||
|
Directory.SetCurrentDirectory(currentDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GenerateIndexMd()
|
||||||
|
{
|
||||||
|
context.Information("DocsBuild: Generate index.md");
|
||||||
|
var content = new StringBuilder();
|
||||||
|
content.AppendLine("---");
|
||||||
|
content.AppendLine("title: Home");
|
||||||
|
content.AppendLine("---");
|
||||||
|
content.Append(context.FileReadText(context.RootDirectory.CombineWithFilePath("README.md")));
|
||||||
|
context.FileWriteText(context.DocsDirectory.CombineWithFilePath("index.md"), content.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
context.EnsureChangelogDetailsExist();
|
||||||
|
|
||||||
|
ReadmeUpdater.Run(context);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Repo.ProductHeader))
|
||||||
|
throw new Exception($"Environment variable '{Repo.ProductHeaderVar}' is not specified!");
|
||||||
|
if (string.IsNullOrEmpty(Repo.Token))
|
||||||
|
throw new Exception($"Environment variable '{Repo.TokenVar}' is not specified!");
|
||||||
|
|
||||||
|
var count = context.Depth;
|
||||||
|
var total = DocumentationHelper.BdnAllVersions.Length;
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
{
|
||||||
|
context.DocfxChangelogDownload(
|
||||||
|
DocumentationHelper.BdnAllVersions.First(),
|
||||||
|
DocumentationHelper.BdnFirstCommit);
|
||||||
|
|
||||||
|
for (int i = 1; i < total; i++)
|
||||||
|
context.DocfxChangelogDownload(
|
||||||
|
DocumentationHelper.BdnAllVersions[i],
|
||||||
|
DocumentationHelper.BdnAllVersions[i - 1]);
|
||||||
|
}
|
||||||
|
else if (count > 0)
|
||||||
|
{
|
||||||
|
for (int i = Math.Max(total - count, 1); i < total; i++)
|
||||||
|
context.DocfxChangelogDownload(
|
||||||
|
DocumentationHelper.BdnAllVersions[i],
|
||||||
|
DocumentationHelper.BdnAllVersions[i - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.DocfxChangelogDownload(
|
||||||
|
DocumentationHelper.BdnNextVersion,
|
||||||
|
DocumentationHelper.BdnAllVersions.Last(),
|
||||||
|
"HEAD");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Prepare()
|
||||||
|
{
|
||||||
|
foreach (var version in DocumentationHelper.BdnAllVersions)
|
||||||
|
context.DocfxChangelogGenerate(version);
|
||||||
|
context.DocfxChangelogGenerate(DocumentationHelper.BdnNextVersion);
|
||||||
|
|
||||||
|
context.Information("DocfxChangelogGenerate: index.md");
|
||||||
|
var indexContent = new StringBuilder();
|
||||||
|
indexContent.AppendLine("---");
|
||||||
|
indexContent.AppendLine("uid: changelog");
|
||||||
|
indexContent.AppendLine("---");
|
||||||
|
indexContent.AppendLine("");
|
||||||
|
indexContent.AppendLine("# ChangeLog");
|
||||||
|
indexContent.AppendLine("");
|
||||||
|
foreach (var version in DocumentationHelper.BdnAllVersions.Reverse())
|
||||||
|
indexContent.AppendLine($"* @changelog.{version}");
|
||||||
|
indexContent.AppendLine("* @changelog.full");
|
||||||
|
context.FileWriteText(context.ChangeLogDirectory.CombineWithFilePath("index.md"), indexContent.ToString());
|
||||||
|
|
||||||
|
context.Information("DocfxChangelogGenerate: full.md");
|
||||||
|
var fullContent = new StringBuilder();
|
||||||
|
fullContent.AppendLine("---");
|
||||||
|
fullContent.AppendLine("uid: changelog.full");
|
||||||
|
fullContent.AppendLine("---");
|
||||||
|
fullContent.AppendLine("");
|
||||||
|
fullContent.AppendLine("# Full ChangeLog");
|
||||||
|
fullContent.AppendLine("");
|
||||||
|
foreach (var version in DocumentationHelper.BdnAllVersions.Reverse())
|
||||||
|
fullContent.AppendLine($"[!include[{version}]({version}.md)]");
|
||||||
|
context.FileWriteText(context.ChangeLogDirectory.CombineWithFilePath("full.md"), fullContent.ToString());
|
||||||
|
|
||||||
|
context.Information("DocfxChangelogGenerate: toc.yml");
|
||||||
|
var tocContent = new StringBuilder();
|
||||||
|
foreach (var version in DocumentationHelper.BdnAllVersions.Reverse())
|
||||||
|
{
|
||||||
|
tocContent.AppendLine($"- name: {version}");
|
||||||
|
tocContent.AppendLine($" href: {version}.md");
|
||||||
|
}
|
||||||
|
|
||||||
|
tocContent.AppendLine("- name: Full ChangeLog");
|
||||||
|
tocContent.AppendLine(" href: full.md");
|
||||||
|
context.FileWriteText(context.ChangeLogDirectory.CombineWithFilePath("toc.yml"), tocContent.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Build()
|
||||||
|
{
|
||||||
|
GenerateIndexMd();
|
||||||
|
RunDocfx(context.DocfxJsonFile);
|
||||||
|
GenerateRedirects();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using BenchmarkDotNet.Build.Meta;
|
||||||
|
using Cake.FileHelpers;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Runners;
|
||||||
|
|
||||||
|
public class ReadmeUpdater
|
||||||
|
{
|
||||||
|
public static void Run(BuildContext context) => new ReadmeUpdater().RunInternal(context);
|
||||||
|
|
||||||
|
private void RunInternal(BuildContext context)
|
||||||
|
{
|
||||||
|
var dependentProjectsNumber = GetDependentProjectsNumber().Result;
|
||||||
|
var updaters = new LineUpdater[]
|
||||||
|
{
|
||||||
|
new(
|
||||||
|
"The library is adopted by",
|
||||||
|
@"\[(\d+)\+ GitHub projects\]",
|
||||||
|
dependentProjectsNumber
|
||||||
|
),
|
||||||
|
new(
|
||||||
|
"BenchmarkDotNet is already adopted by more than ",
|
||||||
|
@"\[(\d+)\+\]",
|
||||||
|
dependentProjectsNumber
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
var file = context.RootDirectory.CombineWithFilePath("README.md");
|
||||||
|
var lines = context.FileReadLines(file);
|
||||||
|
for (var i = 0; i < lines.Length; i++)
|
||||||
|
{
|
||||||
|
foreach (var updater in updaters)
|
||||||
|
lines[i] = updater.Apply(lines[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
context.FileWriteLines(file, lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<int> GetDependentProjectsNumber()
|
||||||
|
{
|
||||||
|
using var httpClient = new HttpClient();
|
||||||
|
const string url = $"{Repo.HttpsUrlBase}/network/dependents";
|
||||||
|
var response = await httpClient.GetAsync(new Uri(url));
|
||||||
|
var dependentsPage = await response.Content.ReadAsStringAsync();
|
||||||
|
var match = new Regex(@"([0-9\,]+)[\n\r\s]+Repositories").Match(dependentsPage);
|
||||||
|
var number = int.Parse(match.Groups[1].Value.Replace(",", ""));
|
||||||
|
number = number / 100 * 100;
|
||||||
|
return number;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class LineUpdater
|
||||||
|
{
|
||||||
|
public string Prefix { get; }
|
||||||
|
public Regex Regex { get; }
|
||||||
|
public int Value { get; }
|
||||||
|
|
||||||
|
public LineUpdater(string prefix, string regex, int value)
|
||||||
|
{
|
||||||
|
Prefix = prefix;
|
||||||
|
Regex = new Regex(regex);
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Apply(string line)
|
||||||
|
{
|
||||||
|
if (!line.StartsWith(Prefix))
|
||||||
|
return line;
|
||||||
|
|
||||||
|
var match = Regex.Match(line);
|
||||||
|
if (!match.Success)
|
||||||
|
return line;
|
||||||
|
|
||||||
|
// Groups[1] refers to the first group (\d+)
|
||||||
|
var numberString = match.Groups[1].Value;
|
||||||
|
var number = int.Parse(numberString);
|
||||||
|
return line.Replace(number.ToString(), Value.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
using BenchmarkDotNet.Build.Helpers;
|
||||||
|
using Cake.Common;
|
||||||
|
using Cake.Common.Diagnostics;
|
||||||
|
using Cake.Common.Tools.DotNet;
|
||||||
|
using Cake.Common.Tools.DotNet.Test;
|
||||||
|
using Cake.Core.IO;
|
||||||
|
|
||||||
|
namespace BenchmarkDotNet.Build.Runners;
|
||||||
|
|
||||||
|
public class UnitTestRunner
|
||||||
|
{
|
||||||
|
private readonly BuildContext context;
|
||||||
|
|
||||||
|
private FilePath UnitTestsProjectFile { get; }
|
||||||
|
private FilePath IntegrationTestsProjectFile { get; }
|
||||||
|
private DirectoryPath TestOutputDirectory { get; }
|
||||||
|
|
||||||
|
public UnitTestRunner(BuildContext context)
|
||||||
|
{
|
||||||
|
this.context = context;
|
||||||
|
UnitTestsProjectFile = context.RootDirectory
|
||||||
|
.Combine("tests")
|
||||||
|
.Combine("BenchmarkDotNet.Tests")
|
||||||
|
.CombineWithFilePath("BenchmarkDotNet.Tests.csproj");
|
||||||
|
IntegrationTestsProjectFile = context.RootDirectory
|
||||||
|
.Combine("tests")
|
||||||
|
.Combine("BenchmarkDotNet.IntegrationTests")
|
||||||
|
.CombineWithFilePath("BenchmarkDotNet.IntegrationTests.csproj");
|
||||||
|
TestOutputDirectory = context.RootDirectory
|
||||||
|
.Combine("TestResults");
|
||||||
|
}
|
||||||
|
|
||||||
|
private DotNetTestSettings GetTestSettingsParameters(FilePath logFile, string tfm)
|
||||||
|
{
|
||||||
|
var settings = new DotNetTestSettings
|
||||||
|
{
|
||||||
|
Configuration = context.BuildConfiguration,
|
||||||
|
Framework = tfm,
|
||||||
|
NoBuild = true,
|
||||||
|
NoRestore = true,
|
||||||
|
Loggers = new[] { "trx", $"trx;LogFileName={logFile.FullPath}", "console;verbosity=detailed" },
|
||||||
|
EnvironmentVariables =
|
||||||
|
{
|
||||||
|
["Platform"] = "" // force the tool to not look for the .dll in platform-specific directory
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunTests(FilePath projectFile, string alias, string tfm)
|
||||||
|
{
|
||||||
|
var os = Utils.GetOs();
|
||||||
|
var trxFileName = $"{os}-{alias}-{tfm}.trx";
|
||||||
|
var trxFile = TestOutputDirectory.CombineWithFilePath(trxFileName);
|
||||||
|
var settings = GetTestSettingsParameters(trxFile, tfm);
|
||||||
|
|
||||||
|
context.Information($"Run tests for {projectFile} ({tfm}), result file: '{trxFile}'");
|
||||||
|
context.DotNetTest(projectFile.FullPath, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RunUnitTests(string tfm) => RunTests(UnitTestsProjectFile, "unit", tfm);
|
||||||
|
|
||||||
|
public void RunUnitTests()
|
||||||
|
{
|
||||||
|
var targetFrameworks = context.IsRunningOnWindows()
|
||||||
|
? new[] { "net462", "net7.0" }
|
||||||
|
: new[] { "net7.0" };
|
||||||
|
|
||||||
|
foreach (var targetFramework in targetFrameworks)
|
||||||
|
RunUnitTests(targetFramework);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunInTests(string tfm) => RunTests(IntegrationTestsProjectFile, "integration", tfm);
|
||||||
|
}
|
661
build/Program.cs
661
build/Program.cs
|
@ -1,661 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Build;
|
|
||||||
using Cake.Common;
|
|
||||||
using Cake.Common.Build;
|
|
||||||
using Cake.Common.Build.AppVeyor;
|
|
||||||
using Cake.Common.Diagnostics;
|
|
||||||
using Cake.Common.IO;
|
|
||||||
using Cake.Common.Tools.DotNet;
|
|
||||||
using Cake.Common.Tools.DotNet.Build;
|
|
||||||
using Cake.Common.Tools.DotNet.MSBuild;
|
|
||||||
using Cake.Common.Tools.DotNet.Pack;
|
|
||||||
using Cake.Common.Tools.DotNet.Restore;
|
|
||||||
using Cake.Common.Tools.DotNet.Test;
|
|
||||||
using Cake.Core;
|
|
||||||
using Cake.Core.IO;
|
|
||||||
using Cake.FileHelpers;
|
|
||||||
using Cake.Frosting;
|
|
||||||
using Cake.Git;
|
|
||||||
|
|
||||||
public static class Program
|
|
||||||
{
|
|
||||||
public static int Main(string[] args)
|
|
||||||
{
|
|
||||||
return new CakeHost()
|
|
||||||
.UseContext<BuildContext>()
|
|
||||||
.Run(args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BuildContext : FrostingContext
|
|
||||||
{
|
|
||||||
public string BuildConfiguration { get; set; }
|
|
||||||
public bool SkipTests { get; set; }
|
|
||||||
public bool SkipSlowTests { get; set; }
|
|
||||||
public string TargetVersion { get; set; }
|
|
||||||
|
|
||||||
public DirectoryPath RootDirectory { get; }
|
|
||||||
public DirectoryPath ArtifactsDirectory { get; }
|
|
||||||
public DirectoryPath ToolsDirectory { get; }
|
|
||||||
public DirectoryPath DocsDirectory { get; }
|
|
||||||
public FilePath DocfxJsonFile { get; }
|
|
||||||
public DirectoryPath TestOutputDirectory { get; }
|
|
||||||
|
|
||||||
public DirectoryPath ChangeLogDirectory { get; }
|
|
||||||
public DirectoryPath ChangeLogGenDirectory { get; }
|
|
||||||
|
|
||||||
public DirectoryPath RedirectRootDirectory { get; }
|
|
||||||
public DirectoryPath RedirectTargetDirectory { get; }
|
|
||||||
|
|
||||||
public FilePath SolutionFile { get; }
|
|
||||||
public FilePath UnitTestsProjectFile { get; }
|
|
||||||
public FilePath IntegrationTestsProjectFile { get; }
|
|
||||||
public FilePath TemplatesTestsProjectFile { get; }
|
|
||||||
public FilePathCollection AllPackableSrcProjects { get; }
|
|
||||||
|
|
||||||
public DotNetMSBuildSettings MsBuildSettingsRestore { get; }
|
|
||||||
public DotNetMSBuildSettings MsBuildSettingsBuild { get; }
|
|
||||||
public DotNetMSBuildSettings MsBuildSettingsPack { get; }
|
|
||||||
|
|
||||||
private IAppVeyorProvider AppVeyor => this.BuildSystem().AppVeyor;
|
|
||||||
public bool IsRunningOnAppVeyor => AppVeyor.IsRunningOnAppVeyor;
|
|
||||||
public bool IsOnAppVeyorAndNotPr => IsRunningOnAppVeyor && !AppVeyor.Environment.PullRequest.IsPullRequest;
|
|
||||||
|
|
||||||
public bool IsOnAppVeyorAndBdnNightlyCiCd => IsOnAppVeyorAndNotPr &&
|
|
||||||
AppVeyor.Environment.Repository.Branch == "master" &&
|
|
||||||
this.IsRunningOnWindows();
|
|
||||||
|
|
||||||
public bool IsLocalBuild => this.BuildSystem().IsLocalBuild;
|
|
||||||
public bool IsCiBuild => !this.BuildSystem().IsLocalBuild;
|
|
||||||
|
|
||||||
public BuildContext(ICakeContext context)
|
|
||||||
: base(context)
|
|
||||||
{
|
|
||||||
BuildConfiguration = context.Argument("Configuration", "Release");
|
|
||||||
SkipTests = context.Argument("SkipTests", false);
|
|
||||||
SkipSlowTests = context.Argument("SkipSlowTests", false);
|
|
||||||
TargetVersion = context.Argument("Version", "");
|
|
||||||
|
|
||||||
RootDirectory = new DirectoryPath(new DirectoryInfo(Directory.GetCurrentDirectory()).Parent.FullName);
|
|
||||||
ArtifactsDirectory = RootDirectory.Combine("artifacts");
|
|
||||||
ToolsDirectory = RootDirectory.Combine("tools");
|
|
||||||
DocsDirectory = RootDirectory.Combine("docs");
|
|
||||||
DocfxJsonFile = DocsDirectory.CombineWithFilePath("docfx.json");
|
|
||||||
TestOutputDirectory = RootDirectory.Combine("TestResults");
|
|
||||||
|
|
||||||
ChangeLogDirectory = RootDirectory.Combine("docs").Combine("changelog");
|
|
||||||
ChangeLogGenDirectory = RootDirectory.Combine("docs").Combine("_changelog");
|
|
||||||
|
|
||||||
RedirectRootDirectory = RootDirectory.Combine("docs").Combine("_redirects");
|
|
||||||
RedirectTargetDirectory = RootDirectory.Combine("docs").Combine("_site");
|
|
||||||
|
|
||||||
SolutionFile = RootDirectory.CombineWithFilePath("BenchmarkDotNet.sln");
|
|
||||||
UnitTestsProjectFile = RootDirectory.Combine("tests").Combine("BenchmarkDotNet.Tests")
|
|
||||||
.CombineWithFilePath("BenchmarkDotNet.Tests.csproj");
|
|
||||||
IntegrationTestsProjectFile = RootDirectory.Combine("tests").Combine("BenchmarkDotNet.IntegrationTests")
|
|
||||||
.CombineWithFilePath("BenchmarkDotNet.IntegrationTests.csproj");
|
|
||||||
TemplatesTestsProjectFile = RootDirectory.Combine("templates")
|
|
||||||
.CombineWithFilePath("BenchmarkDotNet.Templates.csproj");
|
|
||||||
AllPackableSrcProjects = new FilePathCollection(context.GetFiles(RootDirectory.FullPath + "/src/**/*.csproj")
|
|
||||||
.Where(p => !p.FullPath.Contains("Disassembler")));
|
|
||||||
|
|
||||||
MsBuildSettingsRestore = new DotNetMSBuildSettings();
|
|
||||||
MsBuildSettingsBuild = new DotNetMSBuildSettings();
|
|
||||||
MsBuildSettingsPack = new DotNetMSBuildSettings();
|
|
||||||
|
|
||||||
if (IsCiBuild)
|
|
||||||
{
|
|
||||||
System.Environment.SetEnvironmentVariable("BDN_CI_BUILD", "true");
|
|
||||||
|
|
||||||
MsBuildSettingsBuild.MaxCpuCount = 1;
|
|
||||||
MsBuildSettingsBuild.WithProperty("UseSharedCompilation", "false");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(TargetVersion))
|
|
||||||
{
|
|
||||||
MsBuildSettingsRestore.WithProperty("Version", TargetVersion);
|
|
||||||
MsBuildSettingsBuild.WithProperty("Version", TargetVersion);
|
|
||||||
MsBuildSettingsPack.WithProperty("Version", TargetVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NativeAOT build requires VS C++ tools to be added to $path via vcvars64.bat
|
|
||||||
// but once we do that, dotnet restore fails with:
|
|
||||||
// "Please specify a valid solution configuration using the Configuration and Platform properties"
|
|
||||||
if (context.IsRunningOnWindows())
|
|
||||||
{
|
|
||||||
MsBuildSettingsRestore.WithProperty("Platform", "Any CPU");
|
|
||||||
MsBuildSettingsBuild.WithProperty("Platform", "Any CPU");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private DotNetTestSettings GetTestSettingsParameters(FilePath logFile, string tfm)
|
|
||||||
{
|
|
||||||
var settings = new DotNetTestSettings
|
|
||||||
{
|
|
||||||
Configuration = BuildConfiguration,
|
|
||||||
Framework = tfm,
|
|
||||||
NoBuild = true,
|
|
||||||
NoRestore = true,
|
|
||||||
Loggers = new[] { "trx", $"trx;LogFileName={logFile.FullPath}", "console;verbosity=detailed" }
|
|
||||||
};
|
|
||||||
// force the tool to not look for the .dll in platform-specific directory
|
|
||||||
settings.EnvironmentVariables["Platform"] = "";
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RunTests(FilePath projectFile, string alias, string tfm)
|
|
||||||
{
|
|
||||||
var xUnitXmlFile = TestOutputDirectory.CombineWithFilePath(alias + "-" + tfm + ".trx");
|
|
||||||
this.Information($"Run tests for {projectFile} ({tfm}), result file: '{xUnitXmlFile}'");
|
|
||||||
var settings = GetTestSettingsParameters(xUnitXmlFile, tfm);
|
|
||||||
this.DotNetTest(projectFile.FullPath, settings);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EnsureChangelogDetailsExist(bool forceClean = false)
|
|
||||||
{
|
|
||||||
var path = ChangeLogGenDirectory.Combine("details");
|
|
||||||
if (this.DirectoryExists(path) && forceClean)
|
|
||||||
this.DeleteDirectory(path, new DeleteDirectorySettings() { Force = true, Recursive = true });
|
|
||||||
|
|
||||||
if (!this.DirectoryExists(path))
|
|
||||||
{
|
|
||||||
var settings = new GitCloneSettings { Checkout = true, BranchName = "docs-changelog-details" };
|
|
||||||
this.GitClone("https://github.com/dotnet/BenchmarkDotNet.git", path, settings);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DocfxChangelogDownload(string version, string versionPrevious, string lastCommit = "")
|
|
||||||
{
|
|
||||||
EnsureChangelogDetailsExist(true);
|
|
||||||
this.Information("DocfxChangelogDownload: " + version);
|
|
||||||
// Required environment variables: GITHUB_PRODUCT, GITHUB_TOKEN
|
|
||||||
var path = ChangeLogGenDirectory.Combine("details");
|
|
||||||
ChangeLogBuilder.Run(path, version, versionPrevious, lastCommit).Wait();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DocfxChangelogGenerate(string version)
|
|
||||||
{
|
|
||||||
EnsureChangelogDetailsExist();
|
|
||||||
this.Information("DocfxChangelogGenerate: " + version);
|
|
||||||
var header = ChangeLogGenDirectory.Combine("header").CombineWithFilePath(version + ".md");
|
|
||||||
var footer = ChangeLogGenDirectory.Combine("footer").CombineWithFilePath(version + ".md");
|
|
||||||
var details = ChangeLogGenDirectory.Combine("details").CombineWithFilePath(version + ".md");
|
|
||||||
var release = ChangeLogDirectory.CombineWithFilePath(version + ".md");
|
|
||||||
|
|
||||||
var content = new StringBuilder();
|
|
||||||
content.AppendLine("---");
|
|
||||||
content.AppendLine("uid: changelog." + version);
|
|
||||||
content.AppendLine("---");
|
|
||||||
content.AppendLine("");
|
|
||||||
content.AppendLine("# BenchmarkDotNet " + version);
|
|
||||||
content.AppendLine("");
|
|
||||||
content.AppendLine("");
|
|
||||||
|
|
||||||
if (this.FileExists(header))
|
|
||||||
{
|
|
||||||
content.AppendLine(this.FileReadText(header));
|
|
||||||
content.AppendLine("");
|
|
||||||
content.AppendLine("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.FileExists(details))
|
|
||||||
{
|
|
||||||
content.AppendLine(this.FileReadText(details));
|
|
||||||
content.AppendLine("");
|
|
||||||
content.AppendLine("");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.FileExists(footer))
|
|
||||||
{
|
|
||||||
content.AppendLine("## Additional details");
|
|
||||||
content.AppendLine("");
|
|
||||||
content.AppendLine(this.FileReadText(footer));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.FileWriteText(release, content.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RunDocfx(FilePath docfxJson)
|
|
||||||
{
|
|
||||||
this.Information($"Running docfx for '{docfxJson}'");
|
|
||||||
|
|
||||||
var currentDirectory = Directory.GetCurrentDirectory();
|
|
||||||
Directory.SetCurrentDirectory(docfxJson.GetDirectory().FullPath);
|
|
||||||
Microsoft.DocAsCode.Dotnet.DotnetApiCatalog.GenerateManagedReferenceYamlFiles(docfxJson.FullPath).Wait();
|
|
||||||
Microsoft.DocAsCode.Docset.Build(docfxJson.FullPath).Wait();
|
|
||||||
Directory.SetCurrentDirectory(currentDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GenerateRedirects()
|
|
||||||
{
|
|
||||||
var redirectFile = RedirectRootDirectory.CombineWithFilePath("_redirects");
|
|
||||||
if (!this.FileExists(redirectFile))
|
|
||||||
{
|
|
||||||
this.Error($"Redirect file '{redirectFile}' does not exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.EnsureDirectoryExists(RedirectTargetDirectory);
|
|
||||||
|
|
||||||
var redirects = this.FileReadLines(redirectFile)
|
|
||||||
.Select(line => line.Split(' '))
|
|
||||||
.Select(parts => (source: parts[0], target: parts[1]))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
foreach (var (source, target) in redirects)
|
|
||||||
{
|
|
||||||
var fileName = source.StartsWith("/") || source.StartsWith("\\") ? source[1..] : source;
|
|
||||||
var fullFileName = RedirectTargetDirectory.CombineWithFilePath(fileName);
|
|
||||||
var content =
|
|
||||||
$"<!doctype html>" +
|
|
||||||
$"<html lang=en-us>" +
|
|
||||||
$"<head>" +
|
|
||||||
$"<title>{target}</title>" +
|
|
||||||
$"<link rel=canonical href='{target}'>" +
|
|
||||||
$"<meta name=robots content=\"noindex\">" +
|
|
||||||
$"<meta charset=utf-8><meta http-equiv=refresh content=\"0; url={target}\">" +
|
|
||||||
$"</head>" +
|
|
||||||
$"</html>";
|
|
||||||
this.EnsureDirectoryExists(fullFileName.GetDirectory());
|
|
||||||
this.FileWriteText(fullFileName, content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DocumentationHelper
|
|
||||||
{
|
|
||||||
public static readonly string[] BdnAllVersions =
|
|
||||||
{
|
|
||||||
"v0.7.0",
|
|
||||||
"v0.7.1",
|
|
||||||
"v0.7.2",
|
|
||||||
"v0.7.3",
|
|
||||||
"v0.7.4",
|
|
||||||
"v0.7.5",
|
|
||||||
"v0.7.6",
|
|
||||||
"v0.7.7",
|
|
||||||
"v0.7.8",
|
|
||||||
"v0.8.0",
|
|
||||||
"v0.8.1",
|
|
||||||
"v0.8.2",
|
|
||||||
"v0.9.0",
|
|
||||||
"v0.9.1",
|
|
||||||
"v0.9.2",
|
|
||||||
"v0.9.3",
|
|
||||||
"v0.9.4",
|
|
||||||
"v0.9.5",
|
|
||||||
"v0.9.6",
|
|
||||||
"v0.9.7",
|
|
||||||
"v0.9.8",
|
|
||||||
"v0.9.9",
|
|
||||||
"v0.10.0",
|
|
||||||
"v0.10.1",
|
|
||||||
"v0.10.2",
|
|
||||||
"v0.10.3",
|
|
||||||
"v0.10.4",
|
|
||||||
"v0.10.5",
|
|
||||||
"v0.10.6",
|
|
||||||
"v0.10.7",
|
|
||||||
"v0.10.8",
|
|
||||||
"v0.10.9",
|
|
||||||
"v0.10.10",
|
|
||||||
"v0.10.11",
|
|
||||||
"v0.10.12",
|
|
||||||
"v0.10.13",
|
|
||||||
"v0.10.14",
|
|
||||||
"v0.11.0",
|
|
||||||
"v0.11.1",
|
|
||||||
"v0.11.2",
|
|
||||||
"v0.11.3",
|
|
||||||
"v0.11.4",
|
|
||||||
"v0.11.5",
|
|
||||||
"v0.12.0",
|
|
||||||
"v0.12.1",
|
|
||||||
"v0.13.0",
|
|
||||||
"v0.13.1",
|
|
||||||
"v0.13.2",
|
|
||||||
"v0.13.3",
|
|
||||||
"v0.13.4",
|
|
||||||
"v0.13.5"
|
|
||||||
};
|
|
||||||
|
|
||||||
public const string BdnNextVersion = "v0.13.6";
|
|
||||||
public const string BdnFirstCommit = "6eda98ab1e83a0d185d09ff8b24c795711af8db1";
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("Clean")]
|
|
||||||
public class CleanTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.CleanDirectory(context.ArtifactsDirectory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("Restore")]
|
|
||||||
[IsDependentOn(typeof(CleanTask))]
|
|
||||||
public class RestoreTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.DotNetRestore(context.SolutionFile.FullPath,
|
|
||||||
new DotNetRestoreSettings
|
|
||||||
{
|
|
||||||
MSBuildSettings = context.MsBuildSettingsRestore
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("Build")]
|
|
||||||
[IsDependentOn(typeof(RestoreTask))]
|
|
||||||
public class BuildTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.Information("BuildSystemProvider: " + context.BuildSystem().Provider);
|
|
||||||
context.DotNetBuild(context.SolutionFile.FullPath, new DotNetBuildSettings
|
|
||||||
{
|
|
||||||
Configuration = context.BuildConfiguration,
|
|
||||||
NoRestore = true,
|
|
||||||
DiagnosticOutput = true,
|
|
||||||
MSBuildSettings = context.MsBuildSettingsBuild,
|
|
||||||
Verbosity = DotNetVerbosity.Minimal
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("FastTests")]
|
|
||||||
[IsDependentOn(typeof(BuildTask))]
|
|
||||||
public class FastTestsTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override bool ShouldRun(BuildContext context)
|
|
||||||
{
|
|
||||||
return !context.SkipTests;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
var targetFrameworks = context.IsRunningOnWindows()
|
|
||||||
? new[] { "net462", "net7.0" }
|
|
||||||
: new[] { "net7.0" };
|
|
||||||
|
|
||||||
foreach (var targetFramework in targetFrameworks)
|
|
||||||
context.RunTests(context.UnitTestsProjectFile, "UnitTests", targetFramework);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("SlowFullFrameworkTests")]
|
|
||||||
[IsDependentOn(typeof(BuildTask))]
|
|
||||||
public class SlowFullFrameworkTestsTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override bool ShouldRun(BuildContext context)
|
|
||||||
{
|
|
||||||
return !context.SkipTests && !context.SkipSlowTests && context.IsRunningOnWindows() &&
|
|
||||||
!context.IsRunningOnAppVeyor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.RunTests(context.IntegrationTestsProjectFile, "IntegrationTests", "net462");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("SlowTestsNetCore")]
|
|
||||||
[IsDependentOn(typeof(BuildTask))]
|
|
||||||
public class SlowTestsNetCoreTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override bool ShouldRun(BuildContext context)
|
|
||||||
{
|
|
||||||
return !context.SkipTests && !context.SkipSlowTests;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.RunTests(context.IntegrationTestsProjectFile, "IntegrationTests", "net7.0");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("AllTests")]
|
|
||||||
[IsDependentOn(typeof(FastTestsTask))]
|
|
||||||
[IsDependentOn(typeof(SlowFullFrameworkTestsTask))]
|
|
||||||
[IsDependentOn(typeof(SlowTestsNetCoreTask))]
|
|
||||||
public class AllTestsTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("Pack")]
|
|
||||||
[IsDependentOn(typeof(BuildTask))]
|
|
||||||
public class PackTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override bool ShouldRun(BuildContext context)
|
|
||||||
{
|
|
||||||
return context.IsOnAppVeyorAndBdnNightlyCiCd || context.IsLocalBuild;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
var settingsSrc = new DotNetPackSettings
|
|
||||||
{
|
|
||||||
Configuration = context.BuildConfiguration,
|
|
||||||
OutputDirectory = context.ArtifactsDirectory.FullPath,
|
|
||||||
ArgumentCustomization = args => args.Append("--include-symbols").Append("-p:SymbolPackageFormat=snupkg"),
|
|
||||||
MSBuildSettings = context.MsBuildSettingsPack
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var project in context.AllPackableSrcProjects)
|
|
||||||
context.DotNetPack(project.FullPath, settingsSrc);
|
|
||||||
|
|
||||||
var settingsTemplate = new DotNetPackSettings
|
|
||||||
{
|
|
||||||
Configuration = context.BuildConfiguration,
|
|
||||||
OutputDirectory = context.ArtifactsDirectory.FullPath
|
|
||||||
};
|
|
||||||
context.DotNetPack(context.TemplatesTestsProjectFile.FullPath, settingsTemplate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("Default")]
|
|
||||||
[IsDependentOn(typeof(AllTestsTask))]
|
|
||||||
[IsDependentOn(typeof(PackTask))]
|
|
||||||
public class DefaultTask : FrostingTask
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[TaskName("DocFX_Changelog_Download")]
|
|
||||||
public class DocFxChangelogDownloadTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
var count = context.Argument("VersionCount", -1);
|
|
||||||
var total = DocumentationHelper.BdnAllVersions.Length;
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
{
|
|
||||||
context.DocfxChangelogDownload(
|
|
||||||
DocumentationHelper.BdnAllVersions.First(),
|
|
||||||
DocumentationHelper.BdnFirstCommit);
|
|
||||||
|
|
||||||
for (int i = 1; i < total; i++)
|
|
||||||
context.DocfxChangelogDownload(
|
|
||||||
DocumentationHelper.BdnAllVersions[i],
|
|
||||||
DocumentationHelper.BdnAllVersions[i - 1]);
|
|
||||||
}
|
|
||||||
else if (count > 0)
|
|
||||||
{
|
|
||||||
for (int i = Math.Max(total - count, 1); i < total; i++)
|
|
||||||
context.DocfxChangelogDownload(
|
|
||||||
DocumentationHelper.BdnAllVersions[i],
|
|
||||||
DocumentationHelper.BdnAllVersions[i - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.DocfxChangelogDownload(
|
|
||||||
DocumentationHelper.BdnNextVersion,
|
|
||||||
DocumentationHelper.BdnAllVersions.Last(),
|
|
||||||
"HEAD");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("DocFX_Changelog_Generate")]
|
|
||||||
public class DocfxChangelogGenerateTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
foreach (var version in DocumentationHelper.BdnAllVersions)
|
|
||||||
context.DocfxChangelogGenerate(version);
|
|
||||||
context.DocfxChangelogGenerate(DocumentationHelper.BdnNextVersion);
|
|
||||||
|
|
||||||
context.Information("DocfxChangelogGenerate: index.md");
|
|
||||||
var indexContent = new StringBuilder();
|
|
||||||
indexContent.AppendLine("---");
|
|
||||||
indexContent.AppendLine("uid: changelog");
|
|
||||||
indexContent.AppendLine("---");
|
|
||||||
indexContent.AppendLine("");
|
|
||||||
indexContent.AppendLine("# ChangeLog");
|
|
||||||
indexContent.AppendLine("");
|
|
||||||
foreach (var version in DocumentationHelper.BdnAllVersions.Reverse())
|
|
||||||
indexContent.AppendLine($"* @changelog.{version}");
|
|
||||||
indexContent.AppendLine("* @changelog.full");
|
|
||||||
context.FileWriteText(context.ChangeLogDirectory.CombineWithFilePath("index.md"), indexContent.ToString());
|
|
||||||
|
|
||||||
context.Information("DocfxChangelogGenerate: full.md");
|
|
||||||
var fullContent = new StringBuilder();
|
|
||||||
fullContent.AppendLine("---");
|
|
||||||
fullContent.AppendLine("uid: changelog.full");
|
|
||||||
fullContent.AppendLine("---");
|
|
||||||
fullContent.AppendLine("");
|
|
||||||
fullContent.AppendLine("# Full ChangeLog");
|
|
||||||
fullContent.AppendLine("");
|
|
||||||
foreach (var version in DocumentationHelper.BdnAllVersions.Reverse())
|
|
||||||
fullContent.AppendLine($"[!include[{version}]({version}.md)]");
|
|
||||||
context.FileWriteText(context.ChangeLogDirectory.CombineWithFilePath("full.md"), fullContent.ToString());
|
|
||||||
|
|
||||||
context.Information("DocfxChangelogGenerate: toc.yml");
|
|
||||||
var tocContent = new StringBuilder();
|
|
||||||
foreach (var version in DocumentationHelper.BdnAllVersions.Reverse())
|
|
||||||
{
|
|
||||||
tocContent.AppendLine($"- name: {version}");
|
|
||||||
tocContent.AppendLine($" href: {version}.md");
|
|
||||||
}
|
|
||||||
|
|
||||||
tocContent.AppendLine("- name: Full ChangeLog");
|
|
||||||
tocContent.AppendLine(" href: full.md");
|
|
||||||
context.FileWriteText(context.ChangeLogDirectory.CombineWithFilePath("toc.yml"), tocContent.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("DocFX_Generate_Redirects")]
|
|
||||||
public class DocfxGenerateRedirectsTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.GenerateRedirects();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// In order to work around xref issues in DocFx, BenchmarkDotNet and BenchmarkDotNet.Annotations must be build
|
|
||||||
// before running the DocFX_Build target. However, including a dependency on BuildTask here may have unwanted
|
|
||||||
// side effects (CleanTask).
|
|
||||||
// TODO: Define dependencies when a CI workflow scenario for using the "DocFX_Build" target exists.
|
|
||||||
[TaskName("DocFX_Build")]
|
|
||||||
[IsDependentOn(typeof(DocfxChangelogGenerateTask))]
|
|
||||||
public class DocfxBuildTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
context.Information("DocfxBuild: Generate index.md");
|
|
||||||
var content = new StringBuilder();
|
|
||||||
content.AppendLine("---");
|
|
||||||
content.AppendLine("title: Home");
|
|
||||||
content.AppendLine("---");
|
|
||||||
content.Append(context.FileReadText(context.RootDirectory.CombineWithFilePath("README.md")));
|
|
||||||
context.FileWriteText(context.DocsDirectory.CombineWithFilePath("index.md"), content.ToString());
|
|
||||||
|
|
||||||
context.RunDocfx(context.DocfxJsonFile);
|
|
||||||
context.GenerateRedirects();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[TaskName("UpdateStats")]
|
|
||||||
public class UpdateStatsTask : FrostingTask<BuildContext>
|
|
||||||
{
|
|
||||||
public class Updater
|
|
||||||
{
|
|
||||||
public string Prefix { get; }
|
|
||||||
public Regex Regex { get; }
|
|
||||||
public int Value { get; }
|
|
||||||
|
|
||||||
public Updater(string prefix, string regex, int value)
|
|
||||||
{
|
|
||||||
Prefix = prefix;
|
|
||||||
Regex = new Regex(regex);
|
|
||||||
Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Apply(string line)
|
|
||||||
{
|
|
||||||
if (!line.StartsWith(Prefix))
|
|
||||||
return line;
|
|
||||||
|
|
||||||
var match = Regex.Match(line);
|
|
||||||
if (!match.Success)
|
|
||||||
return line;
|
|
||||||
|
|
||||||
// Groups[1] refers to the first group (\d+)
|
|
||||||
var numberString = match.Groups[1].Value;
|
|
||||||
var number = int.Parse(numberString);
|
|
||||||
return line.Replace(number.ToString(), Value.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task<int> GetDependentProjectsNumber()
|
|
||||||
{
|
|
||||||
using var httpClient = new HttpClient();
|
|
||||||
const string url = "https://github.com/dotnet/BenchmarkDotNet/network/dependents";
|
|
||||||
var response = await httpClient.GetAsync(new Uri(url));
|
|
||||||
var dependentsPage = await response.Content.ReadAsStringAsync();
|
|
||||||
var match = new Regex(@"([0-9\,]+)[\n\r\s]+Repositories").Match(dependentsPage);
|
|
||||||
var number = int.Parse(match.Groups[1].Value.Replace(",", ""));
|
|
||||||
number = number / 100 * 100;
|
|
||||||
return number;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Run(BuildContext context)
|
|
||||||
{
|
|
||||||
var dependentProjectsNumber = GetDependentProjectsNumber().Result;
|
|
||||||
var updaters = new Updater[]
|
|
||||||
{
|
|
||||||
new(
|
|
||||||
"The library is adopted by",
|
|
||||||
@"\[(\d+)\+ GitHub projects\]",
|
|
||||||
dependentProjectsNumber
|
|
||||||
),
|
|
||||||
new(
|
|
||||||
"BenchmarkDotNet is already adopted by more than ",
|
|
||||||
@"\[(\d+)\+\]",
|
|
||||||
dependentProjectsNumber
|
|
||||||
),
|
|
||||||
};
|
|
||||||
var files = new[]
|
|
||||||
{
|
|
||||||
context.RootDirectory.CombineWithFilePath("README.md")
|
|
||||||
};
|
|
||||||
foreach (var file in files)
|
|
||||||
{
|
|
||||||
var lines = context.FileReadLines(file);
|
|
||||||
for (var i = 0; i < lines.Length; i++)
|
|
||||||
{
|
|
||||||
foreach (var updater in updaters)
|
|
||||||
lines[i] = updater.Apply(lines[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.FileWriteLines(file, lines);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +1,8 @@
|
||||||
#!/usr/bin/env pwsh
|
#!/usr/bin/env pwsh
|
||||||
|
|
||||||
$DotNetInstallerUri = 'https://dot.net/v1/dotnet-install.ps1';
|
$DotNetInstallerUri = 'https://dot.net/v1/dotnet-install.ps1';
|
||||||
$DotNetUnixInstallerUri = 'https://dot.net/v1/dotnet-install.sh'
|
$BuildPath = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
$PSScriptRoot = Split-Path $PSScriptRoot -Parent
|
||||||
$BuildPath = Join-Path $PSScriptRoot "build"
|
|
||||||
|
|
||||||
# Make sure tools folder exists
|
|
||||||
$ToolPath = Join-Path $PSScriptRoot "tools"
|
|
||||||
if (!(Test-Path $ToolPath)) {
|
|
||||||
Write-Verbose "Creating tools directory..."
|
|
||||||
New-Item -Path $ToolPath -Type Directory -Force | out-null
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if ($PSVersionTable.PSEdition -ne 'Core') {
|
if ($PSVersionTable.PSEdition -ne 'Core') {
|
||||||
# Attempt to set highest encryption available for SecurityProtocol.
|
# Attempt to set highest encryption available for SecurityProtocol.
|
||||||
|
@ -36,7 +28,6 @@ $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
|
||||||
$env:DOTNET_CLI_TELEMETRY_OPTOUT=1
|
$env:DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
$env:DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2
|
$env:DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2
|
||||||
|
|
||||||
|
|
||||||
Function Remove-PathVariable([string]$VariableToRemove)
|
Function Remove-PathVariable([string]$VariableToRemove)
|
||||||
{
|
{
|
||||||
$SplitChar = ';'
|
$SplitChar = ';'
|
||||||
|
@ -60,20 +51,10 @@ Function Remove-PathVariable([string]$VariableToRemove)
|
||||||
}
|
}
|
||||||
|
|
||||||
$InstallPath = Join-Path $PSScriptRoot ".dotnet"
|
$InstallPath = Join-Path $PSScriptRoot ".dotnet"
|
||||||
$GlobalJsonPath = Join-Path $BuildPath "global.json"
|
$SdkPath = Join-Path $BuildPath "sdk"
|
||||||
|
$GlobalJsonPath = Join-Path $SdkPath "global.json"
|
||||||
if (!(Test-Path $InstallPath)) {
|
if (!(Test-Path $InstallPath)) {
|
||||||
New-Item -Path $InstallPath -ItemType Directory -Force | Out-Null;
|
New-Item -Path $InstallPath -ItemType Directory -Force | Out-Null;
|
||||||
}
|
|
||||||
|
|
||||||
if ($IsMacOS -or $IsLinux) {
|
|
||||||
$ScriptPath = Join-Path $InstallPath 'dotnet-install.sh'
|
|
||||||
(New-Object System.Net.WebClient).DownloadFile($DotNetUnixInstallerUri, $ScriptPath);
|
|
||||||
& bash $ScriptPath --jsonfile "$GlobalJsonPath" --install-dir "$InstallPath" --no-path
|
|
||||||
|
|
||||||
Remove-PathVariable "$InstallPath"
|
|
||||||
$env:PATH = "$($InstallPath):$env:PATH"
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$ScriptPath = Join-Path $InstallPath 'dotnet-install.ps1'
|
$ScriptPath = Join-Path $InstallPath 'dotnet-install.ps1'
|
||||||
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, $ScriptPath);
|
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, $ScriptPath);
|
||||||
& $ScriptPath -JSonFile $GlobalJsonPath -InstallDir $InstallPath;
|
& $ScriptPath -JSonFile $GlobalJsonPath -InstallDir $InstallPath;
|
||||||
|
@ -81,11 +62,12 @@ else {
|
||||||
Remove-PathVariable "$InstallPath"
|
Remove-PathVariable "$InstallPath"
|
||||||
$env:PATH = "$InstallPath;$env:PATH"
|
$env:PATH = "$InstallPath;$env:PATH"
|
||||||
}
|
}
|
||||||
|
|
||||||
$env:DOTNET_ROOT=$InstallPath
|
$env:DOTNET_ROOT=$InstallPath
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# RUN BUILD SCRIPT
|
# RUN BUILD SCRIPT
|
||||||
###########################################################################
|
###########################################################################
|
||||||
& dotnet run --project build/Build.csproj -- $args
|
& dotnet run --configuration Release --project build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj -- $args
|
||||||
|
|
||||||
exit $LASTEXITCODE;
|
exit $LASTEXITCODE;
|
|
@ -1,13 +1,7 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Define varibles
|
|
||||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
|
||||||
TOOLS_DIR=$SCRIPT_DIR/tools
|
|
||||||
|
|
||||||
|
# Define variables
|
||||||
# Make sure the tools folder exist.
|
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )
|
||||||
if [ ! -d "$TOOLS_DIR" ]; then
|
|
||||||
mkdir "$TOOLS_DIR"
|
|
||||||
fi
|
|
||||||
|
|
||||||
###########################################################################
|
###########################################################################
|
||||||
# INSTALL .NET CORE CLI
|
# INSTALL .NET CORE CLI
|
||||||
|
@ -20,9 +14,10 @@ export DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2
|
||||||
|
|
||||||
if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then
|
if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then
|
||||||
mkdir "$SCRIPT_DIR/.dotnet"
|
mkdir "$SCRIPT_DIR/.dotnet"
|
||||||
|
curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://dot.net/v1/dotnet-install.sh
|
||||||
|
bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --jsonfile ./build/sdk/global.json --install-dir .dotnet --no-path
|
||||||
fi
|
fi
|
||||||
curl -Lsfo "$SCRIPT_DIR/.dotnet/dotnet-install.sh" https://dot.net/v1/dotnet-install.sh
|
|
||||||
bash "$SCRIPT_DIR/.dotnet/dotnet-install.sh" --jsonfile ./build/global.json --install-dir .dotnet --no-path
|
|
||||||
export PATH="$SCRIPT_DIR/.dotnet":$PATH
|
export PATH="$SCRIPT_DIR/.dotnet":$PATH
|
||||||
export DOTNET_ROOT="$SCRIPT_DIR/.dotnet"
|
export DOTNET_ROOT="$SCRIPT_DIR/.dotnet"
|
||||||
|
|
||||||
|
@ -30,4 +25,4 @@ export DOTNET_ROOT="$SCRIPT_DIR/.dotnet"
|
||||||
# RUN BUILD SCRIPT
|
# RUN BUILD SCRIPT
|
||||||
###########################################################################
|
###########################################################################
|
||||||
|
|
||||||
dotnet run --project ./build/Build.csproj -- "$@"
|
dotnet run --configuration Release --project ./build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj -- "$@"
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "7.0.304",
|
"version": "7.0.305",
|
||||||
"rollForward": "disable"
|
"rollForward": "disable"
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,11 +6,11 @@ There are two recommended options to build BenchmarkDotNet from source:
|
||||||
|
|
||||||
- [Visual Studio](https://www.visualstudio.com/downloads/) (Community, Professional, Enterprise) with .NET 4.6.2 SDK and F# support.
|
- [Visual Studio](https://www.visualstudio.com/downloads/) (Community, Professional, Enterprise) with .NET 4.6.2 SDK and F# support.
|
||||||
|
|
||||||
- [.NET 5 SDK](https://dotnet.microsoft.com/download).
|
- [.NET 7 SDK](https://dotnet.microsoft.com/download).
|
||||||
|
|
||||||
Once all the necessary tools are in place, building is trivial. Simply open solution file **BenchmarkDotNet.sln** that lives at the base of the repository and run Build action.
|
Once all the necessary tools are in place, building is trivial. Simply open solution file **BenchmarkDotNet.sln** that lives at the base of the repository and run Build action.
|
||||||
|
|
||||||
## Cake (C# Make)
|
## Command-line
|
||||||
|
|
||||||
[Cake (C# Make)](https://cakebuild.net/) is a cross platform build automation system with a C# DSL to do things like compiling code, copy files/folders, running unit tests, compress files and build NuGet packages.
|
[Cake (C# Make)](https://cakebuild.net/) is a cross platform build automation system with a C# DSL to do things like compiling code, copy files/folders, running unit tests, compress files and build NuGet packages.
|
||||||
|
|
||||||
|
@ -36,19 +36,6 @@ The build currently depends on the following prerequisites:
|
||||||
- Install [fsharp package](https://fsharp.org/use/mac/)
|
- Install [fsharp package](https://fsharp.org/use/mac/)
|
||||||
- Install the latest version of [OpenSSL](https://www.openssl.org/source/).
|
- Install the latest version of [OpenSSL](https://www.openssl.org/source/).
|
||||||
|
|
||||||
After you have installed these pre-requisites, you can build the BenchmarkDotNet by invoking the build script (`build.ps1` on Windows, or `build.sh` on Linux and macOS) at the base of the BenchmarkDotNet repository. By default the build process also run all the tests. There are quite a few tests, taking a significant amount of time that is not necessary if you just want to experiment with changes. You can skip the tests phase by adding the `skiptests` argument to the build script, e.g. `.\build.ps1 --SkipTests=True` or `./build.sh --skiptests=true`.
|
In order to run various build tasks from terminal, use `build.cmd` file in the repository root.
|
||||||
|
`build.cmd` is a cross-platform script that can be used the same way on Windows, Linux, and macOS.
|
||||||
Build has a number of options that you use. Some of the more important options are
|
When executed without arguments, it prints help information with list of all available build tasks.
|
||||||
|
|
||||||
- **`skiptests`** - do not run the tests. This can shorten build times quite a bit. On Windows: `.\build.ps1 --SkipTests=True` or `./build.sh --skiptests=true` on Linux/macOS.
|
|
||||||
|
|
||||||
- **`configuration`** - build the 'Release' or 'Debug' build type. Default value is 'Release'. On Windows: `.\build.ps1 -Configuration Debug` or `./build.sh --configuration debug` on Linux/macOS.
|
|
||||||
|
|
||||||
- **`target`** - with this parameter you can run a specific target from build pipeline. Default value is 'Default' target. On Windows: `.\build.ps1 -Target Default` or `./build.sh --target default` on Linux/macOS. Available targets:
|
|
||||||
- **`Default`** - run all actions one by one.
|
|
||||||
- **`Clean`** - clean all `obj`, `bin` and `artifacts` directories.
|
|
||||||
- **`Restore`** - automatically execute `Clean` action and after that restore all NuGet dependencies.
|
|
||||||
- **`Build`** - automatically execute `Restore` action, then run MSBuild for the solution file.
|
|
||||||
- **`FastTests`** - automatically execute `Build` action, then run all tests from the BenchmarkDotNet.Tests project.
|
|
||||||
- **`SlowTests`** - automatically execute `Build` action, then run all tests from the BenchmarkDotNet.IntegrationTests project.
|
|
||||||
- **`Pack`** - automatically execute `Build` action and after that creates local NuGet packages.
|
|
|
@ -49,31 +49,12 @@ It will be transformed to:
|
||||||
|
|
||||||
## Building documentation locally
|
## Building documentation locally
|
||||||
|
|
||||||
You can build documentation locally with the help of the `DocFX_Build` Cake target.
|
You can build documentation locally with the help of the `DocsBuild` build task:
|
||||||
Use the `DocFX_Serve` Cake target to build and run the documentation.
|
|
||||||
|
|
||||||
Windows (PowerShell):
|
|
||||||
|
|
||||||
```
|
```
|
||||||
.\build.ps1 --target DocFX_Build
|
build.cmd DocsBuild
|
||||||
.\build.ps1 --target DocFX_Serve
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Windows (Batch):
|
|
||||||
|
|
||||||
```
|
|
||||||
.\build.bat --target DocFX_Build
|
|
||||||
.\build.bat --target DocFX_Serve
|
|
||||||
```
|
|
||||||
|
|
||||||
Linux/macOS (Bash):
|
|
||||||
|
|
||||||
```
|
|
||||||
./build.sh --target DocFX_Build
|
|
||||||
./build.sh --target DocFX_Serve
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
* [DocFX User Manual](https://dotnet.github.io/docfx/tutorial/docfx.exe_user_manual.html)
|
* [DocFX User Manual](https://dotnet.github.io/docfx/tutorial/docfx.exe_user_manual.html)
|
||||||
|
|
|
@ -9,17 +9,21 @@ name: Installing NuGet packages
|
||||||
|
|
||||||
We have the following set of NuGet packages (you can install it directly from `nuget.org`):
|
We have the following set of NuGet packages (you can install it directly from `nuget.org`):
|
||||||
|
|
||||||
* `BenchmarkDotNet`: Basic BenchmarkDotNet infrastructure and logic. This is all you need to run benchmarks.
|
* `BenchmarkDotNet`: BenchmarkDotNet infrastructure and logic. This is all you need to run benchmarks.
|
||||||
|
* `BenchmarkDotNet.Annotations`: Basic BenchmarkDotNet annotations for your benchmarks.
|
||||||
* `BenchmarkDotNet.Diagnostics.Windows`: an additional optional package that provides a set of Windows diagnosers.
|
* `BenchmarkDotNet.Diagnostics.Windows`: an additional optional package that provides a set of Windows diagnosers.
|
||||||
|
* `BenchmarkDotNet.Diagnostics.dotTrace`: an additional optional package that provides DotTraceDiagnoser.
|
||||||
* `BenchmarkDotNet.Templates`: Templates for BenchmarkDotNet.
|
* `BenchmarkDotNet.Templates`: Templates for BenchmarkDotNet.
|
||||||
|
|
||||||
You might find other NuGet packages that start with `BenchmarkDotNet` name, but they are internal BDN packages that should not be installed manually. All that matters are the three packages mentioned above.
|
You might find other NuGet packages that start with `BenchmarkDotNet` name, but they are internal BDN packages that should not be installed manually. All that matters are the three packages mentioned above.
|
||||||
|
|
||||||
## Versioning system and feeds
|
## Versioning system and feeds
|
||||||
|
|
||||||
We have 3 kinds of versions: *stable*, *nightly*, and *develop*.
|
We have 3 kinds of versions: *stable*, *nightly*, and *develop*.
|
||||||
You can get the current version from the source code via `BenchmarkDotNetInfo.FullVersion` and the full title via `BenchmarkDotNetInfo.FullTitle`.
|
You can get the current version from the source code via `BenchmarkDotNetInfo.FullVersion` and the full title via `BenchmarkDotNetInfo.FullTitle`.
|
||||||
|
|
||||||
### Stable
|
### Stable
|
||||||
|
|
||||||
These versions are available from the official NuGet feed.
|
These versions are available from the official NuGet feed.
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
|
@ -28,26 +32,22 @@ These versions are available from the official NuGet feed.
|
||||||
</packageSources>
|
</packageSources>
|
||||||
```
|
```
|
||||||
|
|
||||||
* Example of the main NuGet package: `BenchmarkDotNet.0.10.3.nupkg`.
|
|
||||||
* Example of `BenchmarkDotNetInfo.FullTitle`: `BenchmarkDotNet v0.10.3`.
|
|
||||||
|
|
||||||
### Nightly
|
### Nightly
|
||||||
If you want to use a nightly version of the BenchmarkDotNet, add the `https://ci.appveyor.com/nuget/benchmarkdotnet` feed in the `<packageSources>` section of your `NuGet.config`:
|
|
||||||
|
If you want to use a nightly version of the BenchmarkDotNet, add the `https://www.myget.org/F/benchmarkdotnet/api/v3/index.json` feed in the `<packageSources>` section of your `NuGet.config`:
|
||||||
|
|
||||||
```xml
|
```xml
|
||||||
<packageSources>
|
<packageSources>
|
||||||
<add key="bdn-nightly" value="https://ci.appveyor.com/nuget/benchmarkdotnet" />
|
<add key="bdn-nightly" value="https://www.myget.org/F/benchmarkdotnet/api/v3/index.json" />
|
||||||
</packageSources>
|
</packageSources>
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you can install the packages from the `bdn-nightly` feed.
|
Now you can install the packages from the `bdn-nightly` feed.
|
||||||
|
|
||||||
* Example of the main NuGet package: `BenchmarkDotNet.0.10.3.13.nupkg`.
|
|
||||||
* Example of `BenchmarkDotNetInfo.FullTitle`: `BenchmarkDotNet v0.10.3.13-nightly`.
|
|
||||||
|
|
||||||
### Develop
|
### Develop
|
||||||
You also can build BenchmarkDotNet from source code.
|
|
||||||
The `.nupkg` files could be build with the help of `.\build\build-and-pack.cmd`.
|
|
||||||
|
|
||||||
* Example of the main NuGet package: `BenchmarkDotNet.0.10.3-develop.nupkg`.
|
You also can build BenchmarkDotNet from source code:
|
||||||
* Example of `BenchmarkDotNetInfo.FullTitle`: `BenchmarkDotNet v0.10.3.20170304-develop`.
|
|
||||||
|
```sh
|
||||||
|
build.cmd pack
|
||||||
|
```
|
Загрузка…
Ссылка в новой задаче