Skip to content

Commit

Permalink
Initialisation chargement modules de générateurs depuis nuget
Browse files Browse the repository at this point in the history
  • Loading branch information
JabX committed Sep 2, 2024
1 parent 7af6128 commit 641a60f
Show file tree
Hide file tree
Showing 24 changed files with 205 additions and 8,035 deletions.
2 changes: 1 addition & 1 deletion TopModel.Core/ModelStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public void TryApplyUpdates()
var generatedFiles = _modelWatchers.Where(m => m.GeneratedFiles != null).SelectMany(m => m.GeneratedFiles!);
if (generatedFiles.Any() && !DisableLockfile && _topModelLock != null)
{
_topModelLock.Update(generatedFiles);
_topModelLock.UpdateFiles(generatedFiles);
}

_logger.LogInformation($"Mise à jour terminée avec succès.");
Expand Down
8 changes: 8 additions & 0 deletions TopModel.Generator/ModgenDependency.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using TopModel.Utils;

namespace TopModel.Generator;

public record ModgenDependency(string ConfigKey, string Version)
{
public string FullName => $"TopModel.Generator.{ConfigKey.ToFirstUpper()}";
}
166 changes: 144 additions & 22 deletions TopModel.Generator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,17 @@
using System.Reflection;
using System.Text.Encodings.Web;
using System.Text.Json.Nodes;
using System.Xml;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NuGet.Common;
using NuGet.Packaging;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using TopModel.Core;
using TopModel.Core.Loaders;
using TopModel.Generator;
using TopModel.Generator.Core;
using TopModel.Utils;

Expand All @@ -21,11 +28,11 @@

var command = new RootCommand("Lance le générateur topmodel.") { Name = "modgen" };

var fileOption = new Option<IEnumerable<FileInfo>>(new[] { "-f", "--file" }, "Chemin vers un fichier de config.");
var excludeOption = new Option<IEnumerable<string>>(new[] { "-e", "--exclude" }, "Tag à ignorer lors de la génération.");
var watchOption = new Option<bool>(new[] { "-w", "--watch" }, "Lance le générateur en mode 'watch'");
var checkOption = new Option<bool>(new[] { "-c", "--check" }, "Vérifie que le code généré est conforme au modèle.");
var schemaOption = new Option<bool>(new[] { "-s", "--schema" }, "Génère le fichier de schéma JSON du fichier de config.");
var fileOption = new Option<IEnumerable<FileInfo>>(["-f", "--file"], "Chemin vers un fichier de config.");
var excludeOption = new Option<IEnumerable<string>>(["-e", "--exclude"], "Tag à ignorer lors de la génération.");
var watchOption = new Option<bool>(["-w", "--watch"], "Lance le générateur en mode 'watch'");
var checkOption = new Option<bool>(["-c", "--check"], "Vérifie que le code généré est conforme au modèle.");
var schemaOption = new Option<bool>(["-s", "--schema"], "Génère le fichier de schéma JSON du fichier de config.");
command.AddOption(fileOption);
command.AddOption(excludeOption);
command.AddOption(watchOption);
Expand Down Expand Up @@ -179,7 +186,7 @@ void HandleFile(FileInfo file)
return (configType, configName);
}

var baseGenerators = new FileInfo(Assembly.GetEntryAssembly()!.Location).Directory!.GetFiles("TopModel.Generator.*.dll");
var framework = $"net{Environment.Version.Major}.{Environment.Version.Minor}";
var disposables = new List<IDisposable>();
var loggerProvider = new LoggerProvider();
var hasErrors = Enumerable.Range(0, configs.Count).Select(_ => false).ToArray();
Expand All @@ -197,28 +204,143 @@ void HandleFile(FileInfo file)

Console.WriteLine();

var generators = baseGenerators
.Concat(config.CustomGenerators.SelectMany(cg => new DirectoryInfo(Path.Combine(Path.GetFullPath(cg, new FileInfo(fullName).DirectoryName!), "bin")).GetFiles("TopModel.Generator.*.dll", SearchOption.AllDirectories)))
.Where(a => a.FullName.Contains($"net{Environment.Version.Major}.{Environment.Version.Minor}"))
.DistinctBy(a => a.Name)
.Select(f => Assembly.LoadFrom(f.FullName))
.SelectMany(a => a.GetExportedTypes())
.Where(t => GetIGenRegInterface(t) != null)
.ToList();
var generators = new List<Type>();
var deps = new List<ModgenDependency>();

var undefinedConfigs = config.Generators.Keys.Except(generators.Select(g => GetIGenRegInterfaceAndName(g).Name))!;
foreach (var cg in config.CustomGenerators)
{
var csproj = Directory.GetFiles(Path.GetFullPath(cg, new FileInfo(fullName).DirectoryName!), "*.csproj").FirstOrDefault();
if (csproj == null)
{
logger.LogError($"Aucun fichier csproj trouvé pour le module de générateurs '{cg}'.");
returnCode = 1;
continue;
}

var csprojXml = new XmlDocument();
csprojXml.LoadXml(File.ReadAllText(csproj));

foreach (var dep in csprojXml.GetElementsByTagName("PackageReference").Cast<XmlNode>()
.Where(n => n.ChildNodes.Count == 0)
.ToDictionary(n => n.Attributes!["Include"]!.Value, n => n.Attributes!["Version"]!.Value)
.Where(n => n.Key.StartsWith("TopModel.Generator")))
{
if (dep.Key == "TopModel.Generator.Core")
{
// TODO : Gérer version min.
}
else
{
var configKey = dep.Key.Split('.').Last().ToLower();
if (!topModelLock.Modules.TryGetValue(configKey, out var ev))
{
topModelLock.Modules.Add(configKey, dep.Value);
}
else if (ev != dep.Value)
{
logger.LogError($"Le module personalisé '{cg}' référence le module '{configKey}' en version '{dep.Value}', ce qui n'est pas la version du lockfile ('{ev}').");
returnCode = 1;
continue;
}
}
}

if (undefinedConfigs.Any())
if (returnCode == 0)
{
generators.AddRange(new DirectoryInfo(Path.Combine(Path.GetFullPath(cg, new FileInfo(fullName).DirectoryName!), "bin"))
.GetFiles("TopModel.Generator.*.dll", SearchOption.AllDirectories)
.Where(a => a.FullName.Contains(framework))
.Select(f => Assembly.LoadFrom(f.FullName))
.SelectMany(a => a.GetExportedTypes())
.Where(t => GetIGenRegInterface(t) != null));
}
}

if (returnCode != 0)
{
returnCode = 1;
Console.ForegroundColor = colors[i % colors.Length];
Console.Write($"#{i + 1}");
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($" Aucune configuration de générateur n'a été trouvée pour : {string.Join(", ", undefinedConfigs)}.");
Console.ForegroundColor = ConsoleColor.Gray;
continue;
}

var ct = CancellationToken.None;

var nugetCache = new SourceCacheContext();
var nugetRepository = Repository.Factory.GetCoreV3("https://api.nuget.org/v3/index.json");
var nugetResource = await nugetRepository.GetResourceAsync<FindPackageByIdResource>();

foreach (var configKey in config.Generators.Keys)
{
if (generators.Any(g => GetIGenRegInterfaceAndName(g).Name == configKey))
{
continue;
}

var fullModuleName = $"TopModel.Generator.{configKey.ToFirstUpper()}";

if (!topModelLock.Modules.TryGetValue(configKey, out var moduleVersion))
{
var moduleVersions = await nugetResource.GetAllVersionsAsync(fullModuleName, nugetCache, NullLogger.Instance, ct);

if (!moduleVersions.Any())
{
logger.LogError($"Aucun module de générateurs trouvé pour '{configKey}'.");
returnCode = 1;
continue;
}

var nugetVersion = moduleVersions.Last().Version;
moduleVersion = $"{nugetVersion.Major}.{nugetVersion.Minor}.{nugetVersion.Build}";
topModelLock.Modules.Add(configKey, moduleVersion);

var topmodelDep = (await nugetResource.GetDependencyInfoAsync(fullModuleName, new NuGetVersion(moduleVersion), nugetCache, NullLogger.Instance, ct))
.DependencyGroups
.Single(dg => dg.TargetFramework.ToString() == framework)
.Packages
.Single(d => d.Id == "TopModel.Generator.Core");

// TODO : Gérer version min.
}

deps.Add(new(configKey, moduleVersion));
}

var modgenRoot = Path.GetFullPath(".modgen", config.ModelRoot);
Directory.CreateDirectory(modgenRoot);

foreach (var dep in deps)
{
var moduleFolder = Path.Combine(modgenRoot, $"{dep.ConfigKey}.{dep.Version}");
if (!Directory.Exists(moduleFolder))
{
if (!await nugetResource.DoesPackageExistAsync(dep.FullName, new NuGetVersion(dep.Version), nugetCache, NullLogger.Instance, ct))
{
logger.LogError($"Le package '{dep.FullName}' en version {dep.Version} est introuvable.");
returnCode = 1;
continue;
}

Directory.CreateDirectory(moduleFolder);

using var packageStream = new MemoryStream();
await nugetResource.CopyNupkgToStreamAsync(dep.FullName, new NuGetVersion(dep.Version), packageStream, nugetCache, NullLogger.Instance, ct);
using var packageReader = new PackageArchiveReader(packageStream);
var nuspecReader = await packageReader.GetNuspecReaderAsync(ct);

foreach (var file in packageReader.GetFiles().Where(f => f == $"lib/{framework}/{dep.FullName}.dll" || f.EndsWith("config.json")))
{
packageReader.ExtractFile(file, Path.Combine(moduleFolder, file.Split('/').Last()), NullLogger.Instance);
}
}

generators.AddRange(Assembly.LoadFrom(Path.Combine(moduleFolder, $"{dep.FullName}.dll")).GetExportedTypes().Where(t => GetIGenRegInterface(t) != null));
}

if (returnCode != 0)
{
continue;
}

topModelLock.Write();

if (schemaMode)
{
Console.ForegroundColor = colors[i % colors.Length];
Expand Down
8 changes: 3 additions & 5 deletions TopModel.Generator/TopModel.Generator.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="NuGet.Commands" Version="6.11.0" />
<PackageReference Include="NuGet.ProjectModel" Version="6.11.0" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

Expand All @@ -49,11 +51,7 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\TopModel.Generator.Csharp\TopModel.Generator.Csharp.csproj" />
<ProjectReference Include="..\TopModel.Generator.Javascript\TopModel.Generator.Javascript.csproj" />
<ProjectReference Include="..\TopModel.Generator.Jpa\TopModel.Generator.Jpa.csproj" />
<ProjectReference Include="..\TopModel.Generator.Sql\TopModel.Generator.Sql.csproj" />
<ProjectReference Include="..\TopModel.Generator.Translation\TopModel.Generator.Translation.csproj" />
<ProjectReference Include="..\TopModel.Generator.Core\TopModel.Generator.Core.csproj" />
</ItemGroup>

</Project>
2 changes: 1 addition & 1 deletion TopModel.ModelGenerator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ async Task StartGeneration(string filePath, string directoryName, int i)
generatedFiles.AddRange(await generator.Generate(loggingScope));
}

tmdLock.Update(generatedFiles);
tmdLock.UpdateFiles(generatedFiles);

mainLogger.LogInformation("Mise à jour terminée avec succès.");
}
Expand Down
39 changes: 29 additions & 10 deletions TopModel.Utils/TopModelLock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public TopModelLock(ILogger logger, string modelRoot, string lockFileName)
var lf = _deserializer.Deserialize<TopModelLockFile>(file);
Version = lf.Version;
GeneratedFiles = lf.GeneratedFiles;
Modules = lf.Modules;
}
catch
{
Expand All @@ -53,7 +54,7 @@ public TopModelLock(ILogger logger, string modelRoot, string lockFileName)
Version = version;
}

public void Update(IEnumerable<string> generatedFiles)
public void UpdateFiles(IEnumerable<string> generatedFiles)
{
GeneratedFiles ??= [];

Expand All @@ -77,18 +78,36 @@ public void Update(IEnumerable<string> generatedFiles)

GeneratedFiles = generatedFilesList;

using var fw = new FileWriter(Path.Combine(_modelRoot, _lockFileName), _logger)
{
StartCommentToken = "#"
};
Write();
}

fw.WriteLine($"version: {Version}");
if (GeneratedFiles.Count > 0)
public void Write()
{
if (Modules.Count > 0 || GeneratedFiles.Count > 0)
{
fw.WriteLine("generatedFiles:");
foreach (var genFile in GeneratedFiles)
using var fw = new FileWriter(Path.Combine(_modelRoot, _lockFileName), _logger)
{
StartCommentToken = "#"
};

fw.WriteLine($"version: {Version}");

if (Modules.Count > 0)
{
fw.WriteLine("modules:");
foreach (var module in Modules)
{
fw.WriteLine($" {module.Key}: {module.Value}");
}
}

if (GeneratedFiles.Count > 0)
{
fw.WriteLine($" - {genFile}");
fw.WriteLine("generatedFiles:");
foreach (var genFile in GeneratedFiles)
{
fw.WriteLine($" - {genFile}");
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions TopModel.Utils/TopModelLockFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ public class TopModelLockFile
{
public required string Version { get; set; }

public Dictionary<string, string> Modules { get; set; } = [];

public List<string> GeneratedFiles { get; set; } = [];
}
4 changes: 3 additions & 1 deletion samples/generators/angular/topmodel.config
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# yaml-language-server: $schema=./topmodel.config.schema.json
# yaml-language-server: $schema=./topmodel.config.schema.json
---
app: Angular
modelRoot: ../../model
lockFileName: angular.topmodel.lock
generators:
- ../../../TopModel.Generator.Javascript
javascript:
- tags:
- front
Expand Down
Loading

0 comments on commit 641a60f

Please sign in to comment.