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
+
+
+
+
+
+
+
+
+
+
+
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