dotnet / dotnet/msbuild

Inferred outputs of task's input/output parameter assume no change during execution

Open
#2,790 3 comments 0 reactions 0 assignees View on GitHub
Breaking Change triaged
Dominant language
C#
Stars
5.5k
Forks
1.5k
Avg merge
1d 8h
Merged PRs (30d)
141

Description

### Steps to reproduce

I am suspecting this is not a bug, and even it it is, it's already a feature because I reproduce it with MSBuild 2.0. But here's a stunningly simple project that escapes any explanation. The name `test.xxproj` is hardcoded because MSBuild v2.0 did not have `$(MsBuildThisFile)`, apparently. Any existing normal file will work instead.

So the target T1, which never runs, calls a task that does not even exist, and the task happily returns the list of outputs it did not even see. If you think that sounded like a Zen koan, I'm entirely with you!

```xml










```
The `ThisTaskDoesNotExist` task does not exist (hat tip to Dr. Quine), and I'd expect the `T1` never be ran, and `@(OutOfDateFiles)` be empty. The former is true, but as for the latter, all items from `@(Files)` end up in the `@(OutOfDateFiles)` list. The key part is the name `Foo` is both an input and an output property. Change one of them to `Bar`. and then `@(OutOfDateFiles)` will be empty.

What is going on?

```
C:\Users\kkm\work\msbuildxx>"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe"
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Framework
Project "C:\Users\kkm\work\msbuildxx\test.xxproj" on node 1 (default targets).
T1:
Skipping target "T1" because all output files are up-to-date with respect to the input files.
Build:
Files out of date: 'test.xxproj'
Done Building Project "C:\Users\kkm\work\msbuildxx\test.xxproj" (default targets).
```
MSBuild 4:
```
C:\Users\kkm\work\msbuildxx>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
Microsoft (R) Build Engine version 4.7.2053.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Project "C:\Users\kkm\work\msbuildxx\test.xxproj" on node 1 (default targets).
T1:
Skipping target "T1" because all output files are up-to-date with respect to the input files.
Build:
Files out of date: 'test.xxproj'
Done Building Project "C:\Users\kkm\work\msbuildxx\test.xxproj" (default targets).
```
MSBuild 2:
```
C:\Users\kkm\work\msbuildxx>C:\Windows\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe
Microsoft (R) Build Engine Version 2.0.50727.5483
[Microsoft .NET Framework, Version 2.0.50727.8762]
Project "C:\Users\kkm\work\msbuildxx\test.xxproj" (default targets):
Target T1:
Skipping target "T1" because all output files are up-to-date with respect to the input files.
Target Build:
Files out of date: 'test.xxproj'
```
----
My real intention was to filter lists with quite complex dependencies (I am automating a compiler that generates multiple outputs, and I do not even know which of them will be generated until it is actually run. Fortunately, it also produces a file with the list of outputs it actually wrote, so I can simply `` those it did not generate, for dependency check not to recompile next time. So in the batched compile target, I subtract the (sub-batched) list of actually produced outputs from the sub-batch of expected outputs, and add them to a list to touch later (used also for something else, so I *need* them per-batch with marked metadata, unfortunately, otherwise I'd ran the `` in the same target/batch as the compiler, and Bob's your uncle). The 'ThisTaskDoesNotExist' is née 'CreateItem', naturally; first I replaced it with the one of my my own that I could debug, but when I never hit the breakpoints, I realized the task is even irrelevant. But when a particular batch is not run, as in the pared down example above, I incorrectly get all files in this expected but not generated file list.

I worked around this behavior by creating by own version of CreateItem, with differing input and output property names. But this feels like quite a hack to me. I cannot think of a more idiomatic way of minimizing batched collections using MSBuild dependency up-to-date checks and also collecting some task outputs per batch without hitting this issue though.

----

Here's a bit more realistic, less pared down variant that behaves similarly

```xml










```
```
Microsoft (R) Build Engine version 15.4.8.50001 for .NET Framework
Project "C:\Users\kkm\work\msbuildxx\test.xxproj" on node 1 (default targets).
T1:
Skipping target "T1" because all output files are up-to-date with respect to the input files.
T1:
Adding 'nosuchfile' to OutOfDateFiles
Build:
Files out of date: 'test.xxproj;nosuchfile'
Done Building Project "C:\Users\kkm\work\msbuildxx\test.xxproj" (default targets).
```

---
### Addendum
After some debugger poking [I found this](https://github.com/Microsoft/msbuild/blob/ee6a3f7856a92884b12fe63cea5111509620b14d/src/Build/BackEnd/Components/RequestBuilder/TargetEntry.cs#L535-L547), so my best guess this behavior is expected and correct. But oh boy, if it is, does not it deserve a separate chapter in the MsBuild docs, so very not obvious it is!

And it would be amazing to have a way to distinguish inferred items from up-to-date buckets from those actually generated by executed bucket's tasks!

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.