aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.Protobuf/ProtoCompiler.cs
blob: 2e51de6df680f919e8b96e89ae71cb78690553f8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Tango.MachineEM.UI.Properties {
    
    
    [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
    [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
    internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
        
        private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
        
        public static Settings Default {
            get {
                return defaultInstance;
            }
        }
    }
}
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Tango.Core.Helpers;
using Tango.Core.IO;
using Tango.Logging;

namespace Tango.Protobuf
{
    /// <summary>
    /// Represents a protobuf compiler base class.
    /// </summary>
    /// <seealso cref="Tango.Protobuf.IProtoCompiler" />
    public abstract class ProtoCompiler : IProtoCompiler
    {
        private const String COMPILERS_FOLDER_NAME = "ProtoCompilers"; //Compilers folder name.
        protected String _compilersPath; //Compilers folder path.
        private LogManager logManager = LogManager.Default;
        private int _currentProgress;
        private int _totalProgress;

        /// <summary>
        /// Occurs when the compiler has made some progress.
        /// </summary>
        public event EventHandler<CompilerProgressEventArgs> CompilationProgress;

        /// <summary>
        /// Gets the compiler language.
        /// </summary>
        public abstract CompilerLanguage Language { get; }

        /// <summary>
        /// Gets the proto imports folders.
        /// </summary>
        public List<String> ImportsFolders { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this compiler uses the default folder structure when generating code.
        /// </summary>
        public virtual bool UsesDefaultStructure { get; }

        /// <summary>
        /// Initializes a new instance of the <see cref="ProtoCompiler"/> class.
        /// </summary>
        public ProtoCompiler()
        {
            ImportsFolders = new List<string>();
            _compilersPath = Path.Combine(AssemblyHelper.GetCurrentAssemblyFolder(), COMPILERS_FOLDER_NAME);
        }

        /// <summary>
        /// Compiles the specified .proto message file.
        /// </summary>
        /// <param name="inputFile">.proto file to compile</param>
        /// <returns>
        /// A list of compiled results.
        /// </returns>
        public virtual IEnumerable<CompilerFileResult> CompileFile(string inputFile)
        {
            logManager.Log("Compiling file " + inputFile);

            CompilationProgress?.Invoke(this, new CompilerProgressEventArgs()
            {
                Current = _currentProgress++,
                Total = _totalProgress,
                File = inputFile,
            });

            var tmpPath = TemporaryManager.Default.CreateFolder();

            logManager.Log("Temp path: " + tmpPath);

            String importsString = "--proto_path \"" + Path.GetDirectoryName(inputFile) + "\" ";

            logManager.Log("Added import string: " + importsString);

            foreach (var path in ImportsFolders)
            {
                String importStr = "--proto_path \"" + path + "\" ";
                importsString += importStr;
                logManager.Log("Added import string: " + importStr);
            }

            Process p = new Process();

            logManager.Log("Compilers folder path: " + _compilersPath);

            p.StartInfo.WorkingDirectory = _compilersPath;
            String oldCurrentDirectory = Environment.CurrentDirectory;
            Environment.CurrentDirectory = _compilersPath;
            p.StartInfo.FileName = GetProtoCompilerName();

            logManager.Log("Protobuf executable path: " + p.StartInfo.FileName);

            p.StartInfo.Arguments = String.Format(
                "{0} {1}=\"{2}\" \"{3}\"",
                importsString,
                GetProtoArguments(),
                tmpPath,
                inputFile);

            logManager.Log("Final arguments:\n" + p.StartInfo.Arguments);

            p.StartInfo.CreateNoWindow = true;
            p.StartInfo.UseShellExecute = false;
            p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardOutput = true;


            logManager.Log("Executing compilation...");

            p.Start();
            p.WaitForExit(5000);

            Environment.CurrentDirectory = oldCurrentDirectory;

            String error = p.StandardError.ReadToEnd();

            if (!String.IsNullOrWhiteSpace(error))
            {
                var lines = error.Split(new[] { '\r', '\n' });
                throw logManager.Log(new CompilerException() { Issues = lines.Where(x => x.Length > 0).ToList() });
            }

            List<CompilerFileResult> results = new List<CompilerFileResult>();

            foreach (var file in Directory.GetFiles(tmpPath, "*.*", SearchOption.AllDirectories))
            {
                CompilerFileResult result = new CompilerFileResult(Language, inputFile, Path.GetFileName(file), Path.GetDirectoryName(file).Replace(tmpPath, "").TrimStart('\\', '\\'), File.ReadAllText(file));
                results.Add(result);
            }


            if (tmpPath.Delete())
            {
                logManager.Log("Removed temp path: " + tmpPath);
            }
            else
            {
                logManager.Log("Could not remove temp path: " + tmpPath);
            }

            logManager.Log(Path.GetFileName(inputFile) + "compiled!");

            return results;
        }

        /// <summary>
        /// Compiles the specified .proto message file asynchronously.
        /// </summary>
        /// <param name="inputFile">.proto file to compile</param>
        /// <returns>
        /// A list of compiled results.
        /// </returns>
        public async Task<IEnumerable<CompilerFileResult>> CompileFileAsync(string inputFile)
        {
            return await new Task<IEnumerable<CompilerFileResult>>(() => { return CompileFile(inputFile); });
        }

        /// <summary>
        /// Compiles all files in the specified folder recursively.
        /// </summary>
        /// <param name="sourceFolder">The source folder</param>
        /// <returns>
        /// Compilation result.
        /// </returns>
        public virtual CompilerFolderResult CompileFolder(string sourceFolder, params String[] includeFolders)
        {
            _currentProgress = 0;
            _totalProgress = Directory.GetFiles(sourceFolder, "*.proto", SearchOption.AllDirectories).Length;

            if (!UsesDefaultStructure)
            {
                logManager.Log("Compiling folder: " + sourceFolder);

                ImportsFolders.Clear();
                ImportsFolders.AddRange(Directory.GetDirectories(sourceFolder, "*.*", SearchOption.AllDirectories));
                var result = CompileFolder(sourceFolder, sourceFolder);

                logManager.Log(Path.GetFileName(sourceFolder) + "compiled!");

                return OnPostProcessFolderCompilation(result);
            }
            else
            {
                return CompileFolderDefault(sourceFolder);
            }
        }

        /// <summary>
        /// Compiles all files in the specified folder recursively.
        /// </summary>
        /// <param name="sourceFolder">The source folder</param>
        /// <returns>
        /// Compilation result.
        /// </returns>
        protected virtual CompilerFolderResult CompileFolder(string rootFolder, string sourceFolder)
        {
            List<ICompilerResult> currentFolderResults = new List<ICompilerResult>();
            CompilerFolderResult currentFolder = new CompilerFolderResult(currentFolderResults, Language, sourceFolder, sourceFolder.Replace(rootFolder, ""));

            foreach (String file in Directory.GetFiles(sourceFolder, "*.proto"))
            {
                currentFolderResults.AddRange(CompileFile(file));
            }
            foreach (string folder in Directory.GetDirectories(sourceFolder))
            {
                if (Directory.GetFiles(folder, "*.proto").Length > 0)
                {
                    currentFolderResults.Add(CompileFolder(rootFolder, folder));
                }
            }

            return currentFolder;
        }

        /// <summary>
        /// Compiles all files in the specified folder recursively and asynchronously.
        /// </summary>
        /// <param name="sourceFolder">The source folder</param>
        /// <returns>
        /// Compilation result.
        /// </returns>
        public async Task<CompilerFolderResult> CompileFolderAsync(string sourceFolder)
        {
            return await new Task<CompilerFolderResult>(() => { return CompileFolder(sourceFolder); });
        }

        /// <summary>
        /// Compiles all files in the specified folder recursively using the default structure.
        /// </summary>
        /// <param name="sourceFolder">The source folder</param>
        /// <returns>
        /// Compilation result.
        /// </returns>
        private CompilerFolderResult CompileFolderDefault(string sourceFolder)
        {
            logManager.Log("Compiling folder: " + sourceFolder);

            Dictionary<string, CompilerFileResult> fileResults = new Dictionary<string, CompilerFileResult>();
            ImportsFolders.Clear();
            ImportsFolders.AddRange(Directory.GetDirectories(sourceFolder, "*.*", SearchOption.AllDirectories));

            var tempPath = TemporaryManager.Default.CreateFolder();

            foreach (var file in Directory.GetFiles(sourceFolder, "*.proto", SearchOption.AllDirectories))
            {
                foreach (var fileResult in CompileFile(file))
                {
                    String targetFolder = Path.Combine(tempPath, fileResult.RelativePath);
                    String targetFile = Path.Combine(targetFolder, fileResult.Name);
                    Directory.CreateDirectory(targetFolder);
                    File.WriteAllText(targetFile, fileResult.Content);

                    fileResults.Add(targetFile, fileResult);
                }
            }

            var result = CompileFolderDefault(tempPath, tempPath, fileResults);

            tempPath.Delete();

            logManager.Log(Path.GetFileName(sourceFolder) + "compiled!");

            return OnPostProcessFolderCompilation(result);
        }

        /// <summary>
        /// Compiles all files in the specified folder recursively using the default structure.
        /// </summary>
        /// <param name="rootFolder"></param>
        /// <param name="sourceFolder">The source folder</param>
        /// <returns>
        /// Compilation result.
        /// </returns>
        private CompilerFolderResult CompileFolderDefault(string rootFolder, string sourceFolder, Dictionary<string, CompilerFileResult> fileResults)
        {
            List<ICompilerResult> currentFolderResults = new List<ICompilerResult>();
            CompilerFolderResult currentFolder = new CompilerFolderResult(currentFolderResults, Language, sourceFolder, Path.GetFileName(sourceFolder));

            foreach (String file in Directory.GetFiles(sourceFolder, "*.*"))
            {
                currentFolderResults.Add(fileResults[file]);
            }
            foreach (string folder in Directory.GetDirectories(sourceFolder))
            {
                currentFolderResults.Add(CompileFolderDefault(rootFolder, folder, fileResults));
            }

            return currentFolder;
        }

        /// <summary>
        /// Override in order to manipulate the folder compilation result.
        /// </summary>
        /// <param name="result">The result.</param>
        /// <returns></returns>
        protected virtual CompilerFolderResult OnPostProcessFolderCompilation(CompilerFolderResult result)
        {
            return result;
        }

        /// <summary>
        /// Gets the protobuf compiler CLI arguments (without input/output files!).
        /// </summary>
        /// <returns></returns>
        protected abstract String GetProtoArguments();

        /// <summary>
        /// Gets the protobuf compiler CLI file name (override when using a compiler other than the default 'protoc.exe').
        /// </summary>
        /// <remarks>
        /// The compiler program must be located in the compilers folder.
        /// </remarks>
        /// <returns></returns>
        protected virtual String GetProtoCompilerName()
        {
            return "protoc.exe";
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            //TODO: Dispose...
        }
    }
}