Skip to content
This repository was archived by the owner on Jul 15, 2023. It is now read-only.

Commit 83668dc

Browse files
authored
Update TargetNameParser to understand versions (#757)
* Allow TargetNameParser to parse both target identifiers and FrameworkNames (target identifiers with version). * Remove unneeded private set; * Add tests
1 parent 8ec1e78 commit 83668dc

File tree

2 files changed

+118
-21
lines changed

2 files changed

+118
-21
lines changed

src/lib/Microsoft.Fx.Portability/Analysis/TargetNameParser.cs

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public TargetNameParser(IApiCatalogLookup catalog, string defaultTargets)
1919
DefaultTargets = GetDefaultTargets(defaultTargets).ToList();
2020
}
2121

22-
public IEnumerable<FrameworkName> DefaultTargets { get; private set; }
22+
public IEnumerable<FrameworkName> DefaultTargets { get; }
2323

2424
/// <summary>
2525
/// Maps the list of targets specified as strings to a list of supported target names.
@@ -65,46 +65,67 @@ public IEnumerable<FrameworkName> MapTargetsToExplicitVersions(IEnumerable<strin
6565
private IEnumerable<FrameworkName> GetDefaultTargets(string defaultTargets)
6666
{
6767
// The default targets are kept in a ';' separated string.
68-
string[] defaultTargetsSplit = defaultTargets.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
68+
// Trimming target names because "; .NET Core" is interpreted as
69+
// " .NET Core", which does not exist, but ".NET Core" does.
70+
var defaultTargetsSplit = defaultTargets?.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim())
71+
?? Array.Empty<string>();
6972

7073
// Create a hashset of all the targets specified in the configuration setting.
71-
HashSet<FrameworkName> parsedDefaultTargets = new HashSet<FrameworkName>(ParseTargets(defaultTargetsSplit, skipNonExistent: true));
72-
73-
// return all the public targets (their latest versions) as long as they also show up the default targets set.
74-
return _catalog.GetPublicTargets()
75-
.Select(plat => plat.Identifier)
76-
.Distinct()
77-
.Select(name => _catalog.GetLatestVersion(name))
78-
.Where(plat => plat != null && parsedDefaultTargets.Contains(plat));
74+
var parsedDefaultTargets = ParseTargets(defaultTargetsSplit, skipNonExistent: true);
75+
76+
// Verify that any default targets returned are part of the public interface.
77+
var publicTargets = new HashSet<FrameworkName>(_catalog.GetPublicTargets());
78+
79+
return parsedDefaultTargets.Where(x => publicTargets.Contains(x));
7980
}
8081

8182
/// <summary>
8283
/// Parse a string containing target names into FrameworkNames.
8384
///
8485
/// Try the following in order:
85-
/// 1. Check if the target specified uses the 'simple' name (i.e. Windows, .NET Framework) then get the latest version for it
86-
/// 2. Try to parse it as a target name. If the target was not a valid FrameworkName, an ArgumentException will be thrown and passed down to user.
87-
/// <exception cref="UnknownTargetException">Thrown when a target is unknown</exception>
86+
/// 1. Check if the target specified uses a FrameworkName (ie. .NET Core, Version=1.0)
87+
/// 2. Check if the target specified uses the 'simple' name (i.e. Windows, .NET Framework)
88+
/// then get the latest version for it.
8889
/// </summary>
8990
/// <param name="skipNonExistent">true to suppress <see cref="UnknownTargetException"/>
9091
/// when a target is not found. false, will not throw and skip that target instead.</param>
92+
/// <exception cref="UnknownTargetException">Thrown when a target is unknown.</exception>
9193
private ICollection<FrameworkName> ParseTargets(IEnumerable<string> targets, bool skipNonExistent)
9294
{
93-
var list = new List<FrameworkName>();
94-
95-
foreach (var target in targets.Distinct(StringComparer.OrdinalIgnoreCase))
95+
bool TryParseFrameworkName(string targetName, out FrameworkName frameworkName)
9696
{
9797
try
9898
{
99-
list.Add(_catalog.GetLatestVersion(target) ?? new FrameworkName(target));
99+
frameworkName = new FrameworkName(targetName);
100+
return true;
100101
}
101102
catch (ArgumentException)
102103
{
103104
// Catch ArgumentException because FrameworkName does not have a TryParse method
104-
if (!skipNonExistent)
105-
{
106-
throw new UnknownTargetException(target);
107-
}
105+
frameworkName = null;
106+
return false;
107+
}
108+
}
109+
110+
var list = new List<FrameworkName>();
111+
112+
foreach (var target in targets.Distinct(StringComparer.OrdinalIgnoreCase))
113+
{
114+
var framework = TryParseFrameworkName(target, out var name)
115+
? name
116+
: _catalog.GetLatestVersion(target);
117+
118+
if (framework != null)
119+
{
120+
list.Add(framework);
121+
}
122+
else if (!skipNonExistent)
123+
{
124+
throw new UnknownTargetException(target);
125+
}
126+
else
127+
{
128+
// Trace.TraceWarning("Could not resolve target: {0}", target);
108129
}
109130
}
110131

tests/lib/Microsoft.Fx.Portability.Tests/Analysis/TargetNameParserTests.cs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

44
using Microsoft.Fx.Portability.Analysis;
5+
using Microsoft.Fx.Portability.ObjectModel;
56
using Microsoft.Fx.Portability.TestData;
7+
using NSubstitute;
68
using System;
9+
using System.Collections.Generic;
710
using System.Linq;
811
using System.Runtime.Versioning;
912
using Xunit;
@@ -99,5 +102,78 @@ public static void NonExistentDefaultTarget()
99102
Assert.Single(parser.DefaultTargets);
100103
Assert.Equal(target1Framework, parser.DefaultTargets.Single());
101104
}
105+
106+
[Fact]
107+
public static void RemovesNonPublicVersionedTargets()
108+
{
109+
// Arrange
110+
var defaultTargets = "TargetA, Version=2.0;TargetB; TargetC,Version=1.0";
111+
var target1 = new FrameworkName("TargetA", new Version("2.0"));
112+
113+
// The latest version is 1.5, but is this version not a public target.
114+
var target2 = new FrameworkName("TargetB", new Version("1.5"));
115+
116+
// TargetC is not part of the public platform.
117+
var target3 = new FrameworkName("TargetC", new Version("1.0"));
118+
var expected = new HashSet<FrameworkName> { target1 };
119+
120+
var catalog = Substitute.For<IApiCatalogLookup>();
121+
catalog.GetLatestVersion(target2.Identifier).Returns(target2);
122+
catalog.GetPublicTargets().Returns(new[]
123+
{
124+
new FrameworkName(target1.Identifier, new Version("1.1")),
125+
new FrameworkName(target2.Identifier, new Version("1.0")),
126+
target1,
127+
});
128+
129+
// Act
130+
var parser = new TargetNameParser(catalog, defaultTargets);
131+
132+
// Assert
133+
var actual = new HashSet<FrameworkName>(parser.DefaultTargets);
134+
135+
Assert.Equal(expected.Count, actual.Count);
136+
Assert.All(parser.DefaultTargets, target =>
137+
{
138+
Assert.True(expected.Remove(target));
139+
});
140+
Assert.Empty(expected);
141+
}
142+
143+
[Fact]
144+
public static void CanParseSimplifiedAndVersionedTargets()
145+
{
146+
// Arrange
147+
var defaultTargets = "TargetA, Version=2.0;TargetB; TargetC,Version=1.0";
148+
var target1 = new FrameworkName("TargetA", new Version("2.0"));
149+
var target2 = new FrameworkName("TargetB", new Version("1.5"));
150+
var target3 = new FrameworkName("TargetC", new Version("1.0"));
151+
var expected = new HashSet<FrameworkName> { target1, target2, target3 };
152+
153+
var catalog = Substitute.For<IApiCatalogLookup>();
154+
catalog.GetLatestVersion(target2.Identifier).Returns(target2);
155+
catalog.GetPublicTargets().Returns(new[]
156+
{
157+
new FrameworkName(target1.Identifier, new Version("1.1")),
158+
new FrameworkName(target2.Identifier, new Version("1.2")),
159+
new FrameworkName(target2.Identifier, new Version("2.0")),
160+
target1,
161+
target2,
162+
target3
163+
});
164+
165+
// Act
166+
var parser = new TargetNameParser(catalog, defaultTargets);
167+
168+
// Assert
169+
var actual = new HashSet<FrameworkName>(parser.DefaultTargets);
170+
171+
Assert.Equal(expected.Count, actual.Count);
172+
Assert.All(parser.DefaultTargets, target =>
173+
{
174+
Assert.True(expected.Remove(target));
175+
});
176+
Assert.Empty(expected);
177+
}
102178
}
103179
}

0 commit comments

Comments
 (0)