Redundant recompilation of source code due to file path case sensitivity.
See original GitHub issueIssue Description
It is very easy to trigger redundant recompilation of the source code on Windows.
Steps to Reproduce
- mkdir c:\temp\bug
- dotnet new classlib
- dotnet build -bl:1.binlog
- dotnet build -bl:2.binlog
- cd …\Bug
- dotnet build -bl:3.binlog
Now examine the binary logs:
- 1.binlog indicates the Csc task was run - as expected.
- 2.binlog indicates the Csc task was not run - as expected.
- 3.binlog indicates the Csc task was run - unexpected.
Analysis
The implicating message in the log is: Input file “obj\Debug\net6.0\bug.GeneratedMSBuildEditorConfig.editorconfig” is newer than output file “obj\Debug\net6.0\bug.pdb”.
Indeed, the file c:\temp\bug\obj\Debug\net6.0\bug.GeneratedMSBuildEditorConfig.editorconfig contains the following line:
build_property.ProjectDir = C:\temp\Bug\
But during the first two builds this line was:
build_property.ProjectDir = C:\temp\bug\
Notice the file case - it was affected by the value of the current directory. In the first two runs it was c:\temp\bug, but in the third run it is c:\temp\Bug. This should not trigger a recompilation on Windows.
Versions & Configurations
- OS: Windows
- Reproduces with msbuild 16 and 17
Regression?
I tested with msbuild 15, 16 and 17. The .editorconfig file is generated in 16 and 17 and both exhibit the described behaviour. msbuild 15 does not generate the file, but it recompiles the third time anyway for the reasons unclear to me.
To test with msbuild I had to:
- provide the
--framework
parameter todotnet new
- instead of
dotnet build
runmsbuild /v:m /restore
The redundant recompilation in msbuild 15 reports the reason as Input file “C:\temp\Bug\obj\bug.csproj.nuget.g.props” is newer than output file “obj\Debug\netcoreapp2.1\bug.dll”. which does not make sense, because at the same time the Restore task reports the following message - Assets file has not changed. Skipping assets file writing. Path: C:\temp\Bug\obj\project.assets.json
I did not investigate the msbuild 15 behaviour for lack of “public interest”.
The proposed solution
The task GenerateMSBuildEditorConfig
should first convert the file path to the canonical case as recorded by the Windows OS.
Issue Analytics
- State:
- Created a year ago
- Comments:11 (7 by maintainers)
There is an issue tracking this: https://github.com/dotnet/runtime/issues/23871
It isn’t easy to do as you have to walk the path to find segments that actually exist to find their cased name. There is an API in Windows that does this for you, but it requires an open handle to the target.
You can make blanket assumptions about casing based on the platform you’re running on, but that isn’t going to be 100% correct. Casing sensitivity can change from path segment to path segment too (not likely, but possible). Despite that, we do make platform based assumptions here in System.IO:
https://github.com/dotnet/runtime/blob/ca93024d4c2f5fa2b6e04f282fb0b131cd69102f/src/libraries/Common/src/System/IO/PathInternal.CaseSensitivity.cs#L23-L30
When does this happen?
In the OP it says:
Notice the file case - it was affected by the value of the current directory. In the first two runs it was c:\temp\bug
. Why did the value ofthe current directory
change? Can whatever process is changing that be fixed instead to not do that?Note: ntfs is case sensitive. There may also be mounted shares from systems that have files with different casing. It seems like a bad idea to do any munging of hte name at all. That just leads to a higher chance of disparate systems getting confused about what’s going on.