using Capnp; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.RegularExpressions; [assembly: InternalsVisibleTo("CapnpC.CSharp.Generator.Tests")] namespace CapnpC.CSharp.Generator { /// /// Provides methods for controlling both the C# code generator backend and the frontend "capnpc" /// public static class CapnpCompilation { /// /// Returns the basename of the capnp executable /// public static string CapnpCompilerFilename { get => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "capnp.exe" : "capnp"; } /// /// Generates C# code from given input stream /// /// Input stream containing the binary code generation request, which the frontend capnpc emits /// Configuration options for code generator. If null, default options will be used. /// generation result /// if is null public static GenerationResult GenerateFromStream(Stream input, CodeGen.GeneratorOptions options) { if (input == null) throw new ArgumentNullException(nameof(input)); try { var segments = Framing.ReadSegments(input); var dec = DeserializerState.CreateRoot(segments); var reader = Schema.CodeGeneratorRequest.READER.create(dec); var model = Model.SchemaModel.Create(reader); var codeGen = new CodeGen.CodeGenerator(model, options ?? new CodeGen.GeneratorOptions()); return new GenerationResult(codeGen.Generate()); } catch (Exception exception) { return new GenerationResult(exception); } } /// /// Generates C# code from given input stream /// /// input stream containing the binary code generation request, which the frontend capnpc emits /// generation result /// if is null public static GenerationResult GenerateFromStream(Stream input) => GenerateFromStream(input, null); /// /// Invokes "capnp.exe -o-" with given additional arguments and redirects the output to the C# generator backend. /// /// additional command line arguments /// optional working directory /// generation result /// is null public static GenerationResult InvokeCapnpAndGenerate(IEnumerable arguments, string workingDirectory = null) { if (arguments == null) throw new ArgumentNullException(nameof(arguments)); using (var compiler = new Process()) { var argList = new List(); argList.Add("compile"); argList.Add($"-o-"); argList.AddRange(arguments); compiler.StartInfo.FileName = CapnpCompilerFilename; compiler.StartInfo.Arguments = string.Join(" ", argList); compiler.StartInfo.UseShellExecute = false; compiler.StartInfo.RedirectStandardOutput = true; compiler.StartInfo.RedirectStandardError = true; if (!string.IsNullOrWhiteSpace(workingDirectory)) { compiler.StartInfo.WorkingDirectory = workingDirectory; } try { compiler.Start(); } catch (Exception exception) { return new GenerationResult(exception) { ErrorCategory = CapnpProcessFailure.NotFound }; } var result = GenerateFromStream(compiler.StandardOutput.BaseStream); var messageList = new List(); while (!compiler.StandardError.EndOfStream) { messageList.Add(new CapnpMessage(compiler.StandardError.ReadLine())); } result.Messages = messageList; if (!result.IsSuccess) { compiler.WaitForExit(); int exitCode = compiler.ExitCode; if (exitCode == 0) result.ErrorCategory = CapnpProcessFailure.BadOutput; else result.ErrorCategory = CapnpProcessFailure.BadInput; } return result; } } } }