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

Implementation of implicit imports during evaluation instead of parsing #1492

Merged
merged 6 commits into from
Jan 10, 2017

Conversation

jeffkl
Copy link
Contributor

@jeffkl jeffkl commented Dec 16, 2016

Had to do some hacky stuff in the preprocessor but all of that code is private and we can make it better in the future.

Closes #1447

@jeffkl
Copy link
Contributor Author

jeffkl commented Dec 16, 2016

@dotnet-bot test Windows_NT Build for Desktop please

{
MarkDirty("Add child element named '{0}'", child.ElementName);
}
MarkDirty("Add child element named '{0}'", child.ElementName);
Copy link
Contributor

Choose a reason for hiding this comment

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

This looks like a change in behavior. Does it need to happen for Remove just above this? Or others?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is removed because I'm reverting the original implementation where the stuff is added to the XML DOM. There is no IsImplicit property anymore so the if is removed.

The change above this is removed for the same reason.

@@ -655,6 +650,25 @@ public string InitialTargets
}

/// <summary>
///
Copy link
Contributor

Choose a reason for hiding this comment

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

Fill in the xmldoc

@@ -849,6 +861,14 @@ public ElementLocation InitialTargetsLocation
}

/// <summary>
///
Copy link
Contributor

Choose a reason for hiding this comment

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

Here too

Copy link
Member

@dsplaisted dsplaisted left a comment

Choose a reason for hiding this comment

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

Looks good, but will this affect CPS and its understanding of provenance? Does the object model expose enough information for CPS to see the implicit imports and to know whether they are at the top or bottom of the file?CPS needs this to be able to correctly manipulate project files. For example, if the implicitly imported props have <Compile Include="**\*.cs" />, then CPS needs to understand this if an item is excluded from the project, renamed, etc.

Also, I'd suggest updating Evaluator.GetCurrentDirectoryForConditionEvaluation to return the Sdk directory if there is an Sdk attribute on an import, so that you can have something like Condition="Exists('Sdk\Sdk.props')" on an Sdk import.

@jeffkl
Copy link
Contributor Author

jeffkl commented Dec 20, 2016

@dsplaisted They imports show up in the Project but not in the ProjectRootElement. When you new up a Project, the evaluation occurs and the imports are added without a location. The properties and items do show they are "imported" which I'm hoping is enough for CPS. I'm not familiar enough with CPS to know all of the implications, do you know if them showing up in the Project but not the XML representation will break CPS?

The Sdk attribute on the ProjectRootElement is how they can be controlled but that's the only thing exposed.

@jeffkl jeffkl mentioned this pull request Dec 20, 2016
@cdmihai
Copy link
Contributor

cdmihai commented Dec 20, 2016

@jviau, is this OK for CPS (see last two comments)?

@jviau
Copy link

jviau commented Dec 21, 2016

@jeffkl @cdmihai CPS uses ElementLocation at the ProjectRootElement location for determining how to modify the project file. So this will be a breaking behavior change for us. With this change, for a ProjectItemElement at the ProjectRootElement level, how would we tell if it is from an SDK import, and if it is the top import or the bottom import?

[DebuggerStepThrough]
get
{
return ProjectXmlUtilities.GetAttributeValue(XmlElement, XMakeAttributes.sdk);
Copy link
Contributor

Choose a reason for hiding this comment

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

As things get more involved with the SDKs, it might be worth creating a new SDKProjectElement which is represented as an attribute (reusing the way metadata as attributes represent themselves). This type would be a valid child for ProjectRootElements and ProjectImportElements. Then, the new type could gain responsibilities such as parsing the semicolon delimited string into a {name, version} object, doing data validation, etc

Copy link
Member

Choose a reason for hiding this comment

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

I'm in favor of putting duplicated logic in a central place, but here I don't think it's helpful to represent it as a child element expressed as an attribute. That just complicates the object model without any benefit that I see, and we can centralize the logic into utility methods or similar without changing the object model.

{
// SDK imports are added implicitly where they are evaluated at the top and bottom as if they are in the XML
//
foreach (string sdk in currentProjectOrImport.Sdk.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries)
Copy link
Contributor

@cdmihai cdmihai Dec 21, 2016

Choose a reason for hiding this comment

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

This splitting logic (and the subsequent name / version parsing) is an example of what could go in a new SDKProjectElement type, since most likely other parts of the codebase may want to parse the SDK string (or third party API users).

@@ -187,61 +187,6 @@ private void ParseProjectElement(XmlElementWithLocation element)
// so we have to set it now
_project.SetProjectRootElementFromParser(element, _project);

if (element.HasAttribute(XMakeAttributes.sdk))
Copy link
Contributor

@cdmihai cdmihai Dec 21, 2016

Choose a reason for hiding this comment

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

Shouldn't this method (and the other thing that can have SDKs, ParseImportElement too) do some SDK string validation, such as ensure it's not empty? Better to crash here than when the SDK is used in the evaluator / preprocessor.

@@ -29,24 +30,36 @@ public void SdkImportsAreInImportList()
string sdkPropsPath = Path.Combine(testSdkDirectory, "Sdk.props");
string sdkTargetsPath = Path.Combine(testSdkDirectory, "Sdk.targets");

File.WriteAllText(sdkPropsPath, "<Project />");
Copy link
Contributor

@cdmihai cdmihai Dec 21, 2016

Choose a reason for hiding this comment

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

Can you also do a ProjectSdkExplicitImport_Tests class to test out the explicit SDK imports? Or rename this class to refer to both implcit and explicit SDK elements.

@@ -6,6 +6,7 @@
using System.IO;
using System.Xml;
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you also add some invalid SDK tests, like empty SDKs, or SDK names with slash in them?

Copy link
Contributor

@cdmihai cdmihai left a comment

Choose a reason for hiding this comment

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

LGTM after the ProjectParser validates the SDK strings and there's tests for those situations.

@jeffkl
Copy link
Contributor Author

jeffkl commented Jan 3, 2017

@dsplaisted

will this affect CPS and its understanding of provenance?

I need to understand CPS more to answer this. If it uses the evaluation APIs first, the imports show up in the graph and everything has the IsImported property flag indicating that it came from an import. If it uses the construction APIs for reading the project, then I might have to rethink more of this. I've pinged Jacob to get more info but perhaps you can fill me in?

Also, I'd suggest updating Evaluator.GetCurrentDirectoryForConditionEvaluation to return the Sdk directory if there is an Sdk attribute on an import, so that you can have something like Condition="Exists('Sdk\Sdk.props')" on an Sdk import.

Good idea, I'll do this as part of the SDK resolver work and I've added your comment as a note on the Design doc #1493

@jeffkl
Copy link
Contributor Author

jeffkl commented Jan 3, 2017

@cdmihai The ProjectParser does not parse any of the ProjectRootElement attributes (InitialTargets, DefaultTargets, etc). They are all read by the evaluator as raw XML strings. Let's talk in person about your comments when you're back.

jeffkl added 6 commits January 5, 2017 07:51
…g parsing of projects.

Had to do some hacky stuff in the preprocessor but all of that code is private and we can make it better in the future.

Closes dotnet#1447
Added a unit test for an invalid SDK name
Made ImplicitImportLocation public
@jeffkl
Copy link
Contributor Author

jeffkl commented Jan 9, 2017

@dotnet-bot test OSX Build for Mono please

@rainersigwald
Copy link
Member

rainersigwald commented Jan 17, 2017

Related to #1493 and #1392.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants