5

Given something like so..

 <?xml version="1.0" encoding="utf-8"?>
    <Project DefaultTargets="test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <ItemGroup>
            <ConfigFiles Include="*.config" />

            <DatabaseConfig Include="ABC">
                <Database>DB1</Database>
                <CsString>Database</CsString>
            </DatabaseConfig>

            <DatabaseConfig Include="DEF">
                <Database>DB2</Database>
                <CsString>Logging</CsString>
            </DatabaseConfig>
        </ItemGroup>


        <Target Name="test" >
            <!-- Some sort of join here (or somewhere)... -->
         <Message Text=" %(Combined.ConfigFile) %(Combined.Database) " />
        </Target> 
    </Project>

I'd like the Output to be something like this.. (given two files one.config & two.config)

one.config DB1
two.config DB1
one.config DB2
two.config DB2

(the order is not important, just the full cartesian product of the two ItemGroups)

Dog Ears
  • 9,637
  • 5
  • 37
  • 54

2 Answers2

5

This seems like a tidy solution:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <ItemGroup>
        <ConfigFiles Include="*.config" />

        <DatabaseConfig Include="ABC">
            <Database>DB1</Database>
            <CsString>Database</CsString>
        </DatabaseConfig>

        <DatabaseConfig Include="DEF">
            <Database>DB2</Database>
            <CsString>Logging</CsString>
        </DatabaseConfig>
    </ItemGroup>

    <Target Name="test" >
        <ItemGroup>
            <Combined Include="@(DatabaseConfig)">
                <ConfigFile>%(ConfigFiles.Identity)</ConfigFile>
            </Combined> 
        </ItemGroup>
    <Message Text=" %(Combined.ConfigFile) %(Combined.Database) " />
    </Target> 
</Project>
Dog Ears
  • 9,637
  • 5
  • 37
  • 54
  • This will not combine the item metadata in the way you wanted or produce the desired output. This will just output `DB1` on one line and `DB2` on another line. The answer I suggested allows for flexibility when combining metadata and will produce the outputs you asked for. – Michael Apr 10 '13 at 15:10
  • 1
    Ah, I see what went wrong when I tried locally: when I tried it I think the path to my sample configs was wrong. Yours provides the full path to the config instead of just the config filename/extension. – Michael Apr 10 '13 at 15:59
  • Hahaha, yours is the condensed answer after all. ;-) – Michael Apr 10 '13 at 16:28
4

There is a way you can do this with minimal changes to your existing sample code. You can combine metadata from ConfigFiles items and DatabaseConfig items into a new "combined" item and then output that "combined" item.

To combine the metadata, use target batching with the batched target running once for each DatabaseConfig item. Then you can call another target to output the combined metadata to get the output you described. Take a look at my extension of your sample code to see how this would all be accomplished:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <ItemGroup>
    <ConfigFiles Include="*.config" />

    <DatabaseConfig Include="ABC">
      <Database>DB1</Database>
      <CsString>Database</CsString>
    </DatabaseConfig>

    <DatabaseConfig Include="DEF">
      <Database>DB2</Database>
      <CsString>Logging</CsString>
    </DatabaseConfig>
  </ItemGroup>

  <Target Name="test" DependsOnTargets="test_setup;test_output" >
    <!-- Logic here runs after targets listed in "DependsOnTargets". -->
  </Target>

  <!-- This will run once for each "DatabaseConfig" item. -->
  <Target Name="test_setup" Outputs="%(DatabaseConfig.Identity)">
    <PropertyGroup>
      <!-- Specify the Database for the current DatabaseConfig item -->
      <CurrentDb>%(DatabaseConfig.Database)</CurrentDb>
    </PropertyGroup>
    <ItemGroup>
      <!-- Add a new CombinedOutput item with each run, combining metadata. -->
      <CombinedOutput Include=" %(ConfigFiles.FileName)%(ConfigFiles.Extension) $(CurrentDb) " />
    </ItemGroup>
  </Target>

  <Target Name="test_output">
    <!-- Output the combined metadata from the CombinedOutput items -->
    <Message Text=" %(CombinedOutput.Identity) " />
  </Target>

</Project>

What's happening in the sample:

  1. The test target now just serves as a way to call two other targets to perform the work: test_setup, and test_output
  2. The test_setup target is batched and creates the new CombinedOutput items.
  3. The test_output target is called after test_setup to output the CombinedOutput items' metadata.

Output from test_output:

one.config DB1
two.config DB1
one.config DB2
two.config DB2
Michael
  • 1,272
  • 1
  • 9
  • 18
  • I should have more explicit, I need the two variables to be separate. The message is just to test, the actual variables will be used in an XmlUpdate step. – Dog Ears Apr 10 '13 at 16:17
  • Well, yeah that would have been good to know up front. ;-) My suggestion answers your original criteria but does not focus on keeping those variables separate. – Michael Apr 10 '13 at 16:29