opengl source gen tool works

This commit is contained in:
Youmu Liu
2026-08-27 15:05:43 +08:00
parent 5db9f39a46
commit 648cac9d9b
42 changed files with 10247 additions and 10189 deletions
+149 -11
View File
@@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using StgSharp.GenerateGL.Generation;
using StgSharp.GenerateGL.Registry;
@@ -23,14 +24,15 @@ namespace StgSharp.GenerateGL
private const int QueryErrorExitCode = 1;
private const int SuccessExitCode = 0;
private const int UsageErrorExitCode = 2;
private const string GeneratedFileMarker = "// <auto-generated>";
public static int Generate(
GlRegistryModel registry,
GlRegistryProjection projection,
IReadOnlyList<string> arguments,
out GeneratedFileSet? generatedFiles
)
{
ArgumentNullException.ThrowIfNull(registry);
ArgumentNullException.ThrowIfNull(projection);
ArgumentNullException.ThrowIfNull(arguments);
generatedFiles = null;
@@ -40,17 +42,24 @@ namespace StgSharp.GenerateGL
return SuccessExitCode;
}
if (arguments.Count != 0)
if (arguments.Count > 1)
{
Console.Error.WriteLine(
$"error: unknown generate option '{arguments[0]}'.");
Console.Error.WriteLine("error: generate accepts at most one output directory.");
Console.Error.WriteLine("Run 'generate --help' for command information.");
return UsageErrorExitCode;
}
string? outputDirectory = arguments.Count == 1 ? arguments[0] : null;
string? resolvedOutputDirectory = null;
try
{
generatedFiles = OpenGlGenerator.Generate(registry);
generatedFiles = OpenGlGenerator.Generate(projection);
if (outputDirectory is not null)
{
resolvedOutputDirectory = WriteGeneratedFiles(
generatedFiles,
outputDirectory);
}
}
catch (InvalidDataException exception)
{
@@ -62,14 +71,136 @@ namespace StgSharp.GenerateGL
Console.Error.WriteLine($"error: {exception.Message}");
return QueryErrorExitCode;
}
catch (UnauthorizedAccessException exception)
{
Console.Error.WriteLine($"error: {exception.Message}");
return QueryErrorExitCode;
}
catch (IOException exception)
{
Console.Error.WriteLine($"error: {exception.Message}");
return QueryErrorExitCode;
}
catch (ArgumentException exception)
{
Console.Error.WriteLine($"error: {exception.Message}");
return QueryErrorExitCode;
}
catch (NotSupportedException exception)
{
Console.Error.WriteLine($"error: {exception.Message}");
return QueryErrorExitCode;
}
Console.WriteLine(
$"Generated {generatedFiles.Files.Count} file(s) in memory.");
Console.WriteLine("Use 'list files' and 'preview <filename>' to inspect them.");
Console.WriteLine("No files were written to disk.");
Console.WriteLine(
resolvedOutputDirectory is null
? "No files were written to disk."
: $"Wrote {generatedFiles.Files.Count} file(s) to " +
$"'{resolvedOutputDirectory}'.");
return SuccessExitCode;
}
private static string WriteGeneratedFiles(
GeneratedFileSet files,
string outputDirectory
)
{
ArgumentNullException.ThrowIfNull(files);
ArgumentException.ThrowIfNullOrWhiteSpace(outputDirectory);
string directory = Path.TrimEndingDirectorySeparator(
Path.GetFullPath(outputDirectory));
if (!Directory.Exists(directory))
{
throw new DirectoryNotFoundException(
$"Output directory '{directory}' does not exist.");
}
StringComparison pathComparison = OperatingSystem.IsWindows()
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
List<StagedGeneratedFile> stagedFiles = new List<StagedGeneratedFile>(
files.Files.Count);
try
{
foreach (GeneratedFile file in files.Files)
{
string fileName = Path.GetFileName(file.Name);
if (!string.Equals(fileName, file.Name, StringComparison.Ordinal) ||
string.IsNullOrWhiteSpace(fileName))
{
throw new InvalidDataException(
$"Generated file name '{file.Name}' is not a plain file name.");
}
string targetPath = Path.GetFullPath(Path.Combine(directory, fileName));
if (!string.Equals(
Path.GetDirectoryName(targetPath),
directory,
pathComparison))
{
throw new InvalidDataException(
$"Generated file '{file.Name}' escapes the output directory.");
}
EnsureTargetCanBeReplaced(targetPath);
string temporaryPath = Path.Combine(
directory,
$".{fileName}.{Guid.NewGuid():N}.tmp");
using (FileStream stream = new FileStream(
temporaryPath,
FileMode.CreateNew,
FileAccess.Write,
FileShare.None))
using (StreamWriter writer = new StreamWriter(
stream,
new UTF8Encoding(false)))
{
writer.Write(file.Content);
}
stagedFiles.Add(new StagedGeneratedFile(temporaryPath, targetPath));
}
foreach (StagedGeneratedFile file in stagedFiles)
{
File.Move(file.TemporaryPath, file.TargetPath, true);
}
}
finally
{
foreach (StagedGeneratedFile file in stagedFiles)
{
if (File.Exists(file.TemporaryPath))
{
File.Delete(file.TemporaryPath);
}
}
}
return directory;
}
private static void EnsureTargetCanBeReplaced(
string targetPath
)
{
if (!File.Exists(targetPath))
{
return;
}
string? firstLine = File.ReadLines(targetPath).FirstOrDefault();
if (!string.Equals(firstLine, GeneratedFileMarker, StringComparison.Ordinal))
{
throw new IOException(
$"Refusing to overwrite non-generated file '{targetPath}'.");
}
}
public static int ListFiles(
GeneratedFileSet files,
IReadOnlyList<string> arguments
@@ -90,7 +221,7 @@ namespace StgSharp.GenerateGL
return QueryErrorExitCode;
}
Console.WriteLine("Generated files (memory only)");
Console.WriteLine("Generated files held in the current session");
for (int index = 0; index < files.Files.Count; index++)
{
GeneratedFile file = files.Files[index];
@@ -141,16 +272,23 @@ namespace StgSharp.GenerateGL
Console.WriteLine();
Console.WriteLine("Usage:");
Console.WriteLine(" generate");
Console.WriteLine(" generate <output-directory>");
Console.WriteLine(" generate --help");
Console.WriteLine();
Console.WriteLine("Status:");
Console.WriteLine(" Output is retained in the current process only.");
Console.WriteLine(" Running 'generate' does not create or modify disk files.");
Console.WriteLine("Output:");
Console.WriteLine(" Without a directory, output is retained in memory only.");
Console.WriteLine(" The output directory must already exist and be writable.");
Console.WriteLine(" Existing files are replaced only when marked as generated.");
Console.WriteLine();
Console.WriteLine("Inspection:");
Console.WriteLine(" list files");
Console.WriteLine(" preview <filename>");
}
private sealed record StagedGeneratedFile(
string TemporaryPath,
string TargetPath
);
}
}