Skip to content

Commit

Permalink
fix: Xaml Compiler error when code-behind class contains a `DllImport…
Browse files Browse the repository at this point in the history
…` 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
  • Loading branch information
workgroupengineering authored and maxkatz6 committed Jan 17, 2024
1 parent 1fcf325 commit 798f675
Show file tree
Hide file tree
Showing 13 changed files with 330 additions and 3 deletions.
22 changes: 21 additions & 1 deletion Avalonia.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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}
Expand Down
2 changes: 1 addition & 1 deletion build/SourceGenerators.props
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
2 changes: 1 addition & 1 deletion src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ MethodDefinition CreateTrampolineMethod(bool hasSystemProviderArg)

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++)
Expand Down
Original file line number Diff line number Diff line change
@@ -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 changes: 38 additions & 0 deletions tests/Avalonia.Build.Tasks.UnitTest/CompileAvaloniaXamlTaskTest.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 96 additions & 0 deletions tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngine.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 39 additions & 0 deletions tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngineMessage.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 8 additions & 0 deletions tests/TestFiles/BuildTasks/PInvoke/App.axaml
Original file line number Diff line number Diff line change
@@ -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 changes: 21 additions & 0 deletions tests/TestFiles/BuildTasks/PInvoke/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml
Original file line number Diff line number Diff line change
@@ -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 changes: 22 additions & 0 deletions tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -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 changes: 26 additions & 0 deletions tests/TestFiles/BuildTasks/PInvoke/PInvoke.csproj
Original file line number Diff line number Diff line change
@@ -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 changes: 14 additions & 0 deletions tests/TestFiles/BuildTasks/PInvoke/Program.cs
Original file line number Diff line number Diff line change
@@ -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();
}

0 comments on commit 798f675

Please sign in to comment.