diff --git a/Avalonia.sln b/Avalonia.sln index 98ad0cadae..5ffa3dca50 100644 --- a/Avalonia.sln +++ b/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} diff --git a/build/SourceGenerators.props b/build/SourceGenerators.props index a66bff4999..9b4d3d7cdc 100644 --- a/build/SourceGenerators.props +++ b/build/SourceGenerators.props @@ -15,7 +15,7 @@ diff --git a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs b/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs index 80876f1819..469a51f32a 100644 --- a/src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs +++ b/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++) diff --git a/tests/Avalonia.Build.Tasks.UnitTest/Avalonia.Build.Tasks.UnitTest.csproj b/tests/Avalonia.Build.Tasks.UnitTest/Avalonia.Build.Tasks.UnitTest.csproj new file mode 100644 index 0000000000..0a289ff28b --- /dev/null +++ b/tests/Avalonia.Build.Tasks.UnitTest/Avalonia.Build.Tasks.UnitTest.csproj @@ -0,0 +1,39 @@ + + + + net472 + false + Library + false + true + Debug + + + + + + + + + + Always + + + Always + + + + + + + + + + + + + diff --git a/tests/Avalonia.Build.Tasks.UnitTest/CompileAvaloniaXamlTaskTest.cs b/tests/Avalonia.Build.Tasks.UnitTest/CompileAvaloniaXamlTaskTest.cs new file mode 100644 index 0000000000..fee678c388 --- /dev/null +++ b/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); + } + + +} diff --git a/tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngine.cs b/tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngine.cs new file mode 100644 index 0000000000..7dbbc7d02b --- /dev/null +++ b/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; + +/// +/// This is fake BuildEngine using for testing build task +/// at moment it manage only and +/// other messages are ignored/> +/// +internal class UnitTestBuildEngine : IBuildEngine, IDisposable +{ + private readonly bool _treatWarningAsError; + private readonly bool _assertOnDispose; + private readonly List _errors = new(); + + /// + /// Start new instance of + /// + /// if it is false immediately assert error + /// if it is true treat warning as error + /// if it is true assert on dispose if there are any errors. + /// + 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 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); + } + } + } +} diff --git a/tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngineMessage.cs b/tests/Avalonia.Build.Tasks.UnitTest/UnitTestBuildEngineMessage.cs new file mode 100644 index 0000000000..280e984b28 --- /dev/null +++ b/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); + +} diff --git a/tests/TestFiles/BuildTasks/PInvoke/App.axaml b/tests/TestFiles/BuildTasks/PInvoke/App.axaml new file mode 100644 index 0000000000..2102354427 --- /dev/null +++ b/tests/TestFiles/BuildTasks/PInvoke/App.axaml @@ -0,0 +1,8 @@ + + + + + diff --git a/tests/TestFiles/BuildTasks/PInvoke/App.axaml.cs b/tests/TestFiles/BuildTasks/PInvoke/App.axaml.cs new file mode 100644 index 0000000000..4f6a93c2d8 --- /dev/null +++ b/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(); + } + } +} diff --git a/tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml b/tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml new file mode 100644 index 0000000000..8d2a125f58 --- /dev/null +++ b/tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml @@ -0,0 +1,4 @@ + + diff --git a/tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml.cs b/tests/TestFiles/BuildTasks/PInvoke/MainWindow.axaml.cs new file mode 100644 index 0000000000..997be0988c --- /dev/null +++ b/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); + } +} diff --git a/tests/TestFiles/BuildTasks/PInvoke/PInvoke.csproj b/tests/TestFiles/BuildTasks/PInvoke/PInvoke.csproj new file mode 100644 index 0000000000..7df69b429d --- /dev/null +++ b/tests/TestFiles/BuildTasks/PInvoke/PInvoke.csproj @@ -0,0 +1,26 @@ + + + + WinExe + net6.0;netstandard2.0 + true + true + + false + true + false + + + + + + + + + + + + + + + diff --git a/tests/TestFiles/BuildTasks/PInvoke/Program.cs b/tests/TestFiles/BuildTasks/PInvoke/Program.cs new file mode 100644 index 0000000000..9c5f2b58ef --- /dev/null +++ b/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() + .UsePlatformDetect() + .LogToTrace(); +}