Browse Source

fix: Xaml Compiler error when code-behind class contains a `DllImport` method (#12882)

* fix: Avalonia.Generators not found

if you put SourceGenerators.props in a project with path are like this:"C:\GitHub\Avalonia\sample\mobile\android\ANoteSample\ANoteSample.csproj", the Compiler is not able to found `Avalonia.Generators`

* test: Add test #10046 Xaml Compiler error when code-behind class contains a DllImport method

* fix: Xaml Compiler error when code-behind class contains a DllImport method

* fix: Address Review

* fix: ValidateApiDiff has thrown an exception
pull/12861/head
workgroupengineering 3 years ago
committed by GitHub
parent
commit
1cb271fb37
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 22
      Avalonia.sln
  2. 2
      build/SourceGenerators.props
  3. 2
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  4. 39
      tests/Avalonia.Build.Tasks.UnitTest/Avalonia.Build.Tasks.UnitTest.csproj
  5. 38
      tests/Avalonia.Build.Tasks.UnitTest/CompileAvaloniaXamlTaskTest.cs
  6. 96
      tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngine.cs
  7. 39
      tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngineMessage.cs
  8. 8
      tests/TestFiles/BuildTasks/PInvoke/App.axaml
  9. 21
      tests/TestFiles/BuildTasks/PInvoke/App.axaml.cs
  10. 4
      tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml
  11. 22
      tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml.cs
  12. 26
      tests/TestFiles/BuildTasks/PInvoke/PInvoke.csproj
  13. 14
      tests/TestFiles/BuildTasks/PInvoke/Program.cs

22
Avalonia.sln

@ -273,7 +273,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit.Uni
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Browser", "samples\MobileSandbox.Browser\MobileSandbox.Browser.csproj", "{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestFiles", "TestFiles", "{9D6AEF22-221F-4F4B-B335-A4BA510F002C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildTasks", "BuildTasks", "{5BF0C3B8-E595-4940-AB30-2DA206C2F085}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInvoke", "tests\TestFiles\BuildTasks\PInvoke\PInvoke.csproj", "{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -666,6 +674,14 @@ Global
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.Build.0 = Release|Any CPU
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}.Release|Any CPU.Build.0 = Release|Any CPU
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -748,6 +764,10 @@ Global
{2999D79E-3C20-4A90-B651-CA7E0AC92D35} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{43FCC14E-EEBE-44B3-BCBC-F1C537EECBF8} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{9D6AEF22-221F-4F4B-B335-A4BA510F002C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{5BF0C3B8-E595-4940-AB30-2DA206C2F085} = {9D6AEF22-221F-4F4B-B335-A4BA510F002C}
{0A948D71-99C5-43E9-BACB-B0BA59EA25B4} = {5BF0C3B8-E595-4940-AB30-2DA206C2F085}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

2
build/SourceGenerators.props

@ -15,7 +15,7 @@
<ItemGroup Condition="'$(IncludeAvaloniaGenerators)' == 'true'">
<ProjectReference
Include="../../src/tools/Avalonia.Generators/Avalonia.Generators.csproj"
Include="$(MSBuildThisFileDirectory)/../src/tools/Avalonia.Generators/Avalonia.Generators.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false"
PrivateAssets="all" />

2
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -484,7 +484,7 @@ namespace Avalonia.Build.Tasks
var foundXamlLoader = false;
// Find AvaloniaXamlLoader.Load(this) or AvaloniaXamlLoader.Load(sp, this) and replace it with !XamlIlPopulateTrampoline(this)
foreach (var method in classTypeDefinition.Methods.ToArray())
foreach (var method in classTypeDefinition.Methods.Where(m => m.Body is not null).ToArray())
{
var i = method.Body.Instructions;
for (var c = 1; c < i.Count; c++)

39
tests/Avalonia.Build.Tasks.UnitTest/Avalonia.Build.Tasks.UnitTest.csproj

@ -0,0 +1,39 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<OutputType>Library</OutputType>
<IsPackable>false</IsPackable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
</PropertyGroup>
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\HarfBuzzSharp.props" />
<Import Project="..\..\build\XUnit.props" />
<Import Project="..\..\build\SharedVersion.props" />
<ItemGroup>
<Content Include="..\TestFiles\BuildTasks\PInvoke\bin\$(Configuration)\netstandard2.0\PInvoke.dll" Link="Assets\PInvoke.dll">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<Content Include="..\TestFiles\BuildTasks\PInvoke\bin\$(Configuration)\netstandard2.0\PInvoke.dll.refs" Link="Assets\PInvoke.dll.refs">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Build.Framework" Version="15.1.548" PrivateAssets="All" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj" />
<!-- Ensure PInvoke.csproj is build before Avalonia.Build.Tasks.UnitTest -->
<ProjectReference Include="..\TestFiles\BuildTasks\PInvoke\PInvoke.csproj"
SetConfiguration="Configuration=$(Configuration)"
SetTargetFramework="TargetFramework=netstandard2.0"
ReferenceOutputAssembly="false"
PrivateAssets="all" />
</ItemGroup>
</Project>

38
tests/Avalonia.Build.Tasks.UnitTest/CompileAvaloniaXamlTaskTest.cs

@ -0,0 +1,38 @@
using System;
using System.IO;
using System.Reflection;
using Xunit;
namespace Avalonia.Build.Tasks.UnitTest;
public class CompileAvaloniaXamlTaskTest
{
[Fact]
public void Does_Not_Fail_When_Codebehind_Contains_DllImport()
{
using var engine = UnitTestBuildEngine.Start();
var basePath = Path.Combine(Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath), "Assets");
var originalAssemblyPath = Path.Combine(basePath,
"PInvoke.dll");
var referencesPath = Path.Combine(basePath,
"PInvoke.dll.refs");
var compiledAssemblyPath = "PInvoke.dll";
Assert.True(File.Exists(originalAssemblyPath), $"The original {originalAssemblyPath} don't exists.");
new CompileAvaloniaXamlTask()
{
AssemblyFile = originalAssemblyPath,
ReferencesFilePath = referencesPath,
OutputPath = compiledAssemblyPath,
RefAssemblyFile = null,
BuildEngine = engine,
ProjectDirectory = Directory.GetCurrentDirectory(),
VerifyIl = true
}.Execute();
Assert.Equal(0, engine.Errors.Count);
}
}

96
tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngine.cs

@ -0,0 +1,96 @@
using System;
using System.Collections;
using System.Collections.Generic;
using Microsoft.Build.Framework;
using Xunit;
namespace Avalonia.Build.Tasks.UnitTest;
/// <summary>
/// This is fake BuildEngine using for testing build task
/// at moment it manage only <see cref="BuildErrorEventArgs"/> and <see cref="BuildWarningEventArgs"/>
/// other messages are ignored/>
/// </summary>
internal class UnitTestBuildEngine : IBuildEngine, IDisposable
{
private readonly bool _treatWarningAsError;
private readonly bool _assertOnDispose;
private readonly List<UnitTestBuildEngineMessage> _errors = new();
/// <summary>
/// Start new instance of <see cref="UnitTestBuildEngine"/>
/// </summary>
/// <param name="continueOnError">if it is <c>false</c> immediately assert error</param>
/// <param name="treatWarningAsError">if it is <c>true</c> treat warning as error</param>
/// <param name="assertOnDispose">if it is <c>true</c> assert on dispose if there are any errors.</param>
/// <returns></returns>
public static UnitTestBuildEngine Start(bool continueOnError = false,
bool treatWarningAsError = false,
bool assertOnDispose = false) =>
new UnitTestBuildEngine(continueOnError, treatWarningAsError, assertOnDispose);
private UnitTestBuildEngine(bool continueOnError,
bool treatWarningAsError,
bool assertOnDispose)
{
ContinueOnError = continueOnError;
_treatWarningAsError = treatWarningAsError;
_assertOnDispose = assertOnDispose;
}
public bool ContinueOnError { get; }
public int LineNumberOfTaskNode { get; }
public int ColumnNumberOfTaskNode { get; }
public string ProjectFileOfTaskNode { get; }
public IReadOnlyList<UnitTestBuildEngineMessage> Errors => _errors;
public bool BuildProjectFile(string projectFileName,
string[] targetNames,
IDictionary globalProperties,
IDictionary targetOutputs)
=> throw new NotImplementedException();
public void Dispose()
{
if (_assertOnDispose && _errors.Count > 0)
{
Assert.Fail("There is one o more errors.");
}
}
public void LogCustomEvent(CustomBuildEventArgs e)
{
}
public void LogMessageEvent(BuildMessageEventArgs e)
{
}
public void LogErrorEvent(BuildErrorEventArgs e)
{
var message = UnitTestBuildEngineMessage.From(e);
_errors.Add(message);
if (!ContinueOnError)
{
Assert.Fail(message.Message);
}
}
public void LogWarningEvent(BuildWarningEventArgs e)
{
if (_treatWarningAsError)
{
var message = UnitTestBuildEngineMessage.From(e);
_errors.Add(message);
if (!ContinueOnError)
{
Assert.Fail(message.Message);
}
}
}
}

39
tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngineMessage.cs

@ -0,0 +1,39 @@
using Microsoft.Build.Framework;
namespace Avalonia.Build.Tasks.UnitTest;
enum MessageSource
{
Unknown,
ErrorEvent,
MessageEvent,
CustomEvent,
WarningEvent
}
record class UnitTestBuildEngineMessage
{
private UnitTestBuildEngineMessage(MessageSource Type, LazyFormattedBuildEventArgs Source)
{
this.Type = Type;
this.Source = Source;
Message = Source.Message;
}
public MessageSource Type { get; }
public LazyFormattedBuildEventArgs Source { get; }
public string Message { get; }
public static UnitTestBuildEngineMessage From(BuildWarningEventArgs buildWarning) =>
new UnitTestBuildEngineMessage(MessageSource.WarningEvent, buildWarning);
public static UnitTestBuildEngineMessage From(BuildMessageEventArgs buildMessage) =>
new UnitTestBuildEngineMessage(MessageSource.MessageEvent, buildMessage);
public static UnitTestBuildEngineMessage From(BuildErrorEventArgs buildError) =>
new UnitTestBuildEngineMessage(MessageSource.ErrorEvent, buildError);
public static UnitTestBuildEngineMessage From(CustomBuildEventArgs customBuild) =>
new UnitTestBuildEngineMessage(MessageSource.CustomEvent, customBuild);
}

8
tests/TestFiles/BuildTasks/PInvoke/App.axaml

@ -0,0 +1,8 @@
<Application
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PInvoke.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>

21
tests/TestFiles/BuildTasks/PInvoke/App.axaml.cs

@ -0,0 +1,21 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
namespace PInvoke;
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
{
desktopLifetime.MainWindow = new MainWindow();
}
}
}

4
tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml

@ -0,0 +1,4 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Class="PInvoke.MainWindow">
</Window>

22
tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml.cs

@ -0,0 +1,22 @@
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace PInvoke;
public partial class MainWindow : Window
{
[DllImport(@"libhello")]
extern static int add(int a, int b);
public MainWindow()
{
InitializeComponent();
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
var x = add(1, 2);
}
}

26
tests/TestFiles/BuildTasks/PInvoke/PInvoke.csproj

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFrameworks>net6.0;netstandard2.0</TargetFrameworks>
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
<IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
<!--<AvaloniaXamlIlDebuggerLaunch>true</AvaloniaXamlIlDebuggerLaunch>-->
<EnableAvaloniaXamlCompilation>false</EnableAvaloniaXamlCompilation>
<IncludeAvaloniaGenerators>true</IncludeAvaloniaGenerators>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\..\..\src\Avalonia.Controls\Avalonia.Controls.csproj" />
<ProjectReference Include="..\..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
<ProjectReference Include="..\..\..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
<ProjectReference Include="..\..\..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
</ItemGroup>
<Import Project="..\..\..\..\build\SampleApp.props" />
<Import Project="..\..\..\..\build\ReferenceCoreLibraries.props" />
<Import Project="..\..\..\..\build\BuildTargets.targets" />
<Import Project="..\..\..\..\build\SourceGenerators.props" />
</Project>

14
tests/TestFiles/BuildTasks/PInvoke/Program.cs

@ -0,0 +1,14 @@
using Avalonia;
namespace PInvoke;
public class Program
{
static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);
public static AppBuilder BuildAvaloniaApp() =>
AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace();
}
Loading…
Cancel
Save