From a80fefde9becd9d3db3e376acb56cd9d046d0329 Mon Sep 17 00:00:00 2001 From: Marco Zille Date: Mon, 16 Jan 2017 22:58:47 +0100 Subject: [PATCH 1/3] Cleared command line options to make them more standard --- src/CLI/CLI.cs | 111 +++++++++++++++++++++++++------------------ src/CLI/Generator.cs | 16 +++---- src/CLI/Options.cs | 7 +-- 3 files changed, 75 insertions(+), 59 deletions(-) diff --git a/src/CLI/CLI.cs b/src/CLI/CLI.cs index abea9dee..70e53e68 100644 --- a/src/CLI/CLI.cs +++ b/src/CLI/CLI.cs @@ -10,63 +10,82 @@ 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) + static bool ParseCommandLineArgs(string[] args) { - var showHelp = args.Length == 0; + var showHelp = false; 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); } }, + { "I=", "the {PATH} of a folder to search for include files", i => { AddIncludeDirs(i); } }, + { "l=", "{LIBRARY} that includes the definitions for the generated source code", l => _options.Libraries.Add(l) }, + { "L=", "the {PATH} of a folder to search for additional libraries", l => _options.LibraryDirs.Add(l) }, + { "D:", "additional define with (optional) value to add to be used while parsing the given header files", (n, v) => AddDefine(n, v) }, + { "o=|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 }, + { "g=|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' or 'linux'", p => { GetDestinationPlatform(p); } }, + { "a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or '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); } } + { "h|help", "shows the help", hl => { showHelp = (hl != null); } }, }; - + + List additionalArguments = null; + try { - _assemblies = optionSet.Parse(args); + additionalArguments = optionSet.Parse(args); } catch (Mono.Options.OptionException e) { Console.WriteLine(e.Message); - Environment.Exit(0); + return false; } - - if (showHelp) + + if (showHelp || additionalArguments != null && additionalArguments.Count == 0) { - // 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); + ShowHelp(optionSet); + return false; } - if (_assemblies == null) - { - Console.WriteLine("Invalid arguments."); - Environment.Exit(0); - } + foreach(String s in additionalArguments) + HandleAdditionalArgument(s); + + return true; + } + + static void ShowHelp(Mono.Options.OptionSet options) + { + Console.WriteLine("Usage: {0} [options]+", AppDomain.CurrentDomain.FriendlyName); + Console.WriteLine("Generates target language bindings to interop with unmanaged code."); + Console.WriteLine(); + Console.WriteLine("Options:"); + options.WriteOptionDescriptions(Console.Out); + Console.WriteLine(); + } + + static void AddDefine(String name, String value) + { + if (name == null) + throw new Mono.Options.OptionException("Invalid definition name for option -D.", "-D"); + _options.Defines.Add(name, value); + } + + static void HandleAdditionalArgument(String args) + { + if (System.IO.Directory.Exists(args)) + GetFilesFromPath(args); + else if (System.IO.File.Exists(args)) + _options.HeaderFiles.Add(args); } static void GetFilesFromPath(String path) @@ -115,10 +134,10 @@ namespace CppSharp { case "csharp": _options.Kind = CppSharp.Generators.GeneratorKind.CSharp; - break; + return; case "cli": _options.Kind = CppSharp.Generators.GeneratorKind.CLI; - break; + return; } throw new NotSupportedException("Unknown generator kind: " + generator); @@ -130,13 +149,13 @@ namespace CppSharp { case "win": _options.Platform = TargetPlatform.Windows; - break; + return; case "osx": _options.Platform = TargetPlatform.MacOS; - break; + return; case "linux": _options.Platform = TargetPlatform.Linux; - break; + return; } throw new NotSupportedException("Unknown target platform: " + platform); @@ -148,10 +167,10 @@ namespace CppSharp { case "x86": _options.Architecture = TargetArchitecture.x86; - break; + return; case "x64": - _options.Architecture = TargetArchitecture.x64; - break; + _options.Architecture = TargetArchitecture.x64; + return; } throw new NotSupportedException("Unknown target architecture: " + architecture); @@ -159,17 +178,17 @@ namespace CppSharp static void Main(string[] args) { - ParseCommandLineArgs(args); - - Generator gen = new Generator(_options); - try { + if (ParseCommandLineArgs(args) == false) + return; + + Generator gen = new Generator(_options); gen.Run(); } catch (Exception ex) { - Console.WriteLine("Error: " + ex.ToString()); + Console.WriteLine(ex.Message); } } } diff --git a/src/CLI/Generator.cs b/src/CLI/Generator.cs index c0e53d20..c564b6f1 100644 --- a/src/CLI/Generator.cs +++ b/src/CLI/Generator.cs @@ -98,20 +98,20 @@ namespace CppSharp 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); + foreach (KeyValuePair d in _options.Defines) + { + if(d.Value == null || d.Value == String.Empty) + parserOptions.AddDefines(d.Key); + else + parserOptions.AddDefines(d.Key + "=" + d.Value); + } options.OutputDir = _options.OutputDir; options.OutputNamespace = _options.OutputNamespace; diff --git a/src/CLI/Options.cs b/src/CLI/Options.cs index bb4cf681..e6134b46 100644 --- a/src/CLI/Options.cs +++ b/src/CLI/Options.cs @@ -35,8 +35,8 @@ namespace CppSharp 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 Dictionary _defines = new Dictionary(); + public Dictionary Defines { get { return _defines; } set { _defines = value; } } private String _outputDir = ""; public String OutputDir { get { return _outputDir; } set { _outputDir = value; } } @@ -62,9 +62,6 @@ namespace CppSharp 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; } } From c9eae5c8638f267bf5f5d8797474789fd7d8127b Mon Sep 17 00:00:00 2001 From: Marco Zille Date: Tue, 17 Jan 2017 00:27:14 +0100 Subject: [PATCH 2/3] Improved CLI help and parameters --- src/CLI/CLI.cs | 41 +++++++++++++++++++++++++++++++++++++---- src/CLI/Generator.cs | 16 +++++++++++----- src/CLI/Options.cs | 6 +++--- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/src/CLI/CLI.cs b/src/CLI/CLI.cs index 70e53e68..fc29e75a 100644 --- a/src/CLI/CLI.cs +++ b/src/CLI/CLI.cs @@ -23,19 +23,23 @@ namespace CppSharp var optionSet = new Mono.Options.OptionSet() { { "I=", "the {PATH} of a folder to search for include files", i => { AddIncludeDirs(i); } }, - { "l=", "{LIBRARY} that includes the definitions for the generated source code", l => _options.Libraries.Add(l) }, + { "l=", "{LIBRARY} that that contains the symbols of the generated code", l => _options.Libraries.Add(l) }, { "L=", "the {PATH} of a folder to search for additional libraries", l => _options.LibraryDirs.Add(l) }, { "D:", "additional define with (optional) value to add to be used while parsing the given header files", (n, v) => AddDefine(n, v) }, - { "o=|outputdir=", "the {PATH} for the destination folder that will contain the generated code", od => _options.OutputDir = od }, + + { "o=|output=", "the {PATH} for the generated bindings file (doesn't need the extension since it will depend on the generator)", v => HandleOutputArg(v) }, { "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 }, + + { "iln=|inputlibraryname=", "the {NAME} of the shared library that contains the symbols of the generated code", iln => _options.InputLibraryName = iln }, + { "g=|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' or 'linux'", p => { GetDestinationPlatform(p); } }, { "a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or '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); } }, + { "h|help", "shows the help", hl => { showHelp = (hl != null); } }, }; @@ -71,6 +75,35 @@ namespace CppSharp Console.WriteLine("Options:"); options.WriteOptionDescriptions(Console.Out); Console.WriteLine(); + Console.WriteLine(); + Console.WriteLine("Useful informations:"); + Console.WriteLine(" - the options 'iln' (same as 'inputlibraryname') and 'l' have a similar meaning. Both of them are used to tell"); + Console.WriteLine(" the generator which library has to be used to P/Invoke the functions from your native code."); + Console.WriteLine(" The difference is that if you want to generate the bindings for more than one library within a single managed"); + Console.WriteLine(" file you need to use the 'l' option to specify the names of all the libraries that contain the symbols to be loaded"); + Console.WriteLine(" and you MUST set the 'cs' ('checksymbols') flag to let the generator automatically understand which library"); + Console.WriteLine(" to use to P/Invoke. This can be also used if you plan to generate the bindings for only one library."); + Console.WriteLine(" If you specify the 'iln' (or 'inputlibraryname') options, this option's value will be used for all the P/Invokes"); + Console.WriteLine(" that the generator will create."); + Console.WriteLine(" - If you specify the 'unitybuild' option then the generator will output a file for each given header file that will"); + Console.WriteLine(" contain only the bindings for that header file."); + } + + static void HandleOutputArg(String arg) + { + try + { + String dir = System.IO.Path.GetDirectoryName(arg); + String file = System.IO.Path.GetFileNameWithoutExtension(arg); + + _options.OutputDir = dir; + _options.OutputFileName = file; + } + catch(Exception e) + { + Console.WriteLine("Output error: " + e.Message); + Environment.Exit(0); + } } static void AddDefine(String name, String value) diff --git a/src/CLI/Generator.cs b/src/CLI/Generator.cs index c564b6f1..eaac046c 100644 --- a/src/CLI/Generator.cs +++ b/src/CLI/Generator.cs @@ -46,8 +46,14 @@ namespace CppSharp 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.OutputFileName == String.Empty) + throw new NotSupportedException("Output not specified"); + + if(_options.InputLibraryName == String.Empty && _options.CheckSymbols == false) + throw new NotSupportedException("Input library name not specified and check symbols not enabled. Either set the input library name or the check symbols flag"); + + if(_options.InputLibraryName == String.Empty && _options.CheckSymbols == true && _options.Libraries.Count == 0) + throw new NotSupportedException("Input library name not specified and check symbols is enabled but no libraries were given. Either set the input library name or add at least one library"); if (_options.Architecture == TargetArchitecture.x64) _triple = "x86_64-"; @@ -81,10 +87,10 @@ namespace CppSharp parserOptions.Abi = _abi; var options = driver.Options; - options.LibraryName = _options.InputLibraryName; + options.LibraryName = _options.OutputFileName; - if(_options.InputSharedLibraryName != String.Empty) - options.SharedLibraryName = _options.InputSharedLibraryName; + if(_options.InputLibraryName != String.Empty) + options.SharedLibraryName = _options.InputLibraryName; options.GeneratorKind = _options.Kind; options.Headers.AddRange(_options.HeaderFiles); diff --git a/src/CLI/Options.cs b/src/CLI/Options.cs index e6134b46..a655385c 100644 --- a/src/CLI/Options.cs +++ b/src/CLI/Options.cs @@ -44,12 +44,12 @@ namespace CppSharp private String _outputNamespace = ""; public String OutputNamespace { get { return _outputNamespace; } set { _outputNamespace = value; } } + private String _outputFileName = ""; + public String OutputFileName { get { return _outputFileName; } set { _outputFileName = 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; } } From 06491bb4548010e4bb74157f43f308be9b550325 Mon Sep 17 00:00:00 2001 From: Marco Zille Date: Tue, 17 Jan 2017 00:39:15 +0100 Subject: [PATCH 3/3] Changed CppSharp.CLI project from library to console application --- src/CLI/premake5.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CLI/premake5.lua b/src/CLI/premake5.lua index 34951bca..f9a9bce7 100644 --- a/src/CLI/premake5.lua +++ b/src/CLI/premake5.lua @@ -2,7 +2,7 @@ project "CppSharp.CLI" SetupManagedProject() - kind "SharedLib" + kind "ConsoleApp" language "C#" files { "**.cs" }