Skip to content

Conversation

@tmat
Copy link
Member

@tmat tmat commented Mar 7, 2018

Customer scenario

This change is a piece in larger effort to make SourceLink easy to use, robust and compatible with deterministic builds.

See https://github.com/tmat/repository-info/tree/master/docs for an overview of the end-to-end scenario and dotnet/msbuild#2960 for a list of all components that support the scenario.

Bugs this fixes

#25635

Workarounds, if any

Risk

Small.

Performance impact

Small.

Is this a regression from a previous update?

New feature.

Root cause analysis

How was the bug found?

Test documentation updated?

Implementation details

The data flow among msbuild targets involved here is a bit tricky. The high-level design assumes a set of producers and a set of consumers of source control information. Producing targets (e.g. target wrapping a GitLib) need to execute before consuming targets (e.g. AssemblyInformationalVersion generator, Roslyn compiler, NuSpec generator, etc.), thus they need to synchronize on an in-between target, which is InitializeSourceControlInformation target defined in Microsoft.Common.targets.

Source control info producer populates SourceRoot items. They can also be populated from source packages or added manually to the project.
MapSourceRootPaths takes SourceRoot items and adds MappedPath to metadata of each item. This is the path that the compiler will generate into the DLL/PDB. It's a path with a deterministic prefix if Deterministic is true, and the original path otherwise.
SourceRoot then flows to the other target defined here that calculates PathMap based on the MappedPath metadata of each root. PathMap is then used by csc task.
SoruceRoot also flows to Source Link generator target (which will be supplied by a SourceLink nuget package), which uses MappedPath and other info on the SourceRoot to generate sourcelink.json file that is passed to csc task.

TODO:

@tmat tmat requested a review from a team as a code owner March 7, 2018 22:00
@tmat
Copy link
Member Author

tmat commented Mar 7, 2018

@nguerrera

Copy link
Member Author

Choose a reason for hiding this comment

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

@jaredpar Can these 3 issues with PathMap be addressed in 15.7 so we can clean this up?

Copy link
Member Author

Choose a reason for hiding this comment

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

@jaredpar Ping.

Copy link
Member

Choose a reason for hiding this comment

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

Is this asking to add a new warning / error to the compiler in the default config?

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, that's one of the issues. Right now if you pass /deterministic and /pathmap that doesn't cover all paths you don't know that the compiler outputs are still non-deterministic.

Copy link
Member Author

@tmat tmat Mar 20, 2018

Choose a reason for hiding this comment

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

This particular issue is not blocking the SourceLink change. It's the other issues with quoting and escaping that are (to some extent).

Copy link
Member Author

Choose a reason for hiding this comment

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

Then add a new switch? Or you don't see value in reporting that error?

Copy link
Member Author

Choose a reason for hiding this comment

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

/deterministic:strict

Copy link
Member

Choose a reason for hiding this comment

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

It's not that I don't see value in the switch, I just understand the constraints we live under. There are scenarios which are far more valuable and easy to detect for which I can't issue new diagnostics.


In reply to: 176509304 [](ancestors = 176509304)

Copy link
Member Author

Choose a reason for hiding this comment

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

This scenario is somewhat unique though since one can't write an analyzer that reports these errors currently. We would need to expose a new API that the analyzer could hook into. This would be a callback that's invoked on every path that's embedded into the DLL/PDB. Then an analyzer could hook that and report errors if the paths do not match the PathMap. Would that be a preferred way?

Copy link
Member Author

Choose a reason for hiding this comment

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

I guess the analyzer can be written today that finds all the paths that end up embedded into the DLL/PDB, but it'd be hard to keep it up to date with the compiler.

@tmat tmat force-pushed the SourceLink branch 2 times, most recently from 5772897 to efdc916 Compare March 20, 2018 16:50
@tmat tmat changed the title WIP: Implements targets that calculate PathMap from SourceRoots Implements targets that calculate PathMap from SourceRoots Mar 20, 2018
@tmat tmat added this to the 15.7 milestone Mar 20, 2018
@tmat tmat requested a review from a team as a code owner March 22, 2018 01:01
Copy link
Member

Choose a reason for hiding this comment

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

Split-Path $dotnet [](start = 30, length = 18)

Should use Split-Path -Parent to make the use explicit here.

Copy link
Member

Choose a reason for hiding this comment

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

$env:DOTNET_INSTALL_DIR [](start = 4, length = 24)

This approach overall seems fragile. Our tests run in many contexts, most of which are not driven by this script. Hence there is no way to depend on this value being set.

Copy link
Member Author

Choose a reason for hiding this comment

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

A better idea? The test runner tries to use the DOTNET_INSTALL_DIR and falls back to looking for the right version of the SDK on PATH.

Copy link
Member

Choose a reason for hiding this comment

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

Unfortunately I don't have a better idea.


In reply to: 176506227 [](ancestors = 176506227)

Copy link
Member

Choose a reason for hiding this comment

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

DotNetSdkTests [](start = 17, length = 14)

If the intent is to test MSBuild you need to be deploying a Directory.Build.props, Directory.Build.targets, and editorconfig that disables parent lookup. That's necessary to make these type of tests stable.

Copy link
Contributor

Choose a reason for hiding this comment

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

Why editor config?

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. I'll add those.

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

xml doc would be good here too #Closed

Copy link
Member Author

@tmat tmat Mar 23, 2018

Choose a reason for hiding this comment

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

Will add. #Resolved

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Nit: local functions should be camelCased. If it's ok, please also move to the end of the method. #Closed

Copy link
Member Author

@tmat tmat Mar 23, 2018

Choose a reason for hiding this comment

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

I have actually refactored it a bit here. #Resolved

Copy link
Member Author

@tmat tmat Mar 23, 2018

Choose a reason for hiding this comment

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

Will change the casing.

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 looking at iteration 3. Should I wait until you push something more?


In reply to: 176874575 [](ancestors = 176874575)

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Please add a link for follow-up issue, rather than just a TODO comment. #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Perhaps sourceControlled? #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Perhaps add comment // root isn't nested #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

root.ItemSpec [](start = 56, length = 13)

Perhaps extract local for string path = root.ItemSpec; #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Is there harm to leave the zero in /_0/ in the i==0 case? #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Can or should we assert that there was no metadata called MappedPath on the root before setting our own? #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Maybe "...source-controlled non-nested roots first:" #Closed

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Maybe "...other non-nested roots:" #Closed

@tmat
Copy link
Member Author

tmat commented Mar 23, 2018

@jcouv I rebased the PR and your comments are not visible here anymore:

Is there harm to leave the zero in /_0/ in the i == 0 case?

No harm, just the common case will be a single root, so I didn't want to include 0 for aesthetic reasons :).
Note that these paths will be visible thru CallerFilePathAttribute.

Can or should we assert that there was no metadata called MappedPath on the root before setting our own?

Good question. we can't assert, but we can report a warning if there is one. Rewriting metadata in msbuild does not usually produce warnings though, so I'm not sure we should here.

#Resolved

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

I looked around for a similar UsingTask to import the Csc task. I found it in WriteMSBuildFiles which makes a .props file for the package.
Should we use the same mechanism for the MapSourceRoots task?
#Closed

Copy link
Member Author

@tmat tmat Mar 23, 2018

Choose a reason for hiding this comment

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

No, this needs to be in the Microsoft.Managed.Core.targets. These are targets are only used via a package reference if you explicitly drop Microsoft.Net.Compilers to your project. By default these are picked up from msbuild, where we insert them to.

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

ContinuousIntegrationBuild [](start = 117, length = 26)

Btw, it would be helpful to indicate in your documentation for ContinuousIntegrationBuild that it's a boolean.
https://github.com/tmat/repository-info/tree/master/docs #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

Copy link
Member

@jcouv jcouv Mar 23, 2018

Choose a reason for hiding this comment

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

Typo: This target ... #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed.

Copy link
Member

@jcouv jcouv Mar 24, 2018

Choose a reason for hiding this comment

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

I think the tests/framework simulating end-to-end msbuild are great, and will be useful for future work with custom targets. I wonder if the msbuild repo has something similar by now (seems it would be useful there too). #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

The dotnet SDK repo has this kind of test API. The problem is none of these are in a form that's reusable elsewhere. It would indeed be good to create some model that can be reused via a NuGet package.

Copy link
Member

Choose a reason for hiding this comment

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

Consider EnsureEndsWithSlash.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

Copy link
Member

Choose a reason for hiding this comment

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

      var rootByItemSpec = new Dictionary<string, ITaskItem>(); [](start = 1, length = 68)

Why are we using ordinal path comparison here? The compiler is pretty inconsistent here but generally favors insensiitive.

Copy link
Member Author

Choose a reason for hiding this comment

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

Are you saying we do not support source directories that differ by casing on Linux?

Copy link
Member

Choose a reason for hiding this comment

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

Different parts of the compiler differ here. It's not consistent.

Copy link
Member

Choose a reason for hiding this comment

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

var topLevelMappedPaths = new Dictionary<string, string>(); [](start = 16, length = 59)

Why are we using ordinal path comparison here? The compiler is pretty inconsistent here but generally favors insensiitive.

Copy link
Member Author

Choose a reason for hiding this comment

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

These paths are generally produced by a source control package (e.g. one based on GitLib library). It is expected that the tool produces paths that have consistent casing. This particular dictionary is used to map submodule paths to their containing source roots. When the submodule paths are produced by the GitLib library they should match the casing exactly.

Copy link
Member

Choose a reason for hiding this comment

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

DotNetSdkTestBase [](start = 34, length = 17)

I have concerns with how this is intended to work. There are really two options to doing MSBuild integration tests

  1. Use an official layout mechanism to place the compiler binaries on dusk. For example unzipping the compiler NuGet package.
  2. Adhoc copy the binaries on disk to a single directory.

This test seems to be doing the latter. We've been moving away from that because we've found it's both flaky and it doesn't actually catch all of the issues it's intended to catch. I'm worried we're creating a new case where we're tricking ourselves into believing scenarios are covered that simply aren't.

Copy link
Member Author

@tmat tmat Mar 26, 2018

Choose a reason for hiding this comment

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

I'm not sure I understand. I'm not copying binaries anywhere. The compiler build task is not even executed in these tests scenarios - I replace the csc target. The only binary that matters for these tests is the one that defines the build tasks. The goal of these tests is to test the .targets files, not the compiler.

Copy link
Member

Choose a reason for hiding this comment

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

That's a bit more stable. Overall though I'm highly skeptical of tests like this. There are reasons we yanked out similar ones for MSBuild. Their sustainability doesn't seem to hold up over time.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd be more than happy to use any other mechanism to test these targets once it's available. I think msbuild or dotnet SDK should come up with a test framework that can be imported as a package. Until that happens though we need to test the targets somehow.

@tmat
Copy link
Member Author

tmat commented Mar 26, 2018

test this please #Resolved

@tmat
Copy link
Member Author

tmat commented Mar 27, 2018

test this please

@tmat
Copy link
Member Author

tmat commented Mar 27, 2018

test windows_debug_unit64_prtest please

@tmat
Copy link
Member Author

tmat commented Mar 27, 2018

test windows_release_vs-integration_prtest please

@tmat
Copy link
Member Author

tmat commented Mar 28, 2018

@dotnet/roslyn-compiler Ping.

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM Thanks

@jcouv
Copy link
Member

jcouv commented Mar 29, 2018

LGTM
@jaredpar @dotnet/roslyn-compiler for a second review. Thanks

@tmat
Copy link
Member Author

tmat commented Apr 2, 2018

@jaredpar Is any ask mode approval needed for merge?

@jaredpar
Copy link
Member

jaredpar commented Apr 2, 2018

@tmat yes. Mine 😄. Approved

@tmat
Copy link
Member Author

tmat commented Apr 2, 2018

@jaredpar 👍 Thanks

@tmat tmat merged commit 1ad342d into dotnet:dev15.7.x Apr 2, 2018
@tmat tmat deleted the SourceLink branch April 2, 2018 03:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants