|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Xml;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Xunit;
using InvalidProjectFileException = Microsoft.Build.Exceptions.InvalidProjectFileException;
using TaskItem = Microsoft.Build.Execution.ProjectItemInstance.TaskItem;
#nullable disable
namespace Microsoft.Build.UnitTests.Definition
{
/// <summary>
/// Class containing tests for the ProjectItemDefinition and related functionality.
/// </summary>
public class ItemDefinitionGroup_Tests
{
/// <summary>
/// Test for item definition group definitions showing up in project.
/// </summary>
[Fact]
public void ItemDefinitionGroupExistsInProject()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First>1st</First>
<Second>2nd</Second>
</Compile>
</ItemDefinitionGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(ContainsMetadata(p.ItemDefinitions["Compile"].Metadata, "First", "1st"));
Assert.True(ContainsMetadata(p.ItemDefinitions["Compile"].Metadata, "Second", "2nd"));
}
/// <summary>
/// Test for multiple item definition group definitions showing up in project.
/// </summary>
[Fact]
public void MultipleItemDefinitionGroupExistsInProject()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First>1st</First>
<Second>2nd</Second>
</Compile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<Link>
<Third>3rd</Third>
<Fourth>4th</Fourth>
</Link>
</ItemDefinitionGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(ContainsMetadata(p.ItemDefinitions["Compile"].Metadata, "First", "1st"));
Assert.True(ContainsMetadata(p.ItemDefinitions["Compile"].Metadata, "Second", "2nd"));
Assert.True(ContainsMetadata(p.ItemDefinitions["Link"].Metadata, "Third", "3rd"));
Assert.True(ContainsMetadata(p.ItemDefinitions["Link"].Metadata, "Fourth", "4th"));
}
/// <summary>
/// Tests that items with no metadata inherit from item definition groups
/// </summary>
[Fact]
public void EmptyItemsInheritValues()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First>1st</First>
<Second>2nd</Second>
</Compile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<Link>
<Third>3rd</Third>
<Fourth>4th</Fourth>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs' />
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
Assert.True(ItemContainsMetadata(p, "Compile", "b.cs", "First", "1st"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Second", "2nd"));
Assert.True(ItemContainsMetadata(p, "Compile", "b.cs", "Second", "2nd"));
Assert.False(ItemContainsMetadata(p, "Compile", "a.cs", "Third", "3rd"));
}
/// <summary>
/// Tests that items with metadata override inherited metadata of the same name
/// </summary>
[Fact]
public void ItemMetadataOverridesInheritedValues()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First>1st</First>
<Second>2nd</Second>
</Compile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<Link>
<Third>3rd</Third>
<Fourth>4th</Fourth>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs'>
<Foo>Bar</Foo>
<First>Not1st</First>
</Compile>
<Compile Include='b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<ItemGroup>
<Link Include='a.o'/>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "First", "Not1st"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Second", "2nd"));
Assert.True(ItemContainsMetadata(p, "Compile", "b.cs", "First", "1st"));
Assert.True(ItemContainsMetadata(p, "Compile", "b.cs", "Second", "2nd"));
Assert.True(ItemContainsMetadata(p, "Link", "a.o", "Third", "3rd"));
Assert.True(ItemContainsMetadata(p, "Link", "a.o", "Fourth", "4th"));
}
/// <summary>
/// Tests that item definition doesn't allow item expansion for the conditional.
/// </summary>
[Fact]
public void ItemDefinitionDoesntAllowItemExpansion()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
Project p = new Project(XmlReader.Create(new StringReader(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemGroup>
<Compile Include='a.cs'>
<Foo>Bar</Foo>
<First>Not1st</First>
</Compile>
<Compile Include='b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<ItemDefinitionGroup>
<Compile Condition=""'@(Compile)'!=''"">
<First>1st</First>
<Second>2nd</Second>
</Compile>
</ItemDefinitionGroup>
<Target Name='Build' />
</Project>")));
});
}
/// <summary>
/// Tests that item definition metadata doesn't allow item expansion for the conditional.
/// </summary>
[Fact]
public void ItemDefinitionMetadataConditionDoesntAllowItemExpansion()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
Project p = new Project(XmlReader.Create(new StringReader(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemGroup>
<Compile Include='a.cs'>
<Foo>Bar</Foo>
<First>Not1st</First>
</Compile>
<Compile Include='b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<ItemDefinitionGroup>
<Compile>
<First Condition=""'@(Compile)'!=''"">1st</First>
</Compile>
</ItemDefinitionGroup>
<Target Name='Build' />
</Project>")));
});
}
/// <summary>
/// Tests that item definition metadata doesn't allow item expansion for the value.
/// </summary>
[Fact]
public void ItemDefinitionMetadataDoesntAllowItemExpansion()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
Project p = new Project(XmlReader.Create(new StringReader(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemGroup>
<Compile Include='a.cs'>
<Foo>Bar</Foo>
<First>Not1st</First>
</Compile>
<Compile Include='b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<ItemDefinitionGroup>
<Compile>
<First>@(Compile)</First>
</Compile>
</ItemDefinitionGroup>
<Target Name='Build' />
</Project>")));
});
}
/// <summary>
/// Tests that item metadata which contains a metadata expansion referring to an item type other
/// than the one this item definition refers to expands to blank.
/// </summary>
[Fact]
public void ItemMetadataReferringToDifferentItemGivesEmptyValue()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First>1st</First>
<Second>2nd</Second>
</Compile>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<Link>
<Third>--%(Compile.First)--</Third>
<Fourth>4th</Fourth>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs'>
<Foo>Bar</Foo>
<First>Not1st</First>
</Compile>
<Compile Include='b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<ItemGroup>
<Link Include='a.o'/>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(ItemContainsMetadata(p, "Link", "a.o", "Third", "----"));
}
/// <summary>
/// Tests that empty item definition groups are OK.
/// </summary>
[Fact]
public void EmptyItemDefinitionGroup()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs' />
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
}
/// <summary>
/// Tests that item definition groups with empty item definitions are OK.
/// </summary>
[Fact]
public void EmptyItemDefinitions()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile />
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.True(ItemContainsMetadata(p, "Compile", "b.cs", "Foo", "Bar"));
}
[Fact]
public void SelfReferencingMetadataReferencesUseItemDefinition()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<ItemGroup>
<CppCompile Include='a.cpp'>
<Defines>%(Defines);CODEANALYSIS</Defines>
</CppCompile>
</ItemGroup>
<Target Name='Build'>
<Message Text='[{@(CppCompile)}{%(CppCompile.Defines)}]' />
</Target>
</Project>");
Project p = projectFromString.Project;
p.Build(new string[] { "Build" }, new ILogger[] { logger });
logger.AssertLogContains("[{a.cpp}{DEBUG;CODEANALYSIS}]"); // Unexpected value after evaluation
}
[Fact]
public void SelfReferencingMetadataReferencesUseItemDefinitionInTarget()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<Target Name='Build'>
<ItemGroup>
<CppCompile Include='a.cpp'>
<Defines>%(Defines);CODEANALYSIS</Defines>
</CppCompile>
</ItemGroup>
<Message Text='[{@(CppCompile)}{%(CppCompile.Defines)}]' />
</Target>
</Project>");
Project p = projectFromString.Project;
p.Build(new string[] { "Build" }, new ILogger[] { logger });
logger.AssertLogContains("[{a.cpp}{DEBUG;CODEANALYSIS}]"); // Unexpected value after evaluation
}
[Fact]
public void SelfReferencingMetadataReferencesUseItemDefinitionInTargetModify()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<ItemGroup>
<CppCompile Include='a.cpp' />
</ItemGroup>
<Target Name='Build'>
<ItemGroup>
<CppCompile>
<Defines>%(Defines);CODEANALYSIS</Defines>
</CppCompile>
</ItemGroup>
<Message Text='[{@(CppCompile)}{%(CppCompile.Defines)}]' />
</Target>
</Project>");
Project p = projectFromString.Project;
p.Build(new string[] { "Build" }, new ILogger[] { logger });
logger.AssertLogContains("[{a.cpp}{DEBUG;CODEANALYSIS}]"); // Unexpected value after evaluation
}
/// <summary>
/// Tests that item definition groups with false conditions don't produce definitions
/// </summary>
[Fact]
public void ItemDefinitionGroupWithFalseCondition()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup Condition=""'$(Foo)'!=''"">
<Compile>
<First>1st</First>
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.False(p.ItemDefinitions.ContainsKey("Compile"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.False(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
}
/// <summary>
/// Tests that item definition groups with true conditions produce definitions
/// </summary>
[Fact]
public void ItemDefinitionGroupWithTrueCondition()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup Condition=""'$(Foo)'==''"">
<Compile>
<First>1st</First>
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("Compile"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
}
/// <summary>
/// Tests that item definition with false conditions don't produce definitions
/// </summary>
[Fact]
public void ItemDefinitionWithFalseCondition()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile Condition=""'$(Foo)'!=''"">
<First>1st</First>
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.False(p.ItemDefinitions.ContainsKey("Compile"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.False(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
}
/// <summary>
/// Tests that item definition with true conditions produce definitions
/// </summary>
[Fact]
public void ItemDefinitionWithTrueCondition()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile Condition=""'$(Foo)'==''"">
<First>1st</First>
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("Compile"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
}
/// <summary>
/// Tests that item definition metadata with false conditions don't produce definitions
/// </summary>
[Fact]
public void ItemDefinitionMetadataWithFalseCondition()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First Condition=""'$(Foo)'!=''"">1st</First>
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("Compile"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.False(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
}
/// <summary>
/// Tests that item definition metadata with true conditions produce definitions
/// </summary>
[Fact]
public void ItemDefinitionMetadataWithTrueCondition()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<Compile>
<First Condition=""'$(Foo)'==''"">1st</First>
</Compile>
</ItemDefinitionGroup>
<ItemGroup>
<Compile Include='a.cs;b.cs'>
<Foo>Bar</Foo>
</Compile>
</ItemGroup>
<Target Name='Build' />
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("Compile"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "Foo", "Bar"));
Assert.True(ItemContainsMetadata(p, "Compile", "a.cs", "First", "1st"));
}
/// <summary>
/// Tests that item definition metadata is correctly copied to a destination item
/// </summary>
[Fact]
public void ItemDefinitionMetadataCopiedToTaskItem()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<ItemA>
<MetaA>M-A(b)</MetaA>
<MetaB>M-B(b)</MetaB>
</ItemA>
</ItemDefinitionGroup>
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("ItemA"));
ProjectInstance pi = p.CreateProjectInstance();
ITaskItem withMetaItem;
List<ProjectItemDefinitionInstance> itemdefs = new List<ProjectItemDefinitionInstance>();
itemdefs.Add(pi.ItemDefinitions["ItemA"]);
ITaskItem noMetaItem = new TaskItem("NoMetaItem", pi.FullPath);
withMetaItem = new TaskItem("WithMetaItem", "WithMetaItem", null, itemdefs, ".", false, pi.FullPath);
// Copy the metadata on the item with no metadata onto the item with metadata
// from an item definition. The destination item's metadata should be maintained
noMetaItem.CopyMetadataTo(withMetaItem);
Assert.Equal("M-A(b)", withMetaItem.GetMetadata("MetaA"));
Assert.Equal("M-B(b)", withMetaItem.GetMetadata("MetaB"));
}
/// <summary>
/// Tests that item definition metadata is correctly copied to a destination item
/// </summary>
[Fact]
public void ItemDefinitionMetadataCopiedToTaskItem2()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<ItemA>
<MetaA>M-A(b)</MetaA>
<MetaB>M-B(b)</MetaB>
</ItemA>
</ItemDefinitionGroup>
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("ItemA"));
ProjectInstance pi = p.CreateProjectInstance();
ITaskItem withMetaItem;
List<ProjectItemDefinitionInstance> itemdefs = new List<ProjectItemDefinitionInstance>();
itemdefs.Add(pi.ItemDefinitions["ItemA"]);
ITaskItem noMetaItem = new TaskItem("NoMetaItem", pi.FullPath);
noMetaItem.SetMetadata("MetaA", "NEWMETA_A");
withMetaItem = new TaskItem("WithMetaItem", "WithMetaItem", null, itemdefs, ".", false, pi.FullPath);
// Copy the metadata on the item with no metadata onto the item with metadata
// from an item definition. The destination item's metadata should be maintained
noMetaItem.CopyMetadataTo(withMetaItem);
// New direct metadata takes precedence over item definitions on the destination item
Assert.Equal("NEWMETA_A", withMetaItem.GetMetadata("MetaA"));
Assert.Equal("M-B(b)", withMetaItem.GetMetadata("MetaB"));
}
/// <summary>
/// Tests that item definition metadata is correctly copied to a destination item
/// </summary>
[Fact]
public void ItemDefinitionMetadataCopiedToTaskItem3()
{
using ProjectFromString projectFromString = new(
@"<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<ItemA>
<MetaA>M-A(b)</MetaA>
<MetaB>M-B(b)</MetaB>
</ItemA>
</ItemDefinitionGroup>
<ItemGroup>
<ItemA Include='SomeItemA' />
</ItemGroup>
</Project>");
Project p = projectFromString.Project;
Assert.True(p.ItemDefinitions.ContainsKey("ItemA"));
ProjectInstance pi = p.CreateProjectInstance();
ITaskItem withMetaItem = null;
List<ProjectItemDefinitionInstance> itemdefs = new List<ProjectItemDefinitionInstance>();
itemdefs.Add(pi.ItemDefinitions["ItemA"]);
ITaskItem noMetaItem = new TaskItem("NoMetaItem", pi.FullPath);
// No the ideal way to get the first item, but there is no other way since GetItems returns an IEnumerable :(
foreach (ProjectItemInstance item in pi.GetItems("ItemA"))
{
withMetaItem = item;
}
// Copy the metadata on the item with no metadata onto the item with metadata
// from an item definition. The destination item's metadata should be maintained
noMetaItem.CopyMetadataTo(withMetaItem);
Assert.Equal("M-A(b)", withMetaItem.GetMetadata("MetaA"));
Assert.Equal("M-B(b)", withMetaItem.GetMetadata("MetaB"));
}
#region Project tests
[Fact]
public void BasicItemDefinitionInProject()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<CppCompile Include='a.cpp'/>
</ItemGroup>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<ItemGroup>
<CppCompile Include='b.cpp'/>
</ItemGroup>
<Target Name=""t"">
<Message Text=""[%(CppCompile.Identity)==%(CppCompile.Defines)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[a.cpp==DEBUG]", "[b.cpp==DEBUG]");
}
[Fact]
public void EscapingInItemDefinitionInProject()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup Condition=""'%24'=='$'"">
<i Condition=""'%24'=='$'"">
<m Condition=""'%24'=='$'"">%24(xyz)</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[$(xyz)]");
}
[Fact]
public void ItemDefinitionForOtherItemType()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<j>
<m>m1</m>
</j>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[]");
}
[Fact]
public void RedefinitionLastOneWins()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i>
<m>m2</m>
<o>o1</o>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)-%(i.n)-%(i.o)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2-n1-o1]");
}
[Fact]
public void ItemExpressionInDefaultMetadataValueErrors()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
// We don't allow item expressions on an ItemDefinitionGroup because there are no items when IDG is evaluated.
MockLogger logger = new MockLogger();
Project p = new Project(XmlReader.Create(new StringReader(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>@(x)</m>
</i>
</ItemDefinitionGroup>
</Project>
")));
p.Build("t", new ILogger[] { logger });
});
}
[Fact]
public void UnqualifiedMetadataConditionOnItemDefinitionGroupErrors()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
// We don't allow unqualified metadata on an ItemDefinitionGroup because we don't know what item type it refers to.
MockLogger logger = new MockLogger();
Project p = new Project(XmlReader.Create(new StringReader(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup Condition=""'%(m)'=='m1'""/>
</Project>
")));
p.Build("t", new ILogger[] { logger });
});
}
[Fact]
public void QualifiedMetadataConditionOnItemDefinitionGroupErrors()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
// We don't allow qualified metadata because it's not worth distinguishing from unqualified, when you can just move the condition to the child.
MockLogger logger = new MockLogger();
Project p = new Project(XmlReader.Create(new StringReader(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup Condition=""'%(x.m)'=='m1'""/>
</Project>
")));
p.Build("t", new ILogger[] { logger });
});
}
[Fact]
public void MetadataConditionOnItemDefinition()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
<j Include='j1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
<j>
<n>n1</n>
</j>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i Condition=""'%(m)'=='m1'"">
<m>m2</m>
</i>
<!-- verify j metadata is distinct -->
<j Condition=""'%(j.n)'=='n1' and '%(n)'=='n1'"">
<n>n2</n>
</j>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
<Message Text=""[%(j.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2]", "[n2]");
}
[Fact]
public void QualifiedMetadataConditionOnItemDefinitionBothQualifiedAndUnqualified()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i Condition=""'%(i.m)'=='m1' and '%(m)'=='m1'"">
<m>m2</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2]");
}
[Fact]
public void FalseMetadataConditionOnItemDefinitionBothQualifiedAndUnqualified()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i Condition=""'%(m)'=='m2' or '%(i.m)'!='m1'"">
<m>m3</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1]");
}
[Fact]
public void MetadataConditionOnItemDefinitionChildBothQualifiedAndUnqualified()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i>
<m Condition=""'%(m)'=='m1' and '%(n)'=='n1' and '%(i.m)'=='m1'"">m2</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2]");
}
[Fact]
public void FalseMetadataConditionOnItemDefinitionChildBothQualifiedAndUnqualified()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i>
<m Condition=""'%(m)'=='m2' or !('%(n)'=='n1') or '%(i.m)' != 'm1'"">m3</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1]");
}
[Fact]
public void MetadataConditionOnItemDefinitionAndChildQualifiedWithUnrelatedItemType()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<ItemDefinitionGroup>
<i Condition=""'%(j.m)'=='' and '%(j.m)'!='x'"">
<m Condition=""'%(j.m)'=='' and '%(j.m)'!='x'"">m2</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2]");
}
/// <summary>
/// Make ItemDefinitionGroup inside a target produce a nice error.
/// It will normally produce an error due to the invalid child tag, but
/// we want to error even if there's no child tag. This will make it
/// easier to support it inside targets in a future version.
/// </summary>
[Fact]
public void ItemDefinitionInTargetErrors()
{
Assert.Throws<InvalidProjectFileException>(() =>
{
MockLogger logger = new MockLogger();
Project p = new Project(XmlReader.Create(new StringReader(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<Target Name=""t"">
<ItemDefinitionGroup/>
</Target>
</Project>
")));
bool result = p.Build("t", new ILogger[] { logger });
});
}
#if FEATURE_ASSEMBLY_LOCATION
// Verify that anyone with a task named "ItemDefinitionGroup" can still
// use it by fully qualifying the name.
[Fact]
public void ItemDefinitionGroupTask()
{
MockLogger ml = Helpers.BuildProjectWithNewOMExpectSuccess(String.Format(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<UsingTask TaskName=""ItemDefinitionGroup"" AssemblyFile=""{0}""/>
<Target Name=""Build"">
<Microsoft.Build.UnitTests.Definition.ItemDefinitionGroup/>
</Target>
</Project>
", new Uri(Assembly.GetExecutingAssembly().EscapedCodeBase).LocalPath));
Assert.Contains("In ItemDefinitionGroup task.", ml.FullLog);
}
#endif
[Fact]
public void MetadataOnItemWins()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<CppCompile Include='a.cpp'>
<Defines>RETAIL</Defines>
</CppCompile>
<CppCompile Include='b.cpp'/>
</ItemGroup>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(CppCompile.Identity)==%(CppCompile.Defines)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[a.cpp==RETAIL]", "[b.cpp==DEBUG]");
}
[Fact]
public void MixtureOfItemAndDefaultMetadata()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<CppCompile Include='a.cpp'>
<WarningLevel>4</WarningLevel>
</CppCompile>
</ItemGroup>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(CppCompile.Identity)==%(CppCompile.Defines)]""/>
<Message Text=""[%(CppCompile.Identity)==%(CppCompile.WarningLevel)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[a.cpp==DEBUG]", "[a.cpp==4]");
}
[Fact]
public void IntrinsicTaskModifyingDefaultMetadata()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<ItemGroup>
<i>
<m>m2</m>
</i>
</ItemGroup>
<Message Text=""[%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2]");
}
[Fact]
public void IntrinsicTaskConsumingDefaultMetadata()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<ItemGroup>
<i Condition=""'%(i.m)'=='m1'"">
<n Condition=""'%(m)'=='m1'"">n2</n>
</i>
</ItemGroup>
<Message Text=""[%(i.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[n2]");
}
[Fact]
public void DefinitionInImportedFile()
{
MockLogger logger = new MockLogger();
string importedFile = null;
try
{
importedFile = FileUtilities.GetTemporaryFileName();
File.WriteAllText(importedFile, @"
<Project ToolsVersion='msbuilddefaulttoolsversion'>
<ItemDefinitionGroup>
<CppCompile>
<Defines>DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
</Project>
");
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<CppCompile Include='a.cpp'/>
</ItemGroup>
<Import Project='" + importedFile + @"'/>
<Target Name=""t"">
<Message Text=""[%(CppCompile.Identity)==%(CppCompile.Defines)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[a.cpp==DEBUG]");
}
finally
{
ObjectModelHelpers.DeleteTempFiles(new string[] { importedFile });
}
}
/// <summary>
/// Item added to project should pick up the item
/// definitions that project has.
/// </summary>
[Fact]
public void ProjectAddNewItemPicksUpProjectItemDefinitions()
{
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
</Project>
");
Project p = projectFromString.Project;
p.AddItem("i", "i1");
p.ReevaluateIfNecessary();
Assert.True(ItemContainsMetadata(p, "i", "i1", "m", "m1"));
}
/// <summary>
/// Item added to project should pick up the item
/// definitions that project has.
/// </summary>
[Fact]
public void ProjectAddNewItemExistingGroupPicksUpProjectItemDefinitions()
{
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<ItemGroup>
<i Include='i2'>
<m>m2</m>
</i>
</ItemGroup>
</Project>
");
Project p = projectFromString.Project;
p.AddItem("i", "i1");
p.ReevaluateIfNecessary();
Assert.True(ItemContainsMetadata(p, "i", "i1", "m", "m1"));
Assert.True(ItemContainsMetadata(p, "i", "i2", "m", "m2"));
}
[Fact]
public void ItemsEmittedByTaskPickUpItemDefinitions()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<CreateItem Include=""i1"" AdditionalMetadata=""n=n2"">
<Output ItemName=""i"" TaskParameter=""Include""/>
</CreateItem>
<Message Text=""[%(i.m)][%(i.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1][n2]");
}
[Fact]
public void ItemsEmittedByIntrinsicTaskPickUpItemDefinitions()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<ItemGroup>
<i Include=""i1"">
<n>n2</n>
</i>
</ItemGroup>
<Message Text=""[%(i.m)][%(i.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1][n2]");
}
/// <summary>
/// When items are passed with an item list expression, default metadata values on the source
/// items should become regular metadata values on the new items, unless overridden.
/// </summary>
[Fact]
public void ItemsEmittedByIntrinsicTaskConsumingItemExpression_SourceDefaultMetadataPassed()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""i1""/>
</ItemGroup>
<Target Name=""t"">
<ItemGroup>
<j Include=""@(i)""/>
</ItemGroup>
<Message Text=""[%(j.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1]");
}
/// <summary>
/// Default metadata on the source item list is overridden by matching metadata explicitly on the destination
/// </summary>
[Fact]
public void ItemsEmittedByIntrinsicTaskConsumingItemExpression_DestinationExplicitMetadataBeatsSourceDefaultMetadata()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""i1""/>
</ItemGroup>
<Target Name=""t"">
<ItemGroup>
<j Include=""@(i)"">
<m>m2</m>
</j>
</ItemGroup>
<Message Text=""[%(j.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m2]");
}
/// <summary>
/// When items of type X are copied into a list of type Y, default metadata applicable to type X override
/// any matching default metadata applicable to type Y.
/// </summary>
/// <remarks>
/// Either behavior here is fairly reasonable. We decided on this way around based on feedback from VC.
/// Note: this differs from how Orcas did it.
/// </remarks>
[Fact]
public void ItemsEmittedByIntrinsicTaskConsumingItemExpression_DestinationDefaultMetadataOverriddenBySourceDefaultMetadata()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
<j>
<m>m2</m>
</j>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""n1""/>
<j Include=""@(i)""/>
</ItemGroup>
<Target Name=""t"">
<ItemGroup>
<i Include=""n2""/>
<j Include=""@(i)""/>
</ItemGroup>
<Message Text=""[%(j.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
Assert.Equal("m1", p.GetItems("j").First().GetMetadataValue("m"));
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1]");
logger.AssertLogDoesntContain("[m2]");
}
/// <summary>
/// Default and explicit metadata on both source and destination.
/// Item definition metadata from the source override item definition on the destination.
/// </summary>
[Fact]
public void ItemsEmittedByIntrinsicTaskConsumingItemExpression_Combination_OutsideTarget()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>im1</m>
<n>in1</n>
<o>io1</o>
<p>ip1</p>
</i>
<j>
<m>jm3</m>
<n>jn3</n>
<q>jq3</q>
</j>
<k>
<m>km4</m>
<q>kq4</q>
<r>kr4</r>
</k>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""1"">
<o>io2</o>
<s>is2</s>
</i>
<j Include=""2""/>
<k Include=""3"">
<M>km5</M>
</k>
</ItemGroup>
<ItemGroup>
<j Include=""@(i)"">
<m>jm6</m>
</j>
<k Include=""@(j)"">
<s>ks3</s>
</k>
</ItemGroup>
</Project>
");
Project p = projectFromString.Project;
Assert.Equal("im1", p.GetItems("i").First().GetMetadataValue("m"));
Assert.Equal("in1", p.GetItems("i").First().GetMetadataValue("n"));
Assert.Equal("io2", p.GetItems("i").First().GetMetadataValue("o"));
Assert.Equal("ip1", p.GetItems("i").First().GetMetadataValue("p"));
Assert.Equal("", p.GetItems("i").First().GetMetadataValue("q"));
Assert.Equal("jm3", p.GetItems("j").First().GetMetadataValue("m"));
Assert.Equal("jn3", p.GetItems("j").First().GetMetadataValue("n"));
Assert.Equal("", p.GetItems("j").First().GetMetadataValue("o"));
Assert.Equal("", p.GetItems("j").First().GetMetadataValue("p"));
Assert.Equal("jq3", p.GetItems("j").First().GetMetadataValue("q"));
Assert.Equal("jm6", p.GetItems("j").ElementAt(1).GetMetadataValue("m"));
Assert.Equal("in1", p.GetItems("j").ElementAt(1).GetMetadataValue("n"));
Assert.Equal("io2", p.GetItems("j").ElementAt(1).GetMetadataValue("o"));
Assert.Equal("ip1", p.GetItems("j").ElementAt(1).GetMetadataValue("p"));
Assert.Equal("jq3", p.GetItems("j").ElementAt(1).GetMetadataValue("q"));
Assert.Equal("km5", p.GetItems("k").ElementAt(0).GetMetadataValue("m"));
Assert.Equal("", p.GetItems("k").ElementAt(0).GetMetadataValue("n"));
Assert.Equal("", p.GetItems("k").ElementAt(0).GetMetadataValue("o"));
Assert.Equal("", p.GetItems("k").ElementAt(0).GetMetadataValue("p"));
Assert.Equal("kq4", p.GetItems("k").ElementAt(0).GetMetadataValue("q"));
Assert.Equal("kr4", p.GetItems("k").ElementAt(0).GetMetadataValue("r"));
Assert.Equal("", p.GetItems("k").ElementAt(0).GetMetadataValue("s"));
Assert.Equal("jm3", p.GetItems("k").ElementAt(1).GetMetadataValue("m"));
Assert.Equal("jn3", p.GetItems("k").ElementAt(1).GetMetadataValue("n"));
Assert.Equal("", p.GetItems("k").ElementAt(1).GetMetadataValue("o"));
Assert.Equal("", p.GetItems("k").ElementAt(1).GetMetadataValue("p"));
Assert.Equal("jq3", p.GetItems("k").ElementAt(1).GetMetadataValue("q"));
Assert.Equal("kr4", p.GetItems("k").ElementAt(1).GetMetadataValue("r"));
Assert.Equal("ks3", p.GetItems("k").ElementAt(1).GetMetadataValue("s"));
Assert.Equal("jm6", p.GetItems("k").ElementAt(2).GetMetadataValue("m"));
Assert.Equal("in1", p.GetItems("k").ElementAt(2).GetMetadataValue("n"));
Assert.Equal("io2", p.GetItems("k").ElementAt(2).GetMetadataValue("o"));
Assert.Equal("ip1", p.GetItems("k").ElementAt(2).GetMetadataValue("p"));
Assert.Equal("jq3", p.GetItems("k").ElementAt(2).GetMetadataValue("q"));
Assert.Equal("kr4", p.GetItems("k").ElementAt(2).GetMetadataValue("r"));
Assert.Equal("ks3", p.GetItems("k").ElementAt(1).GetMetadataValue("s"));
}
/// <summary>
/// Default and explicit metadata on both source and destination.
/// Item definition metadata from the source override item definition on the destination.
/// </summary>
[Fact]
public void ItemsEmittedByIntrinsicTaskConsumingItemExpression_Combination_InsideTarget()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>im1</m>
<n>in1</n>
<o>io1</o>
<p>ip1</p>
</i>
<j>
<m>jm3</m>
<n>jn3</n>
<q>jq3</q>
</j>
<k>
<m>km4</m>
<q>kq4</q>
</k>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""1"">
<o>io2</o>
</i>
<j Include=""2""/>
<k Include=""3"">
<M>km5</M>
</k>
</ItemGroup>
<Target Name=""t"">
<ItemGroup>
<j Include=""@(i)"">
<m>jm6</m>
</j>
<k Include=""@(j)""/>
</ItemGroup>
<Message Text=""i:%(identity) [%(i.m)][%(i.n)][%(i.o)][%(i.p)][%(i.q)]""/>
<Message Text=""j:%(identity) [%(j.m)][%(j.n)][%(j.o)][%(j.p)][%(j.q)]""/>
<Message Text=""k:%(identity) [%(k.m)][%(k.n)][%(k.o)][%(k.p)][%(k.q)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("i:1 [im1][in1][io2][ip1][]");
logger.AssertLogContains("j:2 [jm3][jn3][][][jq3]");
logger.AssertLogContains("j:1 [jm6][in1][io2][ip1][jq3]");
logger.AssertLogContains("k:3 [km5][][][][kq4]");
logger.AssertLogContains("k:2 [jm3][jn3][][][jq3]");
logger.AssertLogContains("k:1 [jm6][in1][io2][ip1][jq3]");
}
[Fact]
public void MutualReferenceToDefinition1()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>~%(m)~</n>
</i>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""i1""/>
</ItemGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)][%(i.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1][~m1~]");
}
[Fact]
public void MutualReferenceToDefinition2()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>~%(n)~</m>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""i1""/>
</ItemGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)][%(i.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[~~][n1]");
}
[Fact]
public void MutualReferenceToDefinition3()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemDefinitionGroup>
<i>
<m>m1</m>
<n>%(i.m)</n>
<o>%(j.m)</o>
</i>
</ItemDefinitionGroup>
<ItemGroup>
<i Include=""i1""/>
</ItemGroup>
<Target Name=""t"">
<Message Text=""[%(i.m)][%(i.n)][%(i.o)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[m1][m1][]");
}
[Fact]
public void ProjectReevaluationReevaluatesItemDefinitions()
{
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<PropertyGroup>
<Defines>CODEANALYSIS</Defines>
</PropertyGroup>
<ItemGroup>
<CppCompile Include='a.cpp'/>
</ItemGroup>
<ItemDefinitionGroup>
<CppCompile>
<Defines Condition=""'$(BuildFlavor)'=='ret'"">$(Defines);RETAIL</Defines>
<Defines Condition=""'$(BuildFlavor)'=='chk'"">$(Defines);DEBUG</Defines>
</CppCompile>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[%(CppCompile.Identity)==%(CppCompile.Defines)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.SetProperty("BuildFlavor", "ret");
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[a.cpp==CODEANALYSIS;RETAIL]");
Assert.True(ItemContainsMetadata(p, "CppCompile", "a.cpp", "Defines", "CODEANALYSIS;RETAIL"));
p.SetProperty("BuildFlavor", "chk");
p.ReevaluateIfNecessary();
Assert.True(ItemContainsMetadata(p, "CppCompile", "a.cpp", "Defines", "CODEANALYSIS;DEBUG"));
}
[Fact]
public void MSBuildCallDoesNotAffectCallingProjectsDefinitions()
{
string otherProject = null;
try
{
otherProject = FileUtilities.GetTemporaryFileName();
string otherProjectContent = @"<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m2</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[CHILD:%(i.m)]""/>
</Target>
</Project>";
using (StreamWriter writer = FileUtilities.OpenWrite(otherProject, false))
{
writer.Write(otherProjectContent);
}
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'/>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<m>m1</m>
</i>
</ItemDefinitionGroup>
<Target Name=""t"">
<Message Text=""[PARENT-before:%(i.m)]""/>
<MSBuild Projects=""" + otherProject + @"""/>
<Message Text=""[PARENT-after:%(i.m)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[PARENT-before:m1]", "[CHILD:m2]", "[PARENT-after:m1]");
}
finally
{
File.Delete(otherProject);
}
}
[Fact]
public void DefaultMetadataTravelWithTargetOutputs()
{
string otherProject = null;
try
{
otherProject = FileUtilities.GetTemporaryFileName();
string otherProjectContent = @"<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<ItemGroup>
<i Include='i1'>
<m>m1</m>
</i>
</ItemGroup>
<ItemDefinitionGroup>
<i>
<n>n1</n>
</i>
</ItemDefinitionGroup>
<Target Name=""t"" Outputs=""@(i)"">
<Message Text=""[CHILD:%(i.Identity):m=%(i.m),n=%(i.n)]""/>
</Target>
</Project>";
using (StreamWriter writer = FileUtilities.OpenWrite(otherProject, false))
{
writer.Write(otherProjectContent);
}
MockLogger logger = new MockLogger();
using ProjectFromString projectFromString = new(@"
<Project ToolsVersion=""msbuilddefaulttoolsversion"">
<Target Name=""t"">
<MSBuild Projects=""" + otherProject + @""">
<Output TaskParameter='TargetOutputs' ItemName='i'/>
</MSBuild>
<Message Text=""[PARENT:%(i.Identity):m=%(i.m),n=%(i.n)]""/>
</Target>
</Project>
");
Project p = projectFromString.Project;
p.Build("t", new ILogger[] { logger });
logger.AssertLogContains("[CHILD:i1:m=m1,n=n1]", "[PARENT:i1:m=m1,n=n1]");
}
finally
{
File.Delete(otherProject);
}
}
#endregion
/// <summary>
/// Determines if the specified item contains the specified metadata
/// </summary>
/// <param name="project">The project.</param>
/// <param name="itemType">The item type.</param>
/// <param name="itemInclude">The item include.</param>
/// <param name="name">The metadata name.</param>
/// <param name="value">The metadata value.</param>
/// <returns>True if the item contains the metadata, false otherwise.</returns>
private bool ItemContainsMetadata(Project project, string itemType, string itemInclude, string name, string value)
{
foreach (ProjectItem item in project.GetItems(itemType))
{
if (item.EvaluatedInclude == itemInclude)
{
return ContainsMetadata(item.Metadata, name, value);
}
}
return false;
}
/// <summary>
/// Determines if the metadata collection contains the named metadata with the specified value
/// </summary>
/// <param name="metadata">The collection.</param>
/// <param name="name">The metadata name.</param>
/// <param name="value">The metadata value.</param>
/// <returns>True if the collection contains the metadata, false otherwise.</returns>
private bool ContainsMetadata(IEnumerable<ProjectMetadata> metadata, string name, string value)
{
foreach (ProjectMetadata metadataEntry in metadata)
{
if (metadataEntry.Name == name && metadataEntry.EvaluatedValue == value)
{
return true;
}
}
return false;
}
}
public class ItemDefinitionGroup : Microsoft.Build.Utilities.Task
{
public override bool Execute()
{
Log.LogMessage("In ItemDefinitionGroup task.");
return true;
}
}
}
|