Huge build project refactoring
This commit is contained in:
Родитель
144f5c5552
Коммит
6291a7e724
|
@ -4,32 +4,115 @@ on:
|
|||
pull_request:
|
||||
push:
|
||||
|
||||
permissions: write-all
|
||||
|
||||
jobs:
|
||||
build-windows:
|
||||
|
||||
build-windows-core:
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run
|
||||
shell: cmd
|
||||
run: |
|
||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
|
||||
./build.bat
|
||||
- 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 '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:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Clang
|
||||
uses: egor-tensin/setup-clang@v1
|
||||
with:
|
||||
version: latest
|
||||
platform: x64
|
||||
- name: Set up zlib-static
|
||||
run: sudo apt-get install -y libkrb5-dev
|
||||
- name: Run
|
||||
run: ./build.sh
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Clang
|
||||
uses: egor-tensin/setup-clang@v1
|
||||
with:
|
||||
version: latest
|
||||
platform: x64
|
||||
- name: Set up zlib-static
|
||||
run: sudo apt-get install -y libkrb5-dev
|
||||
- name: Run task 'Build'
|
||||
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:
|
||||
runs-on: macOS-latest
|
||||
runs-on: macos-13
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run
|
||||
run: ./build.sh
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run task 'Build'
|
||||
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
|
||||
|
||||
- name: Download changelog
|
||||
run: ./build.sh --target DocFX_Changelog_Download --VersionCount 1
|
||||
run: ./build.cmd DocsUpdate /p:Depth=1
|
||||
env:
|
||||
GITHUB_PRODUCT: ChangelogBuilder
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
|
@ -19,16 +19,16 @@ jobs:
|
|||
ref: docs-stable
|
||||
|
||||
- name: Build BenchmarkDotNet
|
||||
run: ./build.bat --target Build
|
||||
run: ./build.cmd Build
|
||||
|
||||
- name: Download changelog
|
||||
run: ./build.bat --target DocFX_Changelog_Download --VersionCount 1
|
||||
run: ./build.cmd DocsUpdate /p:Depth=1
|
||||
env:
|
||||
GITHUB_PRODUCT: ChangelogBuilder
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build documentation
|
||||
run: ./build.bat --target DocFX_Build
|
||||
run: ./build.cmd DocsBuild
|
||||
|
||||
- name: Upload Artifacts
|
||||
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:
|
||||
- ps: .\build.ps1
|
||||
- ps: .\build.cmd CI
|
||||
|
||||
test: off
|
||||
deploy: off
|
||||
|
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
parameters:
|
||||
name: Ubuntu
|
||||
vmImage: 'ubuntu-20.04'
|
||||
scriptFileName: ./build.sh
|
||||
scriptFileName: ./build.cmd CI
|
||||
initialization:
|
||||
- bash: |
|
||||
wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key | sudo apt-key add -
|
||||
|
|
|
@ -13,4 +13,4 @@ jobs:
|
|||
parameters:
|
||||
name: Windows
|
||||
vmImage: 'windows-2019'
|
||||
scriptFileName: .\build.ps1
|
||||
scriptFileName: .\build.cmd CI
|
|
@ -13,4 +13,4 @@ jobs:
|
|||
parameters:
|
||||
name: macOS
|
||||
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>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Cake.Frosting" Version="3.0.0" />
|
||||
<PackageReference Include="Cake.FileHelpers" Version="6.1.3" />
|
||||
<PackageReference Include="Cake.Git" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.DocAsCode.App" Version="2.67.5" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2022.3.1" />
|
||||
<PackageReference Include="Octokit" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
</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.Text;
|
||||
using System.Threading.Tasks;
|
||||
using BenchmarkDotNet.Build.Helpers;
|
||||
using BenchmarkDotNet.Build.Meta;
|
||||
using Cake.Core.IO;
|
||||
using JetBrains.Annotations;
|
||||
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
|
||||
? $" ({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)
|
||||
private class Config
|
||||
{
|
||||
var message = commit.Message.Trim().Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.FirstOrDefault() ?? "";
|
||||
return message.Length > 80 ? message.Substring(0, 77) + "..." : message;
|
||||
}
|
||||
public string CurrentMilestone { get; }
|
||||
public string PreviousMilestone { get; }
|
||||
public string LastCommit { get; }
|
||||
|
||||
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") : "";
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
public void Deconstruct(out string currentMilestone, out string previousMilestone, out string lastCommit)
|
||||
{
|
||||
repoOwner = RepoOwner;
|
||||
repoName = RepoName;
|
||||
currentMilestone = CurrentMilestone;
|
||||
previousMilestone = PreviousMilestone;
|
||||
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)
|
||||
{
|
||||
CurrentMilestone = currentMilestone;
|
||||
|
@ -85,20 +35,11 @@ public class ChangeLogBuilder
|
|||
}
|
||||
}
|
||||
|
||||
public class AuthorEqualityComparer : IEqualityComparer<Author>
|
||||
private class MarkdownBuilder
|
||||
{
|
||||
public static readonly IEqualityComparer<Author> Default = new AuthorEqualityComparer();
|
||||
|
||||
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 IReadOnlyList<Milestone>? allMilestones;
|
||||
private static readonly Dictionary<string, string> AuthorNames = new();
|
||||
|
||||
|
||||
private readonly Config config;
|
||||
private readonly StringBuilder builder;
|
||||
|
||||
|
@ -115,17 +56,17 @@ public class ChangeLogBuilder
|
|||
|
||||
private async Task<string> Build()
|
||||
{
|
||||
var (repoOwner, repoName, milestone, previousMilestone, lastCommit) = config;
|
||||
var (milestone, previousMilestone, lastCommit) = config;
|
||||
if (string.IsNullOrEmpty(lastCommit))
|
||||
lastCommit = milestone;
|
||||
|
||||
var client = new GitHubClient(new ProductHeaderValue(config.ProductHeader));
|
||||
var tokenAuth = new Credentials(config.Token);
|
||||
var client = new GitHubClient(new ProductHeaderValue(Repo.ProductHeader));
|
||||
var tokenAuth = new Credentials(Repo.Token);
|
||||
client.Credentials = tokenAuth;
|
||||
|
||||
|
||||
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();
|
||||
foreach (var contributor in allContributors)
|
||||
|
@ -140,17 +81,17 @@ public class ChangeLogBuilder
|
|||
return builder.ToString();
|
||||
}
|
||||
|
||||
if (AllMilestones == null)
|
||||
if (allMilestones == null)
|
||||
{
|
||||
var milestoneRequest = new MilestoneRequest
|
||||
{
|
||||
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>();
|
||||
var targetMilestone = AllMilestones.FirstOrDefault(m => m.Title == milestone);
|
||||
var targetMilestone = allMilestones.FirstOrDefault(m => m.Title == milestone);
|
||||
if (targetMilestone != null)
|
||||
{
|
||||
var issueRequest = new RepositoryIssueRequest
|
||||
|
@ -159,7 +100,7 @@ public class ChangeLogBuilder
|
|||
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
|
||||
|
@ -170,11 +111,11 @@ public class ChangeLogBuilder
|
|||
.Where(issue => issue.PullRequest != null)
|
||||
.OrderBy(issue => issue.Number)
|
||||
.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;
|
||||
|
||||
|
||||
|
||||
|
||||
foreach (var contributor in commits.Select(commit => commit.Author))
|
||||
if (contributor != null && !AuthorNames.ContainsKey(contributor.Login))
|
||||
{
|
||||
|
@ -189,14 +130,14 @@ public class ChangeLogBuilder
|
|||
return $"{AuthorNames[commit.Author.Login]} ({commit.Author.ToLink()})".Trim();
|
||||
return commit.Commit.Author.Name;
|
||||
}
|
||||
|
||||
|
||||
var contributors = compare.Commits
|
||||
.Select(PresentContributor)
|
||||
.OrderBy(it => it)
|
||||
.Distinct()
|
||||
.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();
|
||||
|
@ -218,7 +159,7 @@ public class ChangeLogBuilder
|
|||
}
|
||||
|
||||
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();
|
||||
|
@ -233,8 +174,9 @@ public class ChangeLogBuilder
|
|||
builder.AppendLine();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
{
|
|
@ -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
|
||||
|
||||
$DotNetInstallerUri = 'https://dot.net/v1/dotnet-install.ps1';
|
||||
$DotNetUnixInstallerUri = 'https://dot.net/v1/dotnet-install.sh'
|
||||
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -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
|
||||
}
|
||||
|
||||
$BuildPath = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||
$PSScriptRoot = Split-Path $PSScriptRoot -Parent
|
||||
|
||||
if ($PSVersionTable.PSEdition -ne 'Core') {
|
||||
# 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_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2
|
||||
|
||||
|
||||
Function Remove-PathVariable([string]$VariableToRemove)
|
||||
{
|
||||
$SplitChar = ';'
|
||||
|
@ -60,20 +51,10 @@ Function Remove-PathVariable([string]$VariableToRemove)
|
|||
}
|
||||
|
||||
$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)) {
|
||||
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'
|
||||
(New-Object System.Net.WebClient).DownloadFile($DotNetInstallerUri, $ScriptPath);
|
||||
& $ScriptPath -JSonFile $GlobalJsonPath -InstallDir $InstallPath;
|
||||
|
@ -81,11 +62,12 @@ else {
|
|||
Remove-PathVariable "$InstallPath"
|
||||
$env:PATH = "$InstallPath;$env:PATH"
|
||||
}
|
||||
|
||||
$env:DOTNET_ROOT=$InstallPath
|
||||
|
||||
###########################################################################
|
||||
# RUN BUILD SCRIPT
|
||||
###########################################################################
|
||||
& dotnet run --project build/Build.csproj -- $args
|
||||
& dotnet run --configuration Release --project build/BenchmarkDotNet.Build/BenchmarkDotNet.Build.csproj -- $args
|
||||
|
||||
exit $LASTEXITCODE;
|
|
@ -1,13 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
# Define varibles
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
|
||||
TOOLS_DIR=$SCRIPT_DIR/tools
|
||||
|
||||
|
||||
# Make sure the tools folder exist.
|
||||
if [ ! -d "$TOOLS_DIR" ]; then
|
||||
mkdir "$TOOLS_DIR"
|
||||
fi
|
||||
# Define variables
|
||||
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd )
|
||||
|
||||
###########################################################################
|
||||
# INSTALL .NET CORE CLI
|
||||
|
@ -20,9 +14,10 @@ export DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX=2
|
|||
|
||||
if [ ! -d "$SCRIPT_DIR/.dotnet" ]; then
|
||||
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
|
||||
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 DOTNET_ROOT="$SCRIPT_DIR/.dotnet"
|
||||
|
||||
|
@ -30,4 +25,4 @@ export DOTNET_ROOT="$SCRIPT_DIR/.dotnet"
|
|||
# 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": {
|
||||
"version": "7.0.304",
|
||||
"version": "7.0.305",
|
||||
"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.
|
||||
|
||||
- [.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.
|
||||
|
||||
## 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.
|
||||
|
||||
|
@ -36,19 +36,6 @@ The build currently depends on the following prerequisites:
|
|||
- Install [fsharp package](https://fsharp.org/use/mac/)
|
||||
- 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`.
|
||||
|
||||
Build has a number of options that you use. Some of the more important options are
|
||||
|
||||
- **`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.
|
||||
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.
|
||||
When executed without arguments, it prints help information with list of all available build tasks.
|
|
@ -49,31 +49,12 @@ It will be transformed to:
|
|||
|
||||
## Building documentation locally
|
||||
|
||||
You can build documentation locally with the help of the `DocFX_Build` Cake target.
|
||||
Use the `DocFX_Serve` Cake target to build and run the documentation.
|
||||
|
||||
Windows (PowerShell):
|
||||
You can build documentation locally with the help of the `DocsBuild` build task:
|
||||
|
||||
```
|
||||
.\build.ps1 --target DocFX_Build
|
||||
.\build.ps1 --target DocFX_Serve
|
||||
build.cmd DocsBuild
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
* [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`):
|
||||
|
||||
* `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.dotTrace`: an additional optional package that provides DotTraceDiagnoser.
|
||||
* `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.
|
||||
|
||||
## Versioning system and feeds
|
||||
|
||||
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`.
|
||||
|
||||
### Stable
|
||||
|
||||
These versions are available from the official NuGet feed.
|
||||
|
||||
```xml
|
||||
|
@ -28,26 +32,22 @@ These versions are available from the official NuGet feed.
|
|||
</packageSources>
|
||||
```
|
||||
|
||||
* Example of the main NuGet package: `BenchmarkDotNet.0.10.3.nupkg`.
|
||||
* Example of `BenchmarkDotNetInfo.FullTitle`: `BenchmarkDotNet v0.10.3`.
|
||||
|
||||
### 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
|
||||
<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>
|
||||
```
|
||||
|
||||
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
|
||||
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`.
|
||||
* Example of `BenchmarkDotNetInfo.FullTitle`: `BenchmarkDotNet v0.10.3.20170304-develop`.
|
||||
You also can build BenchmarkDotNet from source code:
|
||||
|
||||
```sh
|
||||
build.cmd pack
|
||||
```
|
Загрузка…
Ссылка в новой задаче