Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ if you're using a lower version.
Prerelease packages are available on the following [NuGet feed](https://dev.azure.com/dotnet/Projects/_packaging?_a=feed&feed=ReproducibleBuilds):
`https://pkgs.dev.azure.com/dotnet/Projects/_packaging/ReproducibleBuilds/nuget/v3/index.json`

## DotNet.ReproducibleBuilds.Isolated Documentation and nuget package
## DotNet.ReproducibleBuilds.Isolated documentation and nuget package

[![NuGet Version](https://img.shields.io/nuget/v/DotNet.ReproducibleBuilds.Isolated?style=flat&label=DotNet.ReproducibleBuilds.Isolated)](https://www.nuget.org/packages/DotNet.ReproducibleBuilds.Isolated)
[![NuGet Downloads](https://img.shields.io/nuget/dt/DotNet.ReproducibleBuilds.Isolated?style=flat)](https://www.nuget.org/packages/DotNet.ReproducibleBuilds.Isolated)

It's highly recommended that all projects enable these settings, either via
adding this package or manually, as described in [Documentation/Reproducible-MSBuild](Documentation/Reproducible-MSBuild/README.md).
adding this package or manually, as described in [docs/Reproducible-MSBuild](docs/Reproducible-MSBuild/README.md).

This package configures a variety of properties and item groups to prevent your build from unintentionally
depending on other installed software that's not described by your repo. All build dependencies should come
Expand Down
File renamed without changes.
20 changes: 20 additions & 0 deletions docs/diagnostics/RPB0001.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# RPB0001: DotNet.ReproducibleBuilds minimum required MSBuild version

## Description

This warning occurs when the project is using an MSBuild version that is older than the minimum required version for
this package.

## Cause

The DotNet.ReproducibleBuilds package requires specific MSBuild features that were introduced in newer versions. Older
versions of MSBuild lack the necessary functionality to ensure reproducible builds.

## Resolution

To resolve this warning, update your development environment to use a supported version.

## Related Documentation

- [Upgrade to a new .NET version](https://learn.microsoft.com/en-us/dotnet/core/install/upgrade)
- [.NET SDK, MSBuild, and Visual Studio versioning](https://learn.microsoft.com/en-us/dotnet/core/porting/versioning-sdk-msbuild-vs)
24 changes: 24 additions & 0 deletions docs/diagnostics/RPB0002.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# RPB0002: TargetFrameworkRootPath not initialized

## Description

This warning occurs when building .NET Framework projects without proper reference assembly configuration.
The `TargetFrameworkRootPath` property is undefined, which prevents the build system from locating the correct
.NET Framework reference assemblies.

## Cause

This warning appears when:
- Building a .NET Framework project (e.g., net462, net48)
- The `TargetFrameworkRootPath` MSBuild property is not properly set
- The `Microsoft.NETFramework.ReferenceAssemblies` NuGet package is not referenced

## Resolution

See [Build apps against Microsoft.NETFramework.ReferenceAssemblies](https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/reference-assemblies)

## Related Documentation

- [Microsoft.NETFramework.ReferenceAssemblies Package](https://www.nuget.org/packages/Microsoft.NETFramework.ReferenceAssemblies/)
- [Build apps against Microsoft.NETFramework.ReferenceAssemblies](https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/reference-assemblies)
- [Techniques/TargetFrameworkRootPath](../Reproducible-MSBuild/Techniques/TargetFrameworkRootPath.md)
6 changes: 4 additions & 2 deletions src/DotNet.ReproducibleBuilds.Isolated/Sdk/Sdk.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<!--
Isolate the build from unrelated installed software.

Each of these settings and rationale are detailed in the Documentation that can be found
at https://github.com/dotnet/reproducible-builds/blob/main/Documentation/Reproducible-MSBuild/Techniques/toc.md
Each of these settings and rationale are detailed in the documentation that can be found
at https://github.com/dotnet/reproducible-builds/blob/main/docs/Reproducible-MSBuild/Techniques/toc.md
-->

<!--
Expand Down Expand Up @@ -32,6 +32,8 @@
<Message Text="TargetFrameworkVersion=$(TargetFrameworkVersion)" />
<Warning
Condition="'$(TargetFrameworkRootPath)' == '[UNDEFINED]' and '$(TargetFrameworkIdentifier)' == '.NETFramework'"
Code="RPB0002"
HelpLink="https://github.com/dotnet/reproducible-builds/blob/main/docs/diagnostics/RPB0002.md"
Text="Error, TargetFrameworkRootPath not initialized. If you're building for net462 or any other version of desktop NETFramework, please reference the 'Microsoft.NETFramework.ReferenceAssemblies' nuget package and run restore on the project to fix up your framework reference paths." />
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<Target Name="_ReproducibleBuildsMSBuildVersionCheck"
Condition="$([MSBuild]::VersionLessThan($(_ReproducibleBuildsMSBuildVersion), $(_ReproducibleBuildsMSBuildMinVersion)))"
BeforeTargets="ResolveAssemblyReferences;Build;Rebuild">
<Warning Text="Reproducible builds requires MSBuild '$(_ReproducibleBuildsMSBuildMinVersion)' or later. This project is using '$(_ReproducibleBuildsMSBuildVersion)'. Use .NET SDK 8.0.100 or VS 17.8 or later." />
<Warning
Code="RPB0001"
HelpLink="https://github.com/dotnet/reproducible-builds/blob/main/docs/diagnostics/RPB0001.md"
Text="DotNet.ReproducibleBuilds requires MSBuild '$(_ReproducibleBuildsMSBuildMinVersion)' or later. This project is using '$(_ReproducibleBuildsMSBuildVersion)'. Use .NET SDK 8.0.100 or VS 17.8 or later." />
</Target>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net472;net8.0;net9.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<OutputType>Exe</OutputType>

<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" />
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="MSBuild.ProjectCreation" />
<PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Polyfill">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<Compile Include="../Shared/*.cs" LinkBase="Shared" />
<None Include="$(RepoRoot)/src/DotNet.ReproducibleBuilds.Isolated/Sdk/*.props" CopyToOutputDirectory="PreserveNewest" />
<None Include="$(RepoRoot)/src/DotNet.ReproducibleBuilds.Isolated/Sdk/*.targets" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.Build.Utilities.ProjectCreation;
using System.Runtime.CompilerServices;

namespace DotNet.ReproducibleBuilds.Isolated.Tests;

internal static class MSBuildModuleInitializer
{
[ModuleInitializer]
internal static void InitializeMSBuild()
{
MSBuildAssemblyResolver.Register();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using DotNet.ReproducibleBuilds.Tests.Shared;

using Microsoft.Build.Evaluation;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Isolated.Tests;

internal static class ProjectTemplates
{
private static readonly string ThisAssemblyDirectory = Path.GetDirectoryName(typeof(ProjectTemplates).Assembly.Location)!;

public static ProjectCreator ReproducibleBuildsIsolatedProject(this ProjectCreatorTemplates templates, FileInfo project, Action<ProjectCreator>? configureProject = null)
{
DirectoryInfo directory = project.Directory ?? throw new ArgumentException("Project's path does not appear to have a parent.", nameof(project));

ProjectCollection projectCollection = new(); // Create a new collection for each project to ensure environment variables aren't shared between tests

return ProjectCreator
.Create(path: project.FullName, sdk: ProjectCreatorConstants.SdkCsprojDefaultSdk, projectCollection: projectCollection)
.Import(Path.Combine(ThisAssemblyDirectory, "Sdk.props"))
.CustomAction(configureProject)
.Import(Path.Combine(ThisAssemblyDirectory, "Sdk.targets"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using DotNet.ReproducibleBuilds.Tests.Shared;

using FluentAssertions;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Isolated.Tests;

public class TargetFrameworkRootPathTests : TestBase
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void WarningCanBeSuppressed(bool suppress)
{
ProjectCreator.Templates
.ReproducibleBuildsIsolatedProject(GetRandomFile(".csproj"), configureProject: project =>
{
project.Property("TargetFramework", "net472");

if (suppress)
{
project.Property("NoWarn", "RPB0002");
}
})
// Run the target directly and _not_ as part of Build because we want to test the warning path and not allow the SDK's
// automatic references to run.
.TryBuild(restore: false, target: "CheckTargetFrameworkRootPath", out bool result, out BuildOutput output);

int expected = suppress ? 0 : 1;

result.Should().BeTrue();
output.Warnings.Should().HaveCount(expected);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FluentAssertions;
using DotNet.ReproducibleBuilds.Tests.Shared;

using FluentAssertions;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
</ItemGroup>

<ItemGroup>
<Compile Include="../Shared/*.cs" LinkBase="Shared" />
<None Include="$(RepoRoot)/src/DotNet.ReproducibleBuilds/*.props" CopyToOutputDirectory="PreserveNewest" />
<None Include="$(RepoRoot)/src/DotNet.ReproducibleBuilds/*.targets" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
Expand Down
29 changes: 20 additions & 9 deletions tests/DotNet.ReproducibleBuilds.Tests/MinimumVersionTests.cs
Original file line number Diff line number Diff line change
@@ -1,26 +1,37 @@
using FluentAssertions;
using DotNet.ReproducibleBuilds.Tests.Shared;

using FluentAssertions;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;

public class MinimumVersionTests : TestBase
{
[Theory]
[InlineData("17.7.0", false)]
[InlineData("17.8.0", true)]
[InlineData("17.9.0", true)]
public void BelowMinimumVersionEmitsWarning(string msbuildVersion, bool success)
[InlineData("17.7.0", false, false)]
[InlineData("17.7.0", false, true)]
[InlineData("17.8.0", true, false)]
[InlineData("17.9.0", true, true)]
public void BelowMinimumVersionEmitsWarning(string msbuildVersion, bool success, bool suppress)
{
Dictionary<string, string> globalProperties = new()
{
["_ReproducibleBuildsMSBuildVersion"] = msbuildVersion
};

ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"))
.TryBuild(restore: false, target: "_ReproducibleBuildsMSBuildVersionCheck", globalProperties, out bool result, out BuildOutput output);
ProjectCreator project = ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"));

if (suppress)
{
project.Property("NoWarn", "RPB0001"); // Suppress the RPB0001 warning
}

project.TryBuild(restore: false, target: "_ReproducibleBuildsMSBuildVersionCheck", globalProperties, out bool result, out BuildOutput output);

int expected = (success || suppress) ? 0 : 1;

result.Should().BeTrue();
output.Warnings.Should().HaveCount(success ? 0 : 1);
output.Warnings.Should().HaveCount(expected);
}
}
4 changes: 3 additions & 1 deletion tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Microsoft.Build.Evaluation;
using DotNet.ReproducibleBuilds.Tests.Shared;

using Microsoft.Build.Evaluation;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;
Expand Down
4 changes: 3 additions & 1 deletion tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using FluentAssertions;
using DotNet.ReproducibleBuilds.Tests.Shared;

using FluentAssertions;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;

namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal static class BooleanExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal static class CollectionExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal static class DictionaryExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal sealed class DisposableCollection : IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal sealed class EnvironmentVariableSuppressor : IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal static class FileSystemInfoExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Microsoft.Build.Evaluation;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

internal static class ProjectCreatorExtensions
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
namespace DotNet.ReproducibleBuilds.Tests;
namespace DotNet.ReproducibleBuilds.Tests.Shared;

public abstract class TestBase : IDisposable
{
Expand Down