private void WriteDocumentation([NotNull] SourceWriter sw, [NotNull] FunctionSignature function) { if (!_documentation.TryGetDocumentation(function, out var documentation)) { Debug.WriteLine ( $"The function \"{function.Name}\" lacks documentation. Consider adding a documentation file for " + $"the function." ); WritePlaceholderDocumentation(sw, function); return; } sw.WriteLine("/// <summary>"); var summaryLines = documentation.Purpose.TrimEnd().Split('\n'); foreach (var summaryLine in summaryLines) { sw.WriteLine($"/// {summaryLine}"); } sw.WriteLine("/// </summary>"); for (int i = 0; i < function.Parameters.Count; ++i) { var parameter = function.Parameters[i]; var parameterDocumentation = documentation.Parameters.FirstOrDefault(dp => dp.Name == parameter.Name); if (parameterDocumentation is null) { sw.WriteLine($"/// <param name=\"{parameter.Name}\">This parameter lacks documentation.</param>"); continue; } // XML documentation doesn't require keyword escaping. if (parameter.Name.TrimStart('@') != parameterDocumentation.Name) { Debug.WriteLine ( $"Parameter {parameter.Name} in function {function.Name} has incorrect documentation name " + $"{parameterDocumentation.Name}." ); } sw.WriteLine($"/// <param name=\"{parameterDocumentation.Name}\">"); if (!(parameter.Count is null)) { if (parameter.Count.IsStatic) { sw.WriteLine($"/// This parameter contains {parameter.Count.Count} elements."); sw.WriteLine("///"); } if (parameter.Count.IsComputed) { var parameterList = parameter.Count.ComputedFrom.Select(cf => cf.Name).Humanize(); sw.WriteLine($"/// This parameter's element count is computed from {parameterList}."); sw.WriteLine("///"); } if (parameter.Count.IsReference) { // ReSharper disable once PossibleNullReferenceException sw.WriteLine($"/// This parameter's element count is taken from {parameter.Count.ValueReference.Name}."); sw.WriteLine("///"); } } var descriptionLines = parameterDocumentation.Description.TrimEnd().Split('\n'); foreach (var descriptionLine in descriptionLines) { sw.WriteLine($"/// {descriptionLine}"); } sw.WriteLine("/// </param>"); } foreach (var genericTypeParameter in function.GenericTypeParameters) { var referencingParameter = function.Parameters.First(f => f.Type.Name == genericTypeParameter.Name); sw.WriteLine ( $"/// <typeparam name=\"{genericTypeParameter.Name}\">The generic type of " + $"{referencingParameter.Name}.</typeparam>" ); } if (!function.ReturnType.Name.Equals(typeof(void).Name, StringComparison.OrdinalIgnoreCase)) { sw.WriteLine("/// <returns>See online documentation.</returns>"); } }
private void WriteCategoryWrapperFile ( [NotNull] string extensionOutputDirectory, [NotNull] string extensionName, [NotNull] string category ) { var translatedExtensionName = _identifierTranslator.Translate(extensionName); var nativeSignatures = _profile.NativeSignatures .Where(ns => ns.Extension == extensionName && ns.Categories.First() == category) .OrderBy(s => s.Name) .ThenBy(s => s.Parameters.Count) .ToList(); var overloads = _profile.Overloads .Where(o => o.Extension == extensionName && o.Categories.First() == category) .ToList(); var categorySignatures = nativeSignatures .Concat(overloads) .OrderBy(s => s.Name) .ThenBy(s => s.Parameters.Count) .ThenBy(s => s.ToString()) .ToList(); if (categorySignatures.Count == 0) { return; } var translatedCategoryName = _identifierTranslator.Translate(category); // Create the category file var tempWrapperOutputPath = Path.GetTempFileName(); using (var outputFile = File.Open(tempWrapperOutputPath, FileMode.OpenOrCreate)) { using (var sw = new SourceWriter(new StreamWriter(outputFile))) { sw.WriteLine(EmbeddedResources.LicenseText); sw.WriteLineNoTabs(); sw.WriteLine("using System;"); sw.WriteLine("using System.Runtime.InteropServices;"); sw.WriteLine("using System.Text;"); sw.WriteLineNoTabs(); sw.WriteLine($"namespace {_generatorSettings.Namespace}"); using (sw.BeginBlock()) { SourceWriterBlock?outerBlock = null; if (extensionName == "Core") { sw.WriteLine($"public sealed partial class {_generatorSettings.ClassName}"); } else { sw.WriteLine($"public sealed partial class {_generatorSettings.ClassName}"); outerBlock = sw.BeginBlock(); sw.WriteLine("/// <summary>"); sw.WriteLine ( $"/// Contains native bindings to functions in the category" + $" \"{translatedCategoryName}\" in the extension " + $"\"{translatedExtensionName}\"." ); sw.WriteLine("/// </summary>"); // Identifiers can't begin with numbers var safeExtensionName = char.IsDigit(extensionName[0]) ? $"{_generatorSettings.ConstantPrefix}{extensionName}" : extensionName; sw.WriteLine($"public static partial class {safeExtensionName}"); } // Write the native signature methods // Write the overload methods using (sw.BeginBlock()) { var last = categorySignatures[categorySignatures.Count - 1]; foreach (var signature in categorySignatures) { WriteMethod(sw, signature); if (signature != last) { sw.WriteLineNoTabs(); } } sw.WriteLineNoTabs(); sw.WriteLine( "#pragma warning disable SA1300 // Element should begin with an upper-case letter"); sw.WriteLineNoTabs(); // Write the native signature externs // These are required by the patcher. foreach (var signature in nativeSignatures) { sw.WriteLine( $"[Slot({_entrypointSlots[signature.NativeEntrypoint].ToString()})]"); sw.WriteLine( "[DllImport(Library, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]"); sw.WriteLine ( $"private static extern {GetNativeDeclarationString(signature)};" ); if (signature != nativeSignatures.Last()) { sw.WriteLineNoTabs(); } } } // We've got a nested class to close outerBlock?.Dispose(); } } } var outputCategorySubPath = Path.Combine(extensionOutputDirectory, $"{translatedCategoryName}.cs"); if (File.Exists(outputCategorySubPath)) { File.Delete(outputCategorySubPath); } File.Move(tempWrapperOutputPath, outputCategorySubPath); }
private void WriteWrapperClassConstructorFile([NotNull] string wrappersOutputDirectory) { var tempInteropFilePath = Path.GetTempFileName(); using (var outputFile = File.Open(tempInteropFilePath, FileMode.OpenOrCreate)) { using (var sw = new SourceWriter(new StreamWriter(outputFile))) { sw.WriteLine(EmbeddedResources.LicenseText); sw.WriteLineNoTabs(); sw.WriteLine("using System;"); sw.WriteLine("using System.Runtime.InteropServices;"); sw.WriteLine("using System.Text;"); sw.WriteLineNoTabs(); sw.WriteLine($"namespace {_generatorSettings.Namespace}"); using (sw.BeginBlock()) { sw.WriteLine($"public sealed partial class {_generatorSettings.ClassName}"); using (sw.BeginBlock()) { // Write constructor sw.WriteLine($"static {_generatorSettings.ClassName}()"); using (sw.BeginBlock()) { var nativeEntrypointsWithPrefix = _profile.NativeSignatures .Select ( ns => $"{_generatorSettings.FunctionPrefix}{ns.Name}" ) .ToList(); // Write entry point names. // Instead of strings, which are costly to construct, // we use a 1d array of ASCII bytes. Names are laid out // sequentially, with a nul-terminator between them. sw.WriteLine("EntryPointNames = new byte[]"); using (sw.BeginBlock(true)) { foreach (var nativeEntrypoint in nativeEntrypointsWithPrefix) { var byteString = string.Join ( ", ", Encoding.ASCII.GetBytes(nativeEntrypoint).Select(b => b.ToString()).ToArray() ); sw.WriteLine($"{byteString}, 0,"); } } sw.WriteLineNoTabs(); // Write entry point name offsets. // This is an array of offsets into the EntryPointNames[] array above. sw.WriteLine("EntryPointNameOffsets = new int[]"); using (sw.BeginBlock(true)) { var offset = 0; foreach (var nativeEntrypoint in nativeEntrypointsWithPrefix) { sw.WriteLine($"{offset.ToString()},"); offset += nativeEntrypoint.Length + 1; } } sw.WriteLineNoTabs(); sw.WriteLine("EntryPoints = new IntPtr[EntryPointNameOffsets.Length];"); } } } } } var outputFilePath = Path.Combine(wrappersOutputDirectory, $"{_generatorSettings.ClassName}.cs"); if (File.Exists(outputFilePath)) { File.Delete(outputFilePath); } File.Move(tempInteropFilePath, outputFilePath); }
/// <summary> /// Initializes a new instance of the <see cref="SourceWriterIndentation"/> class. /// </summary> /// <param name="sourceWriter">The source writer to manage the indentation of.</param> public SourceWriterIndentation([NotNull] SourceWriter sourceWriter) { _sourceWriter = sourceWriter; _sourceWriter.Indent++; }