diff --git a/.github/workflows/nuget.org-linux.yml b/.github/workflows/nuget.org-linux.yml new file mode 100644 index 00000000..cd3844b0 --- /dev/null +++ b/.github/workflows/nuget.org-linux.yml @@ -0,0 +1,34 @@ +# This is a basic workflow to help you get started with Actions + +name: nuget.org-linux + +on: + push: { branches: [ master ] } + pull_request: { branches: [ master ] } + release: { types: [published] } # runs on “Publish release” button + workflow_dispatch: # lets you run it by hand + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + name: Checkout Code + with: + fetch-depth: 0 + + # Necessary for running neato + - name: Install libgts + run: sudo apt-get update && sudo apt-get install -y libgts-0.7-5 + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 8.0.x + + - name: Setup Just + uses: extractions/setup-just@v3 + + - name: Build and test + run: just test-nugetorg diff --git a/.github/workflows/nuget.org-windows.yml b/.github/workflows/nuget.org-windows.yml new file mode 100644 index 00000000..6b9c71c9 --- /dev/null +++ b/.github/workflows/nuget.org-windows.yml @@ -0,0 +1,30 @@ +# This is a basic workflow to help you get started with Actions + +name: nuget.org-windows + +on: + push: { branches: [ master ] } + pull_request: { branches: [ master ] } + release: { types: [published] } # runs on “Publish release” button + workflow_dispatch: # lets you run it by hand + +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + name: Checkout Code + with: + fetch-depth: 0 + + - name: Add msbuild to PATH + uses: microsoft/setup-msbuild@v1.0.2 + + - name: Setup NuGet.exe for use with actions + uses: NuGet/setup-nuget@v1.0.5 + + - name: Setup Just + uses: extractions/setup-just@v3 + + - name: Build and test + run: just test-nugetorg diff --git a/How-to-release.txt b/How-to-release.txt index 41f1ebeb..23023374 100644 --- a/How-to-release.txt +++ b/How-to-release.txt @@ -1,12 +1,10 @@ -- Bump the version number in Rubjerg.Graphviz.csproj to the version to be released. -- Make a PR and grab the nuget package from the CI artifacts. +- Make a prerelease on github (and add a new tag there). This will trigger the CI. +- Grab the nuget package from the CI artifacts. - Upload the .nupkg file to nuget.org. - Upload to https://www.nuget.org/packages/manage/upload + - Mark as unlisted - Make a cup of coffee - Update the version of the referenced Nuget package in the NugetOrgTest project to the package that was just uploaded and run the test. - - dotnet add Rubjerg.Graphviz.NugetOrgTest\Rubjerg.Graphviz.NugetOrgTest.csproj package Rubjerg.Graphviz -- If the build and test succeeds we are ready to release. Merge the PR, add the tag, and push this to github. - - Merge PR on github - - git tag vx.y.z - - git push github --tags -- In GitHub, add a new release of the just pushed commit at https://github.com/Rubjerg/Graphviz.NetWrapper/releases. + - dotnet add NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj package Rubjerg.Graphviz + - Make a PR for this and merge when this succeeds. +- Mark prerelease as latest release on github and nuget.org diff --git a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj index df767f28..dfcef5fb 100644 --- a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj +++ b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj @@ -5,16 +5,20 @@ net8.0 x64 x64 - Chiel ten Brinke - Rubjerg ..\packages true + latest embedded + + + + + true - 2.0.2 + 3.0.0 3.18.3 diff --git a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj index eccd2c9a..cdebedd1 100644 --- a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj +++ b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj @@ -5,13 +5,16 @@ net8.0 x64 x64 - Chiel ten Brinke - Rubjerg ..\packages true + latest embedded + + true + + diff --git a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs index eb1aec2e..35d168c6 100644 --- a/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs +++ b/NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Test.cs @@ -1,5 +1,4 @@ using NUnit.Framework; -using Rubjerg.Graphviz.Test; namespace Rubjerg.Graphviz.NugetOrgTest { diff --git a/Rubjerg.Graphviz.Test/Reproductions.cs b/Rubjerg.Graphviz.Test/Reproductions.cs index de87676a..e935b019 100644 --- a/Rubjerg.Graphviz.Test/Reproductions.cs +++ b/Rubjerg.Graphviz.Test/Reproductions.cs @@ -3,7 +3,7 @@ using System.Globalization; using System.IO; using System.Linq; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; namespace Rubjerg.Graphviz.Test; diff --git a/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj b/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj index ae9652cb..c81c2133 100644 --- a/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj +++ b/Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj @@ -5,8 +5,6 @@ net8.0 x64 x64 - Chiel ten Brinke - Rubjerg ..\packages true latest diff --git a/Rubjerg.Graphviz.Test/TestInterop.cs b/Rubjerg.Graphviz.Test/TestInterop.cs index a4960be4..193cc3c5 100644 --- a/Rubjerg.Graphviz.Test/TestInterop.cs +++ b/Rubjerg.Graphviz.Test/TestInterop.cs @@ -1,7 +1,8 @@ using NUnit.Framework; using System.IO; using System; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; +using static Rubjerg.Graphviz.FFI.TestLib; namespace Rubjerg.Graphviz.Test; diff --git a/Rubjerg.Graphviz.Test/TestXDotLayout.cs b/Rubjerg.Graphviz.Test/TestXDotLayout.cs index 6b98160f..8ab2be4e 100644 --- a/Rubjerg.Graphviz.Test/TestXDotLayout.cs +++ b/Rubjerg.Graphviz.Test/TestXDotLayout.cs @@ -4,6 +4,8 @@ namespace Rubjerg.Graphviz.Test; +using FFI; + [TestFixture()] public class TestXDotLayout { diff --git a/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj b/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj index 4b172ff3..a160b68b 100644 --- a/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj +++ b/Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj @@ -5,14 +5,16 @@ net8.0 x64 x64 - Chiel ten Brinke - Rubjerg ..\packages true latest embedded + + true + + 3.18.3 diff --git a/Rubjerg.Graphviz/CGraphThing.cs b/Rubjerg.Graphviz/CGraphThing.cs index 846a277e..3c34fe1f 100644 --- a/Rubjerg.Graphviz/CGraphThing.cs +++ b/Rubjerg.Graphviz/CGraphThing.cs @@ -2,8 +2,9 @@ using System.Linq; using System.Collections.Generic; using System.Diagnostics; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; using System.Globalization; +using Rubjerg.Graphviz.FFI; namespace Rubjerg.Graphviz; diff --git a/Rubjerg.Graphviz/Constants.cs b/Rubjerg.Graphviz/Constants.cs deleted file mode 100644 index c3a6930b..00000000 --- a/Rubjerg.Graphviz/Constants.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Rubjerg.Graphviz; - -internal static class Constants -{ -#if _WINDOWS - public const string GvcLib = "gvc.dll"; - public const string CGraphLib = "cgraph.dll"; - public const string XDotLib = "xdot.dll"; - public const string GraphvizWrapperLib = "GraphvizWrapper.dll"; -#else - public const string GvcLib = "libgvc.so.6"; - public const string CGraphLib = "libcgraph.so.6"; - public const string XDotLib = "libxdot.so.4"; - public const string GraphvizWrapperLib = "libGraphvizWrapper.so"; -#endif -} diff --git a/Rubjerg.Graphviz/Edge.cs b/Rubjerg.Graphviz/Edge.cs index bb6cc76a..9a22a526 100644 --- a/Rubjerg.Graphviz/Edge.cs +++ b/Rubjerg.Graphviz/Edge.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; namespace Rubjerg.Graphviz; diff --git a/Rubjerg.Graphviz/FFI/Constants.cs b/Rubjerg.Graphviz/FFI/Constants.cs new file mode 100644 index 00000000..37a8e98a --- /dev/null +++ b/Rubjerg.Graphviz/FFI/Constants.cs @@ -0,0 +1,14 @@ +namespace Rubjerg.Graphviz.FFI; + +internal static class Constants +{ + public const string GvcLibNameWindows = "gvc"; + public const string CGraphLibNameWindows = "cgraph"; + public const string XDotLibNameWindows = "xdot"; + + public const string GvcLibNameLinux = "libgvc.so.6"; + public const string CGraphLibNameLinux = "libcgraph.so.6"; + public const string XDotLibNameLinux = "libxdot.so.4"; + + public const string GraphvizWrapperLibName = "GraphvizWrapper"; +} diff --git a/Rubjerg.Graphviz/FFI/GraphvizFFI.cs b/Rubjerg.Graphviz/FFI/GraphvizFFI.cs new file mode 100644 index 00000000..71ec6fdc --- /dev/null +++ b/Rubjerg.Graphviz/FFI/GraphvizFFI.cs @@ -0,0 +1,563 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Marshaling; +using static Platform; + +/// +/// Graphviz is thread unsafe, so we wrap all function calls inside a lock to make sure we don't run into +/// issues caused by multiple threads accessing the graphviz datastructures (like the GC executing a destructor). +/// +internal static class GraphvizFFI +{ + private static readonly object _mutex = new object(); + + public static IntPtr GvContext() + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.gvContext() : GraphvizLibLinux.gvContext(); + } + } + public static int GvFreeContext(IntPtr gvc) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.gvFreeContext(gvc) : GraphvizLibLinux.gvFreeContext(gvc); + } + } + public static int GvLayout(IntPtr gvc, IntPtr graph, string engine) + { + lock (_mutex) + { + return MarshalToUtf8(engine, enginePtr => IsWindows ? GraphvizLibWindows.gvLayout(gvc, graph, enginePtr) : GraphvizLibLinux.gvLayout(gvc, graph, enginePtr)); + } + } + public static int GvFreeLayout(IntPtr gvc, IntPtr graph) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.gvFreeLayout(gvc, graph) : GraphvizLibLinux.gvFreeLayout(gvc, graph); + } + } + public static int GvRender(IntPtr gvc, IntPtr graph, string? format, IntPtr @out) + { + lock (_mutex) + { + return MarshalToUtf8(format, formatPtr => IsWindows ? GraphvizLibWindows.gvRender(gvc, graph, formatPtr, @out) : GraphvizLibLinux.gvRender(gvc, graph, formatPtr, @out)); + } + } + public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string? format, string? filename) + { + lock (_mutex) + { + return MarshalToUtf8(format, formatPtr => MarshalToUtf8(filename, filenamePtr => IsWindows ? GraphvizLibWindows.gvRenderFilename(gvc, graph, formatPtr, filenamePtr) : GraphvizLibLinux.gvRenderFilename(gvc, graph, formatPtr, filenamePtr))); + } + } + public static IntPtr Agnode(IntPtr graph, string? name, int create) + { + lock (_mutex) + { + return MarshalToUtf8(name, namePtr => IsWindows ? GraphvizLibWindows.agnode(graph, namePtr, create) : GraphvizLibLinux.agnode(graph, namePtr, create)); + } + } + public static int Agdegree(IntPtr graph, IntPtr node, int inset, int outset) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agdegree(graph, node, inset, outset) : GraphvizLibLinux.agdegree(graph, node, inset, outset); + } + } + public static IntPtr Agfstout(IntPtr graph, IntPtr node) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agfstout(graph, node) : GraphvizLibLinux.agfstout(graph, node); + } + } + public static IntPtr Agnxtout(IntPtr graph, IntPtr edge) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agnxtout(graph, edge) : GraphvizLibLinux.agnxtout(graph, edge); + } + } + public static IntPtr Agfstin(IntPtr graph, IntPtr node) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agfstin(graph, node) : GraphvizLibLinux.agfstin(graph, node); + } + } + public static IntPtr Agnxtin(IntPtr graph, IntPtr edge) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agnxtin(graph, edge) : GraphvizLibLinux.agnxtin(graph, edge); + } + } + public static IntPtr Agfstedge(IntPtr graph, IntPtr node) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agfstedge(graph, node) : GraphvizLibLinux.agfstedge(graph, node); + } + } + public static IntPtr Agnxtedge(IntPtr graph, IntPtr edge, IntPtr node) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agnxtedge(graph, edge, node) : GraphvizLibLinux.agnxtedge(graph, edge, node); + } + } + public static void Agattr(IntPtr graph, int type, string name, string deflt) + { + lock (_mutex) + { + MarshalToUtf8(deflt, defltPtr => + MarshalToUtf8(name, namePtr => + { + if (IsWindows) + GraphvizLibWindows.agattr(graph, type, namePtr, defltPtr); + else + GraphvizLibLinux.agattr(graph, type, namePtr, defltPtr); + })); + } + } + public static void AgattrHtml(IntPtr graph, int type, string name, string deflt) + { + lock (_mutex) + { + MarshalToUtf8(name, namePtr => + MarshalToUtf8(deflt, defltPtr => + { + if (IsWindows) + { + var htmlPtr = GraphvizLibWindows.agstrdup_html(GraphvizLibWindows.agroot(graph), defltPtr); + GraphvizLibWindows.agattr(graph, type, namePtr, htmlPtr); + } + else + { + var htmlPtr = GraphvizLibLinux.agstrdup_html(GraphvizLibLinux.agroot(graph), defltPtr); + GraphvizLibLinux.agattr(graph, type, namePtr, htmlPtr); + } + })); + } + } + + public static void Agset(IntPtr obj, string name, string value) + { + lock (_mutex) + { + MarshalToUtf8(name, namePtr => + MarshalToUtf8(value, valuePtr => + { + if (IsWindows) + GraphvizLibWindows.agset(obj, namePtr, valuePtr); + else + GraphvizLibLinux.agset(obj, namePtr, valuePtr); + })); + } + } + + public static void AgsetHtml(IntPtr obj, string name, string value) + { + lock (_mutex) + { + MarshalToUtf8(name, namePtr => + MarshalToUtf8(value, valuePtr => + { + if (IsWindows) + { + var htmlPtr = GraphvizLibWindows.agstrdup_html(GraphvizLibWindows.agroot(obj), valuePtr); + GraphvizLibWindows.agset(obj, namePtr, htmlPtr); + } + else + { + var htmlPtr = GraphvizLibLinux.agstrdup_html(GraphvizLibLinux.agroot(obj), valuePtr); + GraphvizLibLinux.agset(obj, namePtr, htmlPtr); + } + })); + } + } + + public static void Agsafeset(IntPtr obj, string name, string? val, string? deflt) + { + lock (_mutex) + { + MarshalToUtf8(name, namePtr => MarshalToUtf8(val, valPtr => MarshalToUtf8(deflt, defltPtr => + { + if (IsWindows) + GraphvizLibWindows.agsafeset(obj, namePtr, valPtr, defltPtr); + else + GraphvizLibLinux.agsafeset(obj, namePtr, valPtr, defltPtr); + }))); + } + } + public static void AgsafesetHtml(IntPtr obj, string name, string? val, string? deflt) + { + lock (_mutex) + { + MarshalToUtf8(name, namePtr => MarshalToUtf8(val, valPtr => MarshalToUtf8(deflt, defltPtr => + { + if (IsWindows) + { + var htmlPtr = GraphvizLibWindows.agstrdup_html(GraphvizLibWindows.agroot(obj), defltPtr); + GraphvizLibWindows.agsafeset(obj, namePtr, valPtr, htmlPtr); + } + else + { + var htmlPtr = GraphvizLibLinux.agstrdup_html(GraphvizLibLinux.agroot(obj), defltPtr); + GraphvizLibLinux.agsafeset(obj, namePtr, valPtr, htmlPtr); + } + }))); + } + } + public static IntPtr Agroot(IntPtr obj) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agroot(obj) : GraphvizLibLinux.agroot(obj); + } + } + public static IntPtr Agnxtattr(IntPtr obj, int kind, IntPtr attribute) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agnxtattr(obj, kind, attribute) : GraphvizLibLinux.agnxtattr(obj, kind, attribute); + } + } + public static int Agcopyattr(IntPtr from, IntPtr to) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agcopyattr(from, to) : GraphvizLibLinux.agcopyattr(from, to); + } + } + public static bool Ageqedge(IntPtr edge1, IntPtr edge2) + { + lock (_mutex) + { + return GraphvizWrapperLib.rj_ageqedge(edge1, edge2); + } + } + public static IntPtr Agtail(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.rj_agtail(node); + } + } + public static IntPtr Aghead(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.rj_aghead(node); + } + } + public static IntPtr Agedge(IntPtr graph, IntPtr tail, IntPtr head, string? name, int create) + { + lock (_mutex) + { + return MarshalToUtf8(name, namePtr => IsWindows ? GraphvizLibWindows.agedge(graph, tail, head, namePtr, create) : GraphvizLibLinux.agedge(graph, tail, head, namePtr, create)); + } + } + public static IntPtr Agmkin(IntPtr edge) + { + lock (_mutex) + { + return GraphvizWrapperLib.rj_agmkin(edge); + } + } + public static IntPtr Agmkout(IntPtr edge) + { + lock (_mutex) + { + return GraphvizWrapperLib.rj_agmkout(edge); + } + } + public static IntPtr Agparent(IntPtr obj) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agparent(obj) : GraphvizLibLinux.agparent(obj); + } + } + public static int Agclose(IntPtr graph) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agclose(graph) : GraphvizLibLinux.agclose(graph); + } + } + public static int Agdelete(IntPtr graph, IntPtr item) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agdelete(graph, item) : GraphvizLibLinux.agdelete(graph, item); + } + } + public static IntPtr Agfstnode(IntPtr graph) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agfstnode(graph) : GraphvizLibLinux.agfstnode(graph); + } + } + public static IntPtr Agnxtnode(IntPtr graph, IntPtr node) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agnxtnode(graph, node) : GraphvizLibLinux.agnxtnode(graph, node); + } + } + public static int Agcontains(IntPtr graph, IntPtr obj) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agcontains(graph, obj) : GraphvizLibLinux.agcontains(graph, obj); + } + } + public static IntPtr Agsubg(IntPtr graph, string? name, int create) + { + lock (_mutex) + { + return MarshalToUtf8(name, namePtr => IsWindows ? GraphvizLibWindows.agsubg(graph, namePtr, create) : GraphvizLibLinux.agsubg(graph, namePtr, create)); + } + } + public static IntPtr Agfstsubg(IntPtr graph) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agfstsubg(graph) : GraphvizLibLinux.agfstsubg(graph); + } + } + public static IntPtr Agnxtsubg(IntPtr graph) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agnxtsubg(graph) : GraphvizLibLinux.agnxtsubg(graph); + } + } + public static int Agisstrict(IntPtr ptr) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agisstrict(ptr) : GraphvizLibLinux.agisstrict(ptr); + } + } + public static int Agisdirected(IntPtr ptr) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agisdirected(ptr) : GraphvizLibLinux.agisdirected(ptr); + } + } + public static int Agisundirected(IntPtr ptr) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agisundirected(ptr) : GraphvizLibLinux.agisundirected(ptr); + } + } + public static IntPtr Agsubedge(IntPtr graph, IntPtr edge, int create) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agsubedge(graph, edge, create) : GraphvizLibLinux.agsubedge(graph, edge, create); + } + } + public static IntPtr Agsubnode(IntPtr graph, IntPtr node, int create) + { + lock (_mutex) + { + return IsWindows ? GraphvizLibWindows.agsubnode(graph, node, create) : GraphvizLibLinux.agsubnode(graph, node, create); + } + } + public static IntPtr EdgeLabel(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.edge_label(node); + } + } + public static string? Rjagmemwrite(IntPtr graph) + { + lock (_mutex) + { + var strPtr = GraphvizWrapperLib.rj_agmemwrite(graph); + return MarshalFromUtf8(strPtr, true); + } + } + public static IntPtr GraphLabel(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.graph_label(node); + } + } + public static string? Agget(IntPtr obj, string name) + { + lock (_mutex) + { + return MarshalToUtf8(name, namePtr => MarshalFromUtf8(IsWindows ? GraphvizLibWindows.agget(obj, namePtr) : GraphvizLibLinux.agget(obj, namePtr), false)); + } + } + public static string? Rjagnameof(IntPtr obj) + { + lock (_mutex) + { + return MarshalFromUtf8(IsWindows ? GraphvizLibWindows.agnameof(obj) : GraphvizLibLinux.agnameof(obj), false); + } + } + public static void CloneAttributeDeclarations(IntPtr graphfrom, IntPtr graphto) + { + lock (_mutex) + { + GraphvizWrapperLib.clone_attribute_declarations(graphfrom, graphto); + } + } + public static string? ImsymKey(IntPtr sym) + { + lock (_mutex) + { + return MarshalFromUtf8(GraphvizWrapperLib.rj_sym_key(sym), false); + } + } + public static double LabelX(IntPtr label) + { + lock (_mutex) + { + return GraphvizWrapperLib.label_x(label); + } + } + public static double LabelY(IntPtr label) + { + lock (_mutex) + { + return GraphvizWrapperLib.label_y(label); + } + } + public static double LabelWidth(IntPtr label) + { + lock (_mutex) + { + return GraphvizWrapperLib.label_width(label); + } + } + public static double LabelHeight(IntPtr label) + { + lock (_mutex) + { + return GraphvizWrapperLib.label_height(label); + } + } + public static string? LabelText(IntPtr label) + { + lock (_mutex) + { + return MarshalFromUtf8(GraphvizWrapperLib.label_text(label), false); + } + } + public static double LabelFontsize(IntPtr label) + { + lock (_mutex) + { + return GraphvizWrapperLib.label_fontsize(label); + } + } + public static string? LabelFontname(IntPtr label) + { + lock (_mutex) + { + return MarshalFromUtf8(GraphvizWrapperLib.label_fontname(label), false); + } + } + public static double NodeX(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.node_x(node); + } + } + public static double NodeY(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.node_y(node); + } + } + public static double NodeWidth(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.node_width(node); + } + } + public static double NodeHeight(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.node_height(node); + } + } + public static IntPtr NodeLabel(IntPtr node) + { + lock (_mutex) + { + return GraphvizWrapperLib.node_label(node); + } + } + public static void ConvertToUndirected(IntPtr graph) + { + lock (_mutex) + { + GraphvizWrapperLib.convert_to_undirected(graph); + } + } + public static IntPtr Rjagmemread(string input) + { + lock (_mutex) + { + return MarshalToUtf8(input, GraphvizWrapperLib.rj_agmemread); + } + } + public static IntPtr Rjagopen(string? name, int graphtype) + { + lock (_mutex) + { + return MarshalToUtf8(name, namePtr => GraphvizWrapperLib.rj_agopen(namePtr, graphtype)); + } + } + + + /// + /// A GraphvizContext is used to store various layout + /// information that is independent of a particular graph and + /// its attributes. It holds the data associated with plugins, + /// parsed - command lines, script engines, and anything else + /// with a scope potentially larger than one graph, up to the + /// scope of the application. In addition, it maintains lists of + /// the available layout algorithms and renderers; it also + /// records the most recent layout algorithm applied to a graph. + /// It can be used to specify multiple renderings of a given + /// graph layout into different associated files.It is also used + /// to store various global information used during rendering. + /// There should be just one GVC created for the entire + /// duration of an application. A single GVC value can be used + /// with multiple graphs, though with only one graph at a + /// time. In addition, if gvLayout() was invoked for a graph and + /// GVC, then gvFreeLayout() should be called before using + /// gvLayout() again, even on the same graph. + /// + public static IntPtr GVC { get; private set; } + static GraphvizFFI() + { + // We initialize the gvc here before interacting with graphviz + // https://gitlab.com/graphviz/graphviz/-/issues/2434 + GVC = GvContext(); + } + +} diff --git a/Rubjerg.Graphviz/FFI/GraphvizLib.Linux.cs b/Rubjerg.Graphviz/FFI/GraphvizLib.Linux.cs new file mode 100644 index 00000000..c6e7c5e7 --- /dev/null +++ b/Rubjerg.Graphviz/FFI/GraphvizLib.Linux.cs @@ -0,0 +1,93 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Constants; + +internal static class GraphvizLibLinux +{ + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void agattr(IntPtr graph, int type, IntPtr name, IntPtr deflt); + + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agedge(IntPtr graph, IntPtr tail, IntPtr head, IntPtr name, int create); + + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agclose(IntPtr graph); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agcontains(IntPtr graph, IntPtr obj); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agcopyattr(IntPtr from, IntPtr to); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agdegree(IntPtr graph, IntPtr node, int inset, int outset); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agdelete(IntPtr graph, IntPtr item); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstedge(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstin(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstnode(IntPtr graph); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstout(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstsubg(IntPtr graph); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agget(IntPtr obj, IntPtr name); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agisdirected(IntPtr ptr); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agisstrict(IntPtr ptr); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agisundirected(IntPtr ptr); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnameof(IntPtr obj); + + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnode(IntPtr graph, IntPtr name, int create); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtattr(IntPtr obj, int kind, IntPtr attribute); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtedge(IntPtr graph, IntPtr edge, IntPtr node); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtin(IntPtr graph, IntPtr edge); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtnode(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtout(IntPtr graph, IntPtr edge); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtsubg(IntPtr graph); + + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agparent(IntPtr obj); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agroot(IntPtr obj); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void agsafeset(IntPtr obj, IntPtr name, IntPtr val, IntPtr deflt); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void agset(IntPtr obj, IntPtr name, IntPtr value); + [DllImport(CGraphLibNameLinux, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agstrdup_html(IntPtr obj, IntPtr html); + + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agsubedge(IntPtr graph, IntPtr edge, int create); + + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agsubg(IntPtr graph, IntPtr name, int create); + [DllImport(CGraphLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agsubnode(IntPtr graph, IntPtr node, int create); + + [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr gvContext(); + [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvFreeContext(IntPtr gvc); + [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvFreeLayout(IntPtr gvc, IntPtr graph); + [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvLayout(IntPtr gvc, IntPtr graph, IntPtr engine); + [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvRender(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr @out); + [DllImport(GvcLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvRenderFilename(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr filename); +} \ No newline at end of file diff --git a/Rubjerg.Graphviz/FFI/GraphvizLib.Windows.cs b/Rubjerg.Graphviz/FFI/GraphvizLib.Windows.cs new file mode 100644 index 00000000..8717ca30 --- /dev/null +++ b/Rubjerg.Graphviz/FFI/GraphvizLib.Windows.cs @@ -0,0 +1,93 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Constants; + +internal static class GraphvizLibWindows +{ + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void agattr(IntPtr graph, int type, IntPtr name, IntPtr deflt); + + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agedge(IntPtr graph, IntPtr tail, IntPtr head, IntPtr name, int create); + + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agclose(IntPtr graph); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agcontains(IntPtr graph, IntPtr obj); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agcopyattr(IntPtr from, IntPtr to); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agdegree(IntPtr graph, IntPtr node, int inset, int outset); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agdelete(IntPtr graph, IntPtr item); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstedge(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstin(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstnode(IntPtr graph); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstout(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agfstsubg(IntPtr graph); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agget(IntPtr obj, IntPtr name); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agisdirected(IntPtr ptr); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agisstrict(IntPtr ptr); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int agisundirected(IntPtr ptr); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnameof(IntPtr obj); + + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnode(IntPtr graph, IntPtr name, int create); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtattr(IntPtr obj, int kind, IntPtr attribute); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtedge(IntPtr graph, IntPtr edge, IntPtr node); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtin(IntPtr graph, IntPtr edge); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtnode(IntPtr graph, IntPtr node); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtout(IntPtr graph, IntPtr edge); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agnxtsubg(IntPtr graph); + + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agparent(IntPtr obj); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agroot(IntPtr obj); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void agsafeset(IntPtr obj, IntPtr name, IntPtr val, IntPtr deflt); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void agset(IntPtr obj, IntPtr name, IntPtr value); + [DllImport(CGraphLibNameWindows, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agstrdup_html(IntPtr obj, IntPtr html); + + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agsubedge(IntPtr graph, IntPtr edge, int create); + + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agsubg(IntPtr graph, IntPtr name, int create); + [DllImport(CGraphLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr agsubnode(IntPtr graph, IntPtr node, int create); + + [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr gvContext(); + [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvFreeContext(IntPtr gvc); + [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvFreeLayout(IntPtr gvc, IntPtr graph); + [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvLayout(IntPtr gvc, IntPtr graph, IntPtr engine); + [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvRender(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr @out); + [DllImport(GvcLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern int gvRenderFilename(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr filename); +} \ No newline at end of file diff --git a/Rubjerg.Graphviz/FFI/GraphvizWrapperLib.cs b/Rubjerg.Graphviz/FFI/GraphvizWrapperLib.cs new file mode 100644 index 00000000..53478c23 --- /dev/null +++ b/Rubjerg.Graphviz/FFI/GraphvizWrapperLib.cs @@ -0,0 +1,255 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Marshaling; +using static Constants; + +internal static class GraphvizWrapperLib +{ + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void clone_attribute_declarations(IntPtr graphfrom, IntPtr graphto); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern void convert_to_undirected(IntPtr graph); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr edge_label(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr graph_label(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr label_fontname(IntPtr label); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double label_fontsize(IntPtr label); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double label_height(IntPtr label); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr label_text(IntPtr label); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double label_width(IntPtr label); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double label_x(IntPtr label); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double label_y(IntPtr label); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double node_height(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr node_label(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double node_width(IntPtr node); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double node_x(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern double node_y(IntPtr node); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + internal static extern bool rj_ageqedge(IntPtr edge1, IntPtr edge2); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_aghead(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_agmemread(IntPtr input); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_agmemwrite(IntPtr graph); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_agmkin(IntPtr edge); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_agmkout(IntPtr edge); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_agtail(IntPtr node); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_sym_key(IntPtr sym); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + internal static extern IntPtr rj_agopen(IntPtr name, int graphtype); + + // Accessors for xdot + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr get_cnt(IntPtr xdot); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_ops(IntPtr xdot); + + // Accessors for xdot_image + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_name_image(IntPtr img); + public static string? GetNameImage(IntPtr img) => MarshalFromUtf8(get_name_image(img), false); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_pos(IntPtr img); + + // Accessors for xdot_font + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_size(IntPtr font); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_name_font(IntPtr font); + public static string? GetNameFont(IntPtr img) => MarshalFromUtf8(get_name_font(img), false); + + // Accessors for xdot_op + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern XDotKind get_kind(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_ellipse(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_polygon(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_polyline(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_bezier(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_text(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_image(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_color(IntPtr op); + public static string? GetColor(IntPtr op) => MarshalFromUtf8(get_color(op), false); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_grad_color(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_font(IntPtr op); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_style(IntPtr op); + public static string? GetStyle(IntPtr op) => MarshalFromUtf8(get_style(op), false); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern uint get_fontchar(IntPtr op); + + // Accessors for xdot_color + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern XDotGradType get_type(IntPtr clr); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_clr(IntPtr clr); + public static string? GetClr(IntPtr clr) => MarshalFromUtf8(get_clr(clr), false); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_ling(IntPtr clr); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_ring(IntPtr clr); + + // Accessors for xdot_text + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x_text(IntPtr txt); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y_text(IntPtr txt); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern TextAlign get_align(IntPtr txt); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_width(IntPtr txt); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_text_str(IntPtr txt); + public static string? GetTextStr(IntPtr txt) => MarshalFromUtf8(get_text_str(txt), false); + + // Accessors for xdot_linear_grad + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x0_ling(IntPtr ling); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y0_ling(IntPtr ling); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x1_ling(IntPtr ling); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y1_ling(IntPtr ling); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern int get_n_stops_ling(IntPtr ling); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_stops_ling(IntPtr ling); + + // Accessors for xdot_radial_grad + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x0_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y0_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_r0_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x1_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y1_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_r1_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern int get_n_stops_ring(IntPtr ring); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_stops_ring(IntPtr ring); + + // Accessors for xdot_color_stop + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern float get_frac(IntPtr stop); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr get_color_stop(IntPtr stop); + public static string? GetColorStop(IntPtr stop) => MarshalFromUtf8(get_color_stop(stop), false); + + // Accessors for xdot_polyline + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern UIntPtr get_cnt_polyline(IntPtr polyline); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_pts_polyline(IntPtr polyline); + + // Accessors for xdot_point + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x_point(IntPtr point); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y_point(IntPtr point); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_z_point(IntPtr point); + + // Accessors for xdot_rect + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_x_rect(IntPtr rect); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_y_rect(IntPtr rect); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_w_rect(IntPtr rect); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern double get_h_rect(IntPtr rect); + + // Index function for xdot_color_stop array + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_color_stop_at_index(IntPtr stops, int index); + + // Index function for xdot_op array + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_op_at_index(IntPtr ops, int index); + + // Index function for xdot_pt array + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr get_pt_at_index(IntPtr pts, int index); +} \ No newline at end of file diff --git a/Rubjerg.Graphviz/Marshaling.cs b/Rubjerg.Graphviz/FFI/Marshaling.cs similarity index 88% rename from Rubjerg.Graphviz/Marshaling.cs rename to Rubjerg.Graphviz/FFI/Marshaling.cs index 8a8c6eab..73b1cfe0 100644 --- a/Rubjerg.Graphviz/Marshaling.cs +++ b/Rubjerg.Graphviz/FFI/Marshaling.cs @@ -3,7 +3,7 @@ using System.Runtime.InteropServices; using System.Text; -namespace Rubjerg.Graphviz; +namespace Rubjerg.Graphviz.FFI; using static Constants; @@ -89,8 +89,6 @@ public static T MarshalToUtf8(string? s, Func continuation, bool f return byteArray; } - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] private static extern void free_str(IntPtr ptr); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void agstrfree(IntPtr root, IntPtr str); } diff --git a/Rubjerg.Graphviz/NativeMethods.cs b/Rubjerg.Graphviz/FFI/NativeMethods.cs similarity index 93% rename from Rubjerg.Graphviz/NativeMethods.cs rename to Rubjerg.Graphviz/FFI/NativeMethods.cs index 1516af79..78561ec4 100644 --- a/Rubjerg.Graphviz/NativeMethods.cs +++ b/Rubjerg.Graphviz/FFI/NativeMethods.cs @@ -2,7 +2,7 @@ using System.IO; using System.Runtime.InteropServices; -namespace Rubjerg.Graphviz; +namespace Rubjerg.Graphviz.FFI; public static class NativeMethods { diff --git a/Rubjerg.Graphviz/FFI/Platform.cs b/Rubjerg.Graphviz/FFI/Platform.cs new file mode 100644 index 00000000..a2a4791c --- /dev/null +++ b/Rubjerg.Graphviz/FFI/Platform.cs @@ -0,0 +1,9 @@ +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +public static class Platform +{ + public static bool IsWindows { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static bool IsLinux { get; } = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); +} diff --git a/Rubjerg.Graphviz/FFI/TestLib.cs b/Rubjerg.Graphviz/FFI/TestLib.cs new file mode 100644 index 00000000..17217f96 --- /dev/null +++ b/Rubjerg.Graphviz/FFI/TestLib.cs @@ -0,0 +1,62 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Marshaling; +using static Constants; + +internal static class TestLib +{ + public enum TestEnum + { + Val1, Val2, Val3, Val4, Val5 + } + + // .NET uses UnmanagedType.Bool by default for P/Invoke, but our C++ code uses UnmanagedType.U1 + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool echobool([MarshalAs(UnmanagedType.U1)] bool arg); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern int echoint(int arg); + + public static string? EchoString(string? str) + { + return MarshalToUtf8(str, ptr => + { + var returnPtr = TestLib.echo_string(ptr); + // echo_string gives us ownership over the string, which means that we have to free it. + return MarshalFromUtf8(returnPtr, true); + }); + } + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern TestEnum echo_enum(TestEnum e); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern int return1(); + public static string? ReturnCopyRight() => MarshalFromUtf8(return_copyright(), false); + public static string? ReturnEmptyString() => MarshalFromUtf8(return_empty_string(), false); + public static string? ReturnHello() => MarshalFromUtf8(return_hello(), false); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern int return_1(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern TestEnum return_enum1(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern TestEnum return_enum2(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern TestEnum return_enum5(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool return_false(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + [return: MarshalAs(UnmanagedType.U1)] + public static extern bool return_true(); + + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr echo_string(IntPtr str); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr return_copyright(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr return_empty_string(); + [DllImport(GraphvizWrapperLibName, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr return_hello(); +} \ No newline at end of file diff --git a/Rubjerg.Graphviz/FFI/XDotFFI.cs b/Rubjerg.Graphviz/FFI/XDotFFI.cs new file mode 100644 index 00000000..ea4d6b37 --- /dev/null +++ b/Rubjerg.Graphviz/FFI/XDotFFI.cs @@ -0,0 +1,22 @@ +using System; + +namespace Rubjerg.Graphviz.FFI; + +using static Marshaling; +using static Platform; + +internal static class XDotFFI +{ + public static IntPtr ParseXDot(string xdotString) + { + return MarshalToUtf8(xdotString, s => IsWindows ? XDotLibWindows.parseXDot(s) : XDotLibLinux.parseXDot(s)); + } + + public static void FreeXDot(IntPtr xdotptr) + { + if (IsWindows) + XDotLibWindows.freeXDot(xdotptr); + else + XDotLibLinux.freeXDot(xdotptr); + } +} diff --git a/Rubjerg.Graphviz/FFI/XDotLib.Linux.cs b/Rubjerg.Graphviz/FFI/XDotLib.Linux.cs new file mode 100644 index 00000000..cbf22a67 --- /dev/null +++ b/Rubjerg.Graphviz/FFI/XDotLib.Linux.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Constants; + +/// +/// See https://graphviz.org/docs/outputs/canon/#xdot +/// +internal static class XDotLibLinux +{ + [DllImport(XDotLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr parseXDot(IntPtr xdotString); + + [DllImport(XDotLibNameLinux, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void freeXDot(IntPtr xdotptr); +} diff --git a/Rubjerg.Graphviz/FFI/XDotLib.Windows.cs b/Rubjerg.Graphviz/FFI/XDotLib.Windows.cs new file mode 100644 index 00000000..7aeda9fa --- /dev/null +++ b/Rubjerg.Graphviz/FFI/XDotLib.Windows.cs @@ -0,0 +1,18 @@ +using System; +using System.Runtime.InteropServices; + +namespace Rubjerg.Graphviz.FFI; + +using static Constants; + +/// +/// See https://graphviz.org/docs/outputs/canon/#xdot +/// +internal static class XDotLibWindows +{ + [DllImport(XDotLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern IntPtr parseXDot(IntPtr xdotString); + + [DllImport(XDotLibNameWindows, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] + public static extern void freeXDot(IntPtr xdotptr); +} diff --git a/Rubjerg.Graphviz/XDotParser.cs b/Rubjerg.Graphviz/FFI/XDotParser.cs similarity index 61% rename from Rubjerg.Graphviz/XDotParser.cs rename to Rubjerg.Graphviz/FFI/XDotParser.cs index b8b9ea95..a29159ff 100644 --- a/Rubjerg.Graphviz/XDotParser.cs +++ b/Rubjerg.Graphviz/FFI/XDotParser.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; -namespace Rubjerg.Graphviz; +namespace Rubjerg.Graphviz.FFI; // These internal types are only used for marshaling // We replace them with more idiomatic types @@ -43,7 +43,7 @@ public static List ParseXDot(string xdotString, CoordinateSystem coordin { if (xdot != IntPtr.Zero) { - XDotFFI.freeXDot(xdot); + XDotFFI.FreeXDot(xdot); } } } @@ -55,78 +55,78 @@ internal static List TranslateXDot(IntPtr xdotPtr, CoordinateSystem coor XDot xdot = new XDot { - Count = (int)XDotFFI.get_cnt(xdotPtr) + Count = (int)GraphvizWrapperLib.get_cnt(xdotPtr) }; // Translate the array of XDotOps int count = xdot.Count; xdot.Ops = new XDotOp[count]; - var opsPtr = XDotFFI.get_ops(xdotPtr); + var opsPtr = GraphvizWrapperLib.get_ops(xdotPtr); var activeFont = Font.Default; var activeFontChar = FontChar.None; for (int i = 0; i < count; ++i) { - IntPtr xdotOpPtr = XDotFFI.get_op_at_index(opsPtr, i); - var kind = XDotFFI.get_kind(xdotOpPtr); + IntPtr xdotOpPtr = GraphvizWrapperLib.get_op_at_index(opsPtr, i); + var kind = GraphvizWrapperLib.get_kind(xdotOpPtr); switch (kind) { case XDotKind.FilledEllipse: - xdot.Ops[i] = new XDotOp.FilledEllipse(TranslateEllipse(XDotFFI.get_ellipse(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.FilledEllipse(TranslateEllipse(GraphvizWrapperLib.get_ellipse(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.UnfilledEllipse: - xdot.Ops[i] = new XDotOp.UnfilledEllipse(TranslateEllipse(XDotFFI.get_ellipse(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.UnfilledEllipse(TranslateEllipse(GraphvizWrapperLib.get_ellipse(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.FilledPolygon: - xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.UnfilledPolygon: - xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.FilledPolygon(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.FilledBezier: - xdot.Ops[i] = new XDotOp.FilledBezier(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.FilledBezier(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.UnfilledBezier: - xdot.Ops[i] = new XDotOp.UnfilledBezier(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.UnfilledBezier(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.Polyline: - xdot.Ops[i] = new XDotOp.PolyLine(TranslatePolyline(XDotFFI.get_polyline(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.PolyLine(TranslatePolyline(GraphvizWrapperLib.get_polyline(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.Text: - xdot.Ops[i] = new XDotOp.Text(TranslateText(XDotFFI.get_text(xdotOpPtr), activeFont, activeFontChar) + xdot.Ops[i] = new XDotOp.Text(TranslateText(GraphvizWrapperLib.get_text(xdotOpPtr), activeFont, activeFontChar) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.FillColor: - xdot.Ops[i] = new XDotOp.FillColor(new Color.Uniform(XDotFFI.GetColor(xdotOpPtr)!)); + xdot.Ops[i] = new XDotOp.FillColor(new Color.Uniform(GraphvizWrapperLib.GetColor(xdotOpPtr)!)); break; case XDotKind.PenColor: - xdot.Ops[i] = new XDotOp.PenColor(new Color.Uniform(XDotFFI.GetColor(xdotOpPtr)!)); + xdot.Ops[i] = new XDotOp.PenColor(new Color.Uniform(GraphvizWrapperLib.GetColor(xdotOpPtr)!)); break; case XDotKind.GradFillColor: - xdot.Ops[i] = new XDotOp.FillColor(TranslateGradColor(XDotFFI.get_grad_color(xdotOpPtr))); + xdot.Ops[i] = new XDotOp.FillColor(TranslateGradColor(GraphvizWrapperLib.get_grad_color(xdotOpPtr))); break; case XDotKind.GradPenColor: - xdot.Ops[i] = new XDotOp.PenColor(TranslateGradColor(XDotFFI.get_grad_color(xdotOpPtr))); + xdot.Ops[i] = new XDotOp.PenColor(TranslateGradColor(GraphvizWrapperLib.get_grad_color(xdotOpPtr))); break; case XDotKind.Font: - activeFont = TranslateFont(XDotFFI.get_font(xdotOpPtr)); + activeFont = TranslateFont(GraphvizWrapperLib.get_font(xdotOpPtr)); break; case XDotKind.Style: - xdot.Ops[i] = new XDotOp.Style(XDotFFI.GetStyle(xdotOpPtr)!); + xdot.Ops[i] = new XDotOp.Style(GraphvizWrapperLib.GetStyle(xdotOpPtr)!); break; case XDotKind.Image: - xdot.Ops[i] = new XDotOp.Image(TranslateImage(XDotFFI.get_image(xdotOpPtr)) + xdot.Ops[i] = new XDotOp.Image(TranslateImage(GraphvizWrapperLib.get_image(xdotOpPtr)) .ForCoordSystem(coordinateSystem, maxY)); break; case XDotKind.FontChar: - activeFontChar = TranslateFontChar(XDotFFI.get_fontchar(xdotOpPtr)); + activeFontChar = TranslateFontChar(GraphvizWrapperLib.get_fontchar(xdotOpPtr)); break; default: throw new ArgumentException($"Unexpected XDotOp.Kind: {kind}"); @@ -145,8 +145,8 @@ private static ImageInfo TranslateImage(IntPtr imagePtr) { ImageInfo image = new ImageInfo ( - Position: TranslateRect(XDotFFI.get_pos(imagePtr)), - Name: XDotFFI.GetNameImage(imagePtr) + Position: TranslateRect(GraphvizWrapperLib.get_pos(imagePtr)), + Name: GraphvizWrapperLib.GetNameImage(imagePtr) ); return image; @@ -156,8 +156,8 @@ private static Font TranslateFont(IntPtr fontPtr) { Font font = new Font ( - Size: XDotFFI.get_size(fontPtr), - Name: XDotFFI.GetNameFont(fontPtr)! + Size: GraphvizWrapperLib.get_size(fontPtr), + Name: GraphvizWrapperLib.GetNameFont(fontPtr)! ); return font; @@ -167,10 +167,10 @@ private static RectangleD TranslateEllipse(IntPtr ellipsePtr) { RectangleD ellipse = RectangleD.Create ( - XDotFFI.get_x_rect(ellipsePtr), - XDotFFI.get_y_rect(ellipsePtr), - XDotFFI.get_w_rect(ellipsePtr), - XDotFFI.get_h_rect(ellipsePtr) + GraphvizWrapperLib.get_x_rect(ellipsePtr), + GraphvizWrapperLib.get_y_rect(ellipsePtr), + GraphvizWrapperLib.get_w_rect(ellipsePtr), + GraphvizWrapperLib.get_h_rect(ellipsePtr) ); return ellipse; @@ -178,15 +178,15 @@ private static RectangleD TranslateEllipse(IntPtr ellipsePtr) private static Color TranslateGradColor(IntPtr colorPtr) { - var type = XDotFFI.get_type(colorPtr); + var type = GraphvizWrapperLib.get_type(colorPtr); switch (type) { case XDotGradType.None: - return new Color.Uniform(XDotFFI.GetClr(colorPtr)!); + return new Color.Uniform(GraphvizWrapperLib.GetClr(colorPtr)!); case XDotGradType.Linear: - return new Color.Linear(TranslateLinearGrad(XDotFFI.get_ling(colorPtr))); + return new Color.Linear(TranslateLinearGrad(GraphvizWrapperLib.get_ling(colorPtr))); case XDotGradType.Radial: - return new Color.Radial(TranslateRadialGrad(XDotFFI.get_ring(colorPtr))); + return new Color.Radial(TranslateRadialGrad(GraphvizWrapperLib.get_ring(colorPtr))); default: throw new ArgumentException($"Unexpected XDotColor.Type: {type}"); } @@ -194,19 +194,19 @@ private static Color TranslateGradColor(IntPtr colorPtr) private static LinearGradient TranslateLinearGrad(IntPtr lingPtr) { - int count = XDotFFI.get_n_stops_ling(lingPtr); + int count = GraphvizWrapperLib.get_n_stops_ling(lingPtr); LinearGradient linearGrad = new LinearGradient ( - Point0: new PointD(XDotFFI.get_x0_ling(lingPtr), XDotFFI.get_y0_ling(lingPtr)), - Point1: new PointD(XDotFFI.get_x1_ling(lingPtr), XDotFFI.get_y1_ling(lingPtr)), + Point0: new PointD(GraphvizWrapperLib.get_x0_ling(lingPtr), GraphvizWrapperLib.get_y0_ling(lingPtr)), + Point1: new PointD(GraphvizWrapperLib.get_x1_ling(lingPtr), GraphvizWrapperLib.get_y1_ling(lingPtr)), Stops: new ColorStop[count] ); // Translate the array of ColorStops - var stopsPtr = XDotFFI.get_stops_ling(lingPtr); + var stopsPtr = GraphvizWrapperLib.get_stops_ling(lingPtr); for (int i = 0; i < count; ++i) { - IntPtr colorStopPtr = XDotFFI.get_color_stop_at_index(stopsPtr, i); + IntPtr colorStopPtr = GraphvizWrapperLib.get_color_stop_at_index(stopsPtr, i); linearGrad.Stops[i] = TranslateColorStop(colorStopPtr); } @@ -215,21 +215,21 @@ private static LinearGradient TranslateLinearGrad(IntPtr lingPtr) private static RadialGradient TranslateRadialGrad(IntPtr ringPtr) { - int count = XDotFFI.get_n_stops_ring(ringPtr); + int count = GraphvizWrapperLib.get_n_stops_ring(ringPtr); RadialGradient radialGrad = new RadialGradient ( - Point0: new PointD(XDotFFI.get_x0_ring(ringPtr), XDotFFI.get_y0_ring(ringPtr)), - Point1: new PointD(XDotFFI.get_x1_ring(ringPtr), XDotFFI.get_y1_ring(ringPtr)), - Radius0: XDotFFI.get_r0_ring(ringPtr), - Radius1: XDotFFI.get_r1_ring(ringPtr), + Point0: new PointD(GraphvizWrapperLib.get_x0_ring(ringPtr), GraphvizWrapperLib.get_y0_ring(ringPtr)), + Point1: new PointD(GraphvizWrapperLib.get_x1_ring(ringPtr), GraphvizWrapperLib.get_y1_ring(ringPtr)), + Radius0: GraphvizWrapperLib.get_r0_ring(ringPtr), + Radius1: GraphvizWrapperLib.get_r1_ring(ringPtr), Stops: new ColorStop[count] ); // Translate the array of ColorStops - var stopsPtr = XDotFFI.get_stops_ring(ringPtr); + var stopsPtr = GraphvizWrapperLib.get_stops_ring(ringPtr); for (int i = 0; i < count; ++i) { - IntPtr colorStopPtr = XDotFFI.get_color_stop_at_index(stopsPtr, i); + IntPtr colorStopPtr = GraphvizWrapperLib.get_color_stop_at_index(stopsPtr, i); radialGrad.Stops[i] = TranslateColorStop(colorStopPtr); } @@ -240,8 +240,8 @@ private static ColorStop TranslateColorStop(IntPtr stopPtr) { ColorStop colorStop = new ColorStop ( - Frac: XDotFFI.get_frac(stopPtr), - HtmlColor: XDotFFI.GetColorStop(stopPtr)! + Frac: GraphvizWrapperLib.get_frac(stopPtr), + HtmlColor: GraphvizWrapperLib.GetColorStop(stopPtr)! ); return colorStop; @@ -249,14 +249,14 @@ private static ColorStop TranslateColorStop(IntPtr stopPtr) private static PointD[] TranslatePolyline(IntPtr polylinePtr) { - int count = (int)XDotFFI.get_cnt_polyline(polylinePtr); + int count = (int)GraphvizWrapperLib.get_cnt_polyline(polylinePtr); var points = new PointD[count]; // Translate the array of Points - var pointsPtr = XDotFFI.get_pts_polyline(polylinePtr); + var pointsPtr = GraphvizWrapperLib.get_pts_polyline(polylinePtr); for (int i = 0; i < count; ++i) { - IntPtr pointPtr = XDotFFI.get_pt_at_index(pointsPtr, i); + IntPtr pointPtr = GraphvizWrapperLib.get_pt_at_index(pointsPtr, i); points[i] = TranslatePoint(pointPtr); } @@ -267,8 +267,8 @@ private static PointD TranslatePoint(IntPtr pointPtr) { var point = new PointD ( - X: XDotFFI.get_x_point(pointPtr), - Y: XDotFFI.get_y_point(pointPtr) + X: GraphvizWrapperLib.get_x_point(pointPtr), + Y: GraphvizWrapperLib.get_y_point(pointPtr) ); return point; @@ -278,10 +278,10 @@ private static RectangleD TranslateRect(IntPtr rectPtr) { var rect = RectangleD.Create ( - x: XDotFFI.get_x_rect(rectPtr), - y: XDotFFI.get_y_rect(rectPtr), - width: XDotFFI.get_w_rect(rectPtr), - height: XDotFFI.get_h_rect(rectPtr) + x: GraphvizWrapperLib.get_x_rect(rectPtr), + y: GraphvizWrapperLib.get_y_rect(rectPtr), + width: GraphvizWrapperLib.get_w_rect(rectPtr), + height: GraphvizWrapperLib.get_h_rect(rectPtr) ); return rect; @@ -291,10 +291,10 @@ private static TextInfo TranslateText(IntPtr txtPtr, Font activeFont, FontChar a { TextInfo text = new TextInfo ( - new PointD(XDotFFI.get_x_text(txtPtr), XDotFFI.get_y_text(txtPtr)), - XDotFFI.get_align(txtPtr), - XDotFFI.get_width(txtPtr), - XDotFFI.GetTextStr(txtPtr)!, + new PointD(GraphvizWrapperLib.get_x_text(txtPtr), GraphvizWrapperLib.get_y_text(txtPtr)), + GraphvizWrapperLib.get_align(txtPtr), + GraphvizWrapperLib.get_width(txtPtr), + GraphvizWrapperLib.GetTextStr(txtPtr)!, activeFont, activeFontChar, CoordinateSystem.BottomLeft diff --git a/Rubjerg.Graphviz/ForeignFunctionInterface.cs b/Rubjerg.Graphviz/ForeignFunctionInterface.cs deleted file mode 100644 index fd15473f..00000000 --- a/Rubjerg.Graphviz/ForeignFunctionInterface.cs +++ /dev/null @@ -1,725 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Rubjerg.Graphviz; - -using static Marshaling; -using static Constants; - -/// -/// Graphviz is thread unsafe, so we wrap all function calls inside a lock to make sure we don't run into -/// issues caused by multiple threads accessing the graphviz datastructures (like the GC executing a destructor). -/// -internal static class ForeignFunctionInterface -{ - private static readonly object _mutex = new object(); - - public static IntPtr GvContext() - { - lock (_mutex) - { - return gvContext(); - } - } - public static int GvFreeContext(IntPtr gvc) - { - lock (_mutex) - { - return gvFreeContext(gvc); - } - } - public static int GvLayout(IntPtr gvc, IntPtr graph, string engine) - { - lock (_mutex) - { - return MarshalToUtf8(engine, enginePtr => gvLayout(gvc, graph, enginePtr)); - } - } - public static int GvFreeLayout(IntPtr gvc, IntPtr graph) - { - lock (_mutex) - { - return gvFreeLayout(gvc, graph); - } - } - public static int GvRender(IntPtr gvc, IntPtr graph, string? format, IntPtr @out) - { - lock (_mutex) - { - return MarshalToUtf8(format, formatPtr => gvRender(gvc, graph, formatPtr, @out)); - } - } - public static int GvRenderFilename(IntPtr gvc, IntPtr graph, string? format, string? filename) - { - lock (_mutex) - { - return - MarshalToUtf8(format, formatPtr => - MarshalToUtf8(filename, filenamePtr => - gvRenderFilename(gvc, graph, formatPtr, filenamePtr))); - } - } - public static IntPtr Agnode(IntPtr graph, string? name, int create) - { - lock (_mutex) - { - return MarshalToUtf8(name, namePtr => agnode(graph, namePtr, create)); - } - } - public static int Agdegree(IntPtr graph, IntPtr node, int inset, int outset) - { - lock (_mutex) - { - return agdegree(graph, node, inset, outset); - } - } - public static IntPtr Agfstout(IntPtr graph, IntPtr node) - { - lock (_mutex) - { - return agfstout(graph, node); - } - } - public static IntPtr Agnxtout(IntPtr graph, IntPtr edge) - { - lock (_mutex) - { - return agnxtout(graph, edge); - } - } - public static IntPtr Agfstin(IntPtr graph, IntPtr node) - { - lock (_mutex) - { - return agfstin(graph, node); - } - } - public static IntPtr Agnxtin(IntPtr graph, IntPtr edge) - { - lock (_mutex) - { - return agnxtin(graph, edge); - } - } - public static IntPtr Agfstedge(IntPtr graph, IntPtr node) - { - lock (_mutex) - { - return agfstedge(graph, node); - } - } - public static IntPtr Agnxtedge(IntPtr graph, IntPtr edge, IntPtr node) - { - lock (_mutex) - { - return agnxtedge(graph, edge, node); - } - } - public static void Agattr(IntPtr graph, int type, string name, string deflt) - { - lock (_mutex) - { - MarshalToUtf8(deflt, defltPtr => - MarshalToUtf8(name, namePtr => - agattr(graph, type, namePtr, defltPtr))); - } - } - public static void AgattrHtml(IntPtr graph, int type, string name, string deflt) - { - lock (_mutex) - { - MarshalToUtf8(name, namePtr => - MarshalToUtf8(deflt, defltPtr => - { - var htmlPtr = agstrdup_html(agroot(graph), defltPtr); - agattr(graph, type, namePtr, htmlPtr); - })); - } - } - - public static void Agset(IntPtr obj, string name, string value) - { - lock (_mutex) - { - MarshalToUtf8(name, namePtr => - MarshalToUtf8(value, valuePtr => - agset(obj, namePtr, valuePtr))); - } - } - - public static void AgsetHtml(IntPtr obj, string name, string value) - { - lock (_mutex) - { - MarshalToUtf8(name, namePtr => - MarshalToUtf8(value, valuePtr => - { - var htmlPtr = agstrdup_html(agroot(obj), valuePtr); - agset(obj, namePtr, htmlPtr); - })); - } - } - - public static void Agsafeset(IntPtr obj, string name, string? val, string? deflt) - { - lock (_mutex) - { - MarshalToUtf8(name, namePtr => - MarshalToUtf8(val, valPtr => - MarshalToUtf8(deflt, defltPtr => - agsafeset(obj, namePtr, valPtr, defltPtr)))); - } - } - public static void AgsafesetHtml(IntPtr obj, string name, string? val, string? deflt) - { - lock (_mutex) - { - MarshalToUtf8(name, namePtr => - MarshalToUtf8(val, valPtr => - MarshalToUtf8(deflt, defltPtr => - { - var htmlPtr = agstrdup_html(agroot(obj), defltPtr); - agsafeset(obj, namePtr, valPtr, htmlPtr); - }))); - } - } - public static IntPtr Agroot(IntPtr obj) - { - lock (_mutex) - { - return agroot(obj); - } - } - public static IntPtr Agnxtattr(IntPtr obj, int kind, IntPtr attribute) - { - lock (_mutex) - { - return agnxtattr(obj, kind, attribute); - } - } - public static int Agcopyattr(IntPtr from, IntPtr to) - { - lock (_mutex) - { - return agcopyattr(from, to); - } - } - public static bool Ageqedge(IntPtr edge1, IntPtr edge2) - { - lock (_mutex) - { - return rj_ageqedge(edge1, edge2); - } - } - public static IntPtr Agtail(IntPtr node) - { - lock (_mutex) - { - return rj_agtail(node); - } - } - public static IntPtr Aghead(IntPtr node) - { - lock (_mutex) - { - return rj_aghead(node); - } - } - public static IntPtr Agedge(IntPtr graph, IntPtr tail, IntPtr head, string? name, int create) - { - lock (_mutex) - { - return MarshalToUtf8(name, namePtr => agedge(graph, tail, head, namePtr, create)); - } - } - public static IntPtr Agmkin(IntPtr edge) - { - lock (_mutex) - { - return rj_agmkin(edge); - } - } - public static IntPtr Agmkout(IntPtr edge) - { - lock (_mutex) - { - return rj_agmkout(edge); - } - } - public static IntPtr Agparent(IntPtr obj) - { - lock (_mutex) - { - return agparent(obj); - } - } - public static int Agclose(IntPtr graph) - { - lock (_mutex) - { - return agclose(graph); - } - } - public static int Agdelete(IntPtr graph, IntPtr item) - { - lock (_mutex) - { - return agdelete(graph, item); - } - } - public static IntPtr Agfstnode(IntPtr graph) - { - lock (_mutex) - { - return agfstnode(graph); - } - } - public static IntPtr Agnxtnode(IntPtr graph, IntPtr node) - { - lock (_mutex) - { - return agnxtnode(graph, node); - } - } - public static int Agcontains(IntPtr graph, IntPtr obj) - { - lock (_mutex) - { - return agcontains(graph, obj); - } - } - public static IntPtr Agsubg(IntPtr graph, string? name, int create) - { - lock (_mutex) - { - return MarshalToUtf8(name, namePtr => agsubg(graph, namePtr, create)); - } - } - public static IntPtr Agfstsubg(IntPtr graph) - { - lock (_mutex) - { - return agfstsubg(graph); - } - } - public static IntPtr Agnxtsubg(IntPtr graph) - { - lock (_mutex) - { - return agnxtsubg(graph); - } - } - public static int Agisstrict(IntPtr ptr) - { - lock (_mutex) - { - return agisstrict(ptr); - } - } - public static int Agisdirected(IntPtr ptr) - { - lock (_mutex) - { - return agisdirected(ptr); - } - } - public static int Agisundirected(IntPtr ptr) - { - lock (_mutex) - { - return agisundirected(ptr); - } - } - public static IntPtr Agsubedge(IntPtr graph, IntPtr edge, int create) - { - lock (_mutex) - { - return agsubedge(graph, edge, create); - } - } - public static IntPtr Agsubnode(IntPtr graph, IntPtr node, int create) - { - lock (_mutex) - { - return agsubnode(graph, node, create); - } - } - public static IntPtr EdgeLabel(IntPtr node) - { - lock (_mutex) - { - return edge_label(node); - } - } - public static string? Rjagmemwrite(IntPtr graph) - { - lock (_mutex) - { - var strPtr = rj_agmemwrite(graph); - return MarshalFromUtf8(strPtr, true); - } - } - public static IntPtr GraphLabel(IntPtr node) - { - lock (_mutex) - { - return graph_label(node); - } - } - public static string? Agget(IntPtr obj, string name) - { - lock (_mutex) - { - return MarshalToUtf8(name, namePtr => MarshalFromUtf8(agget(obj, namePtr), false)); - } - } - public static string? Rjagnameof(IntPtr obj) - { - lock (_mutex) - { - return MarshalFromUtf8(agnameof(obj), false); - } - } - public static void CloneAttributeDeclarations(IntPtr graphfrom, IntPtr graphto) - { - lock (_mutex) - { - clone_attribute_declarations(graphfrom, graphto); - } - } - public static string? ImsymKey(IntPtr sym) - { - lock (_mutex) - { - return MarshalFromUtf8(rj_sym_key(sym), false); - } - } - public static double LabelX(IntPtr label) - { - lock (_mutex) - { - return label_x(label); - } - } - public static double LabelY(IntPtr label) - { - lock (_mutex) - { - return label_y(label); - } - } - public static double LabelWidth(IntPtr label) - { - lock (_mutex) - { - return label_width(label); - } - } - public static double LabelHeight(IntPtr label) - { - lock (_mutex) - { - return label_height(label); - } - } - public static string? LabelText(IntPtr label) - { - lock (_mutex) - { - return MarshalFromUtf8(label_text(label), false); - } - } - public static double LabelFontsize(IntPtr label) - { - lock (_mutex) - { - return label_fontsize(label); - } - } - public static string? LabelFontname(IntPtr label) - { - lock (_mutex) - { - return MarshalFromUtf8(label_fontname(label), false); - } - } - public static double NodeX(IntPtr node) - { - lock (_mutex) - { - return node_x(node); - } - } - public static double NodeY(IntPtr node) - { - lock (_mutex) - { - return node_y(node); - } - } - public static double NodeWidth(IntPtr node) - { - lock (_mutex) - { - return node_width(node); - } - } - public static double NodeHeight(IntPtr node) - { - lock (_mutex) - { - return node_height(node); - } - } - public static IntPtr NodeLabel(IntPtr node) - { - lock (_mutex) - { - return node_label(node); - } - } - public static void ConvertToUndirected(IntPtr graph) - { - lock (_mutex) - { - convert_to_undirected(graph); - } - } - public static IntPtr Rjagmemread(string input) - { - lock (_mutex) - { - return MarshalToUtf8(input, rj_agmemread); - } - } - public static IntPtr Rjagopen(string? name, int graphtype) - { - lock (_mutex) - { - return MarshalToUtf8(name, namePtr => rj_agopen(namePtr, graphtype)); - } - } - - [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr gvContext(); - [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int gvFreeContext(IntPtr gvc); - [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int gvLayout(IntPtr gvc, IntPtr graph, IntPtr engine); - [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int gvFreeLayout(IntPtr gvc, IntPtr graph); - [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int gvRender(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr @out); - [DllImport(GvcLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int gvRenderFilename(IntPtr gvc, IntPtr graph, IntPtr format, IntPtr filename); - - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnode(IntPtr graph, IntPtr name, int create); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agdegree(IntPtr graph, IntPtr node, int inset, int outset); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agfstout(IntPtr graph, IntPtr node); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnxtout(IntPtr graph, IntPtr edge); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agfstin(IntPtr graph, IntPtr node); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnxtin(IntPtr graph, IntPtr edge); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agfstedge(IntPtr graph, IntPtr node); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnxtedge(IntPtr graph, IntPtr edge, IntPtr node); - - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void agattr(IntPtr graph, int type, IntPtr name, IntPtr deflt); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void agset(IntPtr obj, IntPtr name, IntPtr value); - [DllImport(CGraphLib, SetLastError = false, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agstrdup_html(IntPtr obj, IntPtr html); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void agsafeset(IntPtr obj, IntPtr name, IntPtr val, IntPtr deflt); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agroot(IntPtr obj); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnxtattr(IntPtr obj, int kind, IntPtr attribute); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agcopyattr(IntPtr from, IntPtr to); - - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agparent(IntPtr obj); - - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agclose(IntPtr graph); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agdelete(IntPtr graph, IntPtr item); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agfstnode(IntPtr graph); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnxtnode(IntPtr graph, IntPtr node); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agcontains(IntPtr graph, IntPtr obj); - - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agsubg(IntPtr graph, IntPtr name, int create); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agfstsubg(IntPtr graph); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnxtsubg(IntPtr graph); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agisstrict(IntPtr ptr); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agisdirected(IntPtr ptr); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern int agisundirected(IntPtr ptr); - - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agsubedge(IntPtr graph, IntPtr edge, int create); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agsubnode(IntPtr graph, IntPtr node, int create); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agget(IntPtr obj, IntPtr name); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agnameof(IntPtr obj); - [DllImport(CGraphLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr agedge(IntPtr graph, IntPtr tail, IntPtr head, IntPtr name, int create); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - private static extern bool rj_ageqedge(IntPtr edge1, IntPtr edge2); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_agtail(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_aghead(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_agmkin(IntPtr edge); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_agmkout(IntPtr edge); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr edge_label(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_agmemwrite(IntPtr graph); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr graph_label(IntPtr node); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void clone_attribute_declarations(IntPtr graphfrom, IntPtr graphto); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_sym_key(IntPtr sym); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double label_x(IntPtr label); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double label_y(IntPtr label); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double label_width(IntPtr label); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double label_height(IntPtr label); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr label_text(IntPtr label); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double label_fontsize(IntPtr label); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr label_fontname(IntPtr label); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double node_x(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double node_y(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double node_width(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern double node_height(IntPtr node); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr node_label(IntPtr node); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern void convert_to_undirected(IntPtr graph); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_agmemread(IntPtr input); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr rj_agopen(IntPtr name, int graphtype); - - - /// - /// A GraphvizContext is used to store various layout - /// information that is independent of a particular graph and - /// its attributes. It holds the data associated with plugins, - /// parsed - command lines, script engines, and anything else - /// with a scope potentially larger than one graph, up to the - /// scope of the application. In addition, it maintains lists of - /// the available layout algorithms and renderers; it also - /// records the most recent layout algorithm applied to a graph. - /// It can be used to specify multiple renderings of a given - /// graph layout into different associated files.It is also used - /// to store various global information used during rendering. - /// There should be just one GVC created for the entire - /// duration of an application. A single GVC value can be used - /// with multiple graphs, though with only one graph at a - /// time. In addition, if gvLayout() was invoked for a graph and - /// GVC, then gvFreeLayout() should be called before using - /// gvLayout() again, even on the same graph. - /// - public static IntPtr GVC { get; private set; } - static ForeignFunctionInterface() - { - // We initialize the gvc here before interacting with graphviz - // https://gitlab.com/graphviz/graphviz/-/issues/2434 - GVC = GvContext(); - } - - #region debugging and testing - - // .NET uses UnmanagedType.Bool by default for P/Invoke, but our C++ code uses UnmanagedType.U1 - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool echobool([MarshalAs(UnmanagedType.U1)] bool arg); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool return_true(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - [return: MarshalAs(UnmanagedType.U1)] - public static extern bool return_false(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern int echoint(int arg); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern int return1(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern int return_1(); - - public enum TestEnum - { - Val1, Val2, Val3, Val4, Val5 - } - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern TestEnum return_enum1(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern TestEnum return_enum2(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern TestEnum return_enum5(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern TestEnum echo_enum(TestEnum e); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr echo_string(IntPtr str); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr return_empty_string(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr return_hello(); - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr return_copyright(); - - public static string? EchoString(string? str) - { - return MarshalToUtf8(str, ptr => - { - var returnPtr = echo_string(ptr); - // echo_string gives us ownership over the string, which means that we have to free it. - return MarshalFromUtf8(returnPtr, true); - }); - } - public static string? ReturnEmptyString() => MarshalFromUtf8(return_empty_string(), false); - public static string? ReturnHello() => MarshalFromUtf8(return_hello(), false); - public static string? ReturnCopyRight() => MarshalFromUtf8(return_copyright(), false); - #endregion -} diff --git a/Rubjerg.Graphviz/Graph.cs b/Rubjerg.Graphviz/Graph.cs index 5c01880e..4e605b86 100644 --- a/Rubjerg.Graphviz/Graph.cs +++ b/Rubjerg.Graphviz/Graph.cs @@ -3,7 +3,7 @@ using System.Diagnostics; using System.IO; using System.Linq; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; namespace Rubjerg.Graphviz; diff --git a/Rubjerg.Graphviz/Node.cs b/Rubjerg.Graphviz/Node.cs index 1c7ac280..ab4e1476 100644 --- a/Rubjerg.Graphviz/Node.cs +++ b/Rubjerg.Graphviz/Node.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; namespace Rubjerg.Graphviz; diff --git a/Rubjerg.Graphviz/RootGraph.cs b/Rubjerg.Graphviz/RootGraph.cs index cd9d1b10..79161cc5 100644 --- a/Rubjerg.Graphviz/RootGraph.cs +++ b/Rubjerg.Graphviz/RootGraph.cs @@ -1,7 +1,7 @@ using System; using System.IO; using System.Linq; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; namespace Rubjerg.Graphviz; diff --git a/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj b/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj index 3bfc0b4a..fe31a840 100644 --- a/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj +++ b/Rubjerg.Graphviz/Rubjerg.Graphviz.csproj @@ -22,7 +22,7 @@ - + true @@ -81,33 +81,33 @@ - + - + - + - + - + - + - + - + - + - + diff --git a/Rubjerg.Graphviz/SubGraph.cs b/Rubjerg.Graphviz/SubGraph.cs index 82240497..c06acd46 100644 --- a/Rubjerg.Graphviz/SubGraph.cs +++ b/Rubjerg.Graphviz/SubGraph.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using static Rubjerg.Graphviz.ForeignFunctionInterface; +using static Rubjerg.Graphviz.FFI.GraphvizFFI; namespace Rubjerg.Graphviz; diff --git a/Rubjerg.Graphviz/XDotFFI.cs b/Rubjerg.Graphviz/XDotFFI.cs deleted file mode 100644 index b4e85a95..00000000 --- a/Rubjerg.Graphviz/XDotFFI.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace Rubjerg.Graphviz; - -using static Marshaling; -using static Constants; - -/// -/// See https://graphviz.org/docs/outputs/canon/#xdot -/// -internal static class XDotFFI -{ - [DllImport(XDotLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr parseXDot(IntPtr xdotString); - public static IntPtr ParseXDot(string xdotString) => MarshalToUtf8(xdotString, parseXDot); - - [DllImport(XDotLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern void freeXDot(IntPtr xdotptr); - - // Accessors for xdot - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr get_cnt(IntPtr xdot); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_ops(IntPtr xdot); - - // Accessors for xdot_image - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_name_image(IntPtr img); - public static string? GetNameImage(IntPtr img) => MarshalFromUtf8(get_name_image(img), false); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_pos(IntPtr img); - - // Accessors for xdot_font - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_size(IntPtr font); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_name_font(IntPtr font); - public static string? GetNameFont(IntPtr img) => MarshalFromUtf8(get_name_font(img), false); - - // Accessors for xdot_op - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern XDotKind get_kind(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_ellipse(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_polygon(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_polyline(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_bezier(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_text(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_image(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_color(IntPtr op); - public static string? GetColor(IntPtr op) => MarshalFromUtf8(get_color(op), false); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_grad_color(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_font(IntPtr op); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_style(IntPtr op); - public static string? GetStyle(IntPtr op) => MarshalFromUtf8(get_style(op), false); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern uint get_fontchar(IntPtr op); - - // Accessors for xdot_color - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern XDotGradType get_type(IntPtr clr); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_clr(IntPtr clr); - public static string? GetClr(IntPtr clr) => MarshalFromUtf8(get_clr(clr), false); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_ling(IntPtr clr); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_ring(IntPtr clr); - - // Accessors for xdot_text - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x_text(IntPtr txt); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y_text(IntPtr txt); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern TextAlign get_align(IntPtr txt); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_width(IntPtr txt); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_text_str(IntPtr txt); - public static string? GetTextStr(IntPtr txt) => MarshalFromUtf8(get_text_str(txt), false); - - // Accessors for xdot_linear_grad - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x0_ling(IntPtr ling); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y0_ling(IntPtr ling); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x1_ling(IntPtr ling); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y1_ling(IntPtr ling); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern int get_n_stops_ling(IntPtr ling); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_stops_ling(IntPtr ling); - - // Accessors for xdot_radial_grad - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x0_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y0_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_r0_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x1_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y1_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_r1_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern int get_n_stops_ring(IntPtr ring); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_stops_ring(IntPtr ring); - - // Accessors for xdot_color_stop - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern float get_frac(IntPtr stop); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - private static extern IntPtr get_color_stop(IntPtr stop); - public static string? GetColorStop(IntPtr stop) => MarshalFromUtf8(get_color_stop(stop), false); - - // Accessors for xdot_polyline - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern UIntPtr get_cnt_polyline(IntPtr polyline); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_pts_polyline(IntPtr polyline); - - // Accessors for xdot_point - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x_point(IntPtr point); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y_point(IntPtr point); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_z_point(IntPtr point); - - // Accessors for xdot_rect - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_x_rect(IntPtr rect); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_y_rect(IntPtr rect); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_w_rect(IntPtr rect); - - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern double get_h_rect(IntPtr rect); - - // Index function for xdot_color_stop array - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_color_stop_at_index(IntPtr stops, int index); - - // Index function for xdot_op array - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_op_at_index(IntPtr ops, int index); - - // Index function for xdot_pt array - [DllImport(GraphvizWrapperLib, SetLastError = true, CallingConvention = CallingConvention.Cdecl)] - public static extern IntPtr get_pt_at_index(IntPtr pts, int index); - -} diff --git a/justfile b/justfile index eaa7a5fa..5819696a 100644 --- a/justfile +++ b/justfile @@ -7,33 +7,29 @@ default: test-all find-msbuild: powershell -NoProfile -Command '& { & "${env:ProgramFiles(x86)}\\Microsoft Visual Studio\\Installer\\vswhere.exe" -latest -requires Microsoft.Component.MSBuild -find MSBuild\\**\\Bin\\MSBuild.exe }' -# Restore .NET tools -restore-tools: +# Restore .NET tools and main solution packages +restore: dotnet tool restore - -# Restore main solution packages -restore: restore-tools dotnet restore Rubjerg.Graphviz.sln -# Build main app -build-package: restore +build SOLUTION: if {{is-windows}}; then \ - MSBuild.exe Rubjerg.Graphviz.sln //p:Configuration=Release; \ + MSBuild.exe {{SOLUTION}} //p:Configuration=Release; \ else \ - dotnet build Rubjerg.Graphviz.sln --configuration Release --no-restore; \ + dotnet build {{SOLUTION}} --configuration Release --no-restore; \ fi +# Build nuget package +build-package: restore + just build Rubjerg.Graphviz.sln + # Restore test project packages restore-tests: build-package dotnet restore Rubjerg.Graphviz.Tests.sln # Build test projects build-tests: restore-tests - if {{is-windows}}; then \ - MSBuild.exe Rubjerg.Graphviz.Tests.sln //p:Configuration=Release; \ - else \ - dotnet build Rubjerg.Graphviz.Tests.sln --configuration Release --no-restore; \ - fi + just build Rubjerg.Graphviz.Tests.sln # Run unit tests for a project test PROJECT: @@ -50,6 +46,14 @@ test-all: build-tests just test Rubjerg.Graphviz.Test/Rubjerg.Graphviz.Test.csproj just test Rubjerg.Graphviz.TransitiveTest/Rubjerg.Graphviz.TransitiveTest.csproj +# Run the nuget.org tests +test-nugetorg: + dotnet tool restore + dotnet restore NugetOrgTests/Rubjerg.Graphviz.NugetOrgTests.sln + just build NugetOrgTests/Rubjerg.Graphviz.NugetOrgTests.sln + just test NugetOrgTests/Rubjerg.Graphviz.NugetOrgTest/Rubjerg.Graphviz.NugetOrgTest.csproj + just test NugetOrgTests/Rubjerg.Graphviz.NugetOrgTransitiveTest/Rubjerg.Graphviz.NugetOrgTransitiveTest.csproj + locate-nupkg GITHUB_OUTPUT: echo "package=$(find . -name "Rubjerg.Graphviz.*.nupkg" | head -1)" >> "{{GITHUB_OUTPUT}}"