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

Handle ref assemblies in up-to-date check #2414

Merged
merged 7 commits into from
Jun 21, 2017
Merged

Handle ref assemblies in up-to-date check #2414

merged 7 commits into from
Jun 21, 2017

Conversation

panopticoncentral
Copy link
Contributor

@panopticoncentral panopticoncentral commented Jun 9, 2017

Customer scenario

Given three projects that have a dependency as such: ClassLibrary1 -> ClassLibrary2 -> ConsoleApp1, if the two class libraries use the new ref assembly feature, then if ClassLibrary1's implementation is changed but not its ref assembly, the up to date check will think that ConsoleApp1 does not need to be rebuilt. It does, though, so it can copy an updated ClassLibrary1 implementation.

Without this change building projects with reference assemblies will be broken in the IDE. For Roslyn.sln we're seeing around 70% reduction in incremental build times with reference assemblies. So we want to take this change to enable such productivity enhancements.

Bugs this fixes:

Fixes #2254

Workarounds, if any

The user would have to realize that one of their assemblies is out of date and do a rebuild.

Risk

Low. The marker file we depend on is only produced in the new ref assembly case, so it doesn't affect existing projects. Fast up to date checks can be turned off if needed.

Performance impact

Low. We check timestamps on a handful of extra files, small compared to the list of files we already check.

Is this a regression from a previous update?

No.

@panopticoncentral
Copy link
Contributor Author

This changes how the up-to-date check collects references because ResolveProjectReferences is too early. We need to wait until project references have been redirected to ref assemblies, which happens later. It seemed easiest to just pick up the list of actual references that Compile task will use. Let me know if there seems to be a problem with this.

@panopticoncentral
Copy link
Contributor Author

cc: @rainersigwald, @jcouv

@basoundr
Copy link
Contributor

basoundr commented Jun 9, 2017

Are there no tests for this component?

@panopticoncentral
Copy link
Contributor Author

Unfortunately, not yet. We need integration tests, so as soon as they are running, I'm going to add some.

@jcouv
Copy link
Member

jcouv commented Jun 9, 2017

Thanks!
I can't say that I fully understand the change though, so I'll let reviews the project-system team.

I'll stop by Monday to discuss the testing strategy (I'm not familiar with the integration tests). A few scenarios come to mind with the 3 project setup (ref assemblies turned on):
(1) build from scratch,
(2) build with no change (skips all),
(3) add comment into lowest-level library and build,
(4) build with no change,
(5) make a significant change in lowest level library and build.
Scenarios with ref assemblies turned on only in some projects coud be interesting as well. I'm assuming that old scenarios with ref assemblies turned off are already covered.

@panopticoncentral
Copy link
Contributor Author

Ping @dotnet/project-system

Copy link
Member

@rainersigwald rainersigwald left a comment

Choose a reason for hiding this comment

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

Mostly concerned about the implementation-assembly-of-references case we discussed in dotnet/msbuild#2180 (comment).

}
logger.Verbose("Found output marker '{0}'.", _markerFile);

var latestInputMarker = GetLatestInput(_referenceMarkerFiles, timestampCache);
Copy link
Member

Choose a reason for hiding this comment

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

Possible future improvement: you don't actually need the latest here, since the only question is "is any reference newer than the output". You might want to look at MSBuild's IsAnyOutOfDate for ideas.

{
_references[referenceSchema] = new HashSet<string>(changes.After.Items.Select(item => item.Value[ResolvedPath]));
_compilationReferences.Add(item.Value[ResolvedPath]);
Copy link
Member

Choose a reason for hiding this comment

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

And also

                    _compilationReferences.Add(item.Value[OriginalPath]);

if it exists?

if (e.ProjectChanges.TryGetValue(ResolvedAnalyzerReference.SchemaName, out var changes) &&
changes.Difference.AnyChanges)
{
_analyzerReferences = new HashSet<string>(changes.After.Items.Select(item => item.Value[ResolvedPath]));
Copy link
Contributor

Choose a reason for hiding this comment

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

Which sort of string comparison do we want to use for this HashSet?

@@ -118,12 +120,27 @@ private void OnProjectChanged(IProjectSubscriptionUpdate e)
_isDisabled = disableFastUpToDateCheckString != null && string.Equals(disableFastUpToDateCheckString, TrueValue, StringComparison.OrdinalIgnoreCase);

_msBuildProjectFullPath = e.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.MSBuildProjectFullPathProperty, _msBuildProjectFullPath);
foreach (var referenceSchema in ReferenceSchemas)
_markerFile = e.CurrentState.GetPropertyOrDefault(ConfigurationGeneral.SchemaName, ConfigurationGeneral.CopyUpToDateMarkerProperty, null);
Copy link
Member

Choose a reason for hiding this comment

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

In dotnet/msbuild#2213 I'd like to change this to be an item instead of a property. Reasoning is embedded there. Is that a problem?

Copy link
Contributor

Choose a reason for hiding this comment

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

That might actually make it easier i think. #2416 is making it so that we just query the projectitemschemaservice for all item types and we can add CopyUpToDateMarker item to the schema. If we did that, do we still need the CheckMarkers method below? Ideally if we can avoid any special code here for the markers that would be ideal.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Unfortunately, we still need CheckMarkers. The markers have to be checked independently of the project's inputs/outputs because they are independent of one another (i.e. a dependent assembly might have an implementation-only change that won't cause the outputs of the project to be rebuilt).

@panopticoncentral
Copy link
Contributor Author

OK, I've updated this review to handle CopyUpToDate as an item. I had to change slightly how we handle non-build items like CopyUpToDate, UpToDateCheckInput, and UpToDateCheckOutput to make the UI work correctly (i.e. not have them show up in Solution Explorer).

@srivatsn
Copy link
Contributor

Is this ready now? @jcouv @panopticoncentral ?

@jcouv
Copy link
Member

jcouv commented Jun 20, 2017

I have been using it yesterday and this morning. Seems to work on Roslyn.
I'll run some additional pointed validation this afternoon (Jason's scenarios dotnet/roslyn#18612 (comment))

@srivatsn
Copy link
Contributor

Tagging @MattGertz for Preview4

@panopticoncentral
Copy link
Contributor Author

@jcouv Can you test some more? I added two fixes:

  • We could get into a state where a project loses track of it's own marker file. I think this was what was causing the check to fail after a while but succeed when you closed and reopened the project.

  • The algorithm we came up with had to be slightly modified. A project's marker file needs to be compared against the marker files AND the implementation DLLs of any projects it references that produce ref assemblies. We can't just compare against marker files because the project we reference might not itself reference projects that have ref assemblies, in which case it won't have a marker file. If this doesn't make sense, we can talk about it tomorrow.

Regardless, the scenario we were testing today now seems to work correctly.

@jcouv
Copy link
Member

jcouv commented Jun 21, 2017

I'll try this iteration in the morning.
As for the algorithm, what you describe seems correct according to #2254 (see the diagram) if we consider that projects without refout don't produce a marker file.
If the upstream project's primary output is fresher than my output marker, I need to build.

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.

Up-to-date check awareness of reference assemblies
8 participants