-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Don't forward along binlog arguments to runnable applications #43430
Conversation
ILogger? binaryLogger = DetermineBinlogger(RestoreArgs, true); | ||
var project = EvaluateProject(ProjectFileFullPath, RestoreArgs, binaryLogger); | ||
binaryLogger?.Shutdown(); | ||
ValidatePreconditions(project); | ||
InvokeRunArgumentsTarget(project, RestoreArgs, Verbosity); | ||
binaryLogger = DetermineBinlogger(RestoreArgs, false); | ||
InvokeRunArgumentsTarget(project, RestoreArgs, Verbosity, binaryLogger); | ||
binaryLogger?.Shutdown(); |
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.
Here's the split I'm talking about. When I use the same binaryLogger
the 'execution' phase seems to overwrite the evaluation-phase binlog data. Here's what I have from the current state:
In this zip,
- msbuild.binlog is the
-bl
passed to MSbuild to actually build the project - msbuild-dotnet-run-restore.binlog is the binlog generated from line 230 (evaluating the project to get a ProjectInstance)
- msbuild-dotnet-run-build.binlog is the binlog generated from line 234 (executing the Target)
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 can imagine the Initialize is being called in those methods - so the stream is thrown away and file overwritten (regardless of it was flushed or not with Shutdown call):
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.
Yeah - this is where I ended up after trying for an hour or so to get evaluation and execution to use the same binlog.
Let me get an example of that - what I had hoped was that if I used the same binaryLogger
instance in the Projectcollection's logger collection and the ProjectInstance's Build logger collection, it would all just work. Let me get an example of what that looks like pushed up.
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 believe the MuxLogger can come to the rescue here. Though I never used it myself
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.
Here's the result:
> dotnet run -bl
Using launch settings from E:\Code\sdk-container-demo\src\sdk-container-demo\Properties\launchSettings.json...
Building...
E:\Code\dotnet-sdk\artifacts\bin\redist\Debug\dotnet\sdk\9.0.100-dev\Sdks\Microsoft.NET.Sdk\Sdk\Sdk.targets(65,3): warning MSB4011: "C:\Users\chusk\.nuget\packages\microsoft.net.build.containers\8.0.403\build\Microsoft.NET.Build.Containers.targets" cannot be imported again. It was already imported at "E:\Code\sdk-container-demo\artifacts\obj\sdk-container-demo\sdk-container-demo.csproj.nuget.g.targets (4,5)". This is most likely a build authoring error. This subsequent import will be ignored. [E:\Code\sdk-container-demo\src\sdk-container-demo\sdk-container-demo.csproj]
Unhandled exception: Microsoft.Build.Framework.LoggerException: Failed to write to log file "E:\Code\sdk-container-demo\src\sdk-container-demo\msbuild-dotnet-run-restore.binlog". The process cannot access the file 'E:\Code\sdk-container-demo\src\sdk-container-demo\msbuild-dotnet-run-restore.binlog' because it is being used by another process.
---> System.IO.IOException: The process cannot access the file 'E:\Code\sdk-container-demo\src\sdk-container-demo\msbuild-dotnet-run-restore.binlog' because it is being used by another process.
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path, FileMode mode, FileAccess access, FileShare share, FileOptions options, Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.FileStream..ctor(String path, FileMode mode)
at Microsoft.Build.Logging.BinaryLogger.Initialize(IEventSource eventSource)
--- End of inner exception stack trace ---
at Microsoft.Build.Logging.BinaryLogger.Initialize(IEventSource eventSource)
at Microsoft.Build.BackEnd.Logging.LoggingService.InitializeLogger(ILogger logger, IEventSource sourceForLogger)
at Microsoft.Build.BackEnd.Logging.LoggingService.RegisterLogger(ILogger logger)
at Microsoft.Build.Execution.BuildManager.CreateLoggingService(IEnumerable`1 loggers, IEnumerable`1 forwardingLoggers, ISet`1 warningsAsErrors, ISet`1 warningsNotAsErrors, ISet`1 warningsAsMessages)
at Microsoft.Build.Execution.BuildManager.<BeginBuild>g__InitializeLoggingService|63_0()
at Microsoft.Build.Execution.BuildManager.BeginBuild(BuildParameters parameters)
at Microsoft.Build.Execution.BuildManager.Build[TRequestData,TResultData](BuildParameters parameters, TRequestData requestData)
at Microsoft.Build.Execution.BuildManager.Build(BuildParameters parameters, BuildRequestData requestData)
at Microsoft.Build.Execution.ProjectInstance.Build(String[] targets, IEnumerable`1 loggers, IEnumerable`1 remoteLoggers, ILoggingService loggingService, Int32 maxNodeCount, IDictionary`2& targetOutputs)
at Microsoft.Build.Execution.ProjectInstance.Build(String[] targets, IEnumerable`1 loggers, IEnumerable`1 remoteLoggers, ILoggingService loggingService, IDictionary`2& targetOutputs)
at Microsoft.Build.Execution.ProjectInstance.Build(String[] targets, IEnumerable`1 loggers, IEnumerable`1 remoteLoggers, IDictionary`2& targetOutputs)
at Microsoft.DotNet.Tools.Run.RunCommand.<GetTargetCommand>g__InvokeRunArgumentsTarget|50_5(ProjectInstance project, String[] restoreArgs, Nullable`1 verbosity, ILogger binaryLogger) in E:\Code\dotnet-sdk\src\Cli\dotnet\commands\dotnet-run\RunCommand.cs:line 345
at Microsoft.DotNet.Tools.Run.RunCommand.GetTargetCommand() in E:\Code\dotnet-sdk\src\Cli\dotnet\commands\dotnet-run\RunCommand.cs:line 232
at Microsoft.DotNet.Tools.Run.RunCommand.Execute() in E:\Code\dotnet-sdk\src\Cli\dotnet\commands\dotnet-run\RunCommand.cs:line 78
at Microsoft.DotNet.Tools.Run.RunCommand.Run(ParseResult parseResult) in E:\Code\dotnet-sdk\src\Cli\dotnet\commands\dotnet-run\Program.cs:line 69
at System.CommandLine.Invocation.AnonymousSynchronousCliAction.Invoke(ParseResult parseResult)
at System.CommandLine.Invocation.InvocationPipeline.Invoke(ParseResult parseResult)
at System.CommandLine.ParseResult.Invoke()
at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient) in E:\Code\dotnet-sdk\src\Cli\dotnet\Program.cs:line 233
In this case, executing the Target tries to write to the binlog file that's still open from the evaluation phase. This is why I thought I needed to Shutdown the previous logger to flush the outputs, but then you hit the 'overwrite' problem.
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.
Looking at XMake.cs, it seems like it uses the ProjectCollection to initialize BuildParameters, then passes that to the default BuildManager.BeginBuild; perhaps that would work?
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.
Yeah without looking at any details attaching to the ProjectCollection seems right
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.
Pushed up a better implementation based on @JanKrivanek's feedback.
Basically we need to make sure that the BinaryLogger only initializes once, so we need some buffer between the loggers that are used in the eval/exec phases and the actual binlogger.
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.
The problem is the IEventSource - in MSBuild the IEventSource between the evaluation and the build is all the same. When using the MSBuild APIs it seems to be impossible to achieve the same without the kind of code I have here.
ILogger? binaryLogger = DetermineBinlogger(RestoreArgs, true); | ||
var project = EvaluateProject(ProjectFileFullPath, RestoreArgs, binaryLogger); | ||
binaryLogger?.Shutdown(); | ||
ValidatePreconditions(project); | ||
InvokeRunArgumentsTarget(project, RestoreArgs, Verbosity); | ||
binaryLogger = DetermineBinlogger(RestoreArgs, false); | ||
InvokeRunArgumentsTarget(project, RestoreArgs, Verbosity, binaryLogger); | ||
binaryLogger?.Shutdown(); |
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.
Looking at XMake.cs, it seems like it uses the ProjectCollection to initialize BuildParameters, then passes that to the default BuildManager.BeginBuild; perhaps that would work?
It's actually way worse than that. @JanKrivanek gave me some pointers, I'll push something later today with what we have to do here. |
foreach (var arg in applicationArguments) | ||
{ | ||
|
||
if (arg.StartsWith("/bl:") || arg.Equals("/bl") |
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.
if you just did arg.Contains("/bl"
and ignored the :
variant` would that still work and simplify the check a bit?
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.
it would potentially capture user-application arguments that started with bl
but were otherwise distinct from MSBuild's /bl
arg.
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.
The change looks good to me. The code is well done, so thank you.
Having more tests would be ideal, but time is limited.
Note that there is a 'bug' where if you pass -bl -bl
it will fail with an uhandled exception. dotnet run -bl -bl --project .\newconsole.csproj
will fail because Unhandled exception: Microsoft.Build.Framework.LoggerException: Failed to write to log file, used by another process.
Given that it did not work in the first place and anybody who would be impacted by this would also have been impacted by the other break, I still think it can be merged.
good call-out @nagilson - though that would fail in MSBuild for other reasons I think. You can specify any number of BL instances you want as long as they are differently-named, and the default name for |
|
Fixes #43310
In #42240 I introduced a regression in behavior for applications in
dotnet run
. The-bl
argument would get forwarded along as application arguments, not arguments to the implicit build/restore thatdotnet run
performs.A 'proper' way of handling this would be to model the
-bl
argument syntax in S.CL, but this is currently impossible due to design differences between MSBuild's CLI syntax and S.CL.So instead we filter out the
-bl
argument from the application arguments if it exists (along with any of its arguments) and apply it to the restore arguments. We have to do special handling of the binlog file name because binlogs are not appendable and we're actually doing two 'build' operations here - one managed entirely by MSBuild, and one managed by the CLI. As a result, we need to provide different names to the binlogs used in each case.One odd case that I found is that for some reason I cannot use the same
binaryLogger
for the evaluation phase and the execution phase of thedotnet run
protocol - I split this into two binlogs to illustrate the problem. Would love some feedback from @JanKrivanek / @rainersigwald et al about how I'm holding it wrong here.