Skip to content

Commit

Permalink
Merge pull request #1091 from adamreeve/embedded_repositories
Browse files Browse the repository at this point in the history
Support loading repositories from files embedded as resources in GirTool
  • Loading branch information
badcel authored Jul 15, 2024
2 parents 62b4ab4 + d85a553 commit 6ff8449
Show file tree
Hide file tree
Showing 19 changed files with 414 additions and 38 deletions.
31 changes: 31 additions & 0 deletions src/Generation/GirLoader/Helper/ChainedRepositoryResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System.Collections.Generic;
using System.Linq;

namespace GirLoader;

/// <summary>
/// Tries multiple repository resolvers, returning the first result found
/// </summary>
public class ChainedRepositoryResolver : IRepositoryResolver
{
private readonly IReadOnlyCollection<IRepositoryResolver> _resolvers;

public ChainedRepositoryResolver(IReadOnlyCollection<IRepositoryResolver> resolvers)
{
_resolvers = resolvers;
}

public Input.Repository? ResolveRepository(string fileName)
{
foreach (var resolver in _resolvers)
{
var repository = resolver.ResolveRepository(fileName);
if (repository != null)
{
return repository;
}
}

return null;
}
}
31 changes: 31 additions & 0 deletions src/Generation/GirLoader/Helper/DirectoryRepositoryResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using System.IO;

namespace GirLoader;

/// <summary>
/// Resolves repository files from a local directory
/// </summary>
public class DirectoryRepositoryResolver : IRepositoryResolver
{
private readonly string _inputDirectory;

public DirectoryRepositoryResolver(string inputDirectory)
{
_inputDirectory = inputDirectory;
}

public Input.Repository? ResolveRepository(string fileName)
{
var path = Path.Combine(_inputDirectory, fileName);
if (File.Exists(path))
{
using var fileStream = new FileInfo(path).OpenRead();
return fileStream.DeserializeGirInputModel();
}
else
{
return null;
}
}
}
33 changes: 33 additions & 0 deletions src/Generation/GirLoader/Helper/EmbeddedRepositoryResolver.cs
Original file line number Diff line number Diff line change
@@ -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;
_platformName = 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;
}
}
}
9 changes: 9 additions & 0 deletions src/Generation/GirLoader/Helper/IRepositoryResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace GirLoader;

/// <summary>
/// Resolves input repository definitions from GIR file names
/// </summary>
public interface IRepositoryResolver
{
Input.Repository? ResolveRepository(string fileName);
}
15 changes: 5 additions & 10 deletions src/Generation/GirLoader/Helper/IncludeResolver.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
9 changes: 9 additions & 0 deletions src/Generation/GirLoader/Helper/NullRepositoryResolver.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace GirLoader;

public class NullRepositoryResolver : IRepositoryResolver
{
public Input.Repository? ResolveRepository(string fileName)
{
return null;
}
}
39 changes: 39 additions & 0 deletions src/Generation/GirLoader/Helper/RepositoryResolverFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using System.Reflection;

namespace GirLoader;

public class RepositoryResolverFactory
{
private readonly string _platform;
private readonly string? _platformSearchPath;
private readonly Assembly _assembly;

/// <summary>
/// Construct a new RepositoryResolverFactory
/// </summary>
/// <param name="platform">The platform to resolve repositories for</param>
/// <param name="platformSearchPath">Directory containing repository files for this platform.
/// If null, no repositories will be resolved for this platform.</param>
/// <param name="assembly">Assembly containing embedded repository files.
/// These are used as a fallback if no repository file is found in the search path.</param>
public RepositoryResolverFactory(string platform, string? platformSearchPath, Assembly assembly)
{
_platform = platform;
_platformSearchPath = platformSearchPath;
_assembly = assembly;
}

public IRepositoryResolver Create()
{
if (_platformSearchPath == null)
{
return new NullRepositoryResolver();
}

var directoryResolver = new DirectoryRepositoryResolver(_platformSearchPath);
var embeddedResolver = new EmbeddedRepositoryResolver(_assembly, _platform);

return new ChainedRepositoryResolver(
new IRepositoryResolver[] { directoryResolver, embeddedResolver });
}
}
19 changes: 9 additions & 10 deletions src/Generation/GirTool/GenerateCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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 = new RepositoryResolverFactory(
platformName, searchPath, typeof(GenerateCommand).Assembly).Create();

var inputRepositories = input
.Select(x => Path.Join(searchPath, x))
.Where(File.Exists)
.Select(x => new FileInfo(x).OpenRead().DeserializeGirInputModel())
.Select(fileName => repositoryResolver.ResolveRepository(fileName))
.OfType<GirLoader.Input.Repository>()
.ToList();

// Get the namespaces corresponding to the input gir files.
Expand All @@ -154,7 +153,7 @@ 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();

Expand Down
4 changes: 4 additions & 0 deletions src/Generation/GirTool/GirTool.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,8 @@
<ProjectReference Include="../Generator/Generator.csproj" />
<ProjectReference Include="../GirLoader/GirLoader.csproj" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="..\..\..\ext\gir-files\*\*.gir" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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<string, Input.Repository>())
});

var repository = resolver.ResolveRepository("GObject-2.0.gir");

repository.Should().BeNull();
}

[TestMethod]
public void ReturnsFirstResolvedRepositoryWhenMultipleMatch()
{
var resolver = new ChainedRepositoryResolver(new[]
{
new StubRepositoryResolver(new Dictionary<string, Input.Repository>
{
{"GObject-2.0.gir", InputRepositoryHelper.CreateRepository("GObject", "2.1")},
}),
new StubRepositoryResolver(new Dictionary<string, Input.Repository>
{
{"GObject-2.0.gir", InputRepositoryHelper.CreateRepository("GObject", "2.2")},
}),
});

var repository = resolver.ResolveRepository("GObject-2.0.gir");

repository!.Namespace!.Version.Should().Be("2.1");
}

[TestMethod]
public void ReturnsRepositoryFromFallbackResolver()
{
var resolver = new ChainedRepositoryResolver(new[]
{
new StubRepositoryResolver(new Dictionary<string, Input.Repository>
{
{"TestLibrary-1.0.gir", InputRepositoryHelper.CreateRepository("TestLibrary", "1.0")},
}),
new StubRepositoryResolver(new Dictionary<string, Input.Repository>
{
{"GObject-2.0.gir", InputRepositoryHelper.CreateRepository("GObject", "2.0")},
}),
});

var repository = resolver.ResolveRepository("GObject-2.0.gir");

repository!.Namespace!.Name.Should().Be("GObject");
}

private class StubRepositoryResolver : IRepositoryResolver
{
private readonly IReadOnlyDictionary<string, Input.Repository> _repositories;

public StubRepositoryResolver(IReadOnlyDictionary<string, Input.Repository> repositories)
{
_repositories = repositories;
}

public Input.Repository? ResolveRepository(string fileName)
{
return _repositories.GetValueOrDefault(fileName);
}
}
}
2 changes: 1 addition & 1 deletion src/Tests/Generation/GirLoader.Tests/ClassPropertyTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class ClassPropertyTest
{
private static Input.Repository GetRepositoryWithStubClass()
{
var repository = Helper.GetInputRepository("ns", "1.0");
var repository = InputRepositoryHelper.CreateRepository("ns", "1.0");
repository.Namespace!.Classes.Add(new()
{
Name = "ClassName",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
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.Path, "GObject-2.0.gir");
File.WriteAllText(filePath, InputRepositoryHelper.CreateXml("GObject", "2.0"));

var resolver = new DirectoryRepositoryResolver(directory.Path);

var repository = resolver.ResolveRepository("GObject-2.0.gir");

repository!.Namespace!.Name.Should().Be("GObject");
}

[TestMethod]
public void ReturnsNullWhenFileNotFound()
{
using var directory = new DisposableTempDirectory();
var resolver = new DirectoryRepositoryResolver(directory.Path);

var repository = resolver.ResolveRepository("GObject-2.0.gir");

repository.Should().BeNull();
}
}
21 changes: 21 additions & 0 deletions src/Tests/Generation/GirLoader.Tests/DisposableTempDirectory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using System;
using System.IO;

namespace GirLoader.Test;

public class DisposableTempDirectory : IDisposable
{
public DisposableTempDirectory()
{
Path = System.IO.Path.Combine(
System.IO.Path.GetTempPath(), System.IO.Path.GetRandomFileName());
Directory.CreateDirectory(Path);
}

public string Path { get; }

public void Dispose()
{
Directory.Delete(Path, recursive: true);
}
}
Loading

0 comments on commit 6ff8449

Please sign in to comment.