Skip to content

Instantly share code, notes, and snippets.

@tritao
Last active May 3, 2025 11:45
Show Gist options
  • Save tritao/1817eebcf2a2a13b3831df9d85d1ec95 to your computer and use it in GitHub Desktop.
Save tritao/1817eebcf2a2a13b3831df9d85d1ec95 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
using CppSharp;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
var builder = WebApplication.CreateBuilder(args);
// Configure logging
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Information);
// Enable CORS
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", policy =>
policy.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
var app = builder.Build();
app.UseCors("AllowAll");
// POST /generate endpoint
app.MapPost("/generate", async (CodeGenerationRequest request, ILogger<Program> logger) =>
{
if (string.IsNullOrWhiteSpace(request.Code) ||
string.IsNullOrWhiteSpace(request.GenerationOptions.Generator))
{
return Results.BadRequest("Invalid input data");
}
var stopwatch = Stopwatch.StartNew();
try
{
// Create temp dirs for headers and output
using var headersDir = new TempDirectory();
using var outputDir = new TempDirectory();
// Write input header
var headerPath = Path.Combine(headersDir.Path, "Code.h");
await File.WriteAllTextAsync(headerPath, request.Code);
// Set up diagnostics and options
Diagnostics.Implementation = new BufferDiagnostics();
var options = new Options
{
Architecture = TargetArchitecture.x64,
Platform = TargetPlatform.Linux,
Compile = false,
Debug = true,
OutputDir = outputDir.Path
};
options.HeaderFiles.Add("Code.h");
options.IncludeDirs.Add(headersDir.Path);
// Determine generator kind
var errors = new List<string>();
CLI.GetGeneratorKind(options, request.GenerationOptions.Generator, errors);
var generator = new Generator(options);
if (errors.Any() || !generator.ValidateOptions(errors))
{
return Results.Problem(string.Join("\n", errors));
}
// Run generation
generator.Run();
stopwatch.Stop();
// Collect generated files
var files = Directory.GetFiles(outputDir.Path);
var generated = files.Select(file => new GeneratedFile
{
Filename = Path.GetRelativePath(outputDir.Path, file),
Content = File.ReadAllText(file)
}).ToList();
// Build response
var response = new CodeGenerationResponse
{
GeneratedFiles = generated,
ProgramOutputs = ((BufferDiagnostics)Diagnostics.Implementation).Outputs,
GenerationTime = stopwatch.ElapsedMilliseconds.ToString()
};
return Results.Ok(response);
}
catch (Exception ex)
{
logger.LogError(ex, "Code generation failed");
return Results.Problem(detail: ex.ToString());
}
});
app.Run();
/// <summary>
/// Helper for creating and auto‑deleting temp directories.
/// </summary>
public class TempDirectory : IDisposable
{
public string Path { get; }
private readonly DirectoryInfo _dirInfo;
public TempDirectory()
{
_dirInfo = Directory.CreateTempSubdirectory("cppsharp");
Path = _dirInfo.FullName;
}
public void Dispose()
{
try
{
_dirInfo.Delete(true);
}
catch
{
// ignore cleanup errors
}
}
}
/// <summary>
/// Collects diagnostic messages in memory with optional indentation.
/// </summary>
public class BufferDiagnostics : IDiagnostics
{
/// <summary>
/// All the diagnostic messages collected so far.
/// </summary>
public IList<string> Outputs { get; } = new List<string>();
private readonly Stack<int> _indents = new();
/// <summary>
/// The minimum DiagnosticKind to emit.
/// </summary>
public DiagnosticKind Level { get; set; } = DiagnosticKind.Message;
public void Emit(DiagnosticInfo info)
{
if (info.Kind < Level)
return;
int totalIndent = _indents.Sum();
string formatted = new string(' ', totalIndent) + info.Message;
Outputs.Add(formatted);
}
/// <summary>
/// Increase indentation by the given number of spaces (default 2).
/// </summary>
public void PushIndent(int spaces = 2) => _indents.Push(spaces);
/// <summary>
/// Remove the most recently added indent level, if any.
/// </summary>
public void PopIndent()
{
if (_indents.Count > 0)
_indents.Pop();
}
}
/// <summary>
/// Options specifying how code generation should be performed.
/// </summary>
public class GenerationOptions
{
/// <summary>
/// The generator to use (e.g., "C#", "Java", "Python").
/// </summary>
public string Generator { get; set; } = string.Empty;
}
/// <summary>
/// The request payload for the code generation endpoint.
/// </summary>
public class CodeGenerationRequest
{
/// <summary>
/// The C/C++ source code to generate bindings for.
/// </summary>
public string Code { get; set; } = string.Empty;
/// <summary>
/// Options controlling how code generation should proceed.
/// </summary>
public GenerationOptions GenerationOptions { get; set; } = new GenerationOptions();
}
/// <summary>
/// A single file generated by the code generator.
/// </summary>
public class GeneratedFile
{
/// <summary>
/// Relative path or name of the generated file.
/// </summary>
public string Filename { get; set; } = string.Empty;
/// <summary>
/// Contents of the generated file.
/// </summary>
public string Content { get; set; } = string.Empty;
}
/// <summary>
/// The response payload from the code generation endpoint.
/// </summary>
public class CodeGenerationResponse
{
/// <summary>
/// Files generated by the code generator.
/// </summary>
public List<GeneratedFile> GeneratedFiles { get; set; } = new List<GeneratedFile>();
/// <summary>
/// Any textual outputs or diagnostics produced during generation.
/// </summary>
public List<string> ProgramOutputs { get; set; } = new List<string>();
/// <summary>
/// How long generation took, in milliseconds.
/// </summary>
public string GenerationTime { get; set; } = string.Empty;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment