diff --git a/.gitignore b/.gitignore index 32ada6ca2b4..826b4d8a5a6 100644 --- a/.gitignore +++ b/.gitignore @@ -45,8 +45,8 @@ x64/ *.vssscc .builds *.pidb -*.log *.scc +*.binlog # Visual C++ cache files ipch/ diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index f833ddd89a4..44e1bb4696b 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -94,6 +94,7 @@ "RunTests", "RunToolsTests", "ValidateApiDiff", + "VerifyXamlCompilation", "ZipFiles" ] } @@ -131,6 +132,7 @@ "RunTests", "RunToolsTests", "ValidateApiDiff", + "VerifyXamlCompilation", "ZipFiles" ] } diff --git a/dirs.proj b/dirs.proj index 5403847fab1..3853faccf0c 100644 --- a/dirs.proj +++ b/dirs.proj @@ -4,7 +4,7 @@ - + diff --git a/nukebuild/Build.cs b/nukebuild/Build.cs index c71a0c1e39e..10eab82723d 100644 --- a/nukebuild/Build.cs +++ b/nukebuild/Build.cs @@ -361,6 +361,7 @@ await Task.WhenAll( Target CiAzureWindows => _ => _ .DependsOn(Package) + .DependsOn(VerifyXamlCompilation) .DependsOn(ZipFiles); Target BuildToNuGetCache => _ => _ @@ -407,6 +408,67 @@ await Task.WhenAll( file.GenerateCppHeader()); }); + Target VerifyXamlCompilation => _ => _ + .DependsOn(CreateNugetPackages) + .Executes(() => + { + var buildTestsDirectory = RootDirectory / "tests" / "BuildTests"; + var artifactsDirectory = buildTestsDirectory / "artifacts"; + var nugetCacheDirectory = artifactsDirectory / "nuget-cache"; + + DeleteDirectory(artifactsDirectory); + BuildTestsAndVerify("Debug"); + BuildTestsAndVerify("Release"); + + void BuildTestsAndVerify(string configuration) + { + var configName = configuration.ToLowerInvariant(); + + DotNetBuild(settings => settings + .SetConfiguration(configuration) + .SetProperty("AvaloniaVersion", Parameters.Version) + .SetProperty("NuGetPackageRoot", nugetCacheDirectory) + .SetPackageDirectory(nugetCacheDirectory) + .SetProjectFile(buildTestsDirectory / "BuildTests.sln") + .SetProcessArgumentConfigurator(arguments => arguments.Add("--nodeReuse:false"))); + + // Standard compilation - should have compiled XAML + VerifyBuildTestAssembly("bin", "BuildTests"); + VerifyBuildTestAssembly("bin", "BuildTests.Android"); + VerifyBuildTestAssembly("bin", "BuildTests.Browser"); + VerifyBuildTestAssembly("bin", "BuildTests.Desktop"); + VerifyBuildTestAssembly("bin", "BuildTests.FSharp"); + VerifyBuildTestAssembly("bin", "BuildTests.iOS"); + VerifyBuildTestAssembly("bin", "BuildTests.WpfHybrid"); + + // Publish previously built project without rebuilding - should have compiled XAML + PublishBuildTestProject("BuildTests.Desktop", noBuild: true); + VerifyBuildTestAssembly("publish", "BuildTests.Desktop"); + + // Publish NativeAOT build, then run it - should not crash and have the expected output + PublishBuildTestProject("BuildTests.NativeAot"); + var exeExtension = OperatingSystem.IsWindows() ? ".exe" : null; + XamlCompilationVerifier.VerifyNativeAot( + GetBuildTestOutputPath("publish", "BuildTests.NativeAot", exeExtension)); + + void PublishBuildTestProject(string projectName, bool? noBuild = null) + => DotNetPublish(settings => settings + .SetConfiguration(configuration) + .SetProperty("AvaloniaVersion", Parameters.Version) + .SetProperty("NuGetPackageRoot", nugetCacheDirectory) + .SetPackageDirectory(nugetCacheDirectory) + .SetNoBuild(noBuild) + .SetProject(buildTestsDirectory / projectName / (projectName + ".csproj")) + .SetProcessArgumentConfigurator(arguments => arguments.Add("--nodeReuse:false"))); + + void VerifyBuildTestAssembly(string folder, string projectName) + => XamlCompilationVerifier.VerifyAssemblyCompiledXaml( + GetBuildTestOutputPath(folder, projectName, ".dll")); + + AbsolutePath GetBuildTestOutputPath(string folder, string projectName, string extension) + => artifactsDirectory / folder / projectName / configName / (projectName + extension); + } + }); public static int Main() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) diff --git a/nukebuild/XamlCompilationVerifier.cs b/nukebuild/XamlCompilationVerifier.cs new file mode 100644 index 00000000000..ecf84f7f002 --- /dev/null +++ b/nukebuild/XamlCompilationVerifier.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using Mono.Cecil; +using Nuke.Common.Tooling; +using Serilog; + +internal static class XamlCompilationVerifier +{ + public static void VerifyAssemblyCompiledXaml(string assemblyPath) + { + const string avaloniaResourcesTypeName = "CompiledAvaloniaXaml.!AvaloniaResources"; + const string mainViewTypeName = "BuildTests.MainView"; + const string populateMethodName = "!XamlIlPopulate"; + + using var assembly = AssemblyDefinition.ReadAssembly(assemblyPath); + + if (assembly.MainModule.GetType(avaloniaResourcesTypeName) is null) + { + throw new InvalidOperationException( + $"Assembly {assemblyPath} is missing type {avaloniaResourcesTypeName}"); + } + + if (assembly.MainModule.GetType(mainViewTypeName) is not { } mainViewType) + { + throw new InvalidOperationException( + $"Assembly {assemblyPath} is missing type {mainViewTypeName}"); + } + + if (!mainViewType.Methods.Any(method => method.Name == populateMethodName)) + { + throw new InvalidOperationException( + $"Assembly {assemblyPath} is missing method {populateMethodName} on {mainViewTypeName}"); + } + + Log.Information($"Assembly {assemblyPath} correctly has compiled XAML"); + } + + public static void VerifyNativeAot(string programPath) + { + const string expectedOutput = "Hello from AOT"; + + using var process = ProcessTasks.StartProcess(programPath, string.Empty); + + process.WaitForExit(); + process.AssertZeroExitCode(); + + var output = process.Output.Select(o => o.Text).FirstOrDefault(); + if (output != expectedOutput) + { + throw new InvalidOperationException( + $"{programPath} returned text \"{output}\", expected \"{expectedOutput}\""); + } + + Log.Information($"Native program {programPath} correctly has compiled XAML"); + } +} diff --git a/packages/Avalonia/AvaloniaBuildTasks.targets b/packages/Avalonia/AvaloniaBuildTasks.targets index 37e6bd42794..36843071f3a 100644 --- a/packages/Avalonia/AvaloniaBuildTasks.targets +++ b/packages/Avalonia/AvaloniaBuildTasks.targets @@ -63,7 +63,7 @@ $(BuildAvaloniaResourcesDependsOn);AddAvaloniaResources;ResolveReferences;_GenerateAvaloniaResourcesDependencyCache - $(CompileAvaloniaXamlDependsOn);FindReferenceAssembliesForReferences;PrepareToCompileAvaloniaXaml + $(CompileAvaloniaXamlDependsOn);FindReferenceAssembliesForReferences @@ -107,34 +107,39 @@ - + + - false - false - false - <_AvaloniaHasCompiledXaml>true + $(TargetsTriggeredByCompilation);CompileAvaloniaXaml - - - <_DebugSymbolsIntermediatePath Update="*" AvaloniaCompileOutput="%(RelativeDir)Avalonia\%(Filename)%(Extension)"/> - - - - - - + + + + + + + + + false + false + false + + + + + <_DebugSymbolsIntermediatePath Update="*" AvaloniaCompileOutput="%(FullPath)"/> + + + @@ -160,60 +165,6 @@ AnalyzerConfigFiles="@(EditorConfigFiles)"/> - - - <_AvaloniaXamlCompiledAssembly Include="@(IntermediateAssembly->Metadata('AvaloniaCompileOutput'))"/> - - - - <_AvaloniaXamlCompiledRefAssembly Include="@(IntermediateRefAssembly->Metadata('AvaloniaCompileOutput'))"/> - - - - <_AvaloniaXamlCompiledSymbols Include="@(_DebugSymbolsIntermediatePath->Metadata('AvaloniaCompileOutput'))"/> - <_DebugSymbolsIntermediatePath Remove="@(_DebugSymbolsIntermediatePath)"/> - <_DebugSymbolsIntermediatePath Include="@(_AvaloniaXamlCompiledSymbols)"/> - - - <_DeploymentManifestEntryPoint Remove="@(_DeploymentManifestEntryPoint)" /> - <_DeploymentManifestEntryPoint Include="@(_AvaloniaXamlCompiledAssembly)"> - $(TargetFileName) - - - - - - - - - - - - - - - - - - - - - - - - - - Build @@ -252,7 +203,7 @@ Name="AvaloniaDeleteRefAssemblyBeforeOutputCopy" BeforeTargets="CopyFilesToOutputDirectory" Condition=" - '$(_AvaloniaHasCompiledXaml)' == 'true' and + '@(AvaloniaResource)@(AvaloniaXaml)' != '' and '$(TargetRefPath)' != '' and '$(ProduceReferenceAssembly)' == 'true' and ('$(CopyBuildOutputToOutputDirectory)' == '' or '$(CopyBuildOutputToOutputDirectory)' == 'true') and diff --git a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs index 7ad2ffba5d5..138f6d3b90c 100644 --- a/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs +++ b/src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs @@ -51,18 +51,25 @@ public bool Execute() private static void CopyAndTouch(string source, string destination, bool shouldExist = true) { - if (!File.Exists(source)) + var normalizedSource = Path.GetFullPath(source); + var normalizedDestination = Path.GetFullPath(destination); + + if (!File.Exists(normalizedSource)) { if (shouldExist) { - throw new FileNotFoundException($"Could not copy file '{source}'. File does not exist."); + throw new FileNotFoundException($"Could not copy file '{normalizedSource}'. File does not exist."); } return; } - File.Copy(source, destination, overwrite: true); - File.SetLastWriteTimeUtc(destination, DateTime.UtcNow); + if (normalizedSource != normalizedDestination) + { + File.Copy(normalizedSource, normalizedDestination, overwrite: true); + } + + File.SetLastWriteTimeUtc(normalizedDestination, DateTime.UtcNow); } [Required] diff --git a/tests/BuildTests/BuildTests.Android/BuildTests.Android.csproj b/tests/BuildTests/BuildTests.Android/BuildTests.Android.csproj new file mode 100644 index 00000000000..eaac81be0bb --- /dev/null +++ b/tests/BuildTests/BuildTests.Android/BuildTests.Android.csproj @@ -0,0 +1,21 @@ + + + + Exe + net8.0-android + 21 + enable + com.Avalonia.BuildTests + 1 + 1.0 + apk + false + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.Android/MainActivity.cs b/tests/BuildTests/BuildTests.Android/MainActivity.cs new file mode 100644 index 00000000000..046dca9a47f --- /dev/null +++ b/tests/BuildTests/BuildTests.Android/MainActivity.cs @@ -0,0 +1,11 @@ +using Android.App; +using Android.Content.PM; +using Avalonia.Android; + +namespace BuildTests.Android; + +[Activity( + Label = "BuildTests.Android", + MainLauncher = true, + ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize | ConfigChanges.UiMode)] +public class MainActivity : AvaloniaMainActivity; diff --git a/tests/BuildTests/BuildTests.Android/Properties/AndroidManifest.xml b/tests/BuildTests/BuildTests.Android/Properties/AndroidManifest.xml new file mode 100644 index 00000000000..c02ce0d24b7 --- /dev/null +++ b/tests/BuildTests/BuildTests.Android/Properties/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/BuildTests/BuildTests.Browser/BuildTests.Browser.csproj b/tests/BuildTests/BuildTests.Browser/BuildTests.Browser.csproj new file mode 100644 index 00000000000..ee339b1bf0e --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/BuildTests.Browser.csproj @@ -0,0 +1,16 @@ + + + + net8.0-browser + Exe + true + enable + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.Browser/Program.cs b/tests/BuildTests/BuildTests.Browser/Program.cs new file mode 100644 index 00000000000..2572a6aca1a --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/Program.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; +using Avalonia; +using Avalonia.Browser; + +namespace BuildTests.Browser; + +internal static class Program +{ + private static Task Main() + => BuildAvaloniaApp().StartBrowserAppAsync("out"); + + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure(); +} diff --git a/tests/BuildTests/BuildTests.Browser/Properties/AssemblyInfo.cs b/tests/BuildTests/BuildTests.Browser/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..fb787959a85 --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly:System.Runtime.Versioning.SupportedOSPlatform("browser")] diff --git a/tests/BuildTests/BuildTests.Browser/Properties/launchSettings.json b/tests/BuildTests/BuildTests.Browser/Properties/launchSettings.json new file mode 100644 index 00000000000..ea590694172 --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/Properties/launchSettings.json @@ -0,0 +1,13 @@ +{ + "profiles": { + "BuildTests.Browser": { + "commandName": "Project", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "https://localhost:7169;http://localhost:5235", + "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}" + } + } +} diff --git a/tests/BuildTests/BuildTests.Browser/runtimeconfig.template.json b/tests/BuildTests/BuildTests.Browser/runtimeconfig.template.json new file mode 100644 index 00000000000..b96a94320ba --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/runtimeconfig.template.json @@ -0,0 +1,10 @@ +{ + "wasmHostProperties": { + "perHostConfig": [ + { + "name": "browser", + "host": "browser" + } + ] + } +} \ No newline at end of file diff --git a/tests/BuildTests/BuildTests.Browser/wwwroot/app.css b/tests/BuildTests/BuildTests.Browser/wwwroot/app.css new file mode 100644 index 00000000000..1d6f754a36d --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/wwwroot/app.css @@ -0,0 +1,58 @@ +/* HTML styles for the splash screen */ +.avalonia-splash { + position: absolute; + height: 100%; + width: 100%; + background: white; + font-family: 'Outfit', sans-serif; + justify-content: center; + align-items: center; + display: flex; + pointer-events: none; +} + +/* Light theme styles */ +@media (prefers-color-scheme: light) { + .avalonia-splash { + background: white; + } + + .avalonia-splash h2 { + color: #1b2a4e; + } + + .avalonia-splash a { + color: #0D6EFD; + } +} + +@media (prefers-color-scheme: dark) { + .avalonia-splash { + background: #1b2a4e; + } + + .avalonia-splash h2 { + color: white; + } + + .avalonia-splash a { + color: white; + } +} + +.avalonia-splash h2 { + font-weight: 400; + font-size: 1.5rem; +} + +.avalonia-splash a { + text-decoration: none; + font-size: 2.5rem; + display: block; +} + +.avalonia-splash.splash-close { + transition: opacity 200ms, display 200ms; + display: none; + opacity: 0; +} diff --git a/tests/BuildTests/BuildTests.Browser/wwwroot/favicon.ico b/tests/BuildTests/BuildTests.Browser/wwwroot/favicon.ico new file mode 100644 index 00000000000..da8d49ff9b9 Binary files /dev/null and b/tests/BuildTests/BuildTests.Browser/wwwroot/favicon.ico differ diff --git a/tests/BuildTests/BuildTests.Browser/wwwroot/index.html b/tests/BuildTests/BuildTests.Browser/wwwroot/index.html new file mode 100644 index 00000000000..e6b6620b6ff --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/wwwroot/index.html @@ -0,0 +1,36 @@ + + + + + BuildTests.Browser + + + + + + +
+
+

+ Powered by + + + + + + + + + + + + + + +

+
+
+ + + + diff --git a/tests/BuildTests/BuildTests.Browser/wwwroot/main.js b/tests/BuildTests/BuildTests.Browser/wwwroot/main.js new file mode 100644 index 00000000000..bf1555e4362 --- /dev/null +++ b/tests/BuildTests/BuildTests.Browser/wwwroot/main.js @@ -0,0 +1,13 @@ +import { dotnet } from './_framework/dotnet.js' + +const is_browser = typeof window != "undefined"; +if (!is_browser) throw new Error(`Expected to be running in a browser`); + +const dotnetRuntime = await dotnet + .withDiagnosticTracing(false) + .withApplicationArgumentsFromQuery() + .create(); + +const config = dotnetRuntime.getConfig(); + +await dotnetRuntime.runMain(config.mainAssemblyName, [globalThis.location.href]); diff --git a/tests/BuildTests/BuildTests.Desktop/BuildTests.Desktop.csproj b/tests/BuildTests/BuildTests.Desktop/BuildTests.Desktop.csproj new file mode 100644 index 00000000000..837660a5315 --- /dev/null +++ b/tests/BuildTests/BuildTests.Desktop/BuildTests.Desktop.csproj @@ -0,0 +1,17 @@ + + + + WinExe + net8.0 + enable + app.manifest + + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.Desktop/Program.cs b/tests/BuildTests/BuildTests.Desktop/Program.cs new file mode 100644 index 00000000000..9171ae58c86 --- /dev/null +++ b/tests/BuildTests/BuildTests.Desktop/Program.cs @@ -0,0 +1,27 @@ +using System; +using Avalonia; + +namespace BuildTests.Desktop; + +internal static class Program +{ + [STAThread] + public static void Main(string[] args) + => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); + + public static AppBuilder BuildAvaloniaApp() + { + var builder = AppBuilder.Configure() + .UseSkia() + .LogToTrace(); + + // We don't use Avalonia.Desktop with UsePlatformDetect() because Avalonia.Native is only built on macOS, + // causing restore to fail for the exact package versions we're using in this solution. + if (OperatingSystem.IsWindows()) + builder.UseWin32(); + else if (OperatingSystem.IsLinux()) + builder.UseX11(); + + return builder; + } +} diff --git a/tests/BuildTests/BuildTests.Desktop/app.manifest b/tests/BuildTests/BuildTests.Desktop/app.manifest new file mode 100644 index 00000000000..e48cf52a0ff --- /dev/null +++ b/tests/BuildTests/BuildTests.Desktop/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.FSharp/App.axaml.fs b/tests/BuildTests/BuildTests.FSharp/App.axaml.fs new file mode 100644 index 00000000000..209b379c8ec --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/App.axaml.fs @@ -0,0 +1,19 @@ +namespace BuildTests + +open Avalonia +open Avalonia.Controls.ApplicationLifetimes +open Avalonia.Markup.Xaml + +type App() = + inherit Application() + + override this.Initialize() = + AvaloniaXamlLoader.Load(this) + + override this.OnFrameworkInitializationCompleted() = + match this.ApplicationLifetime with + | :? IClassicDesktopStyleApplicationLifetime as desktop -> + desktop.MainWindow <- MainWindow() + | _ -> () + + base.OnFrameworkInitializationCompleted() diff --git a/tests/BuildTests/BuildTests.FSharp/BuildTests.FSharp.fsproj b/tests/BuildTests/BuildTests.FSharp/BuildTests.FSharp.fsproj new file mode 100644 index 00000000000..52d39517971 --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/BuildTests.FSharp.fsproj @@ -0,0 +1,26 @@ + + + + WinExe + net8.0 + app.manifest + true + + + + + + + + + + + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.FSharp/MainView.axaml.fs b/tests/BuildTests/BuildTests.FSharp/MainView.axaml.fs new file mode 100644 index 00000000000..fb0455eee32 --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/MainView.axaml.fs @@ -0,0 +1,13 @@ +namespace BuildTests + +open Avalonia.Controls +open Avalonia.Markup.Xaml + +type MainView() as this = + inherit UserControl() + + do + this.InitializeComponent() + + member private this.InitializeComponent() = + AvaloniaXamlLoader.Load(this) diff --git a/tests/BuildTests/BuildTests.FSharp/MainViewModel.fs b/tests/BuildTests/BuildTests.FSharp/MainViewModel.fs new file mode 100644 index 00000000000..07dee4d42fe --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/MainViewModel.fs @@ -0,0 +1,7 @@ +namespace BuildTests + +open System.Runtime.CompilerServices; + +type MainViewModel() = + + member val HelloText = sprintf "Hello from %s" (if RuntimeFeature.IsDynamicCodeSupported then "JIT" else "AOT") with get, set diff --git a/tests/BuildTests/BuildTests.FSharp/MainWindow.axaml.fs b/tests/BuildTests/BuildTests.FSharp/MainWindow.axaml.fs new file mode 100644 index 00000000000..907d6b8123c --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/MainWindow.axaml.fs @@ -0,0 +1,12 @@ +namespace BuildTests + +open Avalonia.Controls +open Avalonia.Markup.Xaml + +type MainWindow() as this = + inherit Window() + + do this.InitializeComponent() + + member private this.InitializeComponent() = + AvaloniaXamlLoader.Load(this) diff --git a/tests/BuildTests/BuildTests.FSharp/Program.fs b/tests/BuildTests/BuildTests.FSharp/Program.fs new file mode 100644 index 00000000000..7d434106a01 --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/Program.fs @@ -0,0 +1,18 @@ +namespace BuildTests + +open System +open Avalonia + +module Program = + + [] + let buildAvaloniaApp () = + AppBuilder + .Configure() + .UseSkia() + .UseWin32() + .LogToTrace(areas = Array.empty) + + [] + let main argv = + buildAvaloniaApp().StartWithClassicDesktopLifetime(argv) diff --git a/tests/BuildTests/BuildTests.FSharp/app.manifest b/tests/BuildTests/BuildTests.FSharp/app.manifest new file mode 100644 index 00000000000..136cde98b2a --- /dev/null +++ b/tests/BuildTests/BuildTests.FSharp/app.manifest @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.NativeAot/BuildTests.NativeAot.csproj b/tests/BuildTests/BuildTests.NativeAot/BuildTests.NativeAot.csproj new file mode 100644 index 00000000000..efe4d85854e --- /dev/null +++ b/tests/BuildTests/BuildTests.NativeAot/BuildTests.NativeAot.csproj @@ -0,0 +1,12 @@ + + + + Exe + net8.0 + enable + true + + + + + diff --git a/tests/BuildTests/BuildTests.NativeAot/Program.cs b/tests/BuildTests/BuildTests.NativeAot/Program.cs new file mode 100644 index 00000000000..64e35c9bd33 --- /dev/null +++ b/tests/BuildTests/BuildTests.NativeAot/Program.cs @@ -0,0 +1,12 @@ +using System; + +namespace BuildTests.NativeAot; + +internal static class Program +{ + public static void Main() + { + var view = new MainView(); + Console.Out.WriteLine(view.TextBlock.Text); + } +} diff --git a/tests/BuildTests/BuildTests.WpfHybrid/App.xaml b/tests/BuildTests/BuildTests.WpfHybrid/App.xaml new file mode 100644 index 00000000000..e41a2bf4640 --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/App.xaml @@ -0,0 +1,4 @@ + diff --git a/tests/BuildTests/BuildTests.WpfHybrid/App.xaml.cs b/tests/BuildTests/BuildTests.WpfHybrid/App.xaml.cs new file mode 100644 index 00000000000..4049b61126c --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/App.xaml.cs @@ -0,0 +1,3 @@ +namespace BuildTests.WpfHybrid; + +public partial class App; diff --git a/tests/BuildTests/BuildTests.WpfHybrid/AssemblyInfo.cs b/tests/BuildTests/BuildTests.WpfHybrid/AssemblyInfo.cs new file mode 100644 index 00000000000..76af59d23ac --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Windows; + +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] diff --git a/tests/BuildTests/BuildTests.WpfHybrid/BuildTests.WpfHybrid.csproj b/tests/BuildTests/BuildTests.WpfHybrid/BuildTests.WpfHybrid.csproj new file mode 100644 index 00000000000..05654c41da1 --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/BuildTests.WpfHybrid.csproj @@ -0,0 +1,12 @@ + + + + WinExe + net8.0-windows + enable + true + + + + + diff --git a/tests/BuildTests/BuildTests.WpfHybrid/MainWindow.xaml b/tests/BuildTests/BuildTests.WpfHybrid/MainWindow.xaml new file mode 100644 index 00000000000..bb44aba6e53 --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/MainWindow.xaml @@ -0,0 +1,13 @@ + + + + + + diff --git a/tests/BuildTests/BuildTests.WpfHybrid/MainWindow.xaml.cs b/tests/BuildTests/BuildTests.WpfHybrid/MainWindow.xaml.cs new file mode 100644 index 00000000000..e8f5dfe8f45 --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/MainWindow.xaml.cs @@ -0,0 +1,10 @@ +namespace BuildTests.WpfHybrid; + +public partial class MainWindow +{ + public MainWindow() + { + InitializeComponent(); + WpfTextBlock.Text = "Hello from WPF"; + } +} diff --git a/tests/BuildTests/BuildTests.WpfHybrid/UserControl1.xaml b/tests/BuildTests/BuildTests.WpfHybrid/UserControl1.xaml new file mode 100644 index 00000000000..3f9bee5b381 --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/UserControl1.xaml @@ -0,0 +1,11 @@ + + + + + diff --git a/tests/BuildTests/BuildTests.WpfHybrid/UserControl1.xaml.cs b/tests/BuildTests/BuildTests.WpfHybrid/UserControl1.xaml.cs new file mode 100644 index 00000000000..8e97cbe489f --- /dev/null +++ b/tests/BuildTests/BuildTests.WpfHybrid/UserControl1.xaml.cs @@ -0,0 +1,7 @@ +namespace BuildTests.WpfHybrid; + +public partial class UserControl1 +{ + public UserControl1() + => InitializeComponent(); +} diff --git a/tests/BuildTests/BuildTests.iOS/AppDelegate.cs b/tests/BuildTests/BuildTests.iOS/AppDelegate.cs new file mode 100644 index 00000000000..69f3554de32 --- /dev/null +++ b/tests/BuildTests/BuildTests.iOS/AppDelegate.cs @@ -0,0 +1,7 @@ +using Foundation; +using Avalonia.iOS; + +namespace BuildTests.iOS; + +[Register("AppDelegate")] +public sealed class AppDelegate : AvaloniaAppDelegate; diff --git a/tests/BuildTests/BuildTests.iOS/BuildTests.iOS.csproj b/tests/BuildTests/BuildTests.iOS/BuildTests.iOS.csproj new file mode 100644 index 00000000000..6c8236d5353 --- /dev/null +++ b/tests/BuildTests/BuildTests.iOS/BuildTests.iOS.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0-ios + 13.0 + enable + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.iOS/Entitlements.plist b/tests/BuildTests/BuildTests.iOS/Entitlements.plist new file mode 100644 index 00000000000..0c67376ebac --- /dev/null +++ b/tests/BuildTests/BuildTests.iOS/Entitlements.plist @@ -0,0 +1,5 @@ + + + + + diff --git a/tests/BuildTests/BuildTests.iOS/Info.plist b/tests/BuildTests/BuildTests.iOS/Info.plist new file mode 100644 index 00000000000..faf8c40dd1c --- /dev/null +++ b/tests/BuildTests/BuildTests.iOS/Info.plist @@ -0,0 +1,43 @@ + + + + + CFBundleDisplayName + BuildTests + CFBundleIdentifier + companyName.BuildTests + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + MinimumOSVersion + 13.0 + UIDeviceFamily + + 1 + 2 + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/tests/BuildTests/BuildTests.iOS/Main.cs b/tests/BuildTests/BuildTests.iOS/Main.cs new file mode 100644 index 00000000000..3b8047842f9 --- /dev/null +++ b/tests/BuildTests/BuildTests.iOS/Main.cs @@ -0,0 +1,9 @@ +using UIKit; + +namespace BuildTests.iOS; + +internal static class Application +{ + public static void Main(string[] args) + => UIApplication.Main(args, null, typeof(AppDelegate)); +} diff --git a/tests/BuildTests/BuildTests.iOS/Resources/LaunchScreen.xib b/tests/BuildTests/BuildTests.iOS/Resources/LaunchScreen.xib new file mode 100644 index 00000000000..8bbcfcf1df2 --- /dev/null +++ b/tests/BuildTests/BuildTests.iOS/Resources/LaunchScreen.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/BuildTests/BuildTests.sln b/tests/BuildTests/BuildTests.sln new file mode 100644 index 00000000000..f049ba4ba25 --- /dev/null +++ b/tests/BuildTests/BuildTests.sln @@ -0,0 +1,75 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32811.315 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildTests", "BuildTests\BuildTests.csproj", "{EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildTests.Desktop", "BuildTests.Desktop\BuildTests.Desktop.csproj", "{ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildTests.Browser", "BuildTests.Browser\BuildTests.Browser.csproj", "{1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildTests.iOS", "BuildTests.iOS\BuildTests.iOS.csproj", "{EBD9022F-BC83-4846-9A11-6F7F3772DC64}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BuildTests.Android", "BuildTests.Android\BuildTests.Android.csproj", "{7AD1DAC8-7FBE-49D5-8614-7321233DB82E}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3DA99C4E-89E3-4049-9C22-0A7EC60D83D8}" + ProjectSection(SolutionItems) = preProject + Directory.Packages.props = Directory.Packages.props + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + IncludeBuildTestsAvaloniaItems.props = IncludeBuildTestsAvaloniaItems.props + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildTests.NativeAot", "BuildTests.NativeAot\BuildTests.NativeAot.csproj", "{767D97D5-4E74-4B54-ACFB-D2D845A2AB85}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildTests.WpfHybrid", "BuildTests.WpfHybrid\BuildTests.WpfHybrid.csproj", "{B84C58C1-AE11-4C10-8E18-8482085486F1}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "BuildTests.FSharp", "BuildTests.FSharp\BuildTests.FSharp.fsproj", "{7040B498-C281-490A-98D4-39FCDADAFDBF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBFA8512-1EA5-4D8C-B4AC-AB5B48A6D568}.Release|Any CPU.Build.0 = Release|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ABC31E74-02FF-46EB-B3B2-4E6AE43B456C}.Release|Any CPU.Build.0 = Release|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C1A049E-235C-4CD0-B6FA-D53AC418F4DA}.Release|Any CPU.Build.0 = Release|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBD9022F-BC83-4846-9A11-6F7F3772DC64}.Release|Any CPU.Build.0 = Release|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AD1DAC8-7FBE-49D5-8614-7321233DB82E}.Release|Any CPU.Build.0 = Release|Any CPU + {767D97D5-4E74-4B54-ACFB-D2D845A2AB85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {767D97D5-4E74-4B54-ACFB-D2D845A2AB85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {767D97D5-4E74-4B54-ACFB-D2D845A2AB85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {767D97D5-4E74-4B54-ACFB-D2D845A2AB85}.Release|Any CPU.Build.0 = Release|Any CPU + {B84C58C1-AE11-4C10-8E18-8482085486F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B84C58C1-AE11-4C10-8E18-8482085486F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B84C58C1-AE11-4C10-8E18-8482085486F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B84C58C1-AE11-4C10-8E18-8482085486F1}.Release|Any CPU.Build.0 = Release|Any CPU + {7040B498-C281-490A-98D4-39FCDADAFDBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7040B498-C281-490A-98D4-39FCDADAFDBF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7040B498-C281-490A-98D4-39FCDADAFDBF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7040B498-C281-490A-98D4-39FCDADAFDBF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {83CB65B8-011F-4ED7-BCD3-A6CFA935EF7E} + EndGlobalSection +EndGlobal diff --git a/tests/BuildTests/BuildTests/App.axaml b/tests/BuildTests/BuildTests/App.axaml new file mode 100644 index 00000000000..b52a91f24e3 --- /dev/null +++ b/tests/BuildTests/BuildTests/App.axaml @@ -0,0 +1,8 @@ + + + + + diff --git a/tests/BuildTests/BuildTests/App.axaml.cs b/tests/BuildTests/BuildTests/App.axaml.cs new file mode 100644 index 00000000000..55df06f3bde --- /dev/null +++ b/tests/BuildTests/BuildTests/App.axaml.cs @@ -0,0 +1,25 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; + +namespace BuildTests; + +public class App : Application +{ + public override void Initialize() + => AvaloniaXamlLoader.Load(this); + + public override void OnFrameworkInitializationCompleted() + { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = new MainWindow(); + } + else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform) + { + singleViewPlatform.MainView = new MainView(); + } + + base.OnFrameworkInitializationCompleted(); + } +} diff --git a/tests/BuildTests/BuildTests/Assets/avalonia-logo.ico b/tests/BuildTests/BuildTests/Assets/avalonia-logo.ico new file mode 100644 index 00000000000..da8d49ff9b9 Binary files /dev/null and b/tests/BuildTests/BuildTests/Assets/avalonia-logo.ico differ diff --git a/tests/BuildTests/BuildTests/BuildTests.csproj b/tests/BuildTests/BuildTests/BuildTests.csproj new file mode 100644 index 00000000000..4903b3f3d1e --- /dev/null +++ b/tests/BuildTests/BuildTests/BuildTests.csproj @@ -0,0 +1,12 @@ + + + + net8.0 + enable + true + false + + + + + diff --git a/tests/BuildTests/BuildTests/MainView.axaml b/tests/BuildTests/BuildTests/MainView.axaml new file mode 100644 index 00000000000..d025ac348cb --- /dev/null +++ b/tests/BuildTests/BuildTests/MainView.axaml @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/tests/BuildTests/BuildTests/MainView.axaml.cs b/tests/BuildTests/BuildTests/MainView.axaml.cs new file mode 100644 index 00000000000..9bb387a2b70 --- /dev/null +++ b/tests/BuildTests/BuildTests/MainView.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace BuildTests; + +public partial class MainView : UserControl +{ + public MainView() + => InitializeComponent(); +} diff --git a/tests/BuildTests/BuildTests/MainViewModel.cs b/tests/BuildTests/BuildTests/MainViewModel.cs new file mode 100644 index 00000000000..1dc9abf1091 --- /dev/null +++ b/tests/BuildTests/BuildTests/MainViewModel.cs @@ -0,0 +1,8 @@ +using System.Runtime.CompilerServices; + +namespace BuildTests; + +public sealed class MainViewModel +{ + public string HelloText { get; set; } = $"Hello from {(RuntimeFeature.IsDynamicCodeSupported ? "JIT" : "AOT")}"; +} diff --git a/tests/BuildTests/BuildTests/MainWindow.axaml b/tests/BuildTests/BuildTests/MainWindow.axaml new file mode 100644 index 00000000000..339b4911639 --- /dev/null +++ b/tests/BuildTests/BuildTests/MainWindow.axaml @@ -0,0 +1,11 @@ + + + diff --git a/tests/BuildTests/BuildTests/MainWindow.axaml.cs b/tests/BuildTests/BuildTests/MainWindow.axaml.cs new file mode 100644 index 00000000000..f7f8fa3e537 --- /dev/null +++ b/tests/BuildTests/BuildTests/MainWindow.axaml.cs @@ -0,0 +1,9 @@ +using Avalonia.Controls; + +namespace BuildTests; + +public partial class MainWindow : Window +{ + public MainWindow() + => InitializeComponent(); +} diff --git a/tests/BuildTests/Directory.Build.props b/tests/BuildTests/Directory.Build.props new file mode 100644 index 00000000000..667403a4476 --- /dev/null +++ b/tests/BuildTests/Directory.Build.props @@ -0,0 +1,8 @@ + + + + true + false + + + diff --git a/tests/BuildTests/Directory.Build.targets b/tests/BuildTests/Directory.Build.targets new file mode 100644 index 00000000000..3d1d4ebe340 --- /dev/null +++ b/tests/BuildTests/Directory.Build.targets @@ -0,0 +1,3 @@ + + + diff --git a/tests/BuildTests/Directory.Packages.props b/tests/BuildTests/Directory.Packages.props new file mode 100644 index 00000000000..46fda9c2a32 --- /dev/null +++ b/tests/BuildTests/Directory.Packages.props @@ -0,0 +1,22 @@ + + + + true + + 9999.9999.9999 + + + + + + + + + + + + + + + + diff --git a/tests/BuildTests/IncludeBuildTestsAvaloniaItems.props b/tests/BuildTests/IncludeBuildTestsAvaloniaItems.props new file mode 100644 index 00000000000..4761eef4e54 --- /dev/null +++ b/tests/BuildTests/IncludeBuildTestsAvaloniaItems.props @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/BuildTests/nuget.config b/tests/BuildTests/nuget.config new file mode 100644 index 00000000000..a320a12399a --- /dev/null +++ b/tests/BuildTests/nuget.config @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/BuildTests/readme.md b/tests/BuildTests/readme.md new file mode 100644 index 00000000000..0662df85660 --- /dev/null +++ b/tests/BuildTests/readme.md @@ -0,0 +1,5 @@ +# BuildTests + +This directory contains test projects that are built by the `VerifyXamlCompilation` build target. + +These projects aren't included in the main solution because they depend on the Avalonia NuGet packages being locally built first. \ No newline at end of file