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

dotnet cli passing -target:Restore to MSBuild instead of -restore #15485

Closed
jonathanpeppers opened this issue Jan 25, 2021 · 15 comments · Fixed by #16261
Closed

dotnet cli passing -target:Restore to MSBuild instead of -restore #15485

jonathanpeppers opened this issue Jan 25, 2021 · 15 comments · Fixed by #16261
Assignees
Milestone

Comments

@jonathanpeppers
Copy link
Member

jonathanpeppers commented Jan 25, 2021

As seen in: dotnet/maui-samples#52

We have a multi-targeted project with:

<TargetFrameworks>net6.0-android;net6.0-ios</TargetFrameworks>

To deploy to an Android device/emulator (and not iOS), you can run:

> dotnet build -t:Run -p:TargetFramework=net6.0-android

With .NET 6.0.100-alpha.1.21064.27, this command fails with:

error MSB4057: The target "Run" does not exist in the project.

The MSBuild arguments the dotnet cli is passing seem odd:

-target:Restore -t:Run

With .NET 6.0.100-alpha.1.20562.2, it used to pass:

-restore -t:Run

In fact, you can workaround the problem by doing:

> dotnet build -t:Run -p:TargetFramework=net6.0-android --no-restore

I tried creating other, simpler projects to reproduce this issue, but a console app with TargetFrameworks=net5.0;net6.0 works fine. I always see -restore being passed.

I think it is something to with .NET workloads and multi-targeting at the same time?

@dotnet-issue-labeler
Copy link

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Request triage from a team member label Jan 25, 2021
@dsplaisted
Copy link
Member

@sfoslund Can you take a look?

@sfoslund
Copy link
Member

@jonathanpeppers can you give some more information on how to repro this? The binlogs you provided are on two different platforms, does this only repro on windows or mac?

@jonathanpeppers
Copy link
Member Author

@sfoslund I think it happens on all platforms. My Mac has the older 6.0.100-alpha.1.20562.2 installed, so that is where I could grab the working .binlog the easiest.

You can compare main here:

https://github.com/xamarin/net6-samples

vs the branch on my PR:

dotnet/maui-samples#52

There are installation instructions on the README.md, but ping me on Teams if I can help further, thanks!

@sfoslund
Copy link
Member

Okay I think I understand this issue now but I want to verify my understanding of the history with @dsplaisted.

The basic issue is with RestoringCommand, which has logic to decide if we can restore with -restore included in the msbuild arguments or if we need to use a separate command to restore. The deciding factor is if the target framework is specified on the command line (by either -f, --framework, or -p:TargetFramework=...). If it is, then we can't use -restore, for the reasons explained in this comment.

It looks like before the System.CommandLine change there was a bug and we were using -restore when the target framework was specified on the command line (see the command line args in your working binlog: -restore -consoleloggerparameters:Summary HelloForms -p:TargetFramework=net6.0-android...). This was working because there weren't any dependent projects with different target frameworks, but if there had been then the command line target framework would have overridden the dependency projects' tfm's and restore would have failed.

So theoretically I think switching to system.commandline "fixed" this bug in that now we try to use a separate restore command when the target framework is specified on the command line. However, this has created a bug here such that we pass the -t:run option to the separate restore command and it fails because we didn't specify a target framework.

@dsplaisted does this make sense to you? Do you know of any changes in the -restore behavior in regards to target frameworks since Nick's comment (which is from 2017).

@sfoslund sfoslund removed the untriaged Request triage from a team member label Jan 28, 2021
@sfoslund sfoslund added this to the 6.0.1xx milestone Jan 28, 2021
@sfoslund
Copy link
Member

sfoslund commented Feb 2, 2021

After discussing with @dsplaisted, we do want the -target:restore behavior instead of -restore when the target framework is specified on the command line.

@jonathanpeppers is there a reason you need to specify the run target while building? dotnet run -f net6.0-android should be logically the same as it includes an implicit restore/build.

@jonathanpeppers
Copy link
Member Author

I still get this error with dotnet run:

> cd HelloForms
> dotnet run -f net6.0-android
Microsoft (R) Build Engine version 16.9.0-preview-21063-04+27981f15c for .NET
Copyright (C) Microsoft Corporation. All rights reserved.

  Determining projects to restore...
  All projects are up-to-date for restore.
C:\src\net6-samples\HelloForms\HelloForms.csproj : error MSB4057: The target "Run" does not exist in the project.

I also tried adding --no-restore to the dotnet run command, but I'm not sure if that actually does anything.

These commands both work for me:

> dotnet build -t:Run -f net6.0-android --no-restore
> dotnet build -t:Run -p:TargetFramework=net6.0-android --no-restore

@sfoslund
Copy link
Member

sfoslund commented Feb 2, 2021

Yes, I'm seeing that error with 6.0.100-alpha.1.20562.2 (the version you're currently using which doesn't have the system.commandLine change). It's odd because in the binlog I can see the run target from Microsoft.NET.Sdk.targets is being imported and overriding the MSBuild target. I'm not able to repro this issue with simpler projects, do you have any custom logic for running?

@jonathanpeppers
Copy link
Member Author

I just updated to 6.0.100-preview.1.21081.5 and seeing the same thing there, too.

One thing, is we completely replace the Run target with our own:

https://github.com/xamarin/xamarin-android/blob/aad2c29e30e362b19bf526eb14075d8325a1f2d8/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets#L26

We also set $(RunCommand) and $(RunArguments):

https://github.com/xamarin/xamarin-android/blob/aad2c29e30e362b19bf526eb14075d8325a1f2d8/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Application.targets#L12-L13

Maybe we need to specify the arguments here in a better way?

If I comment out where we set $(RunCommand) and $(RunArguments), it tries to run the app locally, which is not what we want:

> dotnet run -f net6.0-android
System.ComponentModel.Win32Exception (2): The system cannot find the file specified.
   at System.Diagnostics.Process.StartWithCreateProcess(ProcessStartInfo startInfo)
   at System.Diagnostics.Process.Start()
   at Microsoft.DotNet.Cli.Utils.Command.Execute(Action`1 processStarted)
   at Microsoft.DotNet.Cli.Utils.Command.Execute()
   at Microsoft.DotNet.Tools.Run.RunCommand.Execute()
   at Microsoft.DotNet.Tools.Run.RunCommand.Run(String[] args)
   at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, ITelemetry telemetryClient)
   at Microsoft.DotNet.Cli.Program.Main(String[] args)

@sfoslund
Copy link
Member

sfoslund commented Feb 2, 2021

Huh, it seems like something with the MSBuild logic then. In the binlog I'm seeing that file imported and $(RunCommand) and $(RunArguments) being reset but the run target is not being overridden, possibly because Microsoft.NET.Sdk.targets is being imported after your custom file, so the run target there overwrites yours. I would dig into the binlog and ensure that the logic is behaving as you expect.

@dsplaisted
Copy link
Member

Huh, it seems like something with the MSBuild logic then. In the binlog I'm seeing that file imported and $(RunCommand) and $(RunArguments) being reset but the run target is not being overridden, possibly because Microsoft.NET.Sdk.targets is being imported after your custom file, so the run target there overwrites yours. I would dig into the binlog and ensure that the logic is behaving as you expect.

Ah, it sounds like the reordering of the workload targets (from #14393) impacted this then.

Targets that need to override targets from the .NET SDK should be refactored into a separate .targets file, which should be added to the AfterMicrosoftNETSdkTargets property in order to have them imported in the correct location. For example, this is how it was being done for WindowsDesktop:

  <PropertyGroup Condition=" '$(ImportWindowsDesktopTargets)' == 'true'">
    <AfterMicrosoftNETSdkTargets>$(AfterMicrosoftNETSdkTargets);$(MSBuildThisFileDirectory)../../Microsoft.NET.Sdk.WindowsDesktop/targets/Microsoft.NET.Sdk.WindowsDesktop.targets</AfterMicrosoftNETSdkTargets>
  </PropertyGroup>

@dsplaisted
Copy link
Member

So theoretically I think switching to system.commandline "fixed" this bug in that now we try to use a separate restore command when the target framework is specified on the command line. However, this has created a bug here such that we pass the -t:run option to the separate restore command and it fails because we didn't specify a target framework.

It seems like Sarah was correct here. Fixing one bug (It sounds like RestoringCommand wasn't correctly detecting when it needed to run a separate restore command) exposed another bug. The new bug is that additional targets specified (such as Run) get passed through to the restore command, when they should be filtered out and only passed to the actual command run after the restore.

@dsplaisted
Copy link
Member

@sfoslund Could you work on fixing this? I tried, but ran into issues where an argument such as -t:Run got interpreted as the SlnOrProjectArgument by the parser, and thus wasn't handled correctly by RestoringCommand.

Here is the change I made, which added some test coverage and tried to fix RestoringCommand: dsplaisted@ad79b3f

@amono
Copy link

amono commented Apr 6, 2021

I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label.

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