0

Here is my code:

    <MSBuild Projects="outs.proj" Targets="Build;MyCustomTarget">
</MSBuild>

I need to check if MyCustomTarget exists in outs.proj before executing.

It throws an error when MyCustomTarget is not imported, so depending on the result use either Build or Build+MyCustomTarget.

Thanks in advance for your help.

Alphapage
  • 811
  • 1
  • 6
  • 26

1 Answers1

1

Getting a list of targets is cumbersome, you can either reflect on TaskHost via BuildEngine to get current Project or re-evaluate the project with an inline task.

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <UsingTask TaskName="Targets" TaskFactory="CodeTaskFactory" AssemblyName="Microsoft.Build.Tasks.v12.0">
    <ParameterGroup>
      <Project ParameterType="System.String" Required="true" />
      <All ParameterType="System.String[]" Output="true" />
      <Run ParameterType="System.String[]" Output="true" />
    </ParameterGroup>
    <Task>
      <Reference Include="Microsoft.Build" />
      <Reference Include="System.Xml" />
      <Code>
        All = new Microsoft.Build.Evaluation.Project(Project).Targets.Select(t => t.Key).ToArray();
        Run = Run.Where(All.Contains).ToArray();
      </Code>
    </Task>
  </UsingTask>

  <Target Name="Foo">
    <Targets Project="$(MSBuildProjectFullPath)" Run="Foo;Baz">
      <Output TaskParameter="All" ItemName="All" />
      <Output TaskParameter="Run" ItemName="Run" />
    </Targets>
    <Message Text="All Targets: @(All)" />
    <Message Text="Run Targets: @(Run)" />
  </Target>

  <Target Name="Bar" />
</Project>

Edit:

You don't provide much details so I can't help with your particular issue, but if new Project(Project) throws may be try ProjectCollection.GlobalProjectCollection.LoadProject(Project) instead to get to the Targets; the same collection has the LoadedProjects property as well as GetLoadedProjects and UnloadProject methods to play around with to get around your exception. If you're in control of the project file and it's flat with no Import you might want to try parsing it as a simple XML file rather than a fully fledged MSBuild project.

<XmlPeek XmlInputPath="$(MSBuildProjectFullPath)" Query="/p:Project/p:Target/@Name" Namespaces="&lt;Namespace Prefix='p' Uri='http://schemas.microsoft.com/developer/msbuild/2003' /&gt;">
  <Output TaskParameter="Result" ItemName="All" />
</XmlPeek>
<ItemGroup>
  <In Include="Foo;Baz" />
  <Out Include="@(In)" Exclude="@(All)" />
  <Run Include="@(In)" Exclude="@(Out)" />
</ItemGroup>
<Message Text"@(Run)" />

In either case, you pass outs.proj path to whichever method you choose go with and get back @(All) with all of the targets in that project (Foo;Bar) then you filter your targets down from Foo;Baz to just Foo since Baz doesn't exit in @(All). Then you do whatever you want to do with this information, e.g. <MSBuild Projects="outs.proj" Targets="Build;@(Run)">.

Community
  • 1
  • 1
Ilya Kozhevnikov
  • 9,626
  • 4
  • 34
  • 66
  • Sorry, my project is running inside Visual Studio, so I get the exception: project loaded, unload the project first. I'm also a little lost on how to apply the result to my MSBuild Projects="outs.proj" Targets="@(RunList)". How do I get RunList from outs.proj ? – Alphapage Aug 20 '14 at 17:27
  • I use ProjectCollection.GlobalProjectCollection.LoadProject(Project), but XmlPeek tip was another solution to the problem: just need recursion for imported files. 2 answers in 1. Really great ! – Alphapage Aug 21 '14 at 10:40