public static int Run(InvocationContext context) { var additionalArgs = context.ParseResult.ValueForOption <string[]>("additional"); var configSwitches = context.ParseResult.ValueForOption <string[]>("config"); var defineMacros = context.ParseResult.ValueForOption <string[]>("define-macro"); var excludedNames = context.ParseResult.ValueForOption <string[]>("exclude"); var files = context.ParseResult.ValueForOption <string[]>("file"); var fileDirectory = context.ParseResult.ValueForOption <string>("file-directory"); var headerFile = context.ParseResult.ValueForOption <string>("headerFile"); var includeDirectories = context.ParseResult.ValueForOption <string[]>("include-directory"); var language = context.ParseResult.ValueForOption <string>("language"); var libraryPath = context.ParseResult.ValueForOption <string>("libraryPath"); var methodClassName = context.ParseResult.ValueForOption <string>("methodClassName"); var methodPrefixToStrip = context.ParseResult.ValueForOption <string>("prefixStrip"); var namespaceName = context.ParseResult.ValueForOption <string>("namespace"); var outputLocation = context.ParseResult.ValueForOption <string>("output"); var remappedNameValuePairs = context.ParseResult.ValueForOption <string[]>("remap"); var std = context.ParseResult.ValueForOption <string>("std"); var testOutputLocation = context.ParseResult.ValueForOption <string>("test-output"); var traversalNames = context.ParseResult.ValueForOption <string[]>("traverse"); var withAttributeNameValuePairs = context.ParseResult.ValueForOption <string[]>("with-attribute"); var withCallConvNameValuePairs = context.ParseResult.ValueForOption <string[]>("with-callconv"); var withLibraryPathNameValuePairs = context.ParseResult.ValueForOption <string[]>("with-librarypath"); var withSetLastErrors = context.ParseResult.ValueForOption <string[]>("with-setlasterror"); var withTypeNameValuePairs = context.ParseResult.ValueForOption <string[]>("with-type"); var withUsingNameValuePairs = context.ParseResult.ValueForOption <string[]>("with-using"); var errorList = new List <string>(); if (!files.Any()) { errorList.Add("Error: No input C/C++ files provided. Use --file or -f"); } if (string.IsNullOrWhiteSpace(namespaceName)) { errorList.Add("Error: No namespace provided. Use --namespace or -n"); } if (string.IsNullOrWhiteSpace(outputLocation)) { errorList.Add("Error: No output file location provided. Use --output or -o"); } ParseKeyValuePairs(remappedNameValuePairs, errorList, out Dictionary <string, string> remappedNames); ParseKeyValuePairs(withAttributeNameValuePairs, errorList, out Dictionary <string, IReadOnlyList <string> > withAttributes); ParseKeyValuePairs(withCallConvNameValuePairs, errorList, out Dictionary <string, string> withCallConvs); ParseKeyValuePairs(withLibraryPathNameValuePairs, errorList, out Dictionary <string, string> withLibraryPath); ParseKeyValuePairs(withTypeNameValuePairs, errorList, out Dictionary <string, string> withTypes); ParseKeyValuePairs(withUsingNameValuePairs, errorList, out Dictionary <string, IReadOnlyList <string> > withUsings); var configOptions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? PInvokeGeneratorConfigurationOptions.None : PInvokeGeneratorConfigurationOptions.GenerateUnixTypes; foreach (var configSwitch in configSwitches) { switch (configSwitch) { case "compatible-codegen": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode; configOptions &= ~PInvokeGeneratorConfigurationOptions.GeneratePreviewCode; break; } case "default-remappings": { configOptions &= ~PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; break; } case "exclude-com-proxies": { configOptions |= PInvokeGeneratorConfigurationOptions.ExcludeComProxies; break; } case "exclude-empty-records": { configOptions |= PInvokeGeneratorConfigurationOptions.ExcludeEmptyRecords; break; } case "exclude-enum-operators": { configOptions |= PInvokeGeneratorConfigurationOptions.ExcludeEnumOperators; break; } case "explicit-vtbls": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateExplicitVtbls; break; } case "generate-macro-bindings": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateMacroBindings; break; } case "generate-tests-nunit": { if (string.IsNullOrWhiteSpace(testOutputLocation)) { errorList.Add("Error: No test output file location provided. Use --test-output or -to"); } if (configOptions.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsXUnit)) { errorList.Add("Cannot generate both NUnit and XUnit tests."); } configOptions |= PInvokeGeneratorConfigurationOptions.GenerateTestsNUnit; break; } case "generate-tests-xunit": { if (string.IsNullOrWhiteSpace(testOutputLocation)) { errorList.Add("Error: No test output file location provided. Use --test-output or -to"); } if (configOptions.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsNUnit)) { errorList.Add("Cannot generate both NUnit and XUnit tests."); } configOptions |= PInvokeGeneratorConfigurationOptions.GenerateTestsXUnit; break; } case "implicit-vtbls": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateExplicitVtbls; break; } case "latest-codegen": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode; configOptions &= ~PInvokeGeneratorConfigurationOptions.GeneratePreviewCode; break; } case "log-exclusions": { configOptions |= PInvokeGeneratorConfigurationOptions.LogExclusions; break; } case "log-visited-files": { configOptions |= PInvokeGeneratorConfigurationOptions.LogVisitedFiles; break; } case "multi-file": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles; break; } case "no-default-remappings": { configOptions |= PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; break; } case "preview-codegen": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode; configOptions |= PInvokeGeneratorConfigurationOptions.GeneratePreviewCode; break; } case "preview-codegen-nint": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode; configOptions |= PInvokeGeneratorConfigurationOptions.GeneratePreviewCodeNint; break; } case "preview-codegen-fnptr": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateCompatibleCode; configOptions |= PInvokeGeneratorConfigurationOptions.GeneratePreviewCodeFnptr; break; } case "single-file": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles; break; } case "unix-types": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateUnixTypes; break; } case "windows-types": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateUnixTypes; break; } default: { errorList.Add($"Error: Unrecognized config switch: {configSwitch}."); break; } } } if (!string.IsNullOrWhiteSpace(testOutputLocation) && !configOptions.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsNUnit) && !configOptions.HasFlag(PInvokeGeneratorConfigurationOptions.GenerateTestsXUnit)) { errorList.Add("Error: No test format provided. Use --config generate-tests-nunit or --config generate-tests-xunit"); } if (errorList.Any()) { foreach (var error in errorList) { context.Console.Error.Write(error); context.Console.Error.Write(Environment.NewLine); } context.Console.Error.Write(Environment.NewLine); new HelpBuilder(context.Console).Write(s_rootCommand); return(-1); } var clangCommandLineArgs = new string[] { $"--language={language}", // Treat subsequent input files as having type <language> $"--std={std}", // Language standard to compile for "-Wno-pragma-once-outside-header" // We are processing files which may be header files }; clangCommandLineArgs = clangCommandLineArgs.Concat(includeDirectories.Select(x => "--include-directory=" + x)).ToArray(); clangCommandLineArgs = clangCommandLineArgs.Concat(defineMacros.Select(x => "--define-macro=" + x)).ToArray(); clangCommandLineArgs = clangCommandLineArgs.Concat(additionalArgs).ToArray(); var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None; translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, testOutputLocation, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames, withAttributes, withCallConvs, withLibraryPath, withSetLastErrors, withTypes, withUsings); if (config.GenerateMacroBindings) { translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord; } int exitCode = 0; using (var pinvokeGenerator = new PInvokeGenerator(config)) { foreach (var file in files) { var filePath = Path.Combine(fileDirectory, file); var translationUnitError = CXTranslationUnit.TryParse(pinvokeGenerator.IndexHandle, filePath, clangCommandLineArgs, Array.Empty <CXUnsavedFile>(), translationFlags, out CXTranslationUnit handle); var skipProcessing = false; if (translationUnitError != CXErrorCode.CXError_Success) { Console.WriteLine($"Error: Parsing failed for '{filePath}' due to '{translationUnitError}'."); skipProcessing = true; } else if (handle.NumDiagnostics != 0) { Console.WriteLine($"Diagnostics for '{filePath}':"); for (uint i = 0; i < handle.NumDiagnostics; ++i) { using var diagnostic = handle.GetDiagnostic(i); Console.Write(" "); Console.WriteLine(diagnostic.Format(CXDiagnostic.DefaultDisplayOptions).ToString()); skipProcessing |= (diagnostic.Severity == CXDiagnosticSeverity.CXDiagnostic_Error); skipProcessing |= (diagnostic.Severity == CXDiagnosticSeverity.CXDiagnostic_Fatal); } } if (skipProcessing) { Console.WriteLine($"Skipping '{filePath}' due to one or more errors listed above."); Console.WriteLine(); exitCode = -1; continue; } using var translationUnit = TranslationUnit.GetOrCreate(handle); Console.WriteLine($"Processing '{filePath}'"); pinvokeGenerator.GenerateBindings(translationUnit, filePath, clangCommandLineArgs, translationFlags); } if (pinvokeGenerator.Diagnostics.Count != 0) { Console.WriteLine("Diagnostics for binding generation:"); foreach (var diagnostic in pinvokeGenerator.Diagnostics) { Console.Write(" "); Console.WriteLine(diagnostic); if (diagnostic.Level == DiagnosticLevel.Warning) { if (exitCode >= 0) { exitCode++; } } else if (diagnostic.Level == DiagnosticLevel.Error) { if (exitCode >= 0) { exitCode = -1; } else { exitCode--; } } } } } return(exitCode); }
public static int Run(InvocationContext context) { var additionalArgs = context.ParseResult.ValueForOption <string[]>("additional"); var configSwitches = context.ParseResult.ValueForOption <string[]>("config"); var defines = context.ParseResult.ValueForOption <string[]>("define"); var excludedNames = context.ParseResult.ValueForOption <string[]>("exclude"); var files = context.ParseResult.ValueForOption <string[]>("file"); var headerFile = context.ParseResult.ValueForOption <string>("headerFile"); var includeDirs = context.ParseResult.ValueForOption <string[]>("include"); var libraryPath = context.ParseResult.ValueForOption <string>("libraryPath"); var methodClassName = context.ParseResult.ValueForOption <string>("methodClassName"); var methodPrefixToStrip = context.ParseResult.ValueForOption <string>("prefixStrip"); var namespaceName = context.ParseResult.ValueForOption <string>("namespace"); var outputLocation = context.ParseResult.ValueForOption <string>("output"); var remappedNameValuePairs = context.ParseResult.ValueForOption <string[]>("remap"); var traversalNames = context.ParseResult.ValueForOption <string[]>("traverse"); var errorList = new List <string>(); if (!files.Any()) { errorList.Add("Error: No input C/C++ files provided. Use --file or -f"); } if (string.IsNullOrWhiteSpace(libraryPath)) { errorList.Add("Error: No library path location provided. Use --libraryPath or -l"); } if (string.IsNullOrWhiteSpace(namespaceName)) { errorList.Add("Error: No namespace provided. Use --namespace or -n"); } if (string.IsNullOrWhiteSpace(outputLocation)) { errorList.Add("Error: No output file location provided. Use --output or -o"); } var remappedNames = new Dictionary <string, string>(); foreach (var remappedNameValuePair in remappedNameValuePairs) { var parts = remappedNameValuePair.Split('='); if (parts.Length != 2) { errorList.Add($"Error: Invalid remap argument: {remappedNameValuePair}. Expected 'name=value'"); continue; } remappedNames[parts[0].TrimEnd()] = parts[1].TrimStart(); } var configOptions = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? PInvokeGeneratorConfigurationOptions.None : PInvokeGeneratorConfigurationOptions.GenerateUnixTypes; foreach (var configSwitch in configSwitches) { switch (configSwitch) { case "default-remappings": { configOptions &= ~PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; break; } case "multi-file": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles; break; } case "no-default-remappings": { configOptions |= PInvokeGeneratorConfigurationOptions.NoDefaultRemappings; break; } case "single-file": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateMultipleFiles; break; } case "unix-types": { configOptions |= PInvokeGeneratorConfigurationOptions.GenerateUnixTypes; break; } case "windows-types": { configOptions &= ~PInvokeGeneratorConfigurationOptions.GenerateUnixTypes; break; } default: { errorList.Add($"Error: Unrecognized config switch: {configSwitch}."); break; } } } if (errorList.Any()) { foreach (var error in errorList) { context.Console.Error.WriteLine(error); } context.Console.Error.WriteLine(); new HelpBuilder(context.Console).Write(s_rootCommand); return(-1); } var clangCommandLineArgs = new string[] { "-std=c++11", // The input files should be compiled for C++ 11 "-xc++", // The input files are C++ "-Wno-pragma-once-outside-header" // We are processing files which may be header files }; clangCommandLineArgs = clangCommandLineArgs.Concat(includeDirs.Select(x => "-I" + x)).ToArray(); clangCommandLineArgs = clangCommandLineArgs.Concat(defines.Select(x => "-D" + x)).ToArray(); clangCommandLineArgs = clangCommandLineArgs.Concat(additionalArgs).ToArray(); var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None; translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited var config = new PInvokeGeneratorConfiguration(libraryPath, namespaceName, outputLocation, configOptions, excludedNames, headerFile, methodClassName, methodPrefixToStrip, remappedNames, traversalNames); int exitCode = 0; using (var pinvokeGenerator = new PInvokeGenerator(config)) { foreach (var file in files) { var translationUnitError = CXTranslationUnit.TryParse(pinvokeGenerator.IndexHandle, file, clangCommandLineArgs, Array.Empty <CXUnsavedFile>(), translationFlags, out CXTranslationUnit handle); var skipProcessing = false; if (translationUnitError != CXErrorCode.CXError_Success) { Console.WriteLine($"Error: Parsing failed for '{file}' due to '{translationUnitError}'."); skipProcessing = true; } else if (handle.NumDiagnostics != 0) { Console.WriteLine($"Diagnostics for '{file}':"); for (uint i = 0; i < handle.NumDiagnostics; ++i) { using var diagnostic = handle.GetDiagnostic(i); Console.Write(" "); Console.WriteLine(diagnostic.Format(CXDiagnosticDisplayOptions.CXDiagnostic_DisplayOption).ToString()); skipProcessing |= (diagnostic.Severity == CXDiagnosticSeverity.CXDiagnostic_Error); skipProcessing |= (diagnostic.Severity == CXDiagnosticSeverity.CXDiagnostic_Fatal); } } if (skipProcessing) { Console.WriteLine($"Skipping '{file}' due to one or more errors listed above."); Console.WriteLine(); exitCode = -1; continue; } using var translationUnit = TranslationUnit.GetOrCreate(handle); Console.WriteLine($"Processing '{file}'"); pinvokeGenerator.GenerateBindings(translationUnit); } if (pinvokeGenerator.Diagnostics.Count != 0) { Console.WriteLine("Diagnostics for binding generation:"); foreach (var diagnostic in pinvokeGenerator.Diagnostics) { Console.Write(" "); Console.WriteLine(diagnostic); if (diagnostic.Level == DiagnosticLevel.Warning) { if (exitCode >= 0) { exitCode++; } } else if (diagnostic.Level == DiagnosticLevel.Error) { if (exitCode >= 0) { exitCode = -1; } else { exitCode--; } } } } } return(exitCode); }