Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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
2 changes: 1 addition & 1 deletion src/Shared/ConversionUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ internal static bool ValidBooleanTrue(string parameterValue)
/// Returns true if the string represents a valid MSBuild boolean false value,
/// such as "!on" "off" "no" "!true"
/// </summary>
private static bool ValidBooleanFalse(string parameterValue)
internal static bool ValidBooleanFalse(string parameterValue)
{
return String.Equals(parameterValue, "false", StringComparison.OrdinalIgnoreCase) ||
String.Equals(parameterValue, "off", StringComparison.OrdinalIgnoreCase) ||
Expand Down
7 changes: 4 additions & 3 deletions src/Tasks/Microsoft.Build.Tasks.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="..\Shared\FileSystemSources.proj" />
<Import Project="..\Shared\DebuggingSources.proj" />
Expand Down Expand Up @@ -329,6 +329,7 @@
</Compile>
<Compile Include="ResourceHandling\*.cs" />
<Compile Include="GetCompatiblePlatform.cs" />
<Compile Include="SetRidAgnosticValueForProjects.cs" />
<Compile Include="ResolveComReference.cs" />
<Compile Include="BuildCacheDisposeWrapper.cs" />
<Compile Include="DownloadFile.cs" />
Expand Down Expand Up @@ -966,8 +967,8 @@
<ItemGroup>
<ProjectReference Include="..\Framework\Microsoft.Build.Framework.csproj" SetTargetFramework="TargetFramework=netstandard2.0" OutputItemType="NetstandardRefAssemblies" />
<ProjectReference Include="..\Utilities\Microsoft.Build.Utilities.csproj" SetTargetFramework="TargetFramework=netstandard2.0" OutputItemType="NetstandardRefAssemblies" />
<ProjectReference Include="..\Framework\Microsoft.Build.Framework.csproj"/>
<ProjectReference Include="..\Utilities\Microsoft.Build.Utilities.csproj"/>
<ProjectReference Include="..\Framework\Microsoft.Build.Framework.csproj" />
<ProjectReference Include="..\Utilities\Microsoft.Build.Utilities.csproj" />
<ProjectReference Include="..\StringTools\StringTools.csproj" />
</ItemGroup>
<ItemGroup>
Expand Down
6 changes: 2 additions & 4 deletions src/Tasks/Microsoft.Common.CrossTargeting.targets
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<TargetPlatformMonikers>@(_TargetFrameworkInfo->'%(TargetPlatformMonikers)')</TargetPlatformMonikers>
<AdditionalPropertiesFromProject>$(_AdditionalPropertiesFromProject)</AdditionalPropertiesFromProject>
<HasSingleTargetFramework>false</HasSingleTargetFramework>
<!-- indicate to caller that project is RID agnostic so that a global property RuntimeIdentifier value can be removed -->
<IsRidAgnostic>false</IsRidAgnostic>
<IsRidAgnostic Condition=" '$(RuntimeIdentifier)' == '' and '$(RuntimeIdentifiers)' == '' ">true</IsRidAgnostic>
<IsRidAgnostic>@(_TargetFrameworkInfo->'%(IsRidAgnostic)')</IsRidAgnostic>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should GetTargetFrameworks depend on GetTargetFrameworksWithPlatformForSingleSingleTargetFramework explicitly?

Copy link
Member Author

Choose a reason for hiding this comment

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

That's what it does for single-targeted projects:

<Target Name="GetTargetFrameworks"
DependsOnTargets="GetTargetFrameworksWithPlatformForSingleTargetFramework"

For multi-targeted projects it has to do inner builds for each target framework, which the GetTargetFrameworksWithPlatformFromInnerBuilds target handles.

<!-- Extract necessary information for SetPlatform negotiation -->
<!-- This target does not run for cpp projects. -->
<IsVcxOrNativeProj>false</IsVcxOrNativeProj>
Expand Down Expand Up @@ -220,4 +218,4 @@ Copyright (C) Microsoft Corporation. All rights reserved.

<Import Project="$(DirectoryBuildTargetsPath)" Condition="'$(ImportDirectoryBuildTargets)' == 'true' and exists('$(DirectoryBuildTargetsPath)')"/>

</Project>
</Project>
41 changes: 32 additions & 9 deletions src/Tasks/Microsoft.Common.CurrentVersion.targets
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!--
<!--
***********************************************************************************************
Microsoft.Common.CurrentVersion.targets

Expand Down Expand Up @@ -1779,7 +1779,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
BuildInParallel="$(BuildInParallel)"
Properties="%(_MSBuildProjectReferenceExistent.SetConfiguration); %(_MSBuildProjectReferenceExistent.SetPlatform)"
ContinueOnError="!$(BuildingProject)"
RemoveProperties="%(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);TargetFramework;RuntimeIdentifier$(_GlobalPropertiesToRemoveFromProjectReferences)"
RemoveProperties="%(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);TargetFramework;RuntimeIdentifier;SelfContained;$(_GlobalPropertiesToRemoveFromProjectReferences)"
Copy link
Contributor

Choose a reason for hiding this comment

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

It was previously RuntimeIdentifier$(_GlobalPropertiesToRemoveFromProjectReferences) (no semicolon)
That looks like a bug to me, but I just wanted to make sure that's intentional 🙂

Copy link
Member Author

Choose a reason for hiding this comment

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

Good point. It does seem like it might have been a bug before. Now there may be extra semicolons, but that should be OK, right? The MSBuild task should just ignore the extra ones I think.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes

Condition="'%(_MSBuildProjectReferenceExistent.SkipGetTargetFrameworkProperties)' != 'true' and '$(EnableDynamicPlatformResolution)' != 'true'"
SkipNonexistentTargets="true">
<Output TaskParameter="TargetOutputs" ItemName="_ProjectReferenceTargetFrameworkPossibilities" />
Expand All @@ -1795,7 +1795,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
Targets="GetTargetFrameworks"
BuildInParallel="$(BuildInParallel)"
ContinueOnError="!$(BuildingProject)"
RemoveProperties="%(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);TargetFramework;RuntimeIdentifier;Platform;Configuration$(_GlobalPropertiesToRemoveFromProjectReferences)"
RemoveProperties="%(_MSBuildProjectReferenceExistent.GlobalPropertiesToRemove);TargetFramework;RuntimeIdentifier;SelfContained;Platform;Configuration$(_GlobalPropertiesToRemoveFromProjectReferences)"
Copy link
Member

Choose a reason for hiding this comment

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

Do we need a ; between Configuration and $(_GlobalPropertiesToRemoveFromProjectReferences)?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at all usages of _GlobalPropertiesToRemoveFromProjectReferences in MSBuild, it seems that it is always expected to have a ; in front of it. That said, I think adding a ; is better anyway.

Condition="'%(_MSBuildProjectReferenceExistent.SkipGetTargetFrameworkProperties)' != 'true' and '$(EnableDynamicPlatformResolution)' == 'true'"
SkipNonexistentTargets="true">
<Output TaskParameter="TargetOutputs" ItemName="_ProjectReferenceTargetFrameworkPossibilities" />
Expand Down Expand Up @@ -1850,6 +1850,20 @@ Copyright (C) Microsoft Corporation. All rights reserved.
Condition="'$(ReferringTargetFrameworkForProjectReferences)' == '' or
('$(EnableDynamicPlatformResolution)' == 'true' and '%(_ProjectReferenceTargetFrameworkPossibilities.IsVcxOrNativeProj)' == 'true')" />

</ItemGroup>

<!-- IsRidAgnostic metadata is used to determine whether global properties such as RuntimeIdentifier and SelfContained flow to a referenced project.
However, for multi-targeted projects there may be a different IsRidAgnostic value for each TargetFramework. In that case, this task selects
the IsRidAgnostic value for the NearestTargetFramework for the project. -->
<SetRidAgnosticValueForProjects Projects="@(AnnotatedProjects)">
<Output ItemName="UpdatedAnnotatedProjects" TaskParameter="UpdatedProjects" />
</SetRidAgnosticValueForProjects>

<ItemGroup>
<AnnotatedProjects Remove="@(AnnotatedProjects)" />
<AnnotatedProjects Include="@(UpdatedAnnotatedProjects)" />
<UpdatedAnnotatedProjects Remove="@(UpdatedAnnotatedProjects)" />
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this line necessary?

Copy link
Member Author

Choose a reason for hiding this comment

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

It just clears it out so that it's not taking up memory anymore. It's a pretty clunky pattern for when you want to have a task that updates item metadata.


<!-- If the NearestTargetFramework property was set and the project multi-targets, SetTargetFramework must be set. -->
<AnnotatedProjects Condition="'@(AnnotatedProjects)' == '%(Identity)' and '%(AnnotatedProjects.NearestTargetFramework)' != '' and '%(AnnotatedProjects.HasSingleTargetFramework)' != 'true'">
<SetTargetFramework>TargetFramework=%(AnnotatedProjects.NearestTargetFramework)</SetTargetFramework>
Expand All @@ -1863,9 +1877,10 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<UndefineProperties>%(AnnotatedProjects.UndefineProperties);TargetFramework</UndefineProperties>
</AnnotatedProjects>

<!-- If the project is RID agnostic, undefine the RuntimeIdentifier property to avoid another evaluation. -->
<AnnotatedProjects Condition="'@(AnnotatedProjects)' == '%(Identity)' and '%(AnnotatedProjects.IsRidAgnostic)' == 'true'">
<UndefineProperties>%(AnnotatedProjects.UndefineProperties);RuntimeIdentifier</UndefineProperties>
Comment on lines -1866 to -1868
Copy link
Member

Choose a reason for hiding this comment

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

Is it possible to keep these and respect them if they're manually implemented? We documented IsRidAgnostic in

* This should return either
* a string of the form `TargetFramework=$(NearestTargetFramework);ProjectHasSingleTargetFramework=$(_HasSingleTargetFramework);ProjectIsRidAgnostic=$(_IsRidAgnostic)`, where the value of `NearestTargetFramework` will be used to formulate `TargetFramework` for the following calls and the other two properties are booleans, or
* an item with metadata `DesiredTargetFrameworkProperties` (key-value pairs of the form `TargetFramework=net46`), `HasSingleTargetFramework` (boolean), and `IsRidAgnostic` (boolean).

Copy link
Member Author

Choose a reason for hiding this comment

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

Do you think anyone would have actually used that? It seems like it would be hard to hook into. Searching GitHub seems to give about 7 results, though it's not actually displaying them for me correctly so I can't tell if they're real.

Copy link
Member

Choose a reason for hiding this comment

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

<!-- Add RuntimeIdentifier and SelfContained to the list of global properties that should not flow to the referenced project,
unless the project is expecting those properties to flow. -->
<AnnotatedProjects Condition="'@(AnnotatedProjects)' == '%(Identity)' and '%(AnnotatedProjects.IsRidAgnostic)' != 'false'">
<UndefineProperties>%(AnnotatedProjects.UndefineProperties);RuntimeIdentifier;SelfContained</UndefineProperties>
</AnnotatedProjects>

<!--
Expand Down Expand Up @@ -1898,9 +1913,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<TargetPlatformMonikers>@(_TargetFrameworkInfo->'%(TargetPlatformMonikers)')</TargetPlatformMonikers>
<AdditionalPropertiesFromProject>$(_AdditionalPropertiesFromProject)</AdditionalPropertiesFromProject>
<HasSingleTargetFramework>true</HasSingleTargetFramework>
<!-- indicate to caller that project is RID agnostic so that a global property RuntimeIdentifier value can be removed -->
<IsRidAgnostic>false</IsRidAgnostic>
<IsRidAgnostic Condition=" '$(RuntimeIdentifier)' == '' and '$(RuntimeIdentifiers)' == '' ">true</IsRidAgnostic>
<IsRidAgnostic>@(_TargetFrameworkInfo->'%(IsRidAgnostic)')</IsRidAgnostic>
<!-- Extract necessary information for SetPlatform negotiation -->
<IsVcxOrNativeProj Condition="'$(MSBuildProjectExtension)' == '.vcxproj' or '$(MSBuildProjectExtension)' == '.nativeproj'">true</IsVcxOrNativeProj>
<Platform Condition="$([MSBuild]::AreFeaturesEnabled('17.4'))">$(Platform)</Platform>
Expand Down Expand Up @@ -1941,6 +1954,16 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<TargetPlatformMonikers>$(TargetPlatformMoniker)</TargetPlatformMonikers>
<TargetPlatformMonikers Condition="'$(TargetPlatformMoniker)' == ''">None</TargetPlatformMonikers>
<AdditionalPropertiesFromProject>$(_AdditionalTargetFrameworkInfoProperties)</AdditionalPropertiesFromProject>

Copy link
Contributor

Choose a reason for hiding this comment

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

Does _TargetFrameworkInfo ever have more than just $(TargetFramework) included in it?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, it does for multi-targeted projects, which will run this target for each target framework and combine the results.

<!-- Determine whether a project is "RID agnostic" for each TargetFramework. "RID agnostic" means that global properties such as SelfContained and RuntimeIdentifier should
not flow across project references.

Generally this value will come from the IsRidAgnostic property set by the .NET SDK. If that's not set, then the fallback logic here will be that the project
is RID agnostic if it doesn't have RuntimeIdentifier or RuntimeIdentifiers properties set. -->
<IsRidAgnostic>$(IsRidAgnostic)</IsRidAgnostic>
<IsRidAgnostic Condition=" '$(IsRidAgnostic)' == '' and '$(RuntimeIdentifier)' == '' and '$(RuntimeIdentifiers)' == '' ">false</IsRidAgnostic>
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be true and the next one false?

Copy link
Member

Choose a reason for hiding this comment

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

+1, it looks like it will set IsRidAgnostic to false when there are no runtime identifiers

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, thanks for catching this.

<IsRidAgnostic Condition=" '$(IsRidAgnostic)' == ''">true</IsRidAgnostic>

</_TargetFrameworkInfo>
</ItemGroup>

Expand Down
3 changes: 2 additions & 1 deletion src/Tasks/Microsoft.Common.tasks
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
<UsingTask TaskName="Microsoft.Build.Tasks.GetFrameworkPath" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<UsingTask TaskName="Microsoft.Build.Tasks.GetFrameworkSdkPath" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<UsingTask TaskName="Microsoft.Build.Tasks.GetReferenceAssemblyPaths" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<UsingTask TaskName="Microsoft.Build.Tasks.SetRidAgnosticValueForProjects" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<UsingTask TaskName="Microsoft.Build.Tasks.Hash" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<UsingTask TaskName="Microsoft.Build.Tasks.LC" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<UsingTask TaskName="Microsoft.Build.Tasks.MakeDir" AssemblyName="Microsoft.Build.Tasks.Core, Version=15.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
Expand Down Expand Up @@ -106,4 +107,4 @@
<UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.Vbc" AssemblyFile="$(RoslynTargetsPath)\Microsoft.Build.Tasks.CodeAnalysis.dll" />
<UsingTask TaskName="Microsoft.CodeAnalysis.BuildTasks.CopyRefAssembly" AssemblyFile="$(RoslynTargetsPath)\Microsoft.Build.Tasks.CodeAnalysis.dll" />

</Project>
</Project>
9 changes: 8 additions & 1 deletion src/Tasks/PublicAPI/net/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.Projects.get -> Microsoft.Build.Framework.ITaskItem[]
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.Projects.set -> void
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.SetRidAgnosticValueForProjects() -> void
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.UpdatedProjects.get -> Microsoft.Build.Framework.ITaskItem[]
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.UpdatedProjects.set -> void
override Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.Execute() -> bool
Microsoft.Build.Tasks.XslTransformation.PreserveWhitespace.get -> bool
Microsoft.Build.Tasks.XslTransformation.PreserveWhitespace.set -> void
Microsoft.Build.Tasks.XslTransformation.PreserveWhitespace.set -> void
9 changes: 8 additions & 1 deletion src/Tasks/PublicAPI/netstandard/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.Projects.get -> Microsoft.Build.Framework.ITaskItem[]
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.Projects.set -> void
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.SetRidAgnosticValueForProjects() -> void
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.UpdatedProjects.get -> Microsoft.Build.Framework.ITaskItem[]
Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.UpdatedProjects.set -> void
Microsoft.Build.Tasks.XslTransformation.PreserveWhitespace.get -> bool
Microsoft.Build.Tasks.XslTransformation.PreserveWhitespace.set -> void
Microsoft.Build.Tasks.XslTransformation.PreserveWhitespace.set -> void
override Microsoft.Build.Tasks.SetRidAgnosticValueForProjects.Execute() -> bool
65 changes: 65 additions & 0 deletions src/Tasks/SetRidAgnosticValueForProjects.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// 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.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;
using Microsoft.Build.Utilities;

namespace Microsoft.Build.Tasks
{
public sealed class SetRidAgnosticValueForProjects : TaskExtension
{
public ITaskItem[] Projects { get; set; } = Array.Empty<ITaskItem>();

[Output]
public ITaskItem[] UpdatedProjects { get; set; } = Array.Empty<ITaskItem>();

public override bool Execute()
{
UpdatedProjects = Projects
.Select(p =>
{
var hasSingleTargetFrameworkString = p.GetMetadata("HasSingleTargetFramework");
if (!ConversionUtilities.ValidBooleanFalse(hasSingleTargetFrameworkString))
Copy link
Contributor

Choose a reason for hiding this comment

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

Why did you choose !false rather than true? Could it be null here? If so, this seems like wrong behavior.

Copy link
Member Author

Choose a reason for hiding this comment

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

If the value is not set I want this to be a no-op. That seems the safest.

For projects using the .NET SDK, this should always be set. For other types of projects it might not be.

{
// No change to item, it should already have a single-valued IsRidAgnostic value
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we have a Debug.Assert here or something?

Copy link
Member Author

Choose a reason for hiding this comment

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

Debug.Assert gets compiled out in release builds, so that would be pretty unlikely to catch anything anyway, wouldn't it be?

Copy link
Contributor

Choose a reason for hiding this comment

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

It is, but if something goes wrong, we'd probably try to debug with a debug build, so I thought it might be helpful. Not a big deal if you don't want to add it.

return p;
}
var updatedItem = new TaskItem(p);

var nearestTargetFramework = p.GetMetadata("NearestTargetFramework");
Copy link
Contributor

Choose a reason for hiding this comment

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

Are projects actually expected to have all of this information? I feel like this relies on projects carrying around a lot of (generally unimportant) baggage. Not saying that doesn't happen, but it's a bit surprising to me.

Copy link
Member Author

Choose a reason for hiding this comment

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

What information are you referring to? These are part of the Project Reference Protocol, or in the case of NearestTargetFramework, set by the GetReferenceNearestTargetFrameworkTask task which runs just before this one.

Copy link
Contributor

Choose a reason for hiding this comment

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

I wasn't aware of NearestTargetFramework, but that makes sense. I was also surprisedby IsRidAgnostic being an array of IsRidAgnostic values with one per target framework. I'd expected (and the Project Reference Protocol seems to say) that it's just a boolean. It seems like you're changing the type in this task from boolean[] to boolean?

if (string.IsNullOrEmpty(nearestTargetFramework))
{
return p;
}

var targetFrameworksArray = p.GetMetadata("TargetFrameworks").Split(';');

int targetFrameworkIndex = Array.IndexOf(targetFrameworksArray, nearestTargetFramework);
if (targetFrameworkIndex < 0)
{
return p;
}

var isRidAgnosticArray = p.GetMetadata("IsRidAgnostic").Split(';');
if (isRidAgnosticArray.Length != targetFrameworksArray.Length)
{
return p;
}

updatedItem.SetMetadata("IsRidAgnostic", isRidAgnosticArray[targetFrameworkIndex]);

return updatedItem;
})
.ToArray();

return true;
}
}
}