public void BasicWrapper(string name) { // Create a unique directory var dir = Path.GetRandomFileName(); _ = Directory.CreateDirectory(dir); try { // Create a file with the right name var file = new FileInfo(Path.Combine(dir, name + ".c")); File.WriteAllText(file.FullName, "int main() { return 0; }"); var index = CXIndex.Create(); var translationUnit = CXTranslationUnit.Parse(index, file.FullName, Array.Empty <string>(), Array.Empty <CXUnsavedFile>(), CXTranslationUnit_Flags.CXTranslationUnit_None); var clangFile = translationUnit.GetFile(file.FullName); var clangFileName = clangFile.Name; var clangFileNameString = clangFileName.CString; Assert.Equal(file.FullName, clangFileNameString); } finally { Directory.Delete(dir, true); } }
private async Task ValidateGeneratedBindingsAsync(string inputContents, string expectedOutputContents, PInvokeGeneratorConfigurationOptions configOptions, string[] excludedNames, IReadOnlyDictionary <string, string> remappedNames, IReadOnlyDictionary <string, IReadOnlyList <string> > withAttributes, IReadOnlyDictionary <string, string> withCallConvs, IReadOnlyDictionary <string, string> withLibraryPaths, string[] withSetLastErrors, IReadOnlyDictionary <string, string> withTypes, IReadOnlyDictionary <string, IReadOnlyList <string> > withUsings, IEnumerable <Diagnostic> expectedDiagnostics, string libraryPath) { Assert.True(File.Exists(DefaultInputFileName)); using var outputStream = new MemoryStream(); using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents); var unsavedFiles = new CXUnsavedFile[] { unsavedFile }; var config = new PInvokeGeneratorConfiguration(libraryPath, DefaultNamespaceName, Path.GetRandomFileName(), testOutputLocation: null, configOptions, excludedNames, headerFile: null, methodClassName: null, methodPrefixToStrip: null, remappedNames, traversalNames: null, withAttributes, withCallConvs, withLibraryPaths, withSetLastErrors, withTypes, withUsings); using (var pinvokeGenerator = new PInvokeGenerator(config, (path) => outputStream)) { var handle = CXTranslationUnit.Parse(pinvokeGenerator.IndexHandle, DefaultInputFileName, DefaultClangCommandLineArgs, unsavedFiles, DefaultTranslationUnitFlags); using var translationUnit = TranslationUnit.GetOrCreate(handle); pinvokeGenerator.GenerateBindings(translationUnit); if (expectedDiagnostics is null) { Assert.Empty(pinvokeGenerator.Diagnostics); } else { Assert.Equal(expectedDiagnostics, pinvokeGenerator.Diagnostics); } } outputStream.Position = 0; var actualOutputContents = await new StreamReader(outputStream).ReadToEndAsync(); Assert.Equal(expectedOutputContents, actualOutputContents); }
protected static TranslationUnit CreateTranslationUnit(string inputContents) { Assert.True(File.Exists(DefaultInputFileName)); using var unsavedFile = CXUnsavedFile.Create(DefaultInputFileName, inputContents); var unsavedFiles = new CXUnsavedFile[] { unsavedFile }; var index = CXIndex.Create(); var translationUnit = CXTranslationUnit.Parse(index, DefaultInputFileName, DefaultClangCommandLineArgs, unsavedFiles, DefaultTranslationUnitFlags); if (translationUnit.NumDiagnostics != 0) { var errorDiagnostics = new StringBuilder(); _ = errorDiagnostics.AppendLine($"The provided {nameof(CXTranslationUnit)} has the following diagnostics which prevent its use:"); var invalidTranslationUnitHandle = false; for (uint i = 0; i < translationUnit.NumDiagnostics; ++i) { using var diagnostic = translationUnit.GetDiagnostic(i); if (diagnostic.Severity is CXDiagnosticSeverity.CXDiagnostic_Error or CXDiagnosticSeverity.CXDiagnostic_Fatal) { invalidTranslationUnitHandle = true; _ = errorDiagnostics.Append(' ', 4); _ = errorDiagnostics.AppendLine(diagnostic.Format(CXDiagnosticDisplayOptions.CXDiagnostic_DisplayOption).ToString()); } } Assert.False(invalidTranslationUnitHandle, errorDiagnostics.ToString()); } return(TranslationUnit.GetOrCreate(translationUnit)); }
public void Run(string[] args, CodeBuilder result) { ParseCommandLineArgs(args, out var commandLineArgs, out var sourceContents); using (var unsavedFile = CXUnsavedFile.Create("Source", sourceContents)) using (var index = Index.Create(false, true)) { var unsavedFiles = new CXUnsavedFile[] { unsavedFile }; var handle = CXTranslationUnit.Parse( index.Handle, unsavedFile.FilenameString, commandLineArgs, unsavedFiles, CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes // Include attributed types in CXType | CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes // Implicit attributes should be visited | CXTranslationUnit_Flags.CXTranslationUnit_SkipFunctionBodies ); var translationUnit = TranslationUnit.GetOrCreate(handle); CheckTranslationUnitErrors(translationUnit, sourceContents); VisitTranslationUnit(translationUnit); } WriteNamespace(result); }
public unsafe void GenerateTypes(string libraryName = "imobiledevice") { string[] arguments = { // Use the C++ backend "-x", "c++", // Parse the doxygen comments "-Wdocumentation", // Target 32-bit OS "-m32" }; arguments = arguments.Concat(this.IncludeDirectories.Select(x => "-I" + x)).ToArray(); FunctionVisitor functionVisitor; using (var createIndex = ClangSharp.Index.Create(false, true)) using (var translationUnit = CXTranslationUnit.Parse(createIndex.Handle, this.InputFile, arguments, null, CXTranslationUnit_Flags.CXTranslationUnit_None)) { StringWriter errorWriter = new StringWriter(); var set = translationUnit.DiagnosticSet; var numDiagnostics = set.NumDiagnostics; bool hasError = false; bool hasWarning = false; for (uint i = 0; i < numDiagnostics; ++i) { CXDiagnostic diagnostic = set.GetDiagnostic(i); var severity = diagnostic.Severity; switch (severity) { case CXDiagnosticSeverity.CXDiagnostic_Error: case CXDiagnosticSeverity.CXDiagnostic_Fatal: hasError = true; break; case CXDiagnosticSeverity.CXDiagnostic_Warning: hasWarning = true; break; } var location = diagnostic.Location; location.GetFileLocation(out CXFile file, out uint line, out _, out _); var fileName = file.Name.CString; var message = diagnostic.Spelling.CString; errorWriter.WriteLine($"{severity}: {fileName}:{line} {message}"); } if (hasError) { throw new Exception(errorWriter.ToString()); } if (hasWarning) { // Dump the warnings to the console output. Console.WriteLine(errorWriter.ToString()); } // Generate the marhaler types for string arrays (char **) var arrayMarshalerGenerator = new ArrayMarshalerGenerator(this); arrayMarshalerGenerator.Generate(); // Creates enums var enumVisitor = new EnumVisitor(this); var realEnumVisitor = new DelegatingCXCursorVisitor(enumVisitor.Visit); translationUnit.Cursor.VisitChildren(realEnumVisitor.Visit, new CXClientData()); // Creates structs var structVisitor = new StructVisitor(this); var realStructVisitor = new DelegatingCXCursorVisitor(structVisitor.Visit); translationUnit.Cursor.VisitChildren(realStructVisitor.Visit, new CXClientData()); // Creates safe handles & delegates var typeDefVisitor = new TypeDefVisitor(this); var realTypeDefVisitor = new DelegatingCXCursorVisitor(typeDefVisitor.Visit); translationUnit.Cursor.VisitChildren(realTypeDefVisitor.Visit, new CXClientData()); // Creates functions in a NativeMethods class functionVisitor = new FunctionVisitor(this, libraryName); var realFunctionVisitor = new DelegatingCXCursorVisitor(functionVisitor.Visit); translationUnit.Cursor.VisitChildren(realFunctionVisitor.Visit, new CXClientData()); createIndex.Dispose(); } // Update the SafeHandle to call the _free method var handles = this.Types.Where(t => t.Name.EndsWith("Handle")); foreach (var handle in handles) { var freeMethod = functionVisitor.NativeMethods.Members .OfType <CodeMemberMethod>() .Where(m => (m.Name.EndsWith("_free") || m.Name.EndsWith("_disconnect")) && m.Parameters.Count == 1 && m.Parameters[0].Type.BaseType == handle.Name) .SingleOrDefault(); if (freeMethod == null) { continue; } var type = (HandleType)((NustacheGeneratedType)handle).Type; type.ReleaseMethodName = freeMethod.Name; type.ReleaseMethodReturnsValue = freeMethod.ReturnType.BaseType != "System.Void"; // Directly pass the IntPtr, becuase the handle itself will already be in the 'closed' state // when this method is called. freeMethod.Parameters[0].Type = new CodeTypeReference(typeof(IntPtr)); freeMethod.Parameters[0].Direction = FieldDirection.In; } // Extract the API interface and class, as well as the Exception class. Used for DI. ApiExtractor extractor = new ApiExtractor(this, functionVisitor); extractor.Generate(); // Add the 'Error' extension IsError and ThrowOnError extension methods var extensionsExtractor = new ErrorExtensionExtractor(this, functionVisitor); extensionsExtractor.Generate(); // Patch the native methods to be compatible with .NET Core - basically, // do the marshalling ourselves NativeMethodOverloadGenerator.Generate(this); }
public static int Run(InvocationContext context) { var additionalArgs = context.ParseResult.ValueForOption <string[]>("additional"); var config = new ConfigurationOptions(context.ParseResult.ValueForOption <string[]>("config")) { ExcludedFunctions = context.ParseResult.ValueForOption <string[]>("excludeFunction"), LibraryPath = context.ParseResult.ValueForOption <string>("libraryPath"), MethodClassName = context.ParseResult.ValueForOption <string>("methodClassName"), Namespace = context.ParseResult.ValueForOption <string>("namespace"), OutputLocation = context.ParseResult.ValueForOption <string>("output"), MethodPrefixToStrip = context.ParseResult.ValueForOption <string>("prefixStrip"), }; var defines = context.ParseResult.ValueForOption <string[]>("define"); var files = context.ParseResult.ValueForOption <string[]>("file"); var includeDirs = context.ParseResult.ValueForOption <string[]>("include"); var errorList = new List <string>(); if (!files.Any()) { errorList.Add("Error: No input C/C++ files provided. Use --file or -f"); } if (string.IsNullOrWhiteSpace(config.Namespace)) { errorList.Add("Error: No namespace provided. Use --namespace or -n"); } if (string.IsNullOrWhiteSpace(config.OutputLocation)) { errorList.Add("Error: No output file location provided. Use --output or -o"); } if (string.IsNullOrWhiteSpace(config.LibraryPath)) { errorList.Add("Error: No library path location provided. Use --libraryPath or -l"); } 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 arr = new string[] { "-xc++", // The input files are C++ "-Wno-pragma-once-outside-header" // We are processing files which may be header files }; arr = arr.Concat(includeDirs.Select(x => "-I" + x)).ToArray(); arr = arr.Concat(defines.Select(x => "-D" + x)).ToArray(); arr = arr.Concat(additionalArgs).ToArray(); var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None; translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_SkipFunctionBodies; // Don't traverse function bodies translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited using (var createIndex = CXIndex.Create()) using (var cursorWriter = new CursorWriter(config)) { foreach (var file in files) { var translationUnitError = CXTranslationUnit.Parse(createIndex, file, arr, Array.Empty <CXUnsavedFile>(), translationFlags, out CXTranslationUnit translationUnitHandle); bool skipProcessing = false; if (translationUnitError != CXErrorCode.CXError_Success) { Console.WriteLine($"Error: Parsing failed for '{file}' due to '{translationUnitError}'."); skipProcessing = true; } else if (translationUnitHandle.NumDiagnostics != 0) { Console.WriteLine($"Diagnostics for '{file}':"); for (uint i = 0; i < translationUnitHandle.NumDiagnostics; ++i) { using (var diagnostic = translationUnitHandle.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(); continue; } using (translationUnitHandle) { var translationUnit = new TranslationUnit(translationUnitHandle.Cursor); translationUnit.Visit(clientData: default);
/// <summary> /// Private method parsing file or content. /// </summary> /// <param name="cppFiles">A list of path to C/C++ header files on the disk to parse</param> /// <param name="options">Options used for parsing this file (e.g include folders...)</param> /// <returns>The result of the compilation</returns> private static CppCompilation ParseInternal(List <CppFileOrString> cppFiles, CppParserOptions options = null) { if (cppFiles == null) { throw new ArgumentNullException(nameof(cppFiles)); } options = options ?? new CppParserOptions(); var arguments = new List <string>(); // Make sure that paths are absolute var normalizedIncludePaths = new List <string>(); normalizedIncludePaths.AddRange(options.IncludeFolders.Select(x => Path.Combine(Environment.CurrentDirectory, x))); var normalizedSystemIncludePaths = new List <string>(); normalizedSystemIncludePaths.AddRange(options.SystemIncludeFolders.Select(x => Path.Combine(Environment.CurrentDirectory, x))); arguments.AddRange(options.AdditionalArguments); arguments.AddRange(normalizedIncludePaths.Select(x => $"-I{x}")); arguments.AddRange(normalizedSystemIncludePaths.Select(x => $"-isystem{x}")); arguments.AddRange(options.Defines.Select(x => $"-D{x}")); if (options.ParseAsCpp && !arguments.Contains("-xc++")) { arguments.Add("-xc++"); } if (!arguments.Any(x => x.StartsWith("--target="))) { arguments.Add($"--target={GetTripleFromOptions(options)}"); } if (options.ParseComments) { arguments.Add("-fparse-all-comments"); } var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None; translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_SkipFunctionBodies; // Don't traverse function bodies translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited if (options.ParseMacros) { translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord; } var argumentsArray = arguments.ToArray(); using (var createIndex = CXIndex.Create()) { var builder = new CppModelBuilder { AutoSquashTypedef = options.AutoSquashTypedef }; var compilation = builder.RootCompilation; string rootFileName = CppAstRootFileName; string rootFileContent = null; // Build the root input source file var tempBuilder = new StringBuilder(); if (options.PreHeaderText != null) { tempBuilder.AppendLine(options.PreHeaderText); } foreach (var file in cppFiles) { if (file.Content != null) { tempBuilder.AppendLine(file.Content); } else { var filePath = Path.Combine(Environment.CurrentDirectory, file.Filename); tempBuilder.AppendLine($"#include \"{filePath}\""); } } if (options.PostHeaderText != null) { tempBuilder.AppendLine(options.PostHeaderText); } // TODO: Add debug rootFileContent = tempBuilder.ToString(); compilation.InputText = rootFileContent; { CXTranslationUnit translationUnit; CXErrorCode translationUnitError; translationUnitError = CXTranslationUnit.Parse(createIndex, rootFileName, argumentsArray, new CXUnsavedFile[] { new CXUnsavedFile() { Contents = rootFileContent, Filename = rootFileName, Length = (uint)Encoding.UTF8.GetByteCount(rootFileContent) } }, translationFlags, out translationUnit); bool skipProcessing = false; if (translationUnitError != CXErrorCode.CXError_Success) { compilation.Diagnostics.Error($"Parsing failed due to '{translationUnitError}'", new CppSourceLocation(rootFileName, 0, 1, 1)); skipProcessing = true; } else if (translationUnit.NumDiagnostics != 0) { for (uint i = 0; i < translationUnit.NumDiagnostics; ++i) { using (var diagnostic = translationUnit.GetDiagnostic(i)) { CppSourceLocation location; var message = GetMessageAndLocation(rootFileContent, diagnostic, out location); switch (diagnostic.Severity) { case CXDiagnosticSeverity.CXDiagnostic_Ignored: case CXDiagnosticSeverity.CXDiagnostic_Note: compilation.Diagnostics.Info(message, location); break; case CXDiagnosticSeverity.CXDiagnostic_Warning: compilation.Diagnostics.Warning(message, location); break; case CXDiagnosticSeverity.CXDiagnostic_Error: case CXDiagnosticSeverity.CXDiagnostic_Fatal: compilation.Diagnostics.Error(message, location); skipProcessing = true; break; } } } } if (skipProcessing) { compilation.Diagnostics.Warning($"Compilation aborted due to one or more errors listed above.", new CppSourceLocation(rootFileName, 0, 1, 1)); } else { using (translationUnit) { translationUnit.Cursor.VisitChildren(builder.VisitTranslationUnit, clientData: default); } } } return(compilation); } }
/// <summary> /// Private method parsing file or content. /// </summary> /// <param name="cppFiles">A list of path to C/C++ header files on the disk to parse</param> /// <param name="options">Options used for parsing this file (e.g include folders...)</param> /// <returns>The result of the compilation</returns> private static unsafe CppCompilation ParseInternal(List <CppFileOrString> cppFiles, CppParserOptions options = null) { if (cppFiles == null) { throw new ArgumentNullException(nameof(cppFiles)); } options = options ?? new CppParserOptions(); var arguments = new List <string>(); // Make sure that paths are absolute var normalizedIncludePaths = new List <string>(); normalizedIncludePaths.AddRange(options.IncludeFolders.Select(x => Path.Combine(Environment.CurrentDirectory, x))); var normalizedSystemIncludePaths = new List <string>(); normalizedSystemIncludePaths.AddRange(options.SystemIncludeFolders.Select(x => Path.Combine(Environment.CurrentDirectory, x))); arguments.AddRange(options.AdditionalArguments); arguments.AddRange(normalizedIncludePaths.Select(x => $"-I{x}")); arguments.AddRange(normalizedSystemIncludePaths.Select(x => $"-isystem{x}")); arguments.AddRange(options.Defines.Select(x => $"-D{x}")); arguments.Add("-dM"); arguments.Add("-E"); if (options.ParseAsCpp && !arguments.Contains("-xc++")) { arguments.Add("-xc++"); } if (!arguments.Any(x => x.StartsWith("--target="))) { arguments.Add($"--target={GetTripleFromOptions(options)}"); } if (options.ParseComments) { arguments.Add("-fparse-all-comments"); } var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None; translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_SkipFunctionBodies; // Don't traverse function bodies translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited if (options.ParseMacros) { translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord; } translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_DetailedPreprocessingRecord; var argumentsArray = arguments.ToArray(); using (var createIndex = CXIndex.Create()) { var builder = new CppModelBuilder { AutoSquashTypedef = options.AutoSquashTypedef, ParseSystemIncludes = options.ParseSystemIncludes, ParseAttributeEnabled = options.ParseAttributes, }; var compilation = builder.RootCompilation; string rootFileName = CppAstRootFileName; string rootFileContent = null; // Creates a fake 'header.hpp' that includes all the files we passed, so they are parsed all at once // Build the root input source file var tempBuilder = new StringBuilder(); if (options.PreHeaderText != null) { tempBuilder.AppendLine(options.PreHeaderText); } foreach (var file in cppFiles) { if (file.Content != null) { tempBuilder.AppendLine(file.Content); } else { var filePath = Path.Combine(Environment.CurrentDirectory, file.Filename); tempBuilder.AppendLine($"#include \"{filePath}\""); } } if (options.PostHeaderText != null) { tempBuilder.AppendLine(options.PostHeaderText); } // TODO: Add debug rootFileContent = tempBuilder.ToString(); var rootFileContentUTF8 = Encoding.UTF8.GetBytes(rootFileContent); compilation.InputText = rootFileContent; fixed(void *rootFileContentUTF8Ptr = rootFileContentUTF8) { CXTranslationUnit translationUnit; var rootFileNameUTF8 = Marshal.StringToHGlobalAnsi(rootFileName); translationUnit = CXTranslationUnit.Parse(createIndex, rootFileName, argumentsArray, new CXUnsavedFile[] { new CXUnsavedFile() { Contents = (sbyte *)rootFileContentUTF8Ptr, Filename = (sbyte *)rootFileNameUTF8, Length = new UIntPtr((uint)rootFileContentUTF8.Length) } }, translationFlags); // Skips the processing of the file if 1 error is raised // We don't want that, so remove that part //bool skipProcessing = false; //if (translationUnit.NumDiagnostics != 0) //{ // for (uint i = 0; i < translationUnit.NumDiagnostics; ++i) // { // using (var diagnostic = translationUnit.GetDiagnostic(i)) // { // var message = GetMessageAndLocation(rootFileContent, diagnostic, out var location); // switch (diagnostic.Severity) // { // case CXDiagnosticSeverity.CXDiagnostic_Ignored: // case CXDiagnosticSeverity.CXDiagnostic_Note: // compilation.Diagnostics.Info(message, location); // break; // case CXDiagnosticSeverity.CXDiagnostic_Warning: // compilation.Diagnostics.Warning(message, location); // break; // case CXDiagnosticSeverity.CXDiagnostic_Error: // case CXDiagnosticSeverity.CXDiagnostic_Fatal: // compilation.Diagnostics.Error(message, location); // skipProcessing = true; // break; // } // } // } //} //if (skipProcessing) //{ // compilation.Diagnostics.Warning($"Compilation aborted due to one or more errors listed above.", new CppSourceLocation(rootFileName, 0, 1, 1)); //} //else //{ using (translationUnit) { translationUnit.Cursor.VisitChildren(builder.VisitTranslationUnit, clientData: default); } //} } return(compilation); } }