Browse Source

Merge pull request #736 from zillemarco/master

Added more information when errors occur or invalid options are given
pull/734/head
Dimitar Dobrev 9 years ago committed by GitHub
parent
commit
d9a15e1bac
  1. 197
      src/CLI/CLI.cs
  2. 66
      src/CLI/Generator.cs

197
src/CLI/CLI.cs

@ -1,47 +1,38 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.IO;
using System.Security.Policy; using Mono.Options;
using System.Text;
using System.Threading.Tasks;
namespace CppSharp namespace CppSharp
{ {
class CLI class CLI
{ {
private static Options _options = new Options(); private static OptionSet optionSet = new OptionSet();
private static Options options = new Options();
static void AddIncludeDirs(String dir) static bool ParseCommandLineArgs(string[] args, List<String> messages, ref bool helpShown)
{
_options.IncludeDirs.Add(dir);
}
static bool ParseCommandLineArgs(string[] args)
{ {
var showHelp = false; var showHelp = false;
var optionSet = new Mono.Options.OptionSet() optionSet.Add("I=", "the {PATH} of a folder to search for include files", (i) => { AddIncludeDirs(i, messages); });
{ optionSet.Add("l=", "{LIBRARY} that that contains the symbols of the generated code", l => options.Libraries.Add(l) );
{ "I=", "the {PATH} of a folder to search for include files", i => { AddIncludeDirs(i); } }, optionSet.Add("L=", "the {PATH} of a folder to search for additional libraries", l => options.LibraryDirs.Add(l) );
{ "l=", "{LIBRARY} that that contains the symbols of the generated code", l => _options.Libraries.Add(l) }, optionSet.Add("D:", "additional define with (optional) value to add to be used while parsing the given header files", (n, v) => AddDefine(n, v, messages) );
{ "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=|output=", "the {PATH} for the generated bindings file (doesn't need the extension since it will depend on the generator)", v => HandleOutputArg(v) }, optionSet.Add("o=|output=", "the {PATH} for the generated bindings file (doesn't need the extension since it will depend on the generator)", v => HandleOutputArg(v, messages) );
{ "on=|outputnamespace=", "the {NAMESPACE} that will be used for the generated code", on => _options.OutputNamespace = on }, optionSet.Add("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 symbols of the generated code", iln => _options.InputLibraryName = iln }, optionSet.Add("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); } }, optionSet.Add("g=|gen=|generator=", "the {TYPE} of generated code: 'chsarp' or 'cli' ('cli' supported only for Windows)", g => { GetGeneratorKind(g, messages); } );
{ "p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux'", p => { GetDestinationPlatform(p); } }, optionSet.Add("p=|platform=", "the {PLATFORM} that the generated code will target: 'win', 'osx' or 'linux'", p => { GetDestinationPlatform(p, messages); } );
{ "a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64'", a => { GetDestinationArchitecture(a); } }, optionSet.Add("a=|arch=", "the {ARCHITECTURE} that the generated code will target: 'x86' or 'x64'", a => { GetDestinationArchitecture(a, messages); } );
{ "c++11", "enables GCC C++ 11 compilation (valid only for Linux platform)", cpp11 => { _options.Cpp11ABI = (cpp11 != null); } }, optionSet.Add("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); } }, optionSet.Add("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); } }, optionSet.Add("ub|unitybuild", "enable unity build", ub => { options.UnityBuild = (ub != null); } );
{ "h|help", "shows the help", hl => { showHelp = (hl != null); } }, optionSet.Add("h|help", "shows the help", hl => { showHelp = (hl != null); });
};
List<String> additionalArguments = null; List<String> additionalArguments = null;
@ -49,7 +40,7 @@ namespace CppSharp
{ {
additionalArguments = optionSet.Parse(args); additionalArguments = optionSet.Parse(args);
} }
catch (Mono.Options.OptionException e) catch (OptionException e)
{ {
Console.WriteLine(e.Message); Console.WriteLine(e.Message);
return false; return false;
@ -57,26 +48,36 @@ namespace CppSharp
if (showHelp || additionalArguments != null && additionalArguments.Count == 0) if (showHelp || additionalArguments != null && additionalArguments.Count == 0)
{ {
ShowHelp(optionSet); helpShown = true;
ShowHelp();
return false; return false;
} }
foreach(String s in additionalArguments) foreach(String s in additionalArguments)
HandleAdditionalArgument(s); HandleAdditionalArgument(s, messages);
return true; return true;
} }
static void ShowHelp(Mono.Options.OptionSet options) static void ShowHelp()
{ {
Console.WriteLine("Usage: {0} [options]+", AppDomain.CurrentDomain.FriendlyName); String appName = System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
appName = Path.GetFileName(appName);
Console.WriteLine();
Console.WriteLine("Usage: {0} [OPTIONS]+ [FILES]+", appName);
Console.WriteLine("Generates target language bindings to interop with unmanaged code."); Console.WriteLine("Generates target language bindings to interop with unmanaged code.");
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Options:"); Console.WriteLine("Options:");
options.WriteOptionDescriptions(Console.Out); optionSet.WriteOptionDescriptions(Console.Out);
Console.WriteLine(); Console.WriteLine();
Console.WriteLine(); Console.WriteLine();
Console.WriteLine("Useful informations:"); Console.WriteLine("Useful informations:");
Console.WriteLine(" - to specify a file to generate bindings from you just have to add their path without any option flag, just like you");
Console.WriteLine(" would do with GCC compiler. You can specify a path to a single file (local or absolute) or a path to a folder with");
Console.WriteLine(" a search query.");
Console.WriteLine(" e.g.: '{0} [OPTIONS]+ my_header.h' will generate the bindings for the file my_header.h", appName);
Console.WriteLine(" e.g.: '{0} [OPTIONS]+ include/*.h' will generate the bindings for all the '.h' files inside the include folder", appName);
Console.WriteLine(" - the options 'iln' (same as 'inputlibraryname') and 'l' have a similar meaning. Both of them are used to tell"); 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 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(" The difference is that if you want to generate the bindings for more than one library within a single managed");
@ -89,44 +90,72 @@ namespace CppSharp
Console.WriteLine(" contain only the bindings for that header file."); Console.WriteLine(" contain only the bindings for that header file.");
} }
static void HandleOutputArg(String arg) static void AddIncludeDirs(String dir, List<String> messages)
{
if (Directory.Exists(dir))
options.IncludeDirs.Add(dir);
else
messages.Add(String.Format("Directory {0} doesn't exist. Not adding as include directory.", dir));
}
static void HandleOutputArg(String arg, List<String> messages)
{ {
try try
{ {
String dir = System.IO.Path.GetDirectoryName(arg); String dir = Path.GetDirectoryName(arg);
String file = System.IO.Path.GetFileNameWithoutExtension(arg); String file = Path.GetFileNameWithoutExtension(arg);
_options.OutputDir = dir; options.OutputDir = dir;
_options.OutputFileName = file; options.OutputFileName = file;
} }
catch(Exception e) catch(Exception e)
{ {
Console.WriteLine("Output error: " + e.Message); messages.Add(e.Message);
Environment.Exit(0);
options.OutputDir = "";
options.OutputFileName = "";
} }
} }
static void AddDefine(String name, String value) static void AddDefine(String name, String value, List<String> messages)
{ {
if (name == null) if (name == null)
throw new Mono.Options.OptionException("Invalid definition name for option -D.", "-D"); messages.Add("Invalid definition name for option -D.");
_options.Defines.Add(name, value); else
options.Defines.Add(name, value);
} }
static void HandleAdditionalArgument(String args) static void HandleAdditionalArgument(String args, List<String> messages)
{
if (Path.IsPathRooted(args) == false)
args = Path.Combine(Directory.GetCurrentDirectory(), args);
try
{
bool searchQuery = args.IndexOf('*') >= 0 || args.IndexOf('?') >= 0;
bool isDirectory = searchQuery || (File.GetAttributes(args) & FileAttributes.Directory) == FileAttributes.Directory;
if (isDirectory)
GetFilesFromPath(args, messages);
else if (File.Exists(args))
options.HeaderFiles.Add(args);
else
{
messages.Add(String.Format("File {0} doesn't exist. Not adding to the list of files to generate bindings from.", args));
}
}
catch(Exception ex)
{ {
if (System.IO.Directory.Exists(args)) messages.Add(String.Format("Error while looking for files inside path {0}. Ignoring.", args));
GetFilesFromPath(args); }
else if (System.IO.File.Exists(args))
_options.HeaderFiles.Add(args);
} }
static void GetFilesFromPath(String path) static void GetFilesFromPath(String path, List<String> messages)
{ {
path = path.Replace(System.IO.Path.DirectorySeparatorChar, System.IO.Path.AltDirectorySeparatorChar); path = path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
String searchPattern = String.Empty; String searchPattern = String.Empty;
int lastSeparatorPosition = path.LastIndexOf(System.IO.Path.AltDirectorySeparatorChar); int lastSeparatorPosition = path.LastIndexOf(Path.AltDirectorySeparatorChar);
if (lastSeparatorPosition >= 0) if (lastSeparatorPosition >= 0)
{ {
@ -141,86 +170,108 @@ namespace CppSharp
{ {
if (searchPattern != String.Empty) if (searchPattern != String.Empty)
{ {
String[] files = System.IO.Directory.GetFiles(path, searchPattern); String[] files = Directory.GetFiles(path, searchPattern);
foreach (String s in files) foreach (String s in files)
_options.HeaderFiles.Add(s); options.HeaderFiles.Add(s);
} }
else else
{ {
String[] files = System.IO.Directory.GetFiles(path); String[] files = Directory.GetFiles(path);
foreach (String s in files) foreach (String s in files)
_options.HeaderFiles.Add(s); options.HeaderFiles.Add(s);
} }
} }
catch (Exception ex) catch (Exception ex)
{ {
Console.WriteLine("Source files path error: " + ex.Message); messages.Add(String.Format("Error while looking for files inside path {0}. Ignoring.", path));
Environment.Exit(0);
} }
} }
static void GetGeneratorKind(String generator) static void GetGeneratorKind(String generator, List<String> messages)
{ {
switch (generator.ToLower()) switch (generator.ToLower())
{ {
case "csharp": case "csharp":
_options.Kind = CppSharp.Generators.GeneratorKind.CSharp; options.Kind = CppSharp.Generators.GeneratorKind.CSharp;
return; return;
case "cli": case "cli":
_options.Kind = CppSharp.Generators.GeneratorKind.CLI; options.Kind = CppSharp.Generators.GeneratorKind.CLI;
return; return;
} }
throw new NotSupportedException("Unknown generator kind: " + generator); messages.Add(String.Format("Unknown generator kind: {0}. Defaulting to {1}", generator, options.Kind.ToString()));
} }
static void GetDestinationPlatform(String platform) static void GetDestinationPlatform(String platform, List<String> messages)
{ {
switch (platform.ToLower()) switch (platform.ToLower())
{ {
case "win": case "win":
_options.Platform = TargetPlatform.Windows; options.Platform = TargetPlatform.Windows;
return; return;
case "osx": case "osx":
_options.Platform = TargetPlatform.MacOS; options.Platform = TargetPlatform.MacOS;
return; return;
case "linux": case "linux":
_options.Platform = TargetPlatform.Linux; options.Platform = TargetPlatform.Linux;
return; return;
} }
throw new NotSupportedException("Unknown target platform: " + platform); messages.Add(String.Format("Unknown target platform: {0}. Defaulting to {1}", platform, options.Platform.ToString()));
} }
static void GetDestinationArchitecture(String architecture) static void GetDestinationArchitecture(String architecture, List<String> messages)
{ {
switch (architecture.ToLower()) switch (architecture.ToLower())
{ {
case "x86": case "x86":
_options.Architecture = TargetArchitecture.x86; options.Architecture = TargetArchitecture.x86;
return; return;
case "x64": case "x64":
_options.Architecture = TargetArchitecture.x64; options.Architecture = TargetArchitecture.x64;
return; return;
} }
throw new NotSupportedException("Unknown target architecture: " + architecture); messages.Add(String.Format("Unknown target architecture: {0}. Defaulting to {1}", architecture, options.Architecture.ToString()));
}
static void PrintMessages(List<String> messages)
{
foreach (String m in messages)
Console.WriteLine(m);
} }
static void Main(string[] args) static void Main(string[] args)
{ {
List<String> messages = new List<String>();
bool helpShown = false;
try try
{ {
if (ParseCommandLineArgs(args) == false) if (ParseCommandLineArgs(args, messages, ref helpShown) == false)
{
PrintMessages(messages);
// Don't need to show the help since if ParseCommandLineArgs returns false the help has already been shown
return; return;
}
Generator gen = new Generator(options);
bool validOptions = gen.ValidateOptions(messages);
PrintMessages(messages);
Generator gen = new Generator(_options); if (validOptions)
gen.Run(); gen.Run();
else if (helpShown == false)
ShowHelp();
} }
catch (Exception ex) catch (Exception ex)
{ {
PrintMessages(messages);
Console.WriteLine(ex.Message); Console.WriteLine(ex.Message);
} }
} }

66
src/CLI/Generator.cs

@ -25,35 +25,65 @@ namespace CppSharp
_options = options; _options = options;
} }
public void ValidateOptions() public bool ValidateOptions(List<String> messages)
{ {
if (Platform.IsWindows && _options.Platform != TargetPlatform.Windows) if (Platform.IsWindows && _options.Platform != TargetPlatform.Windows)
throw new NotSupportedException("Cannot create bindings for a platform other that Windows from a Windows running machine"); {
messages.Add("Cannot create bindings for a platform other that Windows from a Windows running machine");
return false;
}
else if (Platform.IsMacOS && _options.Platform != TargetPlatform.MacOS) 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"); {
messages.Add("Cannot create bindings for a platform other that MacOS from a MacOS running machine");
return false;
}
else if (Platform.IsUnixPlatform && _options.Platform != TargetPlatform.Linux) 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"); {
messages.Add("Cannot create bindings for a platform other that Linux from a Linux running machine");
return false;
}
if(_options.Platform != TargetPlatform.Windows && _options.Kind != GeneratorKind.CSharp) 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"); {
messages.Add("Cannot create bindings for languages other than C# from a non Windows machine");
return false;
}
if(_options.Platform == TargetPlatform.Linux && _options.Architecture != TargetArchitecture.x64) if (_options.Platform == TargetPlatform.Linux && _options.Architecture != TargetArchitecture.x64)
throw new NotSupportedException("Cannot create bindings for architectures other than x64 for Linux machines"); {
messages.Add("Cannot create bindings for architectures other than x64 for Linux machines");
return false;
}
if(_options.HeaderFiles.Count == 0) if (_options.HeaderFiles.Count == 0)
throw new NotSupportedException("No source header file has been given"); {
messages.Add("No source header file has been given to generate bindings from");
return false;
}
if (_options.OutputNamespace == String.Empty) if (_options.OutputNamespace == String.Empty)
throw new NotSupportedException("Output namespace is empty"); {
messages.Add("Output namespace is empty");
return false;
}
if (_options.OutputFileName == String.Empty) if (_options.OutputFileName == String.Empty)
throw new NotSupportedException("Output not specified"); {
messages.Add("Output not specified");
return false;
}
if(_options.InputLibraryName == String.Empty && _options.CheckSymbols == false) 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"); {
messages.Add("Input library name not specified and check symbols not enabled. Either set the input library name or the check symbols flag");
return false;
}
if(_options.InputLibraryName == String.Empty && _options.CheckSymbols == true && _options.Libraries.Count == 0) 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"); {
messages.Add("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");
return false;
}
if (_options.Architecture == TargetArchitecture.x64) if (_options.Architecture == TargetArchitecture.x64)
_triple = "x86_64-"; _triple = "x86_64-";
@ -78,6 +108,8 @@ namespace CppSharp
if(_options.Cpp11ABI) if(_options.Cpp11ABI)
_triple += "-cxx11abi"; _triple += "-cxx11abi";
} }
return true;
} }
public void Setup(Driver driver) public void Setup(Driver driver)
@ -205,8 +237,6 @@ namespace CppSharp
public void Run() public void Run()
{ {
ValidateOptions();
String message = "Generating the "; String message = "Generating the ";
switch(_options.Kind) switch(_options.Kind)

Loading…
Cancel
Save