diff --git a/src/Generation/GirLoader/Helper/ChainedRepositoryResolver.cs b/src/Generation/GirLoader/Helper/ChainedRepositoryResolver.cs
new file mode 100644
index 000000000..2b863fff5
--- /dev/null
+++ b/src/Generation/GirLoader/Helper/ChainedRepositoryResolver.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+using System.Linq;
+
+namespace GirLoader;
+
+///
+/// Tries multiple repository resolvers, returning the first result found
+///
+public class ChainedRepositoryResolver : IRepositoryResolver
+{
+ private readonly IRepositoryResolver[] _resolvers;
+
+ public ChainedRepositoryResolver(IEnumerable resolvers)
+ {
+ _resolvers = resolvers.ToArray();
+ }
+
+ public Input.Repository? ResolveRepository(string fileName)
+ {
+ foreach (var resolver in _resolvers)
+ {
+ var repository = resolver.ResolveRepository(fileName);
+ if (repository != null)
+ {
+ return repository;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/Generation/GirLoader/Helper/DirectoryRepositoryResolver.cs b/src/Generation/GirLoader/Helper/DirectoryRepositoryResolver.cs
new file mode 100644
index 000000000..e44a8354b
--- /dev/null
+++ b/src/Generation/GirLoader/Helper/DirectoryRepositoryResolver.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+
+namespace GirLoader;
+
+///
+/// Resolves repository files from a local directory
+///
+public class DirectoryRepositoryResolver : IRepositoryResolver
+{
+ private readonly string _inputDirectory;
+
+ public DirectoryRepositoryResolver(string inputDirectory)
+ {
+ _inputDirectory = inputDirectory ?? throw new ArgumentNullException(nameof(inputDirectory));
+ }
+
+ public Input.Repository? ResolveRepository(string fileName)
+ {
+ var path = Path.Combine(_inputDirectory, fileName);
+ return File.Exists(path)
+ ? new FileInfo(path).OpenRead().DeserializeGirInputModel()
+ : null;
+ }
+}
diff --git a/src/Generation/GirLoader/Helper/EmbeddedRepositoryResolver.cs b/src/Generation/GirLoader/Helper/EmbeddedRepositoryResolver.cs
new file mode 100644
index 000000000..ebf07f0d6
--- /dev/null
+++ b/src/Generation/GirLoader/Helper/EmbeddedRepositoryResolver.cs
@@ -0,0 +1,33 @@
+using System;
+using System.IO;
+using System.Reflection;
+
+namespace GirLoader;
+
+public class EmbeddedRepositoryResolver : IRepositoryResolver
+{
+ private readonly Assembly _assembly;
+ private readonly string _platformName;
+ private readonly string _assemblyName;
+
+ public EmbeddedRepositoryResolver(Assembly assembly, string platformName)
+ {
+ _assembly = assembly ?? throw new ArgumentNullException(nameof(assembly));
+ _platformName = platformName ?? throw new ArgumentNullException(nameof(platformName));
+ _assemblyName = _assembly.GetName().Name ?? throw new Exception("Could not get assembly name");
+ }
+
+ public Input.Repository? ResolveRepository(string fileName)
+ {
+ var resourceName = $"{_assemblyName}.{_platformName}.{fileName}";
+ try
+ {
+ using var stream = _assembly.GetManifestResourceStream(resourceName);
+ return stream?.DeserializeGirInputModel();
+ }
+ catch (FileNotFoundException)
+ {
+ return null;
+ }
+ }
+}
diff --git a/src/Generation/GirLoader/Helper/IRepositoryResolver.cs b/src/Generation/GirLoader/Helper/IRepositoryResolver.cs
new file mode 100644
index 000000000..43d5c8fc5
--- /dev/null
+++ b/src/Generation/GirLoader/Helper/IRepositoryResolver.cs
@@ -0,0 +1,9 @@
+namespace GirLoader;
+
+///
+/// Resolves input repository definitions from GIR file names
+///
+public interface IRepositoryResolver
+{
+ Input.Repository? ResolveRepository(string fileName);
+}
diff --git a/src/Generation/GirLoader/Helper/IncludeResolver.cs b/src/Generation/GirLoader/Helper/IncludeResolver.cs
index 2dd99d529..d6fb3864e 100644
--- a/src/Generation/GirLoader/Helper/IncludeResolver.cs
+++ b/src/Generation/GirLoader/Helper/IncludeResolver.cs
@@ -1,23 +1,18 @@
-using System.IO;
-
-namespace GirLoader;
+namespace GirLoader;
public class IncludeResolver
{
- private readonly string _inputDirectory;
+ private readonly IRepositoryResolver _repositoryResolver;
- public IncludeResolver(string inputDirectory)
+ public IncludeResolver(IRepositoryResolver repositoryResolver)
{
- _inputDirectory = inputDirectory;
+ _repositoryResolver = repositoryResolver;
}
public Input.Repository? ResolveInclude(Output.Include include)
{
var fileName = $"{include.Name}-{include.Version}.gir";
- var path = Path.Combine(_inputDirectory, fileName);
- return File.Exists(path)
- ? new FileInfo(path).OpenRead().DeserializeGirInputModel()
- : null;
+ return _repositoryResolver.ResolveRepository(fileName);
}
}
diff --git a/src/Generation/GirTool/GenerateCommand.cs b/src/Generation/GirTool/GenerateCommand.cs
index 7c78a8f34..92c098478 100644
--- a/src/Generation/GirTool/GenerateCommand.cs
+++ b/src/Generation/GirTool/GenerateCommand.cs
@@ -115,9 +115,9 @@ private static (DeserializedInput, DeserializedInput, DeserializedInput) LoadRep
DeserializedInput? macosRepositories = null;
DeserializedInput? windowsRepositories = null;
- void SetLinuxRepositories() => linuxRepositories = DeserializeInput(searchPathLinux, input);
- void SetMacosRepositories() => macosRepositories = DeserializeInput(searchPathMacos, input);
- void SetWindowsRepositories() => windowsRepositories = DeserializeInput(searchPathWindows, input);
+ void SetLinuxRepositories() => linuxRepositories = DeserializeInput("linux", searchPathLinux, input);
+ void SetMacosRepositories() => macosRepositories = DeserializeInput("macos", searchPathMacos, input);
+ void SetWindowsRepositories() => windowsRepositories = DeserializeInput("windows", searchPathWindows, input);
if (disableAsync)
{
@@ -137,15 +137,14 @@ private static (DeserializedInput, DeserializedInput, DeserializedInput) LoadRep
return (linuxRepositories!, macosRepositories!, windowsRepositories!);
}
- private static DeserializedInput DeserializeInput(string? searchPath, string[] input)
+ private static DeserializedInput DeserializeInput(string platformName, string? searchPath, string[] input)
{
- if (searchPath is null)
- return DeserializedInput.Empty();
+ var repositoryResolver = GetRepositoryResolver(platformName, searchPath);
var inputRepositories = input
- .Select(x => Path.Join(searchPath, x))
- .Where(File.Exists)
- .Select(x => new FileInfo(x).OpenRead().DeserializeGirInputModel())
+ .Select(fileName => repositoryResolver.ResolveRepository(fileName))
+ .Where(inputRepository => inputRepository != null)
+ .Select(inputRepository => inputRepository!)
.ToList();
// Get the namespaces corresponding to the input gir files.
@@ -154,13 +153,30 @@ private static DeserializedInput DeserializeInput(string? searchPath, string[] i
.Select(repository => repository.Namespace == null ? "" : GetNamespaceName(repository.Namespace))
.ToList();
- var includeResolver = new IncludeResolver(searchPath);
+ var includeResolver = new IncludeResolver(repositoryResolver);
var loader = new GirLoader.Loader(includeResolver.ResolveInclude);
var outputRepositories = loader.Load(inputRepositories).ToList();
return new DeserializedInput(outputRepositories, inputNamespaces);
}
+ private static IRepositoryResolver GetRepositoryResolver(string platformName, string? searchPath)
+ {
+ var assembly = typeof(GenerateCommand).Assembly;
+ var embeddedResolver = new EmbeddedRepositoryResolver(assembly, platformName);
+
+ if (searchPath == null)
+ {
+ return embeddedResolver;
+ }
+ else
+ {
+ var directoryResolver = new DirectoryRepositoryResolver(searchPath);
+ return new ChainedRepositoryResolver(
+ new IRepositoryResolver[] {directoryResolver, embeddedResolver});
+ }
+ }
+
private static string GetNamespaceName(GirModel.Namespace ns)
{
return $"{ns.Name}-{ns.Version}";
diff --git a/src/Generation/GirTool/GirTool.csproj b/src/Generation/GirTool/GirTool.csproj
index 9200795bf..bcf922429 100644
--- a/src/Generation/GirTool/GirTool.csproj
+++ b/src/Generation/GirTool/GirTool.csproj
@@ -9,4 +9,8 @@
+
+
+
+
diff --git a/src/Tests/Generation/GirLoader.Tests/ChainedRepositoryResolverTests.cs b/src/Tests/Generation/GirLoader.Tests/ChainedRepositoryResolverTests.cs
new file mode 100644
index 000000000..0fd585bcd
--- /dev/null
+++ b/src/Tests/Generation/GirLoader.Tests/ChainedRepositoryResolverTests.cs
@@ -0,0 +1,81 @@
+using System.Collections.Generic;
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace GirLoader.Test;
+
+[TestClass, TestCategory("UnitTest")]
+public class ChainedRepositoryResolverTests
+{
+ [TestMethod]
+ public void ReturnsNullWhenNoRepositoryFound()
+ {
+ var resolver = new ChainedRepositoryResolver(new[]
+ {
+ new StubRepositoryResolver(new Dictionary())
+ });
+
+ var repository = resolver.ResolveRepository("GObject-2.0.gir");
+
+ repository.Should().BeNull();
+ }
+
+ [TestMethod]
+ public void ReturnsFirstResolvedRepositoryWhenMultipleMatch()
+ {
+ var resolver = new ChainedRepositoryResolver(new[]
+ {
+ new StubRepositoryResolver(new Dictionary
+ {
+ {"GObject-2.0.gir", Helper.GetInputRepository("GObject", "2.1")},
+ }),
+ new StubRepositoryResolver(new Dictionary
+ {
+ {"GObject-2.0.gir", Helper.GetInputRepository("GObject", "2.2")},
+ }),
+ });
+
+ var repository = resolver.ResolveRepository("GObject-2.0.gir");
+
+ repository.Should().NotBeNull();
+ repository!.Namespace.Should().NotBeNull();
+ repository.Namespace!.Version.Should().Be("2.1");
+ }
+
+ [TestMethod]
+ public void ReturnsRepositoryFromFallbackResolver()
+ {
+ var resolver = new ChainedRepositoryResolver(new[]
+ {
+ new StubRepositoryResolver(new Dictionary
+ {
+ {"TestLibrary-1.0.gir", Helper.GetInputRepository("TestLibrary", "1.0")},
+ }),
+ new StubRepositoryResolver(new Dictionary
+ {
+ {"GObject-2.0.gir", Helper.GetInputRepository("GObject", "2.0")},
+ }),
+ });
+
+ var repository = resolver.ResolveRepository("GObject-2.0.gir");
+
+ repository.Should().NotBeNull();
+ repository!.Namespace.Should().NotBeNull();
+ repository.Namespace!.Name.Should().Be("GObject");
+ }
+
+ private class StubRepositoryResolver : IRepositoryResolver
+ {
+ private readonly IReadOnlyDictionary _repositories;
+
+ public StubRepositoryResolver(IReadOnlyDictionary repositories)
+ {
+ _repositories = repositories;
+ }
+
+ public Input.Repository? ResolveRepository(string fileName)
+ {
+ return _repositories.GetValueOrDefault(fileName);
+ }
+ }
+}
diff --git a/src/Tests/Generation/GirLoader.Tests/DirectoryRepositoryResolverTests.cs b/src/Tests/Generation/GirLoader.Tests/DirectoryRepositoryResolverTests.cs
new file mode 100644
index 000000000..96fdae24f
--- /dev/null
+++ b/src/Tests/Generation/GirLoader.Tests/DirectoryRepositoryResolverTests.cs
@@ -0,0 +1,36 @@
+using System.IO;
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace GirLoader.Test;
+
+[TestClass, TestCategory("UnitTest")]
+public class DirectoryRepositoryResolverTests
+{
+ [TestMethod]
+ public void CanLoadRepositoryFromDirectory()
+ {
+ using var directory = new DisposableTempDirectory();
+ var filePath = Path.Join(directory.DirectoryPath, "GObject-2.0.gir");
+ File.WriteAllText(filePath, Helper.GetInputRepositoryXml("GObject", "2.0"));
+
+ var resolver = new DirectoryRepositoryResolver(directory.DirectoryPath);
+
+ var repository = resolver.ResolveRepository("GObject-2.0.gir");
+
+ repository.Should().NotBeNull();
+ repository!.Namespace.Should().NotBeNull();
+ repository.Namespace!.Name.Should().Be("GObject");
+ }
+
+ [TestMethod]
+ public void ReturnsNullWhenFileNotFound()
+ {
+ using var directory = new DisposableTempDirectory();
+ var resolver = new DirectoryRepositoryResolver(directory.DirectoryPath);
+
+ var repository = resolver.ResolveRepository("GObject-2.0.gir");
+
+ repository.Should().BeNull();
+ }
+}
diff --git a/src/Tests/Generation/GirLoader.Tests/DisposableTempDirectory.cs b/src/Tests/Generation/GirLoader.Tests/DisposableTempDirectory.cs
new file mode 100644
index 000000000..f7c83e371
--- /dev/null
+++ b/src/Tests/Generation/GirLoader.Tests/DisposableTempDirectory.cs
@@ -0,0 +1,22 @@
+using System;
+using System.IO;
+
+namespace GirLoader.Test;
+
+public class DisposableTempDirectory : IDisposable
+{
+ private readonly string _directoryPath;
+
+ public DisposableTempDirectory()
+ {
+ _directoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
+ Directory.CreateDirectory(_directoryPath);
+ }
+
+ public string DirectoryPath => _directoryPath;
+
+ public void Dispose()
+ {
+ Directory.Delete(_directoryPath, recursive: true);
+ }
+}
diff --git a/src/Tests/Generation/GirLoader.Tests/EmbeddedRepositoryResolverTests.cs b/src/Tests/Generation/GirLoader.Tests/EmbeddedRepositoryResolverTests.cs
new file mode 100644
index 000000000..171be3825
--- /dev/null
+++ b/src/Tests/Generation/GirLoader.Tests/EmbeddedRepositoryResolverTests.cs
@@ -0,0 +1,32 @@
+using FluentAssertions;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace GirLoader.Test;
+
+[TestClass, TestCategory("UnitTest")]
+public class EmbeddedRepositoryResolverTests
+{
+ [TestMethod]
+ public void CanLoadRepositoryFromEmbeddedResource()
+ {
+ var resolver = new EmbeddedRepositoryResolver(
+ typeof(EmbeddedRepositoryResolverTests).Assembly, "linux");
+
+ var repository = resolver.ResolveRepository("GObject-2.0.gir");
+
+ repository.Should().NotBeNull();
+ repository!.Namespace.Should().NotBeNull();
+ repository.Namespace!.Name.Should().Be("GObject");
+ }
+
+ [TestMethod]
+ public void ReturnsNullWhenEmbeddedResourceNotFound()
+ {
+ var resolver = new EmbeddedRepositoryResolver(
+ typeof(EmbeddedRepositoryResolverTests).Assembly, "linux");
+
+ var repository = resolver.ResolveRepository("TestLibrary-1.0.gir");
+
+ repository.Should().BeNull();
+ }
+}
diff --git a/src/Tests/Generation/GirLoader.Tests/GirLoader.Tests.csproj b/src/Tests/Generation/GirLoader.Tests/GirLoader.Tests.csproj
index 6a1eca193..60a04d5cd 100644
--- a/src/Tests/Generation/GirLoader.Tests/GirLoader.Tests.csproj
+++ b/src/Tests/Generation/GirLoader.Tests/GirLoader.Tests.csproj
@@ -5,4 +5,8 @@
+
+
+
+
diff --git a/src/Tests/Generation/GirLoader.Tests/Helper.cs b/src/Tests/Generation/GirLoader.Tests/Helper.cs
index 68a6ba0eb..87a73ce7a 100644
--- a/src/Tests/Generation/GirLoader.Tests/Helper.cs
+++ b/src/Tests/Generation/GirLoader.Tests/Helper.cs
@@ -13,4 +13,15 @@ internal static Input.Repository GetInputRepository(string namespaceName, string
}
};
}
+
+ internal static string GetInputRepositoryXml(string namespaceName, string version)
+ {
+ return $"""
+
+
+
+
+
+ """;
+ }
}