Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProjectReferences Negotiate SetPlatform Metadata #6655

Merged
merged 74 commits into from
Jul 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
68c396b
GetPlatforms target added
benvillalobos May 28, 2021
8b85f9e
Add _GetProjectReferencePlatformProperties
benvillalobos May 28, 2021
2c8c91f
Add GetNearestPlatformTask v1
benvillalobos May 28, 2021
e4f95be
Changes to GetNearestPlatformTask, Platform items
benvillalobos May 28, 2021
2edda9e
Flip boolean logic for setting SetPlatform metadata
benvillalobos Jun 1, 2021
e5d4f9c
Check for comma to see if project can multiplatform
benvillalobos Jun 1, 2021
42cf4e1
Don't run GetNearestPlatformTask if a project can't multiplatform. Al…
benvillalobos Jun 1, 2021
8e39ff1
Combine itemgroup logic when transferring items with new metadata to …
benvillalobos Jun 1, 2021
e4c25a0
Remove redundant CanMultiPlatform check
benvillalobos Jun 1, 2021
92f329c
Only account for $(Platforms) when retrieving possible platforms to b…
benvillalobos Jun 1, 2021
e036043
Undefine Platform/PlatformTarget when there is no clear platform to c…
benvillalobos Jun 1, 2021
cc9a401
Add property to simplify vcxproj and nativeproj checks
benvillalobos Jun 2, 2021
4f6e41f
PlatformOptions should be semicolon-delimited
benvillalobos Jun 8, 2021
0cd0e52
Take Platform or PlatformTarget into account when consider platform o…
benvillalobos Jun 8, 2021
ed3578e
Revert "Take Platform or PlatformTarget into account when consider pl…
benvillalobos Jun 8, 2021
1351751
Code cleanup.
benvillalobos Jun 22, 2021
3161d84
Refactor GetNearestPlatformTask
benvillalobos Jun 22, 2021
b6d7aee
Use BuildArchitecturesAllowed as PlatformOptions. VS Uses this proper…
benvillalobos Jun 22, 2021
649cc5b
Account for user-defined PlatformLookupTables when calling GetNearest…
benvillalobos Jun 22, 2021
9594068
Add verbose logging to help debug
benvillalobos Jun 22, 2021
5b2ba53
Pass parent platform based on file extension. Add 'is parent managed'…
benvillalobos Jun 22, 2021
9dae683
Clean up DefaultToVcxPlatformMapping
benvillalobos Jun 22, 2021
84b7d71
Revert "Use BuildArchitecturesAllowed as PlatformOptions. VS Uses thi…
benvillalobos Jun 24, 2021
890bed2
Only run _GetProjectReferencePlatformProperties if there are any proj…
benvillalobos Jun 24, 2021
d5a65fb
Add warning when we want to dynamically figure out platform, but chil…
benvillalobos Jun 24, 2021
097cb71
Undefine Platform and PlatformTarget when getting platforms from a ch…
benvillalobos Jun 24, 2021
8030f10
Revert "Undefine Platform and PlatformTarget when getting platforms f…
benvillalobos Jun 28, 2021
2841da2
Move GetPlatforms target for easier copy/paste
benvillalobos Jun 28, 2021
7ff6614
Only warn on $(Platforms) if it's not going to skip the platform logic
benvillalobos Jun 28, 2021
0ad8a91
Remove defaulting to the single option when $(Platforms) only contain…
benvillalobos Jun 28, 2021
5553b7c
Make GetCompatiblePlatform its own task. Renamed from GetNearestPlatf…
benvillalobos Jul 6, 2021
d480ecf
SetPlatform logic now piggybacks off of SetTF logic. This is accompli…
benvillalobos Jul 7, 2021
c91579c
Remove dependsontargets to avoid TF target from attempting to run mul…
benvillalobos Jul 8, 2021
8cb4045
Default the PlatformLookupTable only when empty
benvillalobos Jul 8, 2021
6f2e1ae
Default PlatformTarget to Platform when empty
benvillalobos Jul 8, 2021
a76be89
Add resources for warning from GetCompatiblePlatform task
benvillalobos Jul 8, 2021
78d70c6
Properly use warning resource
benvillalobos Jul 8, 2021
12a7fee
Add code for warning when a projectreference has no PlatformOptions
benvillalobos Jul 8, 2021
1af7349
Update xlf's
benvillalobos Jul 8, 2021
21908e3
Fix GetTargetFrameworks returning more items than necessary. CrossTar…
benvillalobos Jul 9, 2021
8d7a795
Make output of GetCompatiblePlatform 'private'
benvillalobos Jul 9, 2021
55faa41
Optimize item count checks
benvillalobos Jul 9, 2021
121133e
Move warning to task for translatable strings
benvillalobos Jul 9, 2021
3054e97
Add ProjectReference documentation
benvillalobos Jul 14, 2021
744ca68
Doc changes
benvillalobos Jul 15, 2021
a29d5be
Add tests for GetCompatiblePlatform
benvillalobos Jul 16, 2021
adf8ccc
Allow managed->unmanaged SetPlatform negotiation.
benvillalobos Jul 19, 2021
833ac30
PR feedback & code cleanup
benvillalobos Jul 20, 2021
953e0ba
Fix typo in docs, add example projectreference
benvillalobos Jul 21, 2021
5c1ab9e
Clarify platformlookuptable description
benvillalobos Jul 22, 2021
5acff54
Remove irrelevant comment
benvillalobos Jul 22, 2021
f29408b
GetCompatiblePlatform PR feedback. Rename ParentProjectPlatform to Cu…
benvillalobos Jul 22, 2021
45f7d32
Add test where a ProjectReference's PlatformLookupTable takes priorit…
benvillalobos Jul 22, 2021
041a618
Rename childproject to projectreference in tests. Add test for failin…
benvillalobos Jul 22, 2021
a0f25bb
Add warning for invalid PlatformLookupTables. Tests check for each wa…
benvillalobos Jul 23, 2021
cbd1e8e
Rename PlatformOptions to Platforms
benvillalobos Jul 26, 2021
a991ee0
Matching platforms takes priority over AnyCPU
benvillalobos Jul 27, 2021
f4e8c35
Add default cpp to managed PlatformLookupTable. Let default PlatformL…
benvillalobos Jul 27, 2021
2e7d06f
Remove AnyCPU to Win32 default mapping
benvillalobos Jul 27, 2021
429d097
Matching platforms takes #1 priority
benvillalobos Jul 27, 2021
b1eac04
Construct Platforms via ProjectConfiguration for cpp projects
benvillalobos Jul 27, 2021
4ffc4da
PR Feedback: string.empty, extra keyval check, shouldly
benvillalobos Jul 27, 2021
9a938d7
PR Feedback: Clearer comments and low importance messages
benvillalobos Jul 28, 2021
5530520
Reword 'parent' to 'current'
benvillalobos Jul 28, 2021
56b466f
Reword 'child' to 'projectreference'
benvillalobos Jul 28, 2021
d4fd1fa
Code cleanup, add resources for low prio messages
benvillalobos Jul 28, 2021
f9c3e66
Enable nullable. Marked required params as required
benvillalobos Jul 28, 2021
1dbf392
Trim whitespace around translation table and each entry
benvillalobos Jul 28, 2021
d011e32
Change resource to be more specific about what 'normally' means
benvillalobos Jul 28, 2021
f483ff0
Allow empty entries when splitting individual mappings
benvillalobos Jul 28, 2021
9486435
Revert "Allow empty entries when splitting individual mappings"
benvillalobos Jul 28, 2021
9404a0c
Update SetPlatform documentation
benvillalobos Jul 28, 2021
275738f
dont clear empty entries on equals split. simplify length check
benvillalobos Jul 28, 2021
3cb5a72
PR Feedback: Doc clarification
benvillalobos Jul 29, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 64 additions & 2 deletions documentation/ProjectReference-Protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ If implementing a project with an “outer” (determine what properties to pass
* `TargetFrameworks` indicating what TargetFrameworks are available in the project
* `TargetFrameworkMonikers` and `TargetPlatformMonikers` indicating what framework / platform the `TargetFrameworks` map to. This is to support implicitly setting the target platform version (for example inferring that `net5.0-windows` means the same as `net5.0-windows7.0`) as well as treating the `TargetFramework` values [as aliases](https://github.com/NuGet/Home/issues/5154)
* Boolean metadata for `HasSingleTargetFramework` and `IsRidAgnostic`.
* `Platforms` indicating what platforms are available for the project to build as, and boolean metadata `IsVcxOrNativeProj` (used for [SetPlatform Negotiation](#setplatform-negotiation))
* The `GetReferenceNearestTargetFrameworkTask` (provided by NuGet) is responsible for selecting the best matching `TargetFramework` of the referenced project
* This target is _optional_. If not present, the reference will be built with no additional properties.
* **New** in MSBuild 15.5. (`TargetFrameworkMonikers` and `TargetPlatformMonikers` metadata is new in MSBuild 16.8)
Expand All @@ -84,7 +85,6 @@ If implementing a project with an “outer” (determine what properties to pass
* As of 15.7, this is _optional_. If a project does not contain a `GetCopyToOutputDirectoryItems` target, projects that reference it will not copy any of its outputs to their own output folders, but the build can succeed.
* `Clean` should delete all outputs of the project.
* It is not called during a normal build, only during "Clean" and "Rebuild".

## Other protocol requirements

As with all MSBuild logic, targets can be added to do other work with `ProjectReference`s.
Expand Down Expand Up @@ -119,4 +119,66 @@ These properties will then be gathered via the `GetTargetFrameworks` call. They
</AdditionalProjectProperties>
```

The `NearestTargetFramework` metadata will be the target framework which was selected as the best one to use for the reference (via `GetReferenceNearestTargetFrameworkTask`). This can be used to select which set of properties were used in the target framework that was active for the reference.
The `NearestTargetFramework` metadata will be the target framework which was selected as the best one to use for the reference (via `GetReferenceNearestTargetFrameworkTask`). This can be used to select which set of properties were used in the target framework that was active for the reference.

## SetPlatform Negotiation
As of version 17.0, MSBuild can now dynamically figure out what platform a `ProjectReference` should build as. This includes a new target and task to determine what the `SetPlatform` metadata should be, or whether to undefine the platform so the referenced project builds with its default platform.

* `_GetProjectReferenceTargetFrameworkProperties` target performs the majority of the work for assigning `SetPlatform` metadata to project references.
* Calls the `GetCompatiblePlatform` task, which is responsible for negotiating between the current project's platform and the platforms of the referenced project to assign a `NearestPlatform` metadata to the item.
* Sets or undefines `SetPlatform` based on the `NearestPlatform` assignment from `GetCompatiblePlatform`
benvillalobos marked this conversation as resolved.
Show resolved Hide resolved
* This target explicitly runs after `_GetProjectReferenceTargetFrameworkProperties` because it needs to use the `IsVcxOrNativeProj` and `Platforms` properties returned by the `GetTargetFrameworks` call.

Note: If a `ProjectReference` has `SetPlatform` metadata defined already, the negotiation logic is skipped over.
### Impact on the build
In addition to the above task and target, `.vcxproj` and `.nativeproj` projects will receive an extra MSBuild call to the `GetTargetFrameworks` target. Previously, TargetFramework negotiation skipped over these projects because they could not multi-target in the first place. Because SetPlatform negotiation needs information given from the `GetTargetFrameworks` target, it is required that the `_GetProjectReferenceTargetFrameworkProperties` target calls the MSBuild task on the ProjectReference.

This means most projects will see an evaluation with no global properties defined, unless set by the user.

### How To Opt In
First, set the properties `EnableDynamicPlatformResolution` and `DisableTransitiveProjectReferences` to `true` for **every project** in your solution. The easiest way to do this is by creating a `Directory.Build.props` file and placing it at the root of your project directory:

```xml
<Project>
<PropertyGroup>
<EnableDynamicPlatformResolution>true</EnableDynamicPlatformResolution>
<DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
</PropertyGroup>
</Project>
```

If only set in one project, the `SetPlatform` metadata will carry forward to every consecutive project reference.

Next, every referenced project is required to define a `Platforms` property, where `Platforms` is a semicolon-delimited list of platforms that project could build as. For `.vcxproj` or `.nativeproj` projects, `Platforms` is constructed from the `ProjectConfiguration` items that already exist in the project. For managed SDK projects, the default is `AnyCPU`. Managed non-SDK projects need to define this manually.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Platforms is constructed from the ProjectConfiguration items that already exist in the project

Is this done at execution time instead of evaluation time then? Just curious because items can't usually be used to create properties.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this done at execution time instead of evaluation time then?

Right, because it's running in the GetTargetFrameworks target.


Lastly, a `PlatformLookupTable` may need to be defined for more complex scenarios. A `PlatformLookupTable` is a semicolon-delimited list of mappings between platforms. `<PlatformLookupTable>Win32=x86</PlatformLookupTable>`, for example. This means that when the current project is building as `Win32`, it will attempt to build the referenced project as x86. This property is **required** when a managed AnyCPU project references an unmanaged project because `AnyCPU` does not directly map to an architecture-specific platform. You can define the table in two ways:

1. A standard property within the current project, in a Directory.Build.props/targets
2. Metadata on the `ProjectReference` item. This option takes priority over the first to allow customizations per `ProjectReference`.

### References between managed and unmanaged projects
Some cases of `ProjectReference`s require a `$(PlatformLookupTable)` to correctly determine what a referenced project should build as. References between managed and unmanaged projects also get a default lookup table that can be opted out of by setting the property `UseDefaultPlatformLookupTables` to false. See the table below for details.

Note: Defining a `PlatformLookupTable` overrides the default mapping.
| Project Reference Type | `PlatformLookupTable` Required? | Notes |
| :-- | :-: | :-: |
Forgind marked this conversation as resolved.
Show resolved Hide resolved
| Unmanaged -> Unmanaged | No | |
Forgind marked this conversation as resolved.
Show resolved Hide resolved
| Managed -> Managed | No | |
| Unmanaged -> Managed | Optional | Uses default mapping: `Win32=x86` |
| Managed -> Unmanaged | **Yes** when the project is AnyCPU | Uses default mapping: `x86=Win32` |

Example:
Project A: Managed, building as `AnyCPU`, has a `ProjectReference` on Project B.
Project B: Unmanaged, has `$(Platforms)` constructed from its `Platform` metadata from its `ProjectConfiguration` items, defined as `x64;Win32`.

Because `AnyCPU` does not map to anything architecture-specific, a custom mapping must be defined. Project A can either:
1. Define `PlatformLookupTable` in its project or a Directory.Build.props as `AnyCPU=x64` or `AnyCPU=Win32`.
2. Define `PlatformLookupTable` as metadata on the `ProjectReference` item, which would take priority over a lookup table defined elsewhere.
* When only one mapping is valid, you could also directly define `SetPlatform` metadata as `Platform=foo` (for unmanaged) or `PlatformTarget=bar` (for managed). This would skip over most negotiation logic.

Example of project A defining a lookup table directly on the `ProjectReference`:
```xml
<ItemGroup>
<ProjectReference Include="B.csproj" PlatformLookupTable="AnyCPU=Win32">
</ItemGroup>
```
12 changes: 12 additions & 0 deletions ref/Microsoft.Build.Tasks.Core/net/Microsoft.Build.Tasks.Core.cs
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,18 @@ public GetAssemblyIdentity() { }
public Microsoft.Build.Framework.ITaskItem[] AssemblyFiles { get { throw null; } set { } }
public override bool Execute() { throw null; }
}
public partial class GetCompatiblePlatform : Microsoft.Build.Tasks.TaskExtension
{
public GetCompatiblePlatform() { }
[Microsoft.Build.Framework.RequiredAttribute]
public Microsoft.Build.Framework.ITaskItem[] AnnotatedProjects { get { throw null; } set { } }
[Microsoft.Build.Framework.OutputAttribute]
public Microsoft.Build.Framework.ITaskItem[] AssignedProjectsWithPlatform { get { throw null; } set { } }
[Microsoft.Build.Framework.RequiredAttribute]
public string CurrentProjectPlatform { get { throw null; } set { } }
public string PlatformLookupTable { get { throw null; } set { } }
public override bool Execute() { throw null; }
}
public sealed partial class GetFileHash : Microsoft.Build.Tasks.TaskExtension
{
public GetFileHash() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,18 @@ public GetAssemblyIdentity() { }
public Microsoft.Build.Framework.ITaskItem[] AssemblyFiles { get { throw null; } set { } }
public override bool Execute() { throw null; }
}
public partial class GetCompatiblePlatform : Microsoft.Build.Tasks.TaskExtension
{
public GetCompatiblePlatform() { }
[Microsoft.Build.Framework.RequiredAttribute]
public Microsoft.Build.Framework.ITaskItem[] AnnotatedProjects { get { throw null; } set { } }
[Microsoft.Build.Framework.OutputAttribute]
public Microsoft.Build.Framework.ITaskItem[] AssignedProjectsWithPlatform { get { throw null; } set { } }
[Microsoft.Build.Framework.RequiredAttribute]
public string CurrentProjectPlatform { get { throw null; } set { } }
public string PlatformLookupTable { get { throw null; } set { } }
public override bool Execute() { throw null; }
}
public sealed partial class GetFileHash : Microsoft.Build.Tasks.TaskExtension
{
public GetFileHash() { }
Expand Down
203 changes: 203 additions & 0 deletions src/Tasks.UnitTests/GetCompatiblePlatform_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Build.UnitTests;
using Microsoft.Build.Utilities;
using Shouldly;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Build.Tasks.UnitTests
{
sealed public class GetCompatiblePlatform_Tests
benvillalobos marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly ITestOutputHelper _output;

public GetCompatiblePlatform_Tests(ITestOutputHelper output)
{
_output = output;
}

[Fact]
public void ResolvesViaPlatformLookupTable()
{
// PlatformLookupTable always takes priority. It is typically user-defined.
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x64;x86;AnyCPU");

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "win32",
PlatformLookupTable = "win32=x64",
AnnotatedProjects = new TaskItem[] { projectReference }
};

task.Execute().ShouldBeTrue();

task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x64");
}

[Fact]
public void ResolvesViaProjectReferencesPlatformLookupTable()
{
// A ProjectReference's PlatformLookupTable takes priority over the current project's table.
// This allows overrides on a per-ProjectItem basis.
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x64;x86;AnyCPU");

// ProjectReference will be assigned x86 because its table takes priority
projectReference.SetMetadata("PlatformLookupTable", "win32=x86");

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "win32",
PlatformLookupTable = "win32=x64",
AnnotatedProjects = new TaskItem[] { projectReference }
};

task.Execute().ShouldBeTrue();

task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x86");
}

[Fact]
public void ResolvesViaAnyCPUDefault()
{
// No valid mapping via the lookup table, should default to AnyCPU when the current project
// and ProjectReference platforms don't match.
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x64;AnyCPU");

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "x86",
PlatformLookupTable = "AnyCPU=x64",
AnnotatedProjects = new TaskItem[] { projectReference }
};

task.Execute().ShouldBeTrue();

task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("AnyCPU");
}

[Fact]
public void ResolvesViaSamePlatform()
{
// No valid mapping via the lookup table. If the ProjectReference's platform
// matches the current project's platform, it takes priority over AnyCPU default.
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x86;x64;AnyCPU");
projectReference.SetMetadata("PlatformLookupTable", "x86=AnyCPU"); // matching platform takes priority over lookup tables

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "x86",
PlatformLookupTable = "x86=AnyCPU",
AnnotatedProjects = new TaskItem[] { projectReference }
};

task.Execute().ShouldBeTrue();

task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x86");
}

[Fact]
public void FailsToResolve()
{
// No valid mapping via the lookup table, ProjectReference can't default to AnyCPU,
// it also can't match with current project, log a warning.
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x64");

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "x86",
PlatformLookupTable = "AnyCPU=x64",
AnnotatedProjects = new TaskItem[] { projectReference },
};

task.Execute().ShouldBeTrue();
// When the task logs a warning, it does not set NearestPlatform
task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe(string.Empty);
((MockEngine)task.BuildEngine).AssertLogContains("MSB3981");
}

[Fact]
public void WarnsWhenProjectReferenceHasNoPlatformOptions()
{
// Task should log a warning when a ProjectReference has no options to build as.
// It will continue and have no NearestPlatform metadata.
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", string.Empty);

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "x86",
PlatformLookupTable = "AnyCPU=x64",
AnnotatedProjects = new TaskItem[] { projectReference },
};

task.Execute().ShouldBeTrue();
// When the task logs a warning, it does not set NearestPlatform
task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe(string.Empty);
((MockEngine)task.BuildEngine).AssertLogContains("MSB3982");
}

/// <summary>
/// Invalid format on PlatformLookupTable results in an exception being thrown.
/// </summary>
[Fact]
public void WarnsOnInvalidFormatLookupTable()
{
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x64");

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "AnyCPU",
PlatformLookupTable = "AnyCPU=;A=B", // invalid format
AnnotatedProjects = new TaskItem[] { projectReference },
};

task.Execute().ShouldBeTrue();
// When the platformlookuptable is in an invalid format, it is discarded.
// There shouldn't have been a translation found from AnyCPU to anything.
// Meaning the projectreference would not have NearestPlatform set.
task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe(string.Empty);
((MockEngine)task.BuildEngine).AssertLogContains("MSB3983");
}

/// <summary>
/// Invalid format on PlatformLookupTable from the projectreference results in an exception being thrown.
/// </summary>
[Fact]
public void WarnsOnInvalidFormatProjectReferenceLookupTable()
{
TaskItem projectReference = new TaskItem("foo.bar");
projectReference.SetMetadata("Platforms", "x64;x86");
projectReference.SetMetadata("PlatformLookupTable", "x86=;b=d");

GetCompatiblePlatform task = new GetCompatiblePlatform()
{
BuildEngine = new MockEngine(_output),
CurrentProjectPlatform = "AnyCPU",
PlatformLookupTable = "AnyCPU=x86;A=B", // invalid format
AnnotatedProjects = new TaskItem[] { projectReference },
};

task.Execute().ShouldBeTrue();

// A ProjectReference PlatformLookupTable should take priority, but is thrown away when
// it has an invalid format. The current project's PLT should be the next priority.
task.AssignedProjectsWithPlatform[0].GetMetadata("NearestPlatform").ShouldBe("x86");
((MockEngine)task.BuildEngine).AssertLogContains("MSB3983");
}
}
}
Loading