sdgfsdh
sdgfsdh

Reputation: 37095

Is it possible to remove the full-paths from .NET assemblies created with dotnet build?

I build my project with dotnet build, targeting netcoreapp3.1.

The problem is that the assemblies contain the full path to the source-files:

/home/runner/my-app/App.fs

This means that the hash of the assemblies depends on the directory where the code was built. I want it to only depend on the hash of the source-files and the .NET SDK itself.

Is there a way to make dotnet build use relative paths like this?

./my-app/App.fs

Upvotes: 12

Views: 2644

Answers (3)

mkjeldsen
mkjeldsen

Reputation: 2180

I can find no reason not to prefer either

<DeterministicSourcePaths>true</DeterministicSourcePaths>

where the SDK supports it, or

<PathMap>$(MSBuildThisFileDirectory)=./</PathMap>

over

<PathMap>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=./</PathMap>

However, there may be reason not to use MSBuildThisFileDirectory at all and this is not explained or demonstrated in the documentation.


When used in Directory.Build.props the MSBuildThisFileDirectory property points to the directory of Directory.Build.props, not the project it applies to. This makes

<PathMap>$(MSBuildThisFileDirectory)=./</PathMap>

behave semantically the same whether used in Directory.Build.props in a directory above (below) a .csproj file but possibly not in the desired way. In comparison the MSBuildProjectDirectory property always points to the .csproj directory.

Demonstration

Suppose a tree like

foo/
├── foo.sln
├── Directory.Build.props
├── FooMain/
│   ├── FooMain.csproj
│   └── src/
│       └── Foo.cs
└── FooTest/
    ├── FooTest.csproj
    └── src/
        └── Foo.cs

This table shows how different values for PathMap set in different places affect the outcome of throwing an exception from FooMain/src/Foo.cs; in particular, it shows that only MSBuildProjectDirectory is independent of its placement:

PathMap value Where Sample stack trace path
$(MSBuildThisFileDirectory)=./ Directory.Build.props ./FooMain/src/Foo.cs:line 42
$(MSBuildThisFileDirectory)=./ *.csproj ./src/Foo.cs:line 42
$(MSBuildProjectDirectory)=./ Directory.Build.props ./src/Foo.cs:line 42
$(MSBuildProjectDirectory)=./ *.csproj ./src/Foo.cs:line 42
$(MSBuildProjectDirectory)=./$(MSBuildProjectName)/ Directory.Build.props ./FooMain/src/Foo.cs:line 42
$(MSBuildProjectDirectory)=./$(MSBuildProjectName)/ *.csproj ./FooMain/src/Foo.cs:line 42

Instead of using PathMap directly, supported SDKs contemporary with the recently released .NET 7 offer the DeterministicSourcePaths option. When configured with

<DeterministicSourcePaths>true</DeterministicSourcePaths>

it behaves with the same consistency as

<PathMap>$(MSBuildProjectDirectory)=./$(MSBuildProjectName)/</PathMap>

but the sample stack trace path becomes /_/FooMain/src/Foo.cs:line 42.

To work reliably, though DeterministicSourcePaths also requires adding

<ItemGroup>
    <SourceRoot Include="$(MSBuildThisFileDirectory)/"/>
</ItemGroup>

to the root Directory.Build.props.


Whichever option you use, consider that while isolating the stack trace paths is certainly necessary for build reproducibility (despite not being mentioned in the DotNet.ReproducibleBuilds package as of version 1.1.1) there may be tools that expect to be able to read the absolute path from the stack traces. In particular, it may be practically infeasible to enable deterministic paths for test projects.

Upvotes: 5

cdev
cdev

Reputation: 5381

You can configure a release build with less information or you can override PathMap value to override it.

<PropertyGroup Label="Override Stack trace file locations">
  <PathMap>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=./</PathMap>
</PropertyGroup>

The following Directory.Build.props in the root of the project should work:

<Project>
 <PropertyGroup>
    <Deterministic>true</Deterministic>
    <DeterministicSourcePaths>true</DeterministicSourcePaths>
    <PathMap>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)'))=./</PathMap>
 </PropertyGroup>
</Project>

Upvotes: 16

DaveShaw
DaveShaw

Reputation: 52798

C# compiler (csc) has a compiler switch -deterministic

F# compiler (fsc) appears to have a switch --deterministic+

From looking at the DotNet SDK repo it appears there are 2 properties for project files:

<Deterministic>true</Deterministic>
<DeterministicSourcePaths>true</DeterministicSourcePaths>

You might not need both of these.

Upvotes: 6

Related Questions