diff --git a/ref/net46/Microsoft.Build/Microsoft.Build.cs b/ref/net46/Microsoft.Build/Microsoft.Build.cs
index 0c901b5ba0e..9e45e466a86 100644
--- a/ref/net46/Microsoft.Build/Microsoft.Build.cs
+++ b/ref/net46/Microsoft.Build/Microsoft.Build.cs
@@ -90,10 +90,12 @@ public partial class ProjectImportElement : Microsoft.Build.Construction.Project
{
internal ProjectImportElement() { }
public Microsoft.Build.Construction.ImplicitImportLocation ImplicitImportLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
+ public string MinimumVersion { get { throw null; } set { } }
public string Project { get { throw null; } set { } }
public Microsoft.Build.Construction.ElementLocation ProjectLocation { get { throw null; } }
public string Sdk { get { throw null; } set { } }
public Microsoft.Build.Construction.ElementLocation SdkLocation { get { throw null; } }
+ public string Version { get { throw null; } set { } }
protected override Microsoft.Build.Construction.ProjectElement CreateNewInstance(Microsoft.Build.Construction.ProjectRootElement owner) { throw null; }
}
[System.Diagnostics.DebuggerDisplayAttribute("#Imports={Count} Condition={Condition} Label={Label}")]
diff --git a/ref/netstandard1.3/Microsoft.Build/Microsoft.Build.cs b/ref/netstandard1.3/Microsoft.Build/Microsoft.Build.cs
index 1d02c2f2984..1cec1650f41 100644
--- a/ref/netstandard1.3/Microsoft.Build/Microsoft.Build.cs
+++ b/ref/netstandard1.3/Microsoft.Build/Microsoft.Build.cs
@@ -90,10 +90,12 @@ public partial class ProjectImportElement : Microsoft.Build.Construction.Project
{
internal ProjectImportElement() { }
public Microsoft.Build.Construction.ImplicitImportLocation ImplicitImportLocation { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } }
+ public string MinimumVersion { get { throw null; } set { } }
public string Project { get { throw null; } set { } }
public Microsoft.Build.Construction.ElementLocation ProjectLocation { get { throw null; } }
public string Sdk { get { throw null; } set { } }
public Microsoft.Build.Construction.ElementLocation SdkLocation { get { throw null; } }
+ public string Version { get { throw null; } set { } }
protected override Microsoft.Build.Construction.ProjectElement CreateNewInstance(Microsoft.Build.Construction.ProjectRootElement owner) { throw null; }
}
[System.Diagnostics.DebuggerDisplayAttribute("#Imports={Count} Condition={Condition} Label={Label}")]
diff --git a/src/Build.OM.UnitTests/Construction/ProjectSdkImplicitImport_Tests.cs b/src/Build.OM.UnitTests/Construction/ProjectSdkImplicitImport_Tests.cs
index c99a43537cf..75340899173 100644
--- a/src/Build.OM.UnitTests/Construction/ProjectSdkImplicitImport_Tests.cs
+++ b/src/Build.OM.UnitTests/Construction/ProjectSdkImplicitImport_Tests.cs
@@ -20,6 +20,24 @@ namespace Microsoft.Build.UnitTests.OM.Construction
///
public class ProjectSdkImplicitImport_Tests : IDisposable
{
+ private const string ProjectTemplateSdkAsAttribute = @"
+
+ {1}
+";
+
+ private const string ProjectTemplateSdkAsElement = @"
+
+
+ {1}
+";
+
+ private const string ProjectTemplateSdkAsExplicitImport = @"
+
+
+ {1}
+
+";
+
private const string SdkName = "MSBuildUnitTestSdk";
private readonly string _testSdkRoot;
private readonly string _testSdkDirectory;
@@ -37,29 +55,18 @@ public ProjectSdkImplicitImport_Tests()
}
[Theory]
- [InlineData(@"
-
-
- null
-
-
-")]
- [InlineData(@"
-
-
-
- null
-
-
-")]
- public void SdkImportsAreInLogicalProject(string projectFormatString)
+ [InlineData(ProjectTemplateSdkAsAttribute, false)]
+ [InlineData(ProjectTemplateSdkAsElement, true)]
+ [InlineData(ProjectTemplateSdkAsExplicitImport, false)]
+ public void SdkImportsAreInLogicalProject(string projectFormatString, bool expectImportInLogicalProject)
{
+ string projectInnerContents = @"null";
File.WriteAllText(_sdkPropsPath, "Hello");
File.WriteAllText(_sdkTargetsPath, "World");
using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
{
- string content = string.Format(projectFormatString, SdkName);
+ string content = string.Format(projectFormatString, SdkName, projectInnerContents);
ProjectRootElement projectRootElement = ProjectRootElement.Create(XmlReader.Create(new StringReader(content)));
@@ -68,42 +75,30 @@ public void SdkImportsAreInLogicalProject(string projectFormatString)
IList children = project.GetLogicalProject().ToList();
// style will have an extra ProjectElment.
- var expected = projectFormatString.Contains("Sdk=") ? 6 : 7;
- Assert.Equal(expected, children.Count);
+ Assert.Equal(expectImportInLogicalProject ? 7 : 6, children.Count);
}
}
[Theory]
- [InlineData(@"
-
-
- null
-
-
-")]
- [InlineData(@"
-
-
-
- null
-
-
-")]
- public void SdkImportsAreInImportList(string projectFormatString)
+ [InlineData(ProjectTemplateSdkAsAttribute, false)]
+ [InlineData(ProjectTemplateSdkAsElement, false)]
+ [InlineData(ProjectTemplateSdkAsExplicitImport, true)]
+ public void SdkImportsAreInImportList(string projectFormatString, bool expectImportInLogicalProject)
{
+ string projectInnerContents = @"null";
File.WriteAllText(_sdkPropsPath, "Hello");
File.WriteAllText(_sdkTargetsPath, "World");
using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
{
- string content = string.Format(projectFormatString, SdkName);
+ string content = string.Format(projectFormatString, SdkName, projectInnerContents);
ProjectRootElement projectRootElement = ProjectRootElement.Create(XmlReader.Create(new StringReader(content)));
var project = new Project(projectRootElement);
- // The XML representation of the project should indicate there are no imports
- Assert.Equal(0, projectRootElement.Imports.Count);
+ // The XML representation of the project should only indicate an import if they are not implicit.
+ Assert.Equal(expectImportInLogicalProject ? 2 : 0, projectRootElement.Imports.Count);
// The project representation should have imports
Assert.Equal(2, project.Imports.Count);
@@ -128,14 +123,24 @@ public void SdkImportsAreInImportList(string projectFormatString)
[Theory]
[InlineData(@"
-")]
+", false)]
[InlineData(@"
-")]
- public void SdkSupportsMultiple(string projectFormatString)
+", false)]
+ [InlineData(@"
+
+
+
+
+
+
+
+
+", true)]
+ public void SdkSupportsMultiple(string projectFormatString, bool expectImportInLogicalProject)
{
IList sdkNames = new List
{
@@ -161,7 +166,7 @@ public void SdkSupportsMultiple(string projectFormatString)
Project project = new Project(projectRootElement);
// The XML representation of the project should indicate there are no imports
- Assert.Equal(0, projectRootElement.Imports.Count);
+ Assert.Equal(expectImportInLogicalProject ? 6 : 0, projectRootElement.Imports.Count);
// The project representation should have twice as many imports as SDKs
Assert.Equal(sdkNames.Count * 2, project.Imports.Count);
@@ -173,21 +178,12 @@ public void SdkSupportsMultiple(string projectFormatString)
}
[Theory]
- [InlineData(@"
-")]
- [InlineData(@"
-
-")]
- public void ProjectWithSdkImportsIsCloneable(string projectFileFirstLineFormat)
+ [InlineData(ProjectTemplateSdkAsAttribute)]
+ [InlineData(ProjectTemplateSdkAsElement)]
+ [InlineData(ProjectTemplateSdkAsExplicitImport)]
+ public void ProjectWithSdkImportsIsCloneable(string projectFormatString)
{
- File.WriteAllText(_sdkPropsPath, "");
- File.WriteAllText(_sdkTargetsPath, "");
-
- using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
- {
- // Based on the new-console-project CLI template (but not matching exactly
- // should not be a deal-breaker).
- string content = $@"{string.Format(projectFileFirstLineFormat, SdkName)}
+ string projectInnerContents = @"
Exe
netcoreapp1.0
@@ -200,10 +196,15 @@ public void ProjectWithSdkImportsIsCloneable(string projectFileFirstLineFormat)
-
-
-";
+ ";
+ File.WriteAllText(_sdkPropsPath, " < Project />");
+ File.WriteAllText(_sdkTargetsPath, "");
+ using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
+ {
+ // Based on the new-console-project CLI template (but not matching exactly
+ // should not be a deal-breaker).
+ string content = string.Format(projectFormatString, SdkName, projectInnerContents);
ProjectRootElement project = ProjectRootElement.Create(XmlReader.Create(new StringReader(content)));
project.DeepClone();
@@ -211,21 +212,12 @@ public void ProjectWithSdkImportsIsCloneable(string projectFileFirstLineFormat)
}
[Theory]
- [InlineData(@"
-")]
- [InlineData(@"
-
-")]
- public void ProjectWithSdkImportsIsRemoveable(string projectFileFirstLineFormat)
+ [InlineData(ProjectTemplateSdkAsAttribute)]
+ [InlineData(ProjectTemplateSdkAsElement)]
+ [InlineData(ProjectTemplateSdkAsExplicitImport)]
+ public void ProjectWithSdkImportsIsRemoveable(string projectFormatString)
{
- File.WriteAllText(_sdkPropsPath, "");
- File.WriteAllText(_sdkTargetsPath, "");
-
- using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
- {
- // Based on the new-console-project CLI template (but not matching exactly
- // should not be a deal-breaker).
- string content = $@"{string.Format(projectFileFirstLineFormat, SdkName)}
+ string projectInnerContents = @"
Exe
netcoreapp1.0
@@ -238,10 +230,15 @@ public void ProjectWithSdkImportsIsRemoveable(string projectFileFirstLineFormat)
-
-
-";
+ ";
+ File.WriteAllText(_sdkPropsPath, " < Project />");
+ File.WriteAllText(_sdkTargetsPath, "");
+ using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
+ {
+ // Based on the new-console-project CLI template (but not matching exactly
+ // should not be a deal-breaker).
+ string content = string.Format(projectFormatString, SdkName, projectInnerContents);
ProjectRootElement project = ProjectRootElement.Create(XmlReader.Create(new StringReader(content)));
ProjectRootElement clone = ProjectRootElement.Create(XmlReader.Create(new StringReader(content)));
@@ -280,21 +277,28 @@ public void ProjectWithInvalidSdkName()
///
/// Verifies that an empty SDK attribute works and nothing is imported.
///
- [Fact]
- public void ProjectWithEmptySdkName()
+ [Theory]
+ [InlineData(ProjectTemplateSdkAsAttribute, false)]
+ [InlineData(ProjectTemplateSdkAsElement, true)]
+ [InlineData(ProjectTemplateSdkAsExplicitImport, true)]
+ public void ProjectWithEmptySdkName(string projectFormatString, bool throwsOnEvaluate)
{
+ string projectInnerContents =
+ @"null";
+
using (new Helpers.TemporaryEnvironment("MSBuildSDKsPath", _testSdkRoot))
{
- string content = @"
-
-
- null
-
- ";
-
- Project project = new Project(ProjectRootElement.Create(XmlReader.Create(new StringReader(content))));
-
- Assert.Equal(0, project.Imports.Count);
+ string content = string.Format(projectFormatString, string.Empty, projectInnerContents);
+ if (throwsOnEvaluate)
+ {
+ Assert.Throws(
+ () => new Project(ProjectRootElement.Create(XmlReader.Create(new StringReader(content)))));
+ }
+ else
+ {
+ var project = new Project(ProjectRootElement.Create(XmlReader.Create(new StringReader(content))));
+ Assert.Equal(0, project.Imports.Count);
+ }
}
}
diff --git a/src/Build/Construction/ProjectImportElement.cs b/src/Build/Construction/ProjectImportElement.cs
index aa9ffd01570..8d83b856e44 100644
--- a/src/Build/Construction/ProjectImportElement.cs
+++ b/src/Build/Construction/ProjectImportElement.cs
@@ -22,10 +22,11 @@ public class ProjectImportElement : ProjectElement
///
/// Initialize a parented ProjectImportElement
///
- internal ProjectImportElement(XmlElementWithLocation xmlElement, ProjectElementContainer parent, ProjectRootElement containingProject)
+ internal ProjectImportElement(XmlElementWithLocation xmlElement, ProjectElementContainer parent, ProjectRootElement containingProject, SdkReference sdkReference = null)
: base(xmlElement, parent, containingProject)
{
ErrorUtilities.VerifyThrowArgumentNull(parent, "parent");
+ ParsedSdkReference = sdkReference;
}
///
@@ -75,12 +76,40 @@ public string Sdk
set
{
ErrorUtilities.VerifyThrowArgumentLength(value, XMakeAttributes.sdk);
-
+ if (!CheckUpdatedSdk()) return;
ProjectXmlUtilities.SetOrRemoveAttribute(XmlElement, XMakeAttributes.sdk, value);
MarkDirty("Set Import Sdk {0}", value);
}
}
+ ///
+ /// Gets or sets the version associated with this SDK import
+ ///
+ public string Version
+ {
+ get { return ProjectXmlUtilities.GetAttributeValue(XmlElement, XMakeAttributes.sdkVersion); }
+ set
+ {
+ if (!CheckUpdatedSdk()) return;
+ ProjectXmlUtilities.SetOrRemoveAttribute(XmlElement, XMakeAttributes.sdkVersion, value);
+ MarkDirty("Set Import Version {0}", value);
+ }
+ }
+
+ ///
+ /// Gets or sets the minimum SDK version required by this import.
+ ///
+ public string MinimumVersion
+ {
+ get { return ProjectXmlUtilities.GetAttributeValue(XmlElement, XMakeAttributes.sdkMinimumVersion); }
+ set
+ {
+ if (!CheckUpdatedSdk()) return;
+ ProjectXmlUtilities.SetOrRemoveAttribute(XmlElement, XMakeAttributes.sdkMinimumVersion, value);
+ MarkDirty("Set Import Minimum Version {0}", value);
+ }
+ }
+
///
/// Location of the Sdk attribute
///
@@ -139,5 +168,22 @@ protected override ProjectElement CreateNewInstance(ProjectRootElement owner)
{
return owner.CreateImportElement(this.Project);
}
+
+ private bool CheckUpdatedSdk()
+ {
+
+ SdkReference sdk = new SdkReference(
+ ProjectXmlUtilities.GetAttributeValue(XmlElement, XMakeAttributes.sdk, true),
+ ProjectXmlUtilities.GetAttributeValue(XmlElement, XMakeAttributes.sdkVersion, true),
+ ProjectXmlUtilities.GetAttributeValue(XmlElement, XMakeAttributes.sdkMinimumVersion, true));
+
+ if (sdk.Equals(ParsedSdkReference))
+ {
+ return false;
+ }
+
+ ParsedSdkReference = sdk;
+ return true;
+ }
}
}
diff --git a/src/Build/Evaluation/ProjectParser.cs b/src/Build/Evaluation/ProjectParser.cs
index 9f81b243933..c08247afe34 100644
--- a/src/Build/Evaluation/ProjectParser.cs
+++ b/src/Build/Evaluation/ProjectParser.cs
@@ -11,6 +11,7 @@
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using Microsoft.Build.Framework;
#if (!STANDALONEBUILD)
using Microsoft.Internal.Performance;
@@ -515,12 +516,19 @@ private ProjectImportElement ParseProjectImportElement(XmlElementWithLocation el
);
ProjectXmlUtilities.VerifyThrowProjectAttributes(element, s_validAttributesOnImport);
-
ProjectXmlUtilities.VerifyThrowProjectRequiredAttribute(element, XMakeAttributes.project);
-
ProjectXmlUtilities.VerifyThrowProjectNoChildElements(element);
- return new ProjectImportElement(element, parent, _project);
+ SdkReference sdk = null;
+ if (element.HasAttribute(XMakeAttributes.sdk))
+ {
+ sdk = new SdkReference(
+ element.GetAttribute(XMakeAttributes.sdk),
+ element.GetAttribute(XMakeAttributes.sdkVersion),
+ element.GetAttribute(XMakeAttributes.sdkMinimumVersion));
+ }
+
+ return new ProjectImportElement(element, parent, _project, sdk);
}
///