diff --git a/binding/SkiaSharp/SKStream.cs b/binding/SkiaSharp/SKStream.cs index af0191695ec..9d851d3001d 100644 --- a/binding/SkiaSharp/SKStream.cs +++ b/binding/SkiaSharp/SKStream.cs @@ -149,7 +149,8 @@ public bool Seek (int position) return SkiaApi.sk_stream_seek (Handle, (IntPtr)position); } - public bool Move (long offset) => Move ((int)offset); + [Obsolete ("The native stream move offset is capped at a 32-bit int. Use Move(int) instead.")] + public bool Move (long offset) => Move (checked ((int)offset)); public bool Move (int offset) { diff --git a/binding/SkiaSharp/SkiaApi.generated.cs b/binding/SkiaSharp/SkiaApi.generated.cs index a312edccff8..a3310a4ea55 100644 --- a/binding/SkiaSharp/SkiaApi.generated.cs +++ b/binding/SkiaSharp/SkiaApi.generated.cs @@ -14397,7 +14397,7 @@ internal static bool sk_stream_is_at_end (sk_stream_t cstream) => (sk_stream_is_at_end_delegate ??= GetSymbol ("sk_stream_is_at_end")).Invoke (cstream); #endif - // bool sk_stream_move(sk_stream_t* cstream, int offset) + // bool sk_stream_move(sk_stream_t* cstream, int32_t offset) #if !USE_DELEGATES #if USE_LIBRARY_IMPORT [LibraryImport (SKIA)] @@ -17533,7 +17533,7 @@ namespace SkiaSharp { [return: MarshalAs (UnmanagedType.I1)] internal unsafe delegate bool SKManagedStreamIsAtEndProxyDelegate(sk_stream_managedstream_t s, void* context); - // typedef bool (*)(sk_stream_managedstream_t* s, void* context, int offset)* sk_managedstream_move_proc + // typedef bool (*)(sk_stream_managedstream_t* s, void* context, int32_t offset)* sk_managedstream_move_proc [UnmanagedFunctionPointer (CallingConvention.Cdecl)] [return: MarshalAs (UnmanagedType.I1)] internal unsafe delegate bool SKManagedStreamMoveProxyDelegate(sk_stream_managedstream_t s, void* context, Int32 offset); diff --git a/externals/skia b/externals/skia index 0c28aa73bee..bc1f05a1bb7 160000 --- a/externals/skia +++ b/externals/skia @@ -1 +1 @@ -Subproject commit 0c28aa73beebf4747af531a893b4e8d94690f5cf +Subproject commit bc1f05a1bb7ea07d5e97f1b7cf5b9e8a1e37435f diff --git a/utils/SkiaSharpGenerator/BaseTool.cs b/utils/SkiaSharpGenerator/BaseTool.cs index 29cdbe85f3b..e0f3ded65ae 100644 --- a/utils/SkiaSharpGenerator/BaseTool.cs +++ b/utils/SkiaSharpGenerator/BaseTool.cs @@ -115,6 +115,39 @@ protected void ParseSkiaHeaders() } } + if (OperatingSystem.IsLinux()) + { + var process = new System.Diagnostics.Process + { + StartInfo = new System.Diagnostics.ProcessStartInfo + { + FileName = "clang", + Arguments = "-print-resource-dir", + RedirectStandardOutput = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + try + { + process.Start(); + var resourceDir = process.StandardOutput.ReadToEnd().Trim(); + process.WaitForExit(); + if (!string.IsNullOrEmpty(resourceDir)) + { + var include = Path.Combine(resourceDir, "include"); + if (Directory.Exists(include)) + options.SystemIncludeFolders.Add(include); + } + } + catch { } + foreach (var sys in new[] { "/usr/include/x86_64-linux-gnu", "/usr/include" }) + { + if (Directory.Exists(sys)) + options.SystemIncludeFolders.Add(sys); + } + } + foreach (var header in config.IncludeDirs) { var path = Path.Combine(SkiaRoot, header); @@ -192,14 +225,16 @@ protected void LoadStandardMappings() { "signed int", nameof(Int32) }, { "unsigned", nameof(UInt32) }, { "unsigned int", nameof(UInt32) }, - { "long", nameof(Int64) }, - { "long int", nameof(Int64) }, + // NOTE: C `long` / `unsigned long` / `signed long` (and the + // equivalent `long int` spellings) are intentionally omitted. + // Their size is platform-dependent (32-bit on Windows LLP64, + // 64-bit on Linux/macOS LP64), so no single managed type maps + // correctly on all platforms. GetType() below rejects them with + // a diagnostic pointing at int32_t / int64_t. { "long long", nameof(Int64) }, { "long long int", nameof(Int64) }, - { "signed long", nameof(Int64) }, - { "signed long int", nameof(Int64) }, - { "unsigned long", nameof(UInt64) }, - { "unsigned long int", nameof(UInt64) }, + { "unsigned long long", nameof(UInt64) }, + { "unsigned long long int", nameof(UInt64) }, { "float", nameof(Single) }, { "double", nameof(Double) }, // TODO: long double, wchar_t ? @@ -317,6 +352,16 @@ protected async Task LoadConfigAsync(string configPath) return null; } + // C types whose sizes differ between LLP64 (Windows) and LP64 (Linux/macOS) + // platforms. There is no single managed mapping that is correct on both, + // so we refuse to generate bindings for them instead of silently picking + // one and propagating the platform-dependent bug. + private static readonly HashSet PlatformDependentLongTypes = new() + { + "long", "signed long", "unsigned long", + "long int", "signed long int", "unsigned long int", + }; + protected string GetType(CppType type) { var typeName = GetCppType(type); @@ -326,6 +371,15 @@ protected string GetType(CppType type) var pointers = pointerIndex == -1 ? "" : typeName.Substring(pointerIndex); var noPointers = pointerIndex == -1 ? typeName : typeName.Substring(0, pointerIndex); + if (PlatformDependentLongTypes.Contains(noPointers.TrimEnd())) + { + throw new InvalidOperationException( + $"C type '{noPointers.TrimEnd()}' has a platform-dependent size " + + $"(32-bit on Windows LLP64, 64-bit on Linux/macOS LP64). " + + $"Use int32_t or int64_t explicitly in the C header, " + + $"or add an explicit type override in the JSON config."); + } + if (skiaTypes.TryGetValue(noPointers, out var isStruct)) { if (!isStruct) @@ -352,8 +406,12 @@ protected static string GetCppType(CppType type) { var typeName = type.GetDisplayName(); - // remove the const - typeName = typeName.Replace("const ", ""); + // remove the const (both prefix and suffix forms — CppAst switched between them) + typeName = typeName.Replace("const ", "").Replace(" const", ""); + + // normalize whitespace around pointer/reference sigils: newer CppAst emits + // "T *" instead of "T*", which breaks the pointer/name split downstream + typeName = typeName.Replace(" *", "*").Replace(" &", "&"); // replace the [] with a * int start; diff --git a/utils/SkiaSharpGenerator/Generate/Generator.cs b/utils/SkiaSharpGenerator/Generate/Generator.cs index cd8220840b2..1a306fa3d08 100644 --- a/utils/SkiaSharpGenerator/Generate/Generator.cs +++ b/utils/SkiaSharpGenerator/Generate/Generator.cs @@ -120,7 +120,7 @@ private void WriteDelegate(TextWriter writer, CppTypedef del) functionMappings.TryGetValue(name, out var map); name = map?.CsType ?? CleanName(name); - writer.WriteLine($"\t// {del}"); + writer.WriteLine($"\t// {FormatCppSignature(del.ToString())}"); writer.WriteLine($"\t[UnmanagedFunctionPointer (CallingConvention.Cdecl)]"); var (paramsList, returnType) = GetManagedFunctionArguments(function, map); @@ -193,7 +193,7 @@ private void WriteStruct(TextWriter writer, CppClass klass) var funcPointerType = GetFunctionPointerType(field.Type); var cppT = GetCppType(field.Type); - writer.WriteLine($"\t\t// {field}"); + writer.WriteLine($"\t\t// {FormatCppSignature(field.ToString())}"); var fieldName = field.Name; var isPrivate = fieldName.StartsWith("_private_", StringComparison.OrdinalIgnoreCase); @@ -540,7 +540,7 @@ private void WriteFunctions(TextWriter writer) } writer.WriteLine(); - writer.WriteLine($"\t\t// {function}"); + writer.WriteLine($"\t\t// {FormatCppSignature(function)}"); writer.WriteLine($"\t\t#if !USE_DELEGATES"); writer.WriteLine($"\t\t#if USE_LIBRARY_IMPORT"); writer.WriteLine($"\t\t[LibraryImport ({config.DllName})]"); @@ -654,6 +654,18 @@ private void WriteDelegateProxy(TextWriter writer, CppTypedef del) } } + // Normalize CppAst's ToString() output to the historical style the checked-in + // generated files use: "const T*" instead of "T const *", no space before *. + private static string FormatCppSignature(CppFunction function) => + FormatCppSignature(function.ToString()); + + private static string FormatCppSignature(string s) + { + s = System.Text.RegularExpressions.Regex.Replace(s, @"(\w+)\s+const\b", "const $1"); + s = s.Replace(" *", "*").Replace(" &", "&"); + return s; + } + private void WriteDocIfAny(TextWriter writer, string key, string indent) { if (PreviousDocumentation?.TryGet(key, out var xml) != true) diff --git a/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj b/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj index 39e4a01831c..ac2f26423ee 100644 --- a/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj +++ b/utils/SkiaSharpGenerator/SkiaSharpGenerator.csproj @@ -13,7 +13,7 @@ - +