AvaloniaUI / AvaloniaVSCode

Visual Studio Code Extension for Avalonia UI
https://marketplace.visualstudio.com/items?itemName=AvaloniaTeam.vscode-avalonia
MIT License
103 stars 18 forks source link

Previewer is not available - avalonia-vscode does not support multi-target projects (TargetFrameworks) #99

Open BinToss opened 9 months ago

BinToss commented 9 months ago

Describe the bug

This is one of the causes of the repeated "Previewer is not available. Build the project first." prompt.

Many of the MSBuild properties evaluated and required by this extension are dependent on the singular TargetFramework property being defined and valid. Properties such as TargetPath and AvaloniaPreviewerNetCoreToolPath. However, this property and the plural TargetFrameworks are mutually exclusive. Defining both in the same project silently breaks things.

Note: Given <TargetFrameworks>net6.0;net7.0</TargetFrameworks> and dotnet build multiTargeting.csproj, dotnet will build binaries for each target framework i.e. obj/Debug/net6.0, obj/Debug/net7.0, bin/Debug/net6.0, bin/Debug/net7.0.

To Reproduce

dotnet new avalonia.app -n test -av 11.0.6
tfm="<TargetFramework>net8.0<\/TargetFramework>"
tfms="<TargetFrameworks>net6.0;net8.0<\/TargetFrameworks>"
sed -i -e "s/$tfm/$tfms/g" ./test/test.csproj
# Now, you can try generating preview assets

Avalonia for VS Code

v0.0.29

Avalonia version

11.0.6

VS Code version

v1.86.0

Relevant log output

2024-02-01 07:08:13.166 [info] [EXT - INFO] Solution data path path: C:\Users\Noah\AppData\Local\Temp\GroupBox.Avalonia.sln.json
2024-02-01 07:08:13.229 [info] parser process args: C:\WINDOWS\system32\cmd.exe,/d,/s,/c,"dotnet "c:\Users\Noah\.vscode\extensions\avaloniateam.vscode-avalonia-0.0.29\solutionParserTool\SolutionParser.dll" "c:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia.sln""
2024-02-01 07:08:13.680 [info] parser process exited 0
2024-02-01 07:08:14.075 [info]   Determining projects to restore...

2024-02-01 07:08:14.611 [info]   All projects are up-to-date for restore.

2024-02-01 07:08:14.688 [info]   -- BinToss_EchoAvaloniaPreviewerNetCoreToolPath --
  {
    'AvaloniaPreviewerNetCoreToolPath': 'C:\Users\Noah\.nuget\packages\avalonia\11.0.7\buildTransitive\\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll',
    'TargetFramework': 'net6.0'
  }

2024-02-01 07:08:14.723 [info]   -- BinToss_EchoAvaloniaPreviewerNetCoreToolPath --
  {
    'AvaloniaPreviewerNetCoreToolPath': 'C:\Users\Noah\.nuget\packages\avalonia\11.0.7\buildTransitive\\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll',
    'TargetFramework': 'net8.0'
  }

2024-02-01 07:08:14.740 [info]   -- BinToss_EchoAvaloniaPreviewerNetCoreToolPath --
  {
    'AvaloniaPreviewerNetCoreToolPath': 'C:\Users\Noah\.nuget\packages\avalonia\11.0.7\buildTransitive\\..\tools\netcoreapp2.0\designer\Avalonia.Designer.HostApp.dll',
    'TargetFramework': 'net7.0'
  }

2024-02-01 07:08:15.910 [info]   GroupBox.Avalonia -> C:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia\bin\Debug\net7.0\GroupBox.Avalonia.dll

2024-02-01 07:08:16.208 [info]   GroupBox.Avalonia -> C:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia\bin\Debug\net8.0\GroupBox.Avalonia.dll

2024-02-01 07:08:16.526 [info]   GroupBox.Avalonia -> C:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia\bin\Debug\net6.0\GroupBox.Avalonia.dll

2024-02-01 07:08:16.981 [info]   GroupBox.Avalonia.Sample -> c:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia.Sample\bin\Debug\net8.0\GroupBox.Avalonia.Sample.dll

2024-02-01 07:08:17.707 [info]   GroupBox.Avalonia.Sample -> C:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia.Sample\bin\Debug\net7.0\GroupBox.Avalonia.Sample.dll

2024-02-01 07:08:18.032 [info]   GroupBox.Avalonia.Sample -> c:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia.Sample\bin\Debug\net6.0\GroupBox.Avalonia.Sample.dll

2024-02-01 07:08:18.069 [info] 

2024-02-01 07:08:18.070 [info] Build succeeded.

2024-02-01 07:08:18.070 [info]     0 Warning(s)
    0 Error(s)

2024-02-01 07:08:18.071 [info] 

2024-02-01 07:08:18.071 [info] Time Elapsed 00:00:04.21

2024-02-01 07:08:18.237 [info] [EXT - INFO] Solution data path path: C:\Users\Noah\AppData\Local\Temp\GroupBox.Avalonia.sln.json
2024-02-01 07:08:18.330 [info] parser process args: C:\WINDOWS\system32\cmd.exe,/d,/s,/c,"dotnet "c:\Users\Noah\.vscode\extensions\avaloniateam.vscode-avalonia-0.0.29\solutionParserTool\SolutionParser.dll" "c:\Repos\BinToss\GroupBox.Avalonia\GroupBox.Avalonia.sln""
2024-02-01 07:08:18.854 [info] Previewer assets generated at 
2024-02-01 07:08:18.866 [info] parser process exited 0

Additional context

No response

BinToss commented 9 months ago

A bit more detail...

If we don't specify a target framework when TargetFrameworks is defined, MSBuild...vomits on itself.

dotnet build -getProperty:intermediateOutputPath -getItem:AvaloniaXaml --framework net8.0

{
  "Properties": {
    "intermediateOutputPath": "obj\\Debug\\net8.0\\"
  },
  "Items": {
    "AvaloniaXaml": [
      {
        "Identity": "App.axaml",
        "SubType": "Designer",
        "FullPath": "C:\\Repos\\BinToss\\GroupBox.Avalonia\\GroupBox.Avalonia.Sample\\App.axaml",
        "RootDir": "C:\\",
        "Filename": "App",
        "Extension": ".axaml",
        "RelativeDir": "",
        "Directory": "Repos\\BinToss\\GroupBox.Avalonia\\GroupBox.Avalonia.Sample\\",
        "RecursiveDir": "",
        "ModifiedTime": "2024-02-01 22:30:18.0875806",
        "CreatedTime": "2023-07-11 02:12:30.0670637",
        "AccessedTime": "2024-02-13 01:02:37.8473098",
        "DefiningProjectFullPath": "C:\\Users\\Noah\\.nuget\\packages\\avalonia\\11.0.7\\buildTransitive\\AvaloniaBuildTasks.props",
        "DefiningProjectDirectory": "C:\\Users\\Noah\\.nuget\\packages\\avalonia\\11.0.7\\buildTransitive\\",
        "DefiningProjectName": "AvaloniaBuildTasks",
        "DefiningProjectExtension": ".props"
      },
      {
        "Identity": "MainWindow.axaml",
        "SubType": "Designer",
        "FullPath": "C:\\Repos\\BinToss\\GroupBox.Avalonia\\GroupBox.Avalonia.Sample\\MainWindow.axaml",
        "RootDir": "C:\\",
        "Filename": "MainWindow",
        "Extension": ".axaml",
        "RelativeDir": "",
        "Directory": "Repos\\BinToss\\GroupBox.Avalonia\\GroupBox.Avalonia.Sample\\",
        "RecursiveDir": "",
        "ModifiedTime": "2024-02-03 05:04:33.6008497",
        "CreatedTime": "2023-07-11 02:12:30.1012734",
        "AccessedTime": "2024-02-13 01:02:37.8473098",
        "DefiningProjectFullPath": "C:\\Users\\Noah\\.nuget\\packages\\avalonia\\11.0.7\\buildTransitive\\AvaloniaBuildTasks.props",
        "DefiningProjectDirectory": "C:\\Users\\Noah\\.nuget\\packages\\avalonia\\11.0.7\\buildTransitive\\",
        "DefiningProjectName": "AvaloniaBuildTasks",
        "DefiningProjectExtension": ".props"
      }
    ]
  }
}

dotnet build -getProperty:intermediateOutputPath -getItem:AvaloniaXaml

{
  "Properties": {
    "intermediateOutputPath": "obj\\Debug\\"
  },
  "Items": {
    "AvaloniaXaml": []
  }
}

How to get the project's target frameworks

Instead of parsing the project file (which may have its TargetFrameworks property defined in a separate file e.g. targetframeworks.props), I'd recommend A. Use dotnet-build/MSBuild to evaluate the TargetFrameworks property... dotnet build -getProperty:TargetFrameworks

net6.0;net7.0;net8.0

B. Deserialize/stringify the contents of obj/project.assets.json, access targets or project.frameworks, and parse the names of either one's properties for the target frameworks recently evaluated by dotnet-build/MSBuild. There's a high chance at least assets for at least one of those targets has been built in obj. image