-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
Сurrent directory virtualization for RAR task #6316
Conversation
5765e1c
to
5db2554
Compare
…s for this pr, remove it later.
… ci tests for this pr, remove it later." This reverts commit b1a8473.
// However, if the combined path consists of different path separators (both windows and unix style), | ||
// then the behavior of Path.GetFullPath differs in windows and unix systems, as in Windows both Windows and Unix style separators works and in Unix - not. | ||
// Windows' function eleminates the internal "./" and "../", Unix's function does not. We are using FixFilePath to remove windows-style separators when on unix machine. | ||
return Path.GetFullPath(Path.Combine(taskExecutionContext.StartupDirectory, FileUtilities.FixFilePath(path))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to consider possible switch from Path.GetFullPath -> PathInternal.RemoveRelativeSegments to avoid extra actions in Path.GetFullPath. Unfortunately, RemoveRelativeSegments is not yet available in the Path (dotnet/runtime#2162), so it might only be code duplication.
|
||
namespace Microsoft.Build.Tasks | ||
{ | ||
public static class TaskExecutionContextExtension |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not need public here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, at least as long as these methods are fixed rather than user-manipulatable. Should this explicitly extend TaskExecutionContext?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, there was no need for "?" mark in my comment, actually, this has to be private. That is a reminder for myself to fix this together with the other changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the issue, you mentioned both normalizing and denormalizing as necessary. I saw the first but not the second. Did you determine it's unnecessary?
@@ -0,0 +1,190 @@ | |||
using System; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright header
|
||
string testPath = Path.Combine(Path.GetTempPath(), @"RawFileNameRelative"); | ||
|
||
Directory.CreateDirectory(testPath); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Creating a temporary directory can be a little cleaner with TestEnvironment, though that might be overkill here.
[Fact] | ||
[Trait("Category", "mono-osx-failing")] | ||
[Trait("Category", "netcore-osx-failing")] | ||
[Trait("Category", "netcore-linux-failing")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this failing on linux/mac?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a problem I recently discovered with unit tests for RAR in unix. ResolveAssemblyReferenceTestFixture does not work correctly with unix paths. A lot of tests currently are just excluded for unix/mac because of that (well, that is my suggestion). I managed to fix ResolveAssemblyReferenceTestFixture for all other tests I introduced except this one. This one (and other tests that are currently just do not run in unix) need much more fixes in ResolveAssemblyReferenceTestFixture. I think we should fix it in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so, the problem is in the testing system, with file system's mocking, not with RAR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to glance through that to see if it was a quick fix, and it's clear that some parts were designed with cross-plat in mind...but that's a really big class, and a lot of it wasn't. Sounds like a lot of work for not enough return on investment to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, so far I saw the only problem with this class which prevents it to be cross-plat: the comparison of paths (well, at least tests I saw were failing because of that). Yes, this is a big class and you need to fix it in a lot of places, that's why I did not want to do this in this PR. This is very technical fix and I did not think it will take so much time. But maybe you are right, there also might be other changes needed to make it cross platform, I did not thought much about that.
|
||
Execute(rarTask); | ||
|
||
Assert.Single(rarTask.ResolvedFiles); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: please use Shouldly for asserts.
@@ -1,4 +1,5 @@ | |||
using Microsoft.Build.Shared; | |||
using Microsoft.Build.Framework; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copyright header
@@ -438,7 +445,7 @@ string assemblyFileName | |||
|
|||
if (!Path.IsPathRooted(assemblyFileName)) | |||
{ | |||
reference.FullPath = Path.GetFullPath(assemblyFileName); | |||
reference.FullPath = Path.GetFullPath(_executionContext.GetFullPath(assemblyFileName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is calling GetFullPath twice. Simplify? (Same for other occurrences.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, _executionContext.GetFullPath sometimes, if we do not have any execution context, should not absolutize the path. This is to keep previous behavior as an escape hatch when we do not use RAR as a service. Maybe I should rename the func to clarify this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe renaming to _executionContext.MapPath can imply it is not just FullPath what can happen.
/// Execution context used when task is supposed to run concurrently in multiple threads. | ||
/// If null hosting process do not run this task concurrently and set it execution context on process level. | ||
/// </summary> | ||
public TaskExecutionContext ConcurrencyExecutionContext |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be an auto-property?
@@ -3049,5 +3074,60 @@ public override bool Execute() | |||
} | |||
|
|||
#endregion | |||
|
|||
void IConcurrentTask.ConfigureForConcurrentExecution(TaskExecutionContext executionContext) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: private
/// <returns></returns> | ||
public static string GetFullPath(this TaskExecutionContext taskExecutionContext, string path) | ||
{ | ||
if (String.IsNullOrEmpty(taskExecutionContext.StartupDirectory) || String.IsNullOrEmpty(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is almost identical to FileUtilities.GetFullPath, though that does some extra escaping/unescaping. Do you think any of that is necessary here, and if so, should we just call that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileUtilities.GetFullPath take path and if it is relative absolutize it based on running process 'current directory' while
method takes path and if it is relative absolutize it based on taskExecutionContext.StartupDirectory.
@Forgind What escaping do you have on mind on particular?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileUtilities.GetFullPath gives absolute path using the wrong current directory, without virtualization. Here we use taskExecutionContext.StartupDirectory as our current directory to complete the relative path (if it exists).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileUtilities has an overload to let you specify currentDirectory:
msbuild/src/Shared/FileUtilities.cs
Lines 661 to 680 in 369631b
internal static string GetFullPath(string fileSpec, string currentDirectory) | |
{ | |
// Sending data out of the engine into the filesystem, so time to unescape. | |
fileSpec = FixFilePath(EscapingUtilities.UnescapeAll(fileSpec)); | |
// Data coming back from the filesystem into the engine, so time to escape it back. | |
string fullPath = EscapingUtilities.Escape(NormalizePath(Path.Combine(currentDirectory, fileSpec))); | |
if (NativeMethodsShared.IsWindows && !EndsWithSlash(fullPath)) | |
{ | |
if (FileUtilitiesRegex.IsDrivePattern(fileSpec) || | |
FileUtilitiesRegex.IsUncPattern(fullPath)) | |
{ | |
// append trailing slash if Path.GetFullPath failed to (this happens with drive-specs and UNC shares) | |
fullPath += Path.DirectorySeparatorChar; | |
} | |
} | |
return fullPath; | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh. I wasn't aware of that. I believe this method is to be used during evaluation or other processing on unescaped data. Its name should be GetFullPathEscaped
. Since changed code under review deals with already unescaped Task inputs, we can't use FileUtilities.GetFullPath
for that matter.
|
||
namespace Microsoft.Build.Tasks | ||
{ | ||
public static class TaskExecutionContextExtension |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think so, at least as long as these methods are fixed rather than user-manipulatable. Should this explicitly extend TaskExecutionContext?
I currently think denormalizing is not necessary, but I would like to ask about that more. It is my current understanding that all output is currently supposed to be absolute. |
Moved this PR to draft. We should merge this only with RAR as a Service feature PR, as it is meaningless to have this code otherwise. The future of RAR as a Service feature (when/if it will be merged) is currently unclear. |
} | ||
} | ||
|
||
// TODO: move to own file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO: move to own file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lets keep it here.
string directory = FullSearchPath; | ||
string resolvedPath = ResolveFromDirectory(assemblyName, isPrimaryProjectReference, wantSpecificVersion, executableExtensions, directory, assembliesConsideredAndRejected); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string directory = FullSearchPath; | |
string resolvedPath = ResolveFromDirectory(assemblyName, isPrimaryProjectReference, wantSpecificVersion, executableExtensions, directory, assembliesConsideredAndRejected); | |
string resolvedPath = ResolveFromDirectory(assemblyName, isPrimaryProjectReference, wantSpecificVersion, executableExtensions, directory: FullSearchPath, assembliesConsideredAndRejected); |
On the second though, I think we can have it even more readable by above changes:
/// <returns></returns> | ||
public static string GetFullPath(this TaskExecutionContext taskExecutionContext, string path) | ||
{ | ||
if (String.IsNullOrEmpty(taskExecutionContext.StartupDirectory) || String.IsNullOrEmpty(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FileUtilities.GetFullPath take path and if it is relative absolutize it based on running process 'current directory' while
method takes path and if it is relative absolutize it based on taskExecutionContext.StartupDirectory.
@Forgind What escaping do you have on mind on particular?
/// <returns></returns> | ||
public static string GetFullPath(this TaskExecutionContext taskExecutionContext, string path) | ||
{ | ||
if (String.IsNullOrEmpty(taskExecutionContext.StartupDirectory) || String.IsNullOrEmpty(path)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ohh. I wasn't aware of that. I believe this method is to be used during evaluation or other processing on unescaped data. Its name should be GetFullPathEscaped
. Since changed code under review deals with already unescaped Task inputs, we can't use FileUtilities.GetFullPath
for that matter.
|
||
rawFileNameCandidate = executionContext.GetFullPath(rawFileNameCandidate); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rawFileNameCandidate = executionContext.GetFullPath(rawFileNameCandidate); | |
rawFileNameCandidate = executionContext.GetFullPath(rawFileNameCandidate); |
NIT: unnecessary empty lines
} | ||
else | ||
{ | ||
_concurrencyExecutionContext = new TaskExecutionContext(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please comment here the intent of using default TaskExecutionContext behavior i.e. using 'process current directory' for relative paths.
@@ -438,7 +445,7 @@ string assemblyFileName | |||
|
|||
if (!Path.IsPathRooted(assemblyFileName)) | |||
{ | |||
reference.FullPath = Path.GetFullPath(assemblyFileName); | |||
reference.FullPath = Path.GetFullPath(_executionContext.GetFullPath(assemblyFileName)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe renaming to _executionContext.MapPath can imply it is not just FullPath what can happen.
@@ -1266,8 +1273,7 @@ private void ResolveReference | |||
( | |||
AssemblyNameExtension assemblyName, | |||
string rawFileNameCandidate, | |||
Reference reference | |||
) | |||
Reference reference) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reference reference) | |
Reference reference | |
) |
Is the future of RARaaS more clear? Wondering if we should close this temporarily until we're ready to go back to it. |
Work on "RAR as a service" was cut off, I close this PR. |
Fixes #6218
Context
This PR is part of RAR as a service user story #3139. The current design supposes RAR tasks run concurrently in threads. However, the RAR task also depends on the current directory which is shared between the threads.
Changes Made
In the PR we implement current directory virtualization.
For all the inputs we either absolutize the paths at the start of the RAR task or if we are unable to recognize the input's type, absolutize an entry when we use it, i.e. when we know the type of the entry (path/assembly name/etc).
Testing
There already were unit tests for relative inputs in
Miscellaneous.cs
, we leverage them to check that the virtualization of the current directory works well.Notes