// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.IO; using System.Linq; using EpicGames.Core; using UnrealBuildBase; namespace UnrealBuildTool { /// /// Result from protoc action /// public class ProtocOutput { /// /// C++ files generated by a ProtocAction /// public List CppFiles = []; /// /// Header files generated by a ProtocAction /// public List HeaderFiles = []; } class ProtocAction : Action { public ProtocAction(List ProtoFiles, DirectoryReference ProtoModuleDir, DirectoryReference OutputDir, ProtocOutputType OutputType) : base(ActionType.Compile) { DirectoryReference ProtobufIncludeDir = DirectoryReference.Combine(ProtoExecution.GetProtobufDirectory(), "include"); PrerequisiteItems.UnionWith(ProtoFiles); WorkingDirectory = ProtoModuleDir; CommandPath = ProtoExecution.GetProtocPath(); CommandArguments = $" -I {ProtobufIncludeDir}"; CommandArguments += $" -I {ProtoModuleDir}"; CommandArguments += $" {OutputType.GetArgs(OutputDir)}"; foreach (FileItem ProtoFile in ProtoFiles) { CommandArguments += $" {ProtoFile.AbsolutePath}"; string ProducedName = Path.GetRelativePath(ProtoModuleDir.ToString(), ProtoFile.FullName); ProducedItems.Add(ProtoExecution.GetProducedFileItem(OutputDir, ProducedName, OutputType, ".h")); ProducedItems.Add(ProtoExecution.GetProducedFileItem(OutputDir, ProducedName, OutputType, ".cc")); } } public ProtocAction(BinaryArchiveReader Reader) : base(ActionType.Compile) { PrerequisiteItems = Reader.ReadSortedSet(Reader.ReadFileItem)!; ProducedItems = Reader.ReadSortedSet(Reader.ReadFileItem)!; CommandPath = Reader.ReadFileReference()!; CommandArguments = Reader.ReadString()!; } /// public new void Write(BinaryArchiveWriter Writer) { Writer.WriteSortedSet(PrerequisiteItems, Writer.WriteFileItem); Writer.WriteSortedSet(ProducedItems, Writer.WriteFileItem); Writer.WriteFileReference(CommandPath); Writer.WriteString(CommandArguments); } } /// /// Serializer for instances /// class ProtocActionSerializer : ActionSerializerBase { /// public override ProtocAction Read(BinaryArchiveReader reader) { return new(reader); } /// public override void Write(BinaryArchiveWriter writer, ProtocAction action) { action.Write(writer); } } enum ProtocOutputType { Cpp, GrpcCpp, } static class ProtocOutputExtension { public static string GetArgs(this ProtocOutputType Output, DirectoryReference OutputLocation) { switch (Output) { case ProtocOutputType.Cpp: return $"--cpp_out={OutputLocation}"; case ProtocOutputType.GrpcCpp: return $"--plugin=\"protoc-gen-grpc={ProtoExecution.GetGrpcCppPluginPath()}\" --grpc_out={OutputLocation}"; } return ""; } public static string GetExtensionPrefix(this ProtocOutputType Output) { switch (Output) { case ProtocOutputType.Cpp: return "pb"; case ProtocOutputType.GrpcCpp: return "grpc.pb"; } return ""; } } class ProtoExecution { public static readonly string ProtobufVersion = "30.0"; public static readonly string GrpcVersion = "1.72.1"; public static ProtocOutput CompileProtoFiles(IEnumerable ProtoFiles, DirectoryReference ProtoModuleDir, DirectoryReference OutputDir, IActionGraphBuilder Graph) { ProtocOutput Result = new ProtocOutput(); List ProtoCppFiles = new List(); List ProtoGrpcFiles = new List(); foreach (FileItem ProtoFile in ProtoFiles) { ProtoCppFiles.Add(ProtoFile); string ProducedName = Path.GetRelativePath(ProtoModuleDir.ToString(), ProtoFile.FullName); Result.HeaderFiles.Add(GetProducedFileItem(OutputDir, ProducedName, ProtocOutputType.Cpp, ".h")); Result.CppFiles.Add(GetProducedFileItem(OutputDir, ProducedName, ProtocOutputType.Cpp, ".cc")); if (FileReference.ReadAllText(ProtoFile.Location).Contains("service")) { ProtoGrpcFiles.Add(ProtoFile); Result.HeaderFiles.Add(GetProducedFileItem(OutputDir, ProducedName, ProtocOutputType.GrpcCpp, ".h")); Result.CppFiles.Add(GetProducedFileItem(OutputDir, ProducedName, ProtocOutputType.GrpcCpp, ".cc")); } } if (ProtoCppFiles.Count > 0) { Graph.AddAction(new ProtocAction(ProtoCppFiles, ProtoModuleDir, OutputDir, ProtocOutputType.Cpp)); } if (ProtoGrpcFiles.Count > 0) { Graph.AddAction(new ProtocAction(ProtoGrpcFiles, ProtoModuleDir, OutputDir, ProtocOutputType.GrpcCpp)); } return Result; } public static DirectoryReference GetProtobufDirectory() { return DirectoryReference.Combine(Unreal.EngineDirectory, "Source", "ThirdParty", "Protobuf", ProtobufVersion); } public static DirectoryReference GetGrpcDirectory() { return DirectoryReference.Combine(Unreal.EngineDirectory, "Source", "ThirdParty", "gRPC", GrpcVersion); } public static FileReference GetProtocPath() { string ExecutableName = "protoc" + BuildHostPlatform.Current.BinarySuffix; if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64) { return FileReference.Combine(GetProtobufDirectory(), "bin", "Win64", UnrealArch.Host.Value.WindowsName, "Release", ExecutableName); } else if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { return FileReference.Combine(GetProtobufDirectory(), "bin", "Mac", "Release", ExecutableName); } else if (BuildHostPlatform.Current.Platform.IsInGroup(UnrealPlatformGroup.Unix)) { return FileReference.Combine(GetProtobufDirectory(), "bin", "Unix", UnrealArch.Host.Value.LinuxName, "Release", ExecutableName); } throw new BuildException($"Unsupported platform {BuildHostPlatform.Current.Platform} for Protobuf compilation"); } public static FileReference GetGrpcCppPluginPath() { string ExecutableName = "grpc_cpp_plugin" + BuildHostPlatform.Current.BinarySuffix; if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Win64) { return FileReference.Combine(GetGrpcDirectory(), "bin", "Win64", UnrealArch.Host.Value.WindowsName, "Release", ExecutableName); } else if (BuildHostPlatform.Current.Platform == UnrealTargetPlatform.Mac) { return FileReference.Combine(GetGrpcDirectory(), "bin", "Mac", "Release", ExecutableName); } else if (BuildHostPlatform.Current.Platform.IsInGroup(UnrealPlatformGroup.Unix)) { return FileReference.Combine(GetGrpcDirectory(), "bin", "Unix", UnrealArch.Host.Value.LinuxName, "Release", ExecutableName); } throw new BuildException($"Unsupported platform {BuildHostPlatform.Current.Platform} for Protobuf compilation"); } internal static FileItem GetProducedFileItem(DirectoryReference OutputDir, string BaseName, ProtocOutputType OutputType, string extension) { return FileItem.GetItemByFileReference(FileReference.Combine(OutputDir, Path.ChangeExtension(BaseName, OutputType.GetExtensionPrefix() + extension))); } } }