Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can an MSBuild Item use a Property set by a Target?

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 References 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!

like image 567
Rabadash8820 Avatar asked Oct 15 '22 09:10

Rabadash8820


1 Answers

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:

enter image description here

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 :)

like image 108
LoLance Avatar answered Oct 20 '22 05:10

LoLance