C
C#3mo ago
Fenz

MSBuild : want to detect when files change for the target to be called

I am trying to write targets file that is imported by a project:
<ItemGroup>
<AssetFiles
Include="$(AssetsFullPath)/**/*"
Exclude="$(AssetsFullPath)/bin/*/**;$(AssetsFullPath)/obj/**/*;$(AssetsFullPath)/$(MGCBFileName)"/>
<AssetsDirItem
Include="$(AssetsFullPath)"/>

</ItemGroup>
<Target Name="FenzworkSetup" BeforeTargets="CoreCompile;BeforeBuild">

<PropertyGroup><AssetsTimestampFilePath>$(MSBuildProjectDirectory)/$(IntermediateOutputPath)assets_timestamp.cache</AssetsTimestampFilePath>
</PropertyGroup>

<ItemGroup>
<AssetsTimestamp Include="$(AssetsTimestampFilePath)"/>
</ItemGroup>

<Touch Condition="!Exists($(AssetsTimestampFilePath))"
Files="$(AssetsTimestampFilePath)"
AlwaysCreate="True"/>

</Target>
...
<Target Name="RunGenTool"
Inputs="@(AssetFiles)"
Outputs="@(AssetsTimestamp)">

<Message Text="Running GenTool..." Importance="High" />

<Touch Files="@(AssetsTimestamp)" AlwaysCreate="true" />

<Exec Condition="$(IsUsingSourceGenTool)"
Command="dotnet &quot;$(GenToolDllPath)&quot; build &quot;%(AssetsConfig.Identity)&quot; &quot;%(AssetsDirItem.Identity)&quot; &quot;$(IntermediateOutputPath)&quot;" />
</Target>
<ItemGroup>
<AssetFiles
Include="$(AssetsFullPath)/**/*"
Exclude="$(AssetsFullPath)/bin/*/**;$(AssetsFullPath)/obj/**/*;$(AssetsFullPath)/$(MGCBFileName)"/>
<AssetsDirItem
Include="$(AssetsFullPath)"/>

</ItemGroup>
<Target Name="FenzworkSetup" BeforeTargets="CoreCompile;BeforeBuild">

<PropertyGroup><AssetsTimestampFilePath>$(MSBuildProjectDirectory)/$(IntermediateOutputPath)assets_timestamp.cache</AssetsTimestampFilePath>
</PropertyGroup>

<ItemGroup>
<AssetsTimestamp Include="$(AssetsTimestampFilePath)"/>
</ItemGroup>

<Touch Condition="!Exists($(AssetsTimestampFilePath))"
Files="$(AssetsTimestampFilePath)"
AlwaysCreate="True"/>

</Target>
...
<Target Name="RunGenTool"
Inputs="@(AssetFiles)"
Outputs="@(AssetsTimestamp)">

<Message Text="Running GenTool..." Importance="High" />

<Touch Files="@(AssetsTimestamp)" AlwaysCreate="true" />

<Exec Condition="$(IsUsingSourceGenTool)"
Command="dotnet &quot;$(GenToolDllPath)&quot; build &quot;%(AssetsConfig.Identity)&quot; &quot;%(AssetsDirItem.Identity)&quot; &quot;$(IntermediateOutputPath)&quot;" />
</Target>
It doesn't call that target when i change the input files at all whatever i tried nothing. The way i know if it actually happend is by looking at that timestamp file Let me know if you need more context.
46 Replies
ero
ero3mo ago
$(MSBuildProjectDirectory)/$(IntermediateOutputPath) doesn't seem correct to me that would result in D:\Projects\MyProj\src\MyProj\D:\Projects\MyProj\src\MyProj\obj
Fenz
FenzOP3mo ago
nope IntermediateOutputPath gave me relative path
ero
ero3mo ago
interesting, well if it works
Fenz
FenzOP3mo ago
i removed it tho
ero
ero3mo ago
what's running RunGenTool? why this in FenzworkSetup
<ItemGroup>
<AssetsTimestamp Include="$(AssetsTimestampFilePath)" />
</ItemGroup>

<Touch Condition="!Exists($(AssetsTimestampFilePath))"
Files="$(AssetsTimestampFilePath)"
AlwaysCreate="True" />
<ItemGroup>
<AssetsTimestamp Include="$(AssetsTimestampFilePath)" />
</ItemGroup>

<Touch Condition="!Exists($(AssetsTimestampFilePath))"
Files="$(AssetsTimestampFilePath)"
AlwaysCreate="True" />
and then again
<Touch Files="@(AssetsTimestamp)" AlwaysCreate="true" />
<Touch Files="@(AssetsTimestamp)" AlwaysCreate="true" />
in RunGenTool?
Fenz
FenzOP3mo ago
its leftover of when i wanted it to create file but it shouldn't serve any purpose yeh
ero
ero3mo ago
is there anything that executes RunGenTool?
Fenz
FenzOP3mo ago
Like CallTarget nope Its like as you see
ero
ero3mo ago
well then what would possibly run it...?
Fenz
FenzOP3mo ago
Inputs outputs ?
MarkPflug
MarkPflug3mo ago
Yeah, there's nothing that's including the RunGenTool target in the targets graph. You probably need to add AfterTargets="FenzworkSetup" and BeforeTargets="BeforeBuild" to the RunGenTool target. There are few ways a target will be run: 1) being listed in the Initial/DefaultsTargets property on a project. 2) being the first target in the project xml graph. (I don't think this would be smart to depend on). 3) being related to 1) or 2) by using DependsOnTargets, BeforeTargets, AfterTargets 4) explicitly invoking via the CallTarget task. Your RunGenTool target doesn't satisfy any of those. Or I guess 5) explicitly specifying the target as a command line parameter to msbuild/dotnet build. But, without before/afterTargets, there's nothing to indicate when your target should run.
Fenz
FenzOP3mo ago
Wait so inputs outputs don't call the target they work as conditions?
MarkPflug
MarkPflug3mo ago
Correct. The target will still only be invoked if any of the inputs has a more recent modified date than any of the outputs.
Fenz
FenzOP3mo ago
does it detect deletion and creation of input files? it doesn't seem to detect them
MarkPflug
MarkPflug3mo ago
I assume that if an output file is missing, that would trigger the target to run. If an input is missing, I'd assume not. Creation would cause a newer modified date, so I'd expect that to behave normally. But, you have to fully list the inputs and outputs, so if your ourputs are non-deterministic, you might not be able to use the inputs/outputs mechanism. Indeed, the inputs/outputs is really just an opitimization to avoid running targets/tasks that don't need to do anything. But, they should be idempotent, so running them even when not needed should only be a waste of time.
Fenz
FenzOP3mo ago
So it can follow the items existence in list ?
<ItemGroup>
<AssetFiles
Include="$(AssetsFullPath)/**/*"
Exclude="$(AssetsFullPath)/bin/*/**;$(AssetsFullPath)/obj/**/*;$(AssetsFullPath)/$(MGCBFileName)"/>
</ItemGroup>
<ItemGroup>
<AssetFiles
Include="$(AssetsFullPath)/**/*"
Exclude="$(AssetsFullPath)/bin/*/**;$(AssetsFullPath)/obj/**/*;$(AssetsFullPath)/$(MGCBFileName)"/>
</ItemGroup>
Like i use this but next call will the content stay the same or reincluded? Like it works when modifying the file but not when deleting or creating a file
MarkPflug
MarkPflug3mo ago
Sorry, I don't understand what you're asking.
Fenz
FenzOP3mo ago
AssetsFiles if say has foo/bar;lorem.ipsum;something if something was deleted will it remain in the AssetsFiles?
MarkPflug
MarkPflug3mo ago
Yes. Though, it would be strange for an input file to be deleted during a build...
Fenz
FenzOP3mo ago
hmm i might misunderstood how msbuild work so when it wants to build it execute msbuild and all of the properties and items will not be saved across the executions is that how works ?
MarkPflug
MarkPflug3mo ago
No, nothing is saved between executions. The project is re-evaluated from scratch every time
Fenz
FenzOP3mo ago
ah i see welp i want to detect when a file is created or deleted that's my goal now
MarkPflug
MarkPflug3mo ago
During the build?
Fenz
FenzOP3mo ago
i use CoreCompile
MarkPflug
MarkPflug3mo ago
I don't know what you're trying to accomplish, so I'm not sure how to advise you.
Fenz
FenzOP3mo ago
which i don't know exactly when its called but it gets called behind the scene
MarkPflug
MarkPflug3mo ago
If you need to run your code before or after core compile, just tell MSBuild that using the Before/AfterTargets. BeforeTargets="CoreCompile" will cause your target to be invoked before CoreCompile.\
Fenz
FenzOP3mo ago
i did but Inputs and Outputs don't seem to see the creation nor deletion of the files
MarkPflug
MarkPflug3mo ago
creation or deletion of what files?
Fenz
FenzOP3mo ago
which i put in Inputs
MarkPflug
MarkPflug3mo ago
The inputs/outputs should ideally be deterministic. So, when you start your build all the assets files are the inputs. Why would assets be created/deleted during the build?
Fenz
FenzOP3mo ago
i use CoreCompile because it doesn't get called when building i ve heard its usefull to generate code from MSBuild so i used it
MarkPflug
MarkPflug3mo ago
Yes, generating code from MSBuild is very useful. So, your trying to take assets files, turn them into .cs files, and have those .cs files included in the C# compilation?
Fenz
FenzOP3mo ago
yeh something like this
MarkPflug
MarkPflug3mo ago
And you're having difficulty getting your Target that does the code gen to run? Have you removed the Inputs/Outputs attributes? I'd remove those first. Like I said, treat those as an optimization after you get everything else working.
Fenz
FenzOP3mo ago
but i wanna make sure they work or else i will create real-time tool to do the job maybe
MarkPflug
MarkPflug3mo ago
Make the Inputs/Outputs work after you make everything else work. They are an optimization only. If your process doesn't work, there's nothing to optimize.
MarkPflug
MarkPflug3mo ago
Incremental builds in MSBuild - MSBuild
Explore the incremental build feature in MSBuild, which produces builds that are optimized so up-to-date output files aren't executed.
Fenz
FenzOP3mo ago
it works not that it doesn't
MarkPflug
MarkPflug3mo ago
It works? Okay. What are we talking about then?
Fenz
FenzOP3mo ago
just need to know if thats worth the effort to make it to work once a file changed or created or removed with MSBuild or not with out the Inputs/Outputs sorry if im confusing
MarkPflug
MarkPflug3mo ago
So, maybe your Inputs/Outputs aren't what you think they are? Have you debugged the content of the Inputs/Outputs collection?
Fenz
FenzOP3mo ago
yes wait that's weird it called the target when i removed a file than it stoped doing that
MarkPflug
MarkPflug3mo ago
the file you removed was an input or output file?
Fenz
FenzOP3mo ago
oh wait no it worked cause i removed inputs outputs so the outputs is basically just a file that i touch when the target RunGenTool is called
MarkPflug
MarkPflug3mo ago
And you're finding that if you modify an input file (asset) then the target isn't running as you expect?

Did you find this page helpful?