Reputation: 2524
I'm trying to achieve the following with MSBuild: my main project (MyProject.csproj
) should include a couple Reference
items, but the path to one of those Reference
s is the value of the SomeProperty
property, which is set by a Target. Specifically, the value for SomeProperty
is parsed from a file using ReadLinesFromFileTask
.
Here is the high-level structure of MyProject.csproj
:
<Project>
<Target Name="CreateSomeProperty">
<!-- Tasks that ultimately set $(SomeProperty) by parsing a value with ReadLinesFromFileTask -->
</Target>
<ItemGroup>
<Reference Include="$(SomeProperty)" />
<!-- Other Reference items -->
</ItemGroup>
</Project>
Unfortunately, this setup is not working. I see those little yellow triangles under the Dependencies
node of MyProject
in the VS Solution Explorer, since the project is looking for a DLL at a path with missing characters. Similarly, when I build the project, I get a bunch of The type or namespace name could not be found
errors, even though I still see the output from a Message
Task inside my Target. Presumably, the CreatePathProperty
Target is running during the execution phase, after the Reference
items have already failed to load during the evaluation phase.
Is there a way to make a setup like this work? I've tried setting BeforeTargets="Build"
in the Target
element, and setting InitialTargets="CreateSomeProperty"
in the Project
element, but nothing seems to work. Any help would be much appreciated!
Upvotes: 5
Views: 2971
Reputation: 2820
In msbuild, static items can't depend on dynamic properties by design.
Accepted answer provides a work-around: use dynamic items, which, naturally, can depend on dynamic properties. But dynamic items don't show up in the IDE.
When dealing with files, more IDE-friendly approach is to create the item statically and update it when the target runs:
<!-- Static item definition -->
<ItemGroup>
<SomeItem Include="item_file" />
</ItemGroup>
<Target Name="...">
<!-- When the target runs, find the static item using Condition and change its properties -->
<ItemGroup>
<SomeItem Condition="'%(SomeItem.Identity)' == 'item_file'">
<SomeProperty>New Value</SomeProperty>
</SomeItem>
</ItemGroup>
</Target>
There's also an open msbuild issue (#889) to support improved syntax for this:
<Target Name="...">
<ItemGroup>
<!-- Update has the same syntax as Include -->
<SomeItem Update="item_file">
<SomeProperty>New Value</SomeProperty>
</SomeItem>
</ItemGroup>
</Target>
Upvotes: 1
Reputation: 28126
Can an MSBuild Item use a Property set by a Target?
Yes, I'm sure it's possible if you're in .net framework project with old csproj format
and what you want is a supported scenario in VS2017(Only did the test in VS2017).
Tips:
Normally msbuild
reads the Properties
and Items
before it executes your custom target. So we should use something like BeforeTargets="BeforeResolveReferences"
to make sure the correct order in this scenario is custom target runs=>create the property=>msbuild reads the info about references and the property
.
Otherwise the order(wrong order when BeforeTargets="Build"
or what) should be: Msbuild reads the info about references(now the property is not defined yet)=>the target runs and creates the property
.
Solution: Add this script to the bottom of your xx.csproj.
<!-- Make sure it executes before msbuild reads the ItemGroup above and loads the references -->
<Target Name="MyTest" BeforeTargets="BeforeResolveReferences">
<ItemGroup>
<!-- Define a TestFile to represent the file I read -->
<TestFile Include="D:\test.txt" />
</ItemGroup>
<!-- Pass the file to read to the ReadLinesFromFile task -->
<ReadLinesFromFile File="@(TestFile)">
<!--Create the Property that represents the normal hintpath(SomePath\DLLName.dll)-->
<Output TaskParameter="Lines" PropertyName="HintPathFromFile" />
</ReadLinesFromFile>
<!-- Output the HintPath in output log to check if the path is right -->
<Message Text="$(HintPathFromFile)" Importance="high" />
<ItemGroup>
<Reference Include="TestReference">
<!--It actually equals:<HintPath>D:\AssemblyFolder\TestReference.dll</HintPath>-->
<HintPath>$(HintPathFromFile)</HintPath>
</Reference>
</ItemGroup>
</Target>
In addition:
I did the test with test.txt
file whose content is:
I'm not sure about the actual content(and format) of your file, but if you only have path like D:\AssemblyFolder\
in that file, you should pass the D:\AssemblyFolder\+YourAssemblyName.dll
to <HintPath>
metadata. Cause the default reference
format with hintpath
looks like this:
<Reference Include="ClassLibrary1">
<HintPath>path\ClassLibrary1.dll</HintPath>
</Reference>
Pay attention to the path format! Hope my answer helps :)
Upvotes: 5