diff --git a/src/CLI/CLI.cs b/src/CLI/CLI.cs new file mode 100644 index 00000000..abea9dee --- /dev/null +++ b/src/CLI/CLI.cs @@ -0,0 +1,176 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Policy; +using System.Text; +using System.Threading.Tasks; + +namespace CppSharp +{ + class CLI + { + private static Options _options = new Options(); + private static List _assemblies; + + static void AddIncludeDirs(String dir) + { + _options.IncludeDirs.Add(dir); + } + + static void ParseCommandLineArgs(string[] args) + { + var showHelp = args.Length == 0; + + var optionSet = new Mono.Options.OptionSet() + { + { "h|header=", "the path to an header file to generate source from", h => _options.HeaderFiles.Add(h) }, + { "pa|path=", "the path of a folder whose files will generate code (can append a filter at the end like '/*.hpp'", pa => { GetFilesFromPath(pa); } }, + { "inc|includedir=", "the path of a folder to search for include files", i => { AddIncludeDirs(i); } }, + { "l|library=", "the path of a library that includes the definitions for the generated source code", l => _options.Libraries.Add(l) }, + { "ld|librarydir=", "the path of a folder to search for additional libraries", l => _options.LibraryDirs.Add(l) }, + { "d|define=", "a define to add for the parse of the given header files", d => _options.Defines.Add(d) }, + { "od|outputdir=", "the path for the destination folder that will contain the generated code", od => _options.OutputDir = od }, + { "on|outputnamespace=", "the namespace that will be used for the generated code", on => _options.OutputNamespace = on }, + { "iln|inputlibraryname=", "the name of the shared library that contains the actual definitions (without extension)", iln => _options.InputLibraryName = iln }, + { "isln|inputsharedlibraryname=", "the full name of the shared library that contains the actual definitions (with extension)", isln => _options.InputSharedLibraryName = isln }, + { "gen|generator=", "the type of generated code: 'chsarp' or 'cli' ('cli' supported only for Windows)", g => { GetGeneratorKind(g); } }, + { "p|platform=", "the platform that the generated code will target: 'win', 'osx', 'linux'", p => { GetDestinationPlatform(p); } }, + { "a|arch=", "the architecture that the generated code will target: 'x86', 'x64'", a => { GetDestinationArchitecture(a); } }, + { "c++11", "enables GCC C++ 11 compilation (valid only for Linux platform)", cpp11 => { _options.Cpp11ABI = (cpp11 != null); } }, + { "cs|checksymbols", "enable the symbol check for the generated code", cs => { _options.CheckSymbols = (cs != null); } }, + { "ub|unitybuild", "enable unity build", ub => { _options.UnityBuild = (ub != null); } }, + { "help", "shows the help", hl => { showHelp = (hl != null); } } + }; + + try + { + _assemblies = optionSet.Parse(args); + } + catch (Mono.Options.OptionException e) + { + Console.WriteLine(e.Message); + Environment.Exit(0); + } + + if (showHelp) + { + // Print usage and exit. + Console.WriteLine("{0} [options]+", AppDomain.CurrentDomain.FriendlyName); + Console.WriteLine("Generates target language bindings for interop with unmanaged code."); + Console.WriteLine(); + optionSet.WriteOptionDescriptions(Console.Out); + Environment.Exit(0); + } + + if (_assemblies == null) + { + Console.WriteLine("Invalid arguments."); + Environment.Exit(0); + } + } + + static void GetFilesFromPath(String path) + { + path = path.Replace(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar); + + String searchPattern = String.Empty; + int lastSeparatorPosition = path.LastIndexOf(System.IO.Path.AltDirectorySeparatorChar); + + if (lastSeparatorPosition >= 0) + { + if (path.IndexOf('*', lastSeparatorPosition) >= lastSeparatorPosition || path.IndexOf('?', lastSeparatorPosition) >= lastSeparatorPosition) + { + searchPattern = path.Substring(lastSeparatorPosition + 1); + path = path.Substring(0, lastSeparatorPosition); + } + } + + try + { + if (searchPattern != String.Empty) + { + String[] files = System.IO.Directory.GetFiles(path, searchPattern); + + foreach (String s in files) + _options.HeaderFiles.Add(s); + } + else + { + String[] files = System.IO.Directory.GetFiles(path); + + foreach (String s in files) + _options.HeaderFiles.Add(s); + } + } + catch (Exception ex) + { + Console.WriteLine("Source files path error: " + ex.Message); + Environment.Exit(0); + } + } + + static void GetGeneratorKind(String generator) + { + switch (generator.ToLower()) + { + case "csharp": + _options.Kind = CppSharp.Generators.GeneratorKind.CSharp; + break; + case "cli": + _options.Kind = CppSharp.Generators.GeneratorKind.CLI; + break; + } + + throw new NotSupportedException("Unknown generator kind: " + generator); + } + + static void GetDestinationPlatform(String platform) + { + switch (platform.ToLower()) + { + case "win": + _options.Platform = TargetPlatform.Windows; + break; + case "osx": + _options.Platform = TargetPlatform.MacOS; + break; + case "linux": + _options.Platform = TargetPlatform.Linux; + break; + } + + throw new NotSupportedException("Unknown target platform: " + platform); + } + + static void GetDestinationArchitecture(String architecture) + { + switch (architecture.ToLower()) + { + case "x86": + _options.Architecture = TargetArchitecture.x86; + break; + case "x64": + _options.Architecture = TargetArchitecture.x64; + break; + } + + throw new NotSupportedException("Unknown target architecture: " + architecture); + } + + static void Main(string[] args) + { + ParseCommandLineArgs(args); + + Generator gen = new Generator(_options); + + try + { + gen.Run(); + } + catch (Exception ex) + { + Console.WriteLine("Error: " + ex.ToString()); + } + } + } +} \ No newline at end of file diff --git a/src/CLI/Generator.cs b/src/CLI/Generator.cs new file mode 100644 index 00000000..4c1e4c32 --- /dev/null +++ b/src/CLI/Generator.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CppSharp.AST; +using CppSharp.Generators; +using CppSharp.Passes; +using CppSharp.Types; +using CppAbi = CppSharp.Parser.AST.CppAbi; +using CppSharp.Parser; +using CppSharp; + +namespace CppSharp +{ + class Generator : ILibrary + { + private Options _options; + + private String _triple = ""; + private CppAbi _abi = CppAbi.Microsoft; + + public Generator(Options options) + { + if (options == null) + throw new ArgumentNullException("options"); + + _options = options; + } + + public void ValidateOptions() + { + if (Platform.IsWindows && _options.Platform != TargetPlatform.Windows) + throw new NotSupportedException("Cannot create bindings for a platform other that Windows from a Windows running machine"); + else if (Platform.IsMacOS && _options.Platform != TargetPlatform.MacOS) + throw new NotSupportedException("Cannot create bindings for a platform other that MacOS from a MacOS running machine"); + else if (Platform.IsUnixPlatform && _options.Platform != TargetPlatform.Linux) + throw new NotSupportedException("Cannot create bindings for a platform other that Linux from a Linux running machine"); + + if(_options.Platform != TargetPlatform.Windows && _options.Kind != GeneratorKind.CSharp) + throw new NotSupportedException("Cannot create bindings for languages other than C# from a non Windows machine"); + + if(_options.Platform == TargetPlatform.Linux && _options.Architecture != TargetArchitecture.x64) + throw new NotSupportedException("Cannot create bindings for architectures other than x64 for Linux machines"); + + if(_options.HeaderFiles.Count == 0) + throw new NotSupportedException("No source header file has been given"); + + if (_options.OutputNamespace == String.Empty) + throw new NotSupportedException("Output namespace is empty"); + + if (_options.InputLibraryName == String.Empty) + throw new NotSupportedException("Input library name is empty"); + + if (_options.Architecture == TargetArchitecture.x64) + _triple = "x86_64-"; + else if(_options.Architecture == TargetArchitecture.x86) + _triple = "i686-"; + + if (_options.Platform == TargetPlatform.Windows) + { + _triple += "pc-win32-msvc"; + _abi = CppAbi.Microsoft; + } + else if (_options.Platform == TargetPlatform.MacOS) + { + _triple += "apple-darwin12.4.0"; + _abi = CppAbi.Itanium; + } + else if (_options.Platform == TargetPlatform.Linux) + { + _triple += "linux-gnu"; + _abi = CppAbi.Itanium; + + if(_options.Cpp11ABI) + _triple += "-cxx11abi"; + } + } + + public void Setup(Driver driver) + { + var parserOptions = driver.ParserOptions; + parserOptions.TargetTriple = _triple; + parserOptions.Abi = _abi; + + var options = driver.Options; + options.LibraryName = _options.InputLibraryName; + + if(_options.InputSharedLibraryName != String.Empty) + options.SharedLibraryName = _options.InputSharedLibraryName; + + options.GeneratorKind = _options.Kind; + options.Headers.AddRange(_options.HeaderFiles); + options.Libraries.AddRange(_options.Libraries); + + if (_abi == CppAbi.Microsoft) + parserOptions.MicrosoftMode = true; + + if (_triple.Contains("apple")) + SetupMacOptions(parserOptions); + + if (_triple.Contains("linux")) + SetupLinuxOptions(parserOptions); + + Console.WriteLine("\n\nAdding " + (_options.IncludeDirs.Count) + " include dirs\n\n"); + + foreach (String s in _options.IncludeDirs) + { + parserOptions.AddIncludeDirs(s); + Console.WriteLine("Add include: " + s); + } + + foreach (String s in _options.LibraryDirs) + parserOptions.AddLibraryDirs(s); + + foreach (String s in _options.Defines) + parserOptions.AddDefines(s); + + options.OutputDir = _options.OutputDir; + options.OutputNamespace = _options.OutputNamespace; + options.CheckSymbols = _options.CheckSymbols; + options.UnityBuild = _options.UnityBuild; + } + + private void SetupLinuxOptions(ParserOptions options) + { + options.MicrosoftMode = false; + options.NoBuiltinIncludes = true; + + var headersPath = string.Empty; + + // Search for the available GCC versions on the provided headers. + var versions = Directory.EnumerateDirectories(Path.Combine(headersPath, "usr/include/c++")); + + if (versions.Count() == 0) + throw new Exception("No valid GCC version found on system include paths"); + + string gccVersionPath = versions.First(); + string gccVersion = gccVersionPath.Substring(gccVersionPath.LastIndexOf(Path.DirectorySeparatorChar) + 1); + + string[] systemIncludeDirs = { + Path.Combine("usr", "include", "c++", gccVersion), + Path.Combine("usr", "include", "x86_64-linux-gnu", "c++", gccVersion), + Path.Combine("usr", "include", "c++", gccVersion, "backward"), + Path.Combine("usr", "lib", "gcc", "x86_64-linux-gnu", gccVersion, "include"), + Path.Combine("usr", "include", "x86_64-linux-gnu"), + Path.Combine("usr", "include") + }; + + foreach (var dir in systemIncludeDirs) + options.AddSystemIncludeDirs(Path.Combine(headersPath, dir)); + + options.AddDefines("_GLIBCXX_USE_CXX11_ABI=" + (_options.Cpp11ABI ? "1" : "0")); + } + + private static void SetupMacOptions(ParserOptions options) + { + options.MicrosoftMode = false; + options.NoBuiltinIncludes = true; + + if (Platform.IsMacOS) + { + var headersPaths = new List { + // Path.Combine(GetSourceDirectory("deps"), "llvm/tools/clang/lib/Headers"), + // Path.Combine(GetSourceDirectory("deps"), "libcxx", "include"), + "/usr/include" + }; + + foreach (var header in headersPaths) + Console.WriteLine(header); + + foreach (var header in headersPaths) + options.AddSystemIncludeDirs(header); + } + + // var headersPath = Path.Combine(GetSourceDirectory("build"), "headers", "osx"); + + // options.AddSystemIncludeDirs(Path.Combine(headersPath, "include")); + // options.AddSystemIncludeDirs(Path.Combine(headersPath, "clang", "4.2", "include")); + // options.AddSystemIncludeDirs(Path.Combine(headersPath, "libcxx", "include")); + options.AddArguments("-stdlib=libc++"); + } + + public void SetupPasses(Driver driver) + { + driver.AddTranslationUnitPass(new CheckMacroPass()); + + driver.Context.TranslationUnitPasses.RenameDeclsUpperCase(RenameTargets.Any); + driver.Context.TranslationUnitPasses.AddPass(new FunctionToInstanceMethodPass()); + driver.Context.TranslationUnitPasses.AddPass(new MarshalPrimitivePointersAsRefTypePass()); + } + + public void Preprocess(Driver driver, ASTContext ctx) + { + } + + public void Postprocess(Driver driver, ASTContext ctx) + { + new CaseRenamePass( + RenameTargets.Function | RenameTargets.Method | RenameTargets.Property | RenameTargets.Delegate | + RenameTargets.Field | RenameTargets.Variable, + RenameCasePattern.UpperCamelCase).VisitASTContext(driver.Context.ASTContext); + } + + public void Run() + { + ValidateOptions(); + + String message = "Generating the "; + + switch(_options.Kind) + { + case GeneratorKind.CLI: + message += "C++/CLI"; + break; + case GeneratorKind.CSharp: + message += "C#"; + break; + } + + message += " parser bindings for "; + + switch (_options.Platform) + { + case TargetPlatform.Linux: + message += "Linux"; + break; + case TargetPlatform.MacOS: + message += "OSX"; + break; + case TargetPlatform.Windows: + message += "Windows"; + break; + } + + message += " "; + + switch (_options.Architecture) + { + case TargetArchitecture.x86: + message += "x86"; + break; + case TargetArchitecture.x64: + message += "x64"; + break; + } + + if(_options.Cpp11ABI) + message += " (GCC C++11 ABI)"; + + message += "..."; + + Console.WriteLine(message); + + ConsoleDriver.Run(this); + + Console.WriteLine(); + } + } +} diff --git a/src/CLI/Options.cs b/src/CLI/Options.cs new file mode 100644 index 00000000..bb4cf681 --- /dev/null +++ b/src/CLI/Options.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using CppSharp.Generators; + +namespace CppSharp +{ + enum TargetPlatform + { + Windows, + MacOS, + Linux + } + + enum TargetArchitecture + { + x86, + x64 + } + + class Options + { + private List _headerFiles = new List(); + public List HeaderFiles { get { return _headerFiles; } set { _headerFiles = value; } } + + private List _includeDirs = new List(); + public List IncludeDirs { get { return _includeDirs; } set { _includeDirs = value; } } + + private List _libraryDirs = new List(); + public List LibraryDirs { get { return _libraryDirs; } set { _libraryDirs = value; } } + + private List _libraries = new List(); + public List Libraries { get { return _libraries; } set { _libraries = value; } } + + private List _defines = new List(); + public List Defines { get { return _defines; } set { _defines = value; } } + + private String _outputDir = ""; + public String OutputDir { get { return _outputDir; } set { _outputDir = value; } } + + private String _outputNamespace = ""; + public String OutputNamespace { get { return _outputNamespace; } set { _outputNamespace = value; } } + + private String _inputLibraryName = ""; + public String InputLibraryName { get { return _inputLibraryName; } set { _inputLibraryName = value; } } + + private String _inputSharedLibraryName = ""; + public String InputSharedLibraryName { get { return _inputSharedLibraryName; } set { _inputSharedLibraryName = value; } } + + private String _triple = ""; + public String Triple { get { return _triple; } set { _triple = value; } } + + private TargetPlatform _platform = TargetPlatform.Windows; + public TargetPlatform Platform { get { return _platform; } set { _platform = value; } } + + private TargetArchitecture _architecture = TargetArchitecture.x86; + public TargetArchitecture Architecture { get { return _architecture; } set { _architecture = value; } } + + private GeneratorKind _kind = GeneratorKind.CSharp; + public GeneratorKind Kind { get { return _kind; } set { _kind = value; } } + + //private bool _verbose = false; + //public bool Verbose { get { return _verbose; } set { _verbose = value; } } + + private bool _checkSymbols = false; + public bool CheckSymbols { get { return _checkSymbols; } set { _checkSymbols = value; } } + + private bool _unityBuild = false; + public bool UnityBuild { get { return _unityBuild; } set { _unityBuild = value; } } + + private bool _cpp11ABI = false; + public bool Cpp11ABI { get { return _cpp11ABI; } set { _cpp11ABI = value; } } + } +} \ No newline at end of file