-
-
Save KirillOsenkov/f20cb84d37a89b01db63f8aafe03f19b to your computer and use it in GitHub Desktop.
<Project Sdk="Microsoft.NET.Sdk"> | |
<PropertyGroup> | |
<OutputType>Exe</OutputType> | |
<TargetFramework>net472</TargetFramework> | |
</PropertyGroup> | |
<PropertyGroup> | |
<GeneratedText><![CDATA[ | |
using System%3B | |
public class Hello$(TargetFramework) | |
{ | |
public void Print() | |
{ | |
Console.WriteLine("Hello $(TargetFramework)!")%3B | |
} | |
} | |
]]></GeneratedText> | |
</PropertyGroup> | |
<Target Name="AddGeneratedFile" BeforeTargets="BeforeCompile;CoreCompile" Inputs="$(MSBuildAllProjects)" Outputs="$(IntermediateOutputPath)GeneratedFile.cs"> | |
<PropertyGroup> | |
<GeneratedFilePath>$(IntermediateOutputPath)GeneratedFile.cs</GeneratedFilePath> | |
</PropertyGroup> | |
<ItemGroup> | |
<Compile Include="$(GeneratedFilePath)" /> | |
<FileWrites Include="$(GeneratedFilePath)" /> | |
</ItemGroup> | |
<WriteLinesToFile Lines="$(GeneratedText)" File="$(GeneratedFilePath)" WriteOnlyWhenDifferent="true" Overwrite="true" /> | |
</Target> | |
</Project> |
Then use something like $(TargetFramework)
to avoid reading env variable.
Having extra inputs is a good idea, but it needs to be a different sample. This sample is about literally a one-liner that comes from somewhere that's not a file (think Git commit SHA for instance). UserName is just for illustrative purposes.
A Git commit SHA is a good example. In that case, I think you would need something like this:
<Target Name="AddGeneratedFile" BeforeTargets="BeforeCompile;CoreCompile"
Inputs="$(MSBuildAllProjects)" Outputs="$(IntermediateOutputPath)GeneratedFile.$(GitCommitSHA).cs">
<PropertyGroup>
<GeneratedFilePath>$(IntermediateOutputPath)GeneratedFile.$(GitCommitSHA).cs</GeneratedFilePath>
</PropertyGroup>
If you don't include the $(GitCommitSHA)
in the path, the GeneratedFile.cs
will get stuck on the first commit.
or you could just remove the Inputs and Outputs so the target always runs
or you could just remove the Inputs and Outputs so the target always runs
Will this mean the project is always dirty or will it only run when other files are out of date?
Will this mean the project is always dirty or will it only run when other files are out of date?
The answer to that is complex.
MSBuild itself will always build all the targets in a project, possibly skipping them at the target level based on target inputs and outputs.
Visual Studio, however, does not always invoke MSBuild for a project. When VS is asked to build, it delegates that operation to each project's project system. That project system can then decide whether to report success (because it thinks the project is up to date) or actually invoke a build operation. C# projects use one of two project systems, both of which have the concept of a "fast up-to-date check". That is essentially a big list of all of the inputs to any target in the project and all of its outputs--if any input is newer than any output, the project system will invoke MSBuild, which will then build using its own target incrementality. There are documented extension points for extending project-system understanding of project inputs and outputs.
So for your question, if you'r talking about VS scenarios, the project won't always be considered out of date because it won't (by default) know about the input(s) to the nonincremental target.
possibly skipping them at the target level based on target inputs and outputs.
Would this explain why the following generates GeneratedFile.cs
the first time, but never regenerates it, even after a Rebuild
?
<Target ... Inputs="$(MSBuildAllProjects)" Outputs="$(IntermediateOutputPath)GeneratedFile.cs">
Does Rebuild
force the up-to-date check to return false, but MSBuild decides the target still doesn't need to execute because GeneratedFile.cs
was updated more recently than $(MSBuildAllProjects)
?
Rebuild
does Clean;Build
. I think in that case, because the generated file isn't added to @(FileWrites)
, it's not getting deleted/cleaned up on Clean
, so Rebuild
doesn't reset its state.
Incremental build for targets is unconfigurable: it's always on.
A log at detailed
or diagnostic
level should show the target being skipped as up to date.
Having extra inputs is a good idea, but it needs to be a different sample. This sample is about literally a one-liner that comes from somewhere that's not a file (think Git commit SHA for instance). UserName is just for illustrative purposes.