mono / CppSharp

Tools and libraries to glue C/C++ APIs to high-level languages
MIT License
3.1k stars 509 forks source link

[feature request] Support `std::shared_ptr` (and complete Github Actions workflow example file with generating bindings of popular libraries) #1860

Open vadimkantorov opened 3 weeks ago

vadimkantorov commented 3 weeks ago

UPD: the missing std::shared_ptr<T> support context is in the message https://github.com/mono/CppSharp/issues/1860#issuecomment-2297731111. Currently this support seems missing, and shared_ptr/unique_ptr are quite common types in modern C++ APIs to be processed

JavaCpp seems to support binding std::shared_ptr<T>: https://github.com/bytedeco/javacpp/discussions/623

Below is a very long thread where we ended up rolling a fully complete GitHub Actions Workflow file.


GitHub Actions allow for a reproducible environment, so pulling in-tree a collection of GitHub Actions workflow files for popular libraries from https://github.com/mono/CppSharp?tab=readme-ov-file#users (like ffmpeg or dearimgui) could be a great start for new users showcasing the install procedures.

And sometimes it would be possible to just copy the filly self-contained workflow-file and modify it to create a command for binding-generation for a new library. Then users could send PRs with such workflows/scripts, and they could be tested easily (and especially - tested against new versions of the libraries). Currently https://github.com/mono/CppSharp/tree/main/tests doesn't have many real-world library examples.

I found https://github.com/mono/CppSharp/blob/main/.github/workflows/main.yml. Could it be considered a base for such a workflow file producing a fully functioning llvm+CppSharp install?

vadimkantorov commented 3 weeks ago

I wrote down a primer of a minimalistic workflow file I meant above, looking to generate C API bindings to https://github.com/triton-inference-server/core/blob/main/include/triton/core/tritonserver.h

Does CppSharp absolutely need to have access to CppSharp compiled libtritonserver.so? (module.LibraryDirs / module.Libraries)

How could

name: tritonservercppsharp

on: workflow_dispatch

env:
  PLATFORM: x64

jobs:
  tritonservercppsharp:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v2

      - name: Write down the bindings code
        run: |
          tee tritonservercppsharp.cs <<EOF
            namespace CppSharpTransformer { class DllDemoGenerator : CppSharp.ILibrary {
                public static void Main(string[] args) { CppSharp.ConsoleDriver.Run(new DllDemoGenerator()); }
                public void SetupPasses(CppSharp.Driver driver) { }
                public void Preprocess(CppSharp.Driver driver, ASTContext ctx) { }
                public void Postprocess(CppSharp.Driver driver, ASTContext ctx) { }
                void ILibrary.Setup(CppSharp.Driver driver) { }
                void Setup(CppSharp.Driver driver) {
                    var options = driver.Options;
                    options.GeneratorKind = GeneratorKind.CSharp;
                    var module = options.AddModule("TritonServerCppSharp");
                    module.IncludeDirs.Add("core/include");
                    module.Headers.Add("triton/core/tritonserver.h");
                    //module.LibraryDirs.Add("/path/to/triton/which/containslibtritonserver.so/");
                    //module.Libraries.Add("libtritonserver.so");
                }
            } }
          EOF

      - name: Clone and build CppSharp
        run: |
          #wget https://dot.net/v1/dotnet-install.sh && bash dotnet-install.sh --channel 9.0
          git clone --single-branch --depth 1 --branch v1.1 https://github.com/mono/CppSharp
          cd CppSharp
          bash build/build.sh generate -configuration Release -platform $PLATFORM
          bash build/build.sh download_llvm -platform $PLATFORM
          bash build/build.sh restore -platform $PLATFORM
          bash build/build.sh -platform $PLATFORM -build_only
          find bin
          dotnet --version

      - name: Generating bindings
        run: |
          git clone --single-branch --depth 1 --branch r23.03 https://github.com/triton-inference-server/core
          #"$HOME/.dotnet"
          DOTNET_ROOT=/usr/share/dotnet
          DOTNETSDKVER=$(dotnet --version)
          DOTNETFWKVER=$(dotnet --list-runtimes | grep Microsoft.NETCore.App | tail -n 1 | cut -d " " -f2)
          DOTNETLIBDIR="$DOTNET_ROOT/shared/Microsoft.NETCore.App/$DOTNETFWKVER"
          LD_LIBRARY_PATH=CppSharp/bin/Release_x64/ dotnet "$DOTNET_ROOT/sdk/$DOTNETSDKVER/Roslyn/bincore/csc.dll" -r:CppSharp/bin/Release_x64/CppSharp.dll -r:CppSharp/bin/Release_x64/CppSharp.AST.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.dll $(find "$DOTNETLIBDIR" -name "*.dll" -printf '-r:"%p" ')  -target:library -out:tritonservercppsharp.exe tritonservercppsharp.cs
          dotnet tritonservercppsharp.exe

#      - uses: actions/upload-artifact@v4
#        with:
#          path: CppSharp/bin/Release_x64/
tritao commented 3 weeks ago

Could it be considered a base for such a workflow file producing a fully functioning llvm+CppSharp install?

Sure, though this way will compile CppSharp from source (which I personally like, though takes a bit more time). Another valid approach would be to re-use the Nuget packages published by CppSharp's own CI step.

Does CppSharp absolutely need to have access to CppSharp compiled libtritonserver.so? (module.LibraryDirs / module.Libraries)

IIRC it does not need it, it's only used to find which symbols are provided by the compiled libraries. Especially for C libraries this is not a concern, because header-only inline symbols are usually not present. For C++, it might be a concern depending on the library.

Also to note, for some applications, the CppSharp CLI might be useful since it does not need a custom C# code setup: https://github.com/mono/CppSharp/blob/main/src/CLI/CLI.cs

Customization is harder in that case, but does not have to be the case. I would be very helpful to provide a declarative customization model for simple cases (YAML or even C# script based).

vadimkantorov commented 3 weeks ago

Also to note, for some applications, the CppSharp CLI might be useful since it does not need a custom C# code setup

Is there an example of using this tool anywhere? For simple things (and as a baseline), it should indeed be sufficient! I think it's worth advertising a complete example of using this option in the CppSharp's README directly (to have a complete example with dotnet add package and dotnet tool ... or similar) :)

Another valid approach would be to re-use the Nuget packages published by CppSharp's own CI step.

For this case, do I need to do dotnet add package CppSharp? And in both cases, which assemblies do I need to reference for csc.dll to get the basic CppSharp.ILibrary and CppSharp.Driver?

Thanks!

tritao commented 3 weeks ago

Is there an example of using this tool anywhere? For simple things (and as a baseline), it should indeed be sufficient!

The test suite is using it, but I would check out the CLI tool directly, as it provides help and description for the parameters: https://github.com/mono/CppSharp/blob/main/tests/napi/test.sh#L13

For this case, do I need to do dotnet add package CppSharp? And in both cases, which assemblies do I need to reference for csc.dll to get the basic CppSharp.ILibrary and CppSharp.Driver?

I think these are the main ones:

I am not the best person to ask about Nuget as I rarely use it in my workflows.

But development versions are pushed to GitHub packages:

          dotnet nuget push "*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source "https://nuget.pkg.github.com/mono/index.json" --skip-duplicate

https://github.com/mono/CppSharp/pkgs/nuget/CppSharp

The last version says it was published 4 months ago, so maybe publishing for ongoing development commits is not working correctly after all.

vadimkantorov commented 3 weeks ago

For some reason I'm getting problems loading the generated exe (depsite that I copy all the .dlls and .sos in the current dir:

Unhandled exception. System.IO.FileNotFoundException: Could not load file or assembly 'CppSharp.Generator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'. The system cannot find the file specified.

File name: 'CppSharp.Generator, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
/home/runner/work/_temp/388ed1a4-2143-4a23-bfad-d50c5b136892.sh: line 25:  2764 Aborted                 (core dumped) dotnet 
name: tritonservercppsharp

on: workflow_dispatch

env:
  PLATFORM: x64

jobs:
  tritonservercppsharp:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v2

      - name: Write down the bindings code
        run: |
          tee tritonservercppsharp.cs <<EOF
              namespace CppSharpTransformer { public class DllDemoGenerator : CppSharp.ILibrary {
              public static void Main(string[] args) { CppSharp.ConsoleDriver.Run(new DllDemoGenerator()); }
              public void SetupPasses(CppSharp.Driver driver) { }
              public void Preprocess(CppSharp.Driver driver, CppSharp.AST.ASTContext ctx) { }
              public void Postprocess(CppSharp.Driver driver, CppSharp.AST.ASTContext ctx) { }
              public void Setup(CppSharp.Driver driver) {
              var options = driver.Options;
              options.GeneratorKind = CppSharp.Generators.GeneratorKind.CSharp;
              var module = options.AddModule("TritonServerCppSharp");
              module.IncludeDirs.Add("core/include");
              module.Headers.Add("triton/core/tritonserver.h");
              module.Headers.Add("triton/core/tritonbackend.h");
              module.Headers.Add("triton/core/tritoncache.h");
              module.Headers.Add("triton/core/tritonrepoagent.h");
              //module.LibraryDirs.Add("/path/to/triton/server.so");
              //module.Libraries.Add("tritonserver.so");
              } } }
          EOF

      - name: Clone and build CppSharp
        run: |
          #wget https://dot.net/v1/dotnet-install.sh && bash dotnet-install.sh --channel 9.0
          git clone --single-branch --depth 1 https://github.com/mono/CppSharp
          cd CppSharp
          bash build/build.sh generate -configuration Release -platform $PLATFORM
          bash build/build.sh download_llvm -platform $PLATFORM
          bash build/build.sh restore -platform $PLATFORM
          bash build/build.sh -platform $PLATFORM -build_only
          find bin

      - name: Generating bindings
        run: |
          git clone --single-branch --depth 1 https://github.com/triton-inference-server/core
          #"$HOME/.dotnet"
          DOTNET_ROOT=/usr/share/dotnet
          DOTNETSDKVER=$(dotnet --version)
          DOTNETFWKVER=$(dotnet --list-runtimes | grep Microsoft.NETCore.App | tail -n 1 | cut -d " " -f2)
          DOTNETLIBDIR="$DOTNET_ROOT/shared/Microsoft.NETCore.App/$DOTNETFWKVER"
          dotnet --version
          dotnet --list-runtimes
          echo $DOTNETSDKVER $DOTNETFWKVER
          # -r:CppSharp/bin/Release_x64/CppSharp.dll -r:CppSharp/bin/Release_x64/CppSharp.AST.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.dll

          #echo 'namespace ProgramNamespace { public static class Program { public static void Main(string[] args) { System.Console.WriteLine("Hello world!"); } } }' > footest.cs
          #dotnet "$DOTNET_ROOT/sdk/$DOTNETSDKVER/Roslyn/bincore/csc.dll" $(find "$DOTNETLIBDIR" -name "*.dll" -printf '-r:"%p" ') -target:exe -out:footest.exe footest.cs
          #echo '{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":"'$DOTNETFWKVER'"}}}' > footest.runtimeconfig.json
          #dotnet footest.exe

          find CppSharp/bin/Release_x64 -name 'CppSharp*.dll'
          # -r:CppSharp/bin/Release_x64/CppSharp.dll -r:CppSharp/bin/Release_x64/CppSharp.AST.dll -r:CppSharp/bin/Release_x64/CppSharp.Runtime.dll -r:CppSharp/bin/Release_x64/CppSharp.CLI.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.CSharp.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.Bootstrap.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.Gen.dll

          dotnet "$DOTNET_ROOT/sdk/$DOTNETSDKVER/Roslyn/bincore/csc.dll" $(find "$DOTNETLIBDIR" -name "*.dll" -printf '-r:"%p" ') $(find CppSharp/bin -name "*.dll" -printf '-r:"%p" ') -target:exe -out:tritonservercppsharp.exe tritonservercppsharp.cs
          echo '{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":"'$DOTNETFWKVER'"}}}' > tritonservercppsharp.runtimeconfig.json
          echo BEFORE
          find CppSharp/bin -name "*.dll" -o -name "*.so" -exec cp {} . ';'
          dotnet tritonservercppsharp.exe
tritao commented 3 weeks ago

Have you tried this locally or are you running only on CI?

vadimkantorov commented 3 weeks ago

Only on CI. It appears that dotnet does not like the libSystem.Native.so which is found successfully on system path and so it proceeds checking the local directory, it cannot find it and then aborts (after loading 10 various unicode libraries to maybe localize the error message :) ):

2024-08-19T17:11:59.5137891Z [pid  2844] openat(AT_FDCWD, "/home/runner/work/tritonservercppsharp/tritonservercppsharp/libSystem.Native.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
2024-08-19T17:11:59.5140546Z [pid  2844] openat(AT_FDCWD, "/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.7/libSystem.Native.so", O_RDONLY|O_CLOEXEC) = 15
2024-08-19T17:11:59.5142439Z [pid  2844] read(15, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
2024-08-19T17:11:59.5144051Z [pid  2844] newfstatat(15, "", {st_mode=S_IFREG|0777, st_size=90488, ...}, AT_EMPTY_PATH) = 0
2024-08-19T17:11:59.5145178Z [pid  2844] close(15)                   = 0
2024-08-19T17:11:59.5146687Z [pid  2844] access("", F_OK)            = -1 ENOENT (No such file or directory)
2024-08-19T17:11:59.5148223Z [pid  2844] access("opt/corebreadcrumbs", F_OK) = -1 ENOENT (No such file or directory)
2024-08-19T17:11:59.5149673Z [pid  2844] readlink("/home", 0x7fff9273a7e0, 1023) = -1 EINVAL (Invalid argument)
2024-08-19T17:11:59.5151334Z [pid  2844] readlink("/home/runner", 0x7fff9273a7e0, 1023) = -1 EINVAL (Invalid argument)
2024-08-19T17:11:59.5152984Z [pid  2844] readlink("/home/runner/work", 0x7fff9273a7e0, 1023) = -1 EINVAL (Invalid argument)
2024-08-19T17:11:59.5154785Z [pid  2844] readlink("/home/runner/work/tritonservercppsharp", 0x7fff9273a7e0, 1023) = -1 EINVAL (Invalid argument)
2024-08-19T17:11:59.5157228Z [pid  2844] readlink("/home/runner/work/tritonservercppsharp/tritonservercppsharp", 0x7fff9273a7e0, 1023) = -1 EINVAL (Invalid argument)
2024-08-19T17:11:59.5159757Z [pid  2844] stat("/home/runner/work/tritonservercppsharp/tritonservercppsharp/tritonservercppsharp.exe", {st_mode=S_IFREG|0644, st_size=4608, ...}) = 0
2024-08-19T17:11:59.5162486Z [pid  2844] openat(AT_FDCWD, "/home/runner/work/tritonservercppsharp/tritonservercppsharp/tritonservercppsharp.exe", O_RDONLY) = 15
2024-08-19T17:11:59.5179454Z [pid  2844] openat(AT_FDCWD, "/proc/self/status", O_RDONLY) = 19
2024-08-19T17:11:59.5180713Z [pid  2844] read(19, "Name:\tdotnet\nUmask:\t0022\nState:\t"..., 2047) = 1442
2024-08-19T17:11:59.5181800Z [pid  2844] close(19)                   = 0
2024-08-19T17:11:59.5240540Z Unhandled exception. [pid  2844] openat(AT_FDCWD, "/home/runner/work/tritonservercppsharp/tritonservercppsharp/libSystem.Native.so", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
2024-08-19T17:11:59.5242833Z [pid  2844] openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 19
vadimkantorov commented 3 weeks ago

With the similar command:

 echo 'namespace ProgramNamespace { public static class Program { public static void Main(string[] args) { System.Console.WriteLine("Hello world!"); } } }' > footest.cs
 dotnet "$DOTNET_ROOT/sdk/$DOTNETSDKVER/Roslyn/bincore/csc.dll" $(find "$DOTNETLIBDIR" -name "*.dll" -printf '-r:"%p" ') -target:exe -out:footest.exe footest.cs
 echo '{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":"'$DOTNETFWKVER'"}}}' > footest.runtimeconfig.json
dotnet footest.exe

this runs with success

but maybe the problem is that the build command uses a different dotnet / runtime and hence incompat with the runtime I use for running the compiled app?

DOTNET_ROOT=/usr/share/dotnet
          DOTNETSDKVER=$(dotnet --version)
          DOTNETFWKVER=$(dotnet --list-runtimes | grep Microsoft.NETCore.App | tail -n 1 | cut -d " " -f2)
          DOTNETLIBDIR="$DOTNET_ROOT/shared/Microsoft.NETCore.App/$DOTNETFWKVER"
          dotnet --version
          dotnet --list-runtimes
          echo $DOTNETSDKVER $DOTNETFWKVER

2024-08-19T17:11:57.7465811Z 8.0.303
2024-08-19T17:11:57.7524923Z Microsoft.AspNetCore.App 6.0.32 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
2024-08-19T17:11:57.7526906Z Microsoft.AspNetCore.App 7.0.20 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
2024-08-19T17:11:57.7528649Z Microsoft.AspNetCore.App 8.0.7 [/usr/share/dotnet/shared/Microsoft.AspNetCore.App]
2024-08-19T17:11:57.7531310Z Microsoft.NETCore.App 6.0.32 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
2024-08-19T17:11:57.7532927Z Microsoft.NETCore.App 7.0.20 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
2024-08-19T17:11:57.7534428Z Microsoft.NETCore.App 8.0.7 [/usr/share/dotnet/shared/Microsoft.NETCore.App]
2024-08-19T17:11:57.7535843Z 8.0.303 8.0.7
tritao commented 3 weeks ago

Seems likely, any reason you are installing a separate .NET version instead of using the system one?

vadimkantorov commented 3 weeks ago

any reason you are installing a separate .NET version instead of using the system one?

I am not installing any separate .NET. This line is commented out #wget https://dot.net/v1/dotnet-install.sh && bash dotnet-install.sh --channel 9.0. I was not sure if Ubuntu comes with the system one or not

tritao commented 3 weeks ago

Ok, have you tried without this tritonservercppsharp.runtimeconfig.json? Why are you generating it?

vadimkantorov commented 3 weeks ago

It appears in CppSharp.Generator.dll the following. So it seems that build script use the 6.0 version of .NET despite that default (and latest) version is 8.0 (as reported by dotnet --version).

{
  "runtimeTarget": {
    "name": ".NETCoreApp,Version=v6.0",
    "signature": ""
  },
vadimkantorov commented 3 weeks ago

Why are you generating it?

Because otherwise dotnet command cannot run the executables built directly with csc :(

tritao commented 3 weeks ago

Ok, must be a new .NET feature, previously I don't remember that being necessary.

https://github.com/mono/CppSharp/commit/8cf6e3f2805babe78b1b30b1d719d6c62c0cbaac

Maybe this can help, you can pass -target-framework to the build script.

vadimkantorov commented 3 weeks ago

I've tried that. This indeed makes "runtimeTarget": {"name": ".NETCoreApp,Version=v8.0", "signature": ""}, into the deps.json, but still same error at the end :(

name: tritonservercppsharp

on: workflow_dispatch

env:
  PLATFORM: x64
  FRAMEWORK: net80
  DOTNET_ROOT: /usr/share/dotnet
  DOTNETSDKVER: 8.0.303
  DOTNETFWKVER: 8.0.7

jobs:
  tritonservercppsharp:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v2

      - name: Write down the bindings code
        run: |
          tee tritonservercppsharp.cs <<EOF
              namespace CppSharpTransformer { public class DllDemoGenerator : CppSharp.ILibrary {
              public static void Main(string[] args) { CppSharp.ConsoleDriver.Run(new DllDemoGenerator()); }
              public void SetupPasses(CppSharp.Driver driver) { }
              public void Preprocess(CppSharp.Driver driver, CppSharp.AST.ASTContext ctx) { }
              public void Postprocess(CppSharp.Driver driver, CppSharp.AST.ASTContext ctx) { }
              public void Setup(CppSharp.Driver driver) {
              var options = driver.Options;
              options.GeneratorKind = CppSharp.Generators.GeneratorKind.CSharp;
              var module = options.AddModule("TritonServerCppSharp");
              module.IncludeDirs.Add("core/include");
              module.Headers.Add("triton/core/tritonserver.h");
              module.Headers.Add("triton/core/tritonbackend.h");
              module.Headers.Add("triton/core/tritoncache.h");
              module.Headers.Add("triton/core/tritonrepoagent.h");
              //module.LibraryDirs.Add("/path/to/triton/server.so");
              //module.Libraries.Add("tritonserver.so");
              } } }
          EOF

      - name: Clone and build CppSharp
        run: |
          git clone --single-branch --depth 1 https://github.com/mono/CppSharp
          cd CppSharp
          bash build/build.sh generate -configuration Release -platform $PLATFORM -target-framework $FRAMEWORK
          bash build/build.sh download_llvm -platform $PLATFORM -target-framework $FRAMEWORK
          bash build/build.sh restore -platform $PLATFORM -target-framework $FRAMEWORK
          bash build/build.sh -platform $PLATFORM -build_only -target-framework $FRAMEWORK
          find bin

      - name: Generating bindings
        run: |
          git clone --single-branch --depth 1 https://github.com/triton-inference-server/core
          dotnet --version
          dotnet --list-runtimes
          DOTNETLIBDIR="$DOTNET_ROOT/shared/Microsoft.NETCore.App/$DOTNETFWKVER"
          #DOTNETSDKVER=$(dotnet --version)
          #DOTNETFWKVER=$(dotnet --list-runtimes | grep Microsoft.NETCore.App | tail -n 1 | cut -d " " -f2)

          echo 'namespace ProgramNamespace { public static class Program { public static void Main(string[] args) { System.Console.WriteLine("Hello world!"); } } }' > footest.cs
          dotnet "$DOTNET_ROOT/sdk/$DOTNETSDKVER/Roslyn/bincore/csc.dll" $(find "$DOTNETLIBDIR" -name "*.dll" -printf '-r:"%p" ') -target:exe -out:footest.exe footest.cs
          echo '{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":"'$DOTNETFWKVER'"}}}' > footest.runtimeconfig.json
          dotnet footest.exe

          # -r:CppSharp/bin/Release_x64/CppSharp.dll -r:CppSharp/bin/Release_x64/CppSharp.AST.dll -r:CppSharp/bin/Release_x64/CppSharp.Runtime.dll -r:CppSharp/bin/Release_x64/CppSharp.CLI.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.CSharp.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.Bootstrap.dll -r:CppSharp/bin/Release_x64/CppSharp.Parser.Gen.dll
          dotnet "$DOTNET_ROOT/sdk/$DOTNETSDKVER/Roslyn/bincore/csc.dll" $(find "$DOTNETLIBDIR" -name "*.dll" -printf '-r:"%p" ') $(find CppSharp/bin -name "*.dll" -printf '-r:"%p" ') -target:exe -out:tritonservercppsharp.exe tritonservercppsharp.cs
          echo '{"runtimeOptions":{"framework":{"name":"Microsoft.NETCore.App","version":"'$DOTNETFWKVER'"}}}' > tritonservercppsharp.runtimeconfig.json
          find CppSharp/bin -name "*.dll" -o -name "*.so" -exec cp {} . ';'
          dotnet tritonservercppsharp.exe || true

      - uses: actions/upload-artifact@v4
        with:
          path: |
            CppSharp/bin/
            tritonservercppsharp.cs
            tritonservercppsharp.runtimeconfig.json
            tritonservercppsharp.exe
vadimkantorov commented 3 weeks ago

Ha-ha, it seems that running a csc-compiled file referencing assemblies obtained from dotnet build is somehow incompatible. I guess I need somehow a csproj/sln files examples (that are known to work) referencing the compiled cppsharp assemblies... Very unfortunate that .NET makes it super-hard to just run a C# file without all the ceremonies of csharpproj and runtimeconfig...

vadimkantorov commented 3 weeks ago

I've tried also using CSharp.Gen:

cp ./CppSharp/bin/Release_x64/libCppSharp.CppParser.so ./CppSharp/bin/Release_x64/libStd-symbols.so .
mkdir output && ./CppSharp/bin/Release_x64/CSharp.Gen -I core/include/triton/core -o ./output/

More errors:

Generating bindings for CSharp (CSharp)
    Unable to translate macro 'MY_MACRO_TEST2_0' to en enum value: Variable [_invalid] unknown in expression : [0_invalid]
    Linking library libStd-symbols.so...
    Linking success.
    Linking library libCSharp-symbols.so...
    Linking success.
    Symbol not found: _ZNSpecializationOfClassWithNonTypeTemplateArgumentD2Ev
    Symbol not found: _ZN48SpecializationOfClassWithNonTypeTemplateArgumentC2Ev
    Symbol not found: HasPureVirtualWithDefaultArg_HasPureVirtualWithDefaultArg
    Symbol not found: ProtectedConstructorDestructor_ProtectedConstructorDestructor

But I couldn't figure out where the symbols _ZNSpecializationOfClassWithNonTypeTemplateArgumentD2Ev, _ZN48SpecializationOfClassWithNonTypeTemplateArgumentC2Ev, HasPureVirtualWithDefaultArg_HasPureVirtualWithDefaultArg, ProtectedConstructorDestructor_ProtectedConstructorDestructor are supposed to be coming from? from some CLR shared lib?

So somehow these are some test functions from CppSharp itself: https://github.com/mono/CppSharp/blob/b658ff32f126ecde6fb56d36558d42af7a518c04/tests/dotnet/CSharp/CSharpTemplates.h#L936

why is it searching for these symbols? Is it trying to run some tests?

vadimkantorov commented 3 weeks ago

And the Driver path gives this error. How do I make this Clang resource file findable?

Unhandled exception. System.Exception: Clang resource folder 'lib/clang/18/include' was not found.
   at CppSharp.Parser.ParserOptions.SetupIncludes(TargetPlatform targetPlatform) in /home/runner/work/tritonservercppsharp/tritonservercppsharp/CppSharp/src/Parser/ParserOptions.cs:line 405
   at CppSharp.Parser.ParserOptions.Setup(TargetPlatform targetPlatform) in /home/runner/work/tritonservercppsharp/tritonservercppsharp/CppSharp/src/Parser/ParserOptions.cs:line 293
   at CppSharp.Driver.Setup() in /home/runner/work/tritonservercppsharp/tritonservercppsharp/CppSharp/src/Generator/Driver.cs:line [57](https://github.com/vadimkantorov/tritonservercppsharp/actions/runs/10461058179/job/28968586864#step:5:58)
   at CppSharp.ConsoleDriver.Run(ILibrary library) in /home/runner/work/tritonservercppsharp/tritonservercppsharp/CppSharp/src/Generator/Driver.cs:line 399
   at CppSharpTransformer.DllDemoGenerator.Main(String[] args) in /home/runner/work/tritonservercppsharp/tritonservercppsharp/tritonservercppsharp.cs:line 2
tritao commented 3 weeks ago

I've tried also using CSharp.Gen:

cp ./CppSharp/bin/Release_x64/libCppSharp.CppParser.so ./CppSharp/bin/Release_x64/libStd-symbols.so .
mkdir output && ./CppSharp/bin/Release_x64/CSharp.Gen -I core/include/triton/core -o ./output/

More errors:

Generating bindings for CSharp (CSharp)
    Unable to translate macro 'MY_MACRO_TEST2_0' to en enum value: Variable [_invalid] unknown in expression : [0_invalid]
    Linking library libStd-symbols.so...
    Linking success.
    Linking library libCSharp-symbols.so...
    Linking success.
    Symbol not found: _ZNSpecializationOfClassWithNonTypeTemplateArgumentD2Ev
    Symbol not found: _ZN48SpecializationOfClassWithNonTypeTemplateArgumentC2Ev
    Symbol not found: HasPureVirtualWithDefaultArg_HasPureVirtualWithDefaultArg
    Symbol not found: ProtectedConstructorDestructor_ProtectedConstructorDestructor

But I couldn't figure out where the symbols _ZNSpecializationOfClassWithNonTypeTemplateArgumentD2Ev, _ZN48SpecializationOfClassWithNonTypeTemplateArgumentC2Ev, HasPureVirtualWithDefaultArg_HasPureVirtualWithDefaultArg, ProtectedConstructorDestructor_ProtectedConstructorDestructor are supposed to be coming from? from some CLR shared lib?

So somehow these are some test functions from CppSharp itself:

https://github.com/mono/CppSharp/blob/b658ff32f126ecde6fb56d36558d42af7a518c04/tests/dotnet/CSharp/CSharpTemplates.h#L936

why is it searching for these symbols? Is it trying to run some tests?

I think CSharp.Gen is just an artifact built for generating the tests bindings.

And the Driver path gives this error. How do I make this Clang resource file findable?

Sure, there should be a lib/clang folder which should be copied into your bin folder.

vadimkantorov commented 3 weeks ago

Okay, I managed to build two variants. Some notes:

I guess I'll try now to bind the C++ headers at https://github.com/triton-inference-server/developer_tools/tree/main/server/include/triton/developer_tools

My overall impression: complete, fully self-contained examples are desperately needed (and especially of the ./CppSharp/bin/Release_x64/CppSharp.CLI command usage), especially for someone un-versed in msbuild/dotnet command over-complicated madness.

name: tritonservercppsharp

on: workflow_dispatch

env:
  PLATFORM: x64
  FRAMEWORK: net80
  FRAMEWORKDOT: net8.0

jobs:
  tritonservercppsharp:
    runs-on: ubuntu-22.04
    steps:
      - name: Clone tritonserver
        run: |
          git clone --single-branch --depth 1 --branch r24.08 https://github.com/triton-inference-server/core
          git clone --single-branch --depth 1 --branch r24.08 https://github.com/triton-inference-server/developer_tools

      - name: Clone and build CppSharp
        run: |
          git clone --single-branch --depth 1 https://github.com/mono/CppSharp
          cd CppSharp
          bash build/build.sh generate -configuration Release -platform $PLATFORM -target-framework $FRAMEWORK
          bash build/build.sh download_llvm -platform $PLATFORM -target-framework $FRAMEWORK
          bash build/build.sh restore -platform $PLATFORM -target-framework $FRAMEWORK
          bash build/build.sh -platform $PLATFORM -build_only -target-framework $FRAMEWORK

      - name: Variant 1
        run: ./CppSharp/bin/Release_x64/CppSharp.CLI -m tritonserver -g csharp -p linux -a x64 -o ./variant1/ -I=core/include core/include/triton/core/tritonserver.h core/include/triton/core/tritonbackend.h core/include/triton/core/tritoncache.h core/include/triton/core/tritonrepoagent.h

      - name: Variant 2
        run: |
          echo '<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><RunWorkingDirectory>$(MSBuildProjectDirectory)</RunWorkingDirectory><OutputType>Exe</OutputType><TargetFramework>net8.0</TargetFramework><EnableDefaultItems>false</EnableDefaultItems></PropertyGroup><ItemGroup><Compile Remove="**/*.cs"/><Compile Include="tritonservercppsharp.cs"/><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.Runtime.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.AST.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.Generator.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.CLI.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.Parser.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.Parser.CSharp.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.Parser.Bootstrap.dll</HintPath></Reference><Reference Include="MyAssembly"><HintPath>./CppSharp/bin/Release_x64/CppSharp.Parser.Gen.dll</HintPath></Reference></ItemGroup></Project>' > tritonservercppsharp.csproj
          tee tritonservercppsharp.cs <<EOF
              namespace CppSharpTransformer { public class DllDemoGenerator : CppSharp.ILibrary {
              public static void Main(string[] args) { CppSharp.ConsoleDriver.Run(new DllDemoGenerator()); }
              public void SetupPasses(CppSharp.Driver driver) { }
              public void Preprocess(CppSharp.Driver driver, CppSharp.AST.ASTContext ctx) { }
              public void Postprocess(CppSharp.Driver driver, CppSharp.AST.ASTContext ctx) { }
              public void Setup(CppSharp.Driver driver) {
              var options = driver.Options;
              options.GeneratorKind = CppSharp.Generators.GeneratorKind.CSharp;
              var module = options.AddModule("tritonserver");
              options.OutputDir = "variant2";
              module.IncludeDirs.Add(".");
              module.IncludeDirs.Add("core/include");
              module.Headers.Add("core/include/triton/core/tritonserver.h");
              module.Headers.Add("core/include/triton/core/tritonbackend.h");
              module.Headers.Add("core/include/triton/core/tritoncache.h");
              module.Headers.Add("core/include/triton/core/tritonrepoagent.h");
              //module.LibraryDirs.Add("/path/to/triton/server.so");
              //module.Libraries.Add("tritonserver.so");
              } } }
          EOF

          mkdir -p bin/x64/Release/$FRAMEWORKDOT && cp -r ./CppSharp/bin/Release_x64/lib bin/x64/Release/$FRAMEWORKDOT/lib
          LD_PRELOAD=$PWD/CppSharp/bin/Release_x64/libCppSharp.CppParser.so:$PWD/CppSharp/bin/Release_x64/libStd-symbols.so dotnet run -c Release

      - uses: actions/upload-artifact@v4
        with:
          path: |
            variant1/
            variant2/
vadimkantorov commented 3 weeks ago

Tried adding ./CppSharp/bin/Release_x64/CppSharp.CLI -m tritondevelopertoolsserver -g csharp -p linux -a x64 -o ./variant3/ -I=developer_tools/server/include -I=core/include -I=developer_tools/server/include/triton/developer_tools developer_tools/server/include/triton/developer_tools/server_wrapper.h developer_tools/server/include/triton/developer_tools/generic_server_wrapper.h developer_tools/server/include/triton/developer_tools/common.h

to wrap https://github.com/triton-inference-server/developer_tools/blob/main/server/include/triton/developer_tools/server_wrapper.h - getting an error error: no template named 'shared_ptr' in namespace 'std' trying to wrap https://github.com/triton-inference-server/developer_tools/blob/main/server/include/triton/developer_tools/common.h. Does CppSharp support std::shared_ptr? Could it be TypeMap'd? This seems quite a fundamental and a frequent type (especially in modern C++), probably worth supporting it for APIs in core CppSharp...


  ServerOptions(
      const std::vector<std::string>& model_repository_paths,
      const LoggingOptions& logging, const MetricsOptions& metrics,
      const std::vector<BackendConfig>& be_config, const std::string& server_id,
      const std::string& backend_dir, const std::string& repo_agent_dir,
      const bool disable_auto_complete_config,
      const ModelControlMode& model_control_mode,
      const int32_t repository_poll_secs,
      const std::set<std::string>& startup_models,
      const std::vector<RateLimitResource>& rate_limit_resource,
      const int64_t pinned_memory_pool_byte_size,
      const std::vector<CUDAMemoryPoolByteSize>& cuda_memory_pool_byte_size,
      const uint64_t response_cache_byte_size,
      const double& min_cuda_compute_capability, const bool exit_on_error,
      const int32_t exit_timeout_secs,
      const int32_t buffer_manager_thread_count,
      const uint32_t model_load_thread_count,
      const std::vector<ModelLoadGPULimit>& model_load_gpu_limit,
      const std::vector<HostPolicy>& host_policy, std::shared_ptr<Trace> trace);

  InferOptions(
      const std::string& model_name, const int64_t model_version,
      const std::string& request_id, const uint64_t correlation_id,
      const std::string& correlation_id_str, const bool sequence_start,
      const bool sequence_end, const uint64_t priority,
      const uint64_t request_timeout,
      std::shared_ptr<Allocator> custom_allocator,
      std::shared_ptr<Trace> trace);
HenrikAkseliValve commented 2 weeks ago

For this case, do I need to do dotnet add package CppSharp? And in both cases, which assemblies do I need to reference for csc.dll to get the basic CppSharp.ILibrary and CppSharp.Driver?

I think these are the main ones:

* CppSharp

* CppSharp.AST

* CppSharp.Generator

* CppSharp.Parser

* CppSharp.Parser.CppSharp

I am not the best person to ask about Nuget as I rarely use it in my workflows.

But development versions are pushed to GitHub packages:

          dotnet nuget push "*.nupkg" --api-key ${{ secrets.GITHUB_TOKEN }} --source "https://nuget.pkg.github.com/mono/index.json" --skip-duplicate

https://github.com/mono/CppSharp/pkgs/nuget/CppSharp

The last version says it was published 4 months ago, so maybe publishing for ongoing development commits is not working correctly after all.

I was not able build package via scripts on windows so I made nuspec file which works to get some context... although I'm pretty sure it is dropping lib files at the moment.

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata>
        <id>CppSharp</id>
        <version>x.y.z</version>
        <authors>CppSharp Devs</authors>
        <owners>Mono</owners>
        <requireLicenseAcceptance>false</requireLicenseAcceptance>
        <description>YOU!</description>
        <dependencies>
            <dependency id="Microsoft.Win32.Registry" version="5.0.0"/>
        </dependencies>
        <contentFiles>
            <files include="any/any/**/*.h" buildAction="Content" copyToOutput="true" />
        </contentFiles>
    </metadata>
    <files>
        <file src="bin\Release_x64\CppSharp.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.AST.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.Generator.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.Parser.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.CppParser.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.CppParser.lib" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.Parser.CLI.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\CppSharp.Parser.CLI.lib" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\Std-symbols.dll" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\Std-symbols.lib" target="lib\netstandard2.1" />
        <file src="bin\Release_x64\Ijwhost.dll" target="lib\netstandard2.1" />
        <!-- Clang headers which path changes depend upon what version of Clang is downloaded -->
        <!-- Are needed see "Parser/ParserOptions.cs"                                                                      -->
        <file src="build\llvm\llvm-6eb36a-windows-vs2022-x64-RelWithDebInfo\lib\**\include\**\*.h" target="contentFiles\any\any\lib\" />
    </files>
</package>
vadimkantorov commented 2 weeks ago

What is CppSharp behavior for binding C++ classes destructors? Does it generate Dispose methods calling the destructor? Does it generate C# finalizers calling Dispose/destructors?

Regarding binding std::shared_ptr<T> - maybe System.Nullable<T> could be somewhat abused to represent std::shared_ptr<T> (might be good as it's a system type always present, so "copies" of this type in bindings of several separately generated bindings are not needed)? Or maybe some new Box<T> or similar could be produced

In modern C++, plenty of various pointer wrappers could now be used in APIs like std::unique_ptr<T> and others... So it's important to have some binding of them (even if not fully performant or requiring additional user's manual intervention to trigger garbage collection / call destructors)

Otherwise, in C# classes have reference/shared_ptr-like semantics anyway, so maybe no wrappers are needed...

tritao commented 1 week ago

Tried adding ./CppSharp/bin/Release_x64/CppSharp.CLI -m tritondevelopertoolsserver -g csharp -p linux -a x64 -o ./variant3/ -I=developer_tools/server/include -I=core/include -I=developer_tools/server/include/triton/developer_tools developer_tools/server/include/triton/developer_tools/server_wrapper.h developer_tools/server/include/triton/developer_tools/generic_server_wrapper.h developer_tools/server/include/triton/developer_tools/common.h

to wrap https://github.com/triton-inference-server/developer_tools/blob/main/server/include/triton/developer_tools/server_wrapper.h - getting an error error: no template named 'shared_ptr' in namespace 'std' trying to wrap https://github.com/triton-inference-server/developer_tools/blob/main/server/include/triton/developer_tools/common.h. Does CppSharp support std::shared_ptr?

We do not support shared_ptr, but this error is actually just a general parsing error. That header is not designed to be included on its own, and does not include all its own dependencies, so in this case its just a general C++ issue, would happen with a regular compiler too.

What is CppSharp behavior for binding C++ classes destructors? Does it generate Dispose methods calling the destructor? Does it generate C# finalizers calling Dispose/destructors?

Yes, see the GenerateFinalizers option: https://github.com/mono/CppSharp/blob/345de8b/src/Generator/Options.cs#L125

Regarding binding std::shared_ptr<T> - maybe System.Nullable<T> could be somewhat abused to represent std::shared_ptr<T> (might be good as it's a system type always present, so "copies" of this type in bindings of several separately generated bindings are not needed)? Or maybe some new Box<T> or similar could be produced

We could start by just allowing for manual reference count increase and decrease, which at least would already allow supporting those objects. But the main reason I did not spend too much time with standard library constructs is that it leads to non portable bindings, where you need generated C# bindings code for each ABI, which complicates packaging and distribution on the C# side.

Hence I always recommend modifying the original C++ code, or creating a portable wrapper on top of it. You can check out the Cpp generator type in CppSharp for this task, I have used it before to create a pure C++ wrapper of a complicated C++ library. That in turns was used to generate C# bindings with CppSharp.

vadimkantorov commented 1 week ago

Thanks for explaining! Deleting common.h from the command gives and succeeds. How does it succeed without shared_ptr support? :)

./CppSharp/bin/Release_x64/CppSharp.CLI -m tritondevelopertoolsserver -g csharp -p linux -a x64 -o ./variant3/ -I=developer_tools/server/include -I=core/include -I=developer_tools/server/include/triton/developer_tools developer_tools/server/include/triton/developer_tools/server_wrapper.h developer_tools/server/include/triton/developer_tools/generic_server_wrapper.h
Generating C# bindings for Linux x64...
Parsing libraries...
Parsing code...
Parsed '/home/runner/work/tritonservercppsharp/tritonservercppsharp/developer_tools/server/include/triton/developer_tools/server_wrapper.h, /home/runner/work/tritonservercppsharp/tritonservercppsharp/developer_tools/server/include/triton/developer_tools/generic_server_wrapper.h'
Processing code...
Generating code...
Generated 'Std.cs'
Generated 'developer_tools.cs'

I think for bindings not distributed publicly but generated, compiled and consumed on the target platform, it is okay to support non-ABI-portable things like shared_ptr. For reference, https://github.com/triton-inference-server/server/blob/main/docs/customization_guide/inference_protocols.md#java-bindings-for-in-process-triton-server-api also distribute these Java bindings (although do not recommend using them) to these same C++ code: https://github.com/bytedeco/javacpp-presets/tree/master/tritonserver/src/gen/java/org/bytedeco/tritonserver/tritondevelopertoolsserver

So overall, JavaCpp seems to support shared_ptr: https://github.com/bytedeco/javacpp/discussions/623

Maybe for CppSharp, the model of a single repo with GitHub Actions - i.e. JavaCpp presets repo: https://github.com/bytedeco/javacpp-presets, reproducbile bindings generation could also be very useful: https://github.com/bytedeco/javacpp-presets

tritao commented 1 week ago

Thanks for explaining! Deleting common.h from the command gives and succeeds. How does it succeed without shared_ptr support? :)

It will ignore any methods and fields that are not supported, so the binding can still be generated.

I think for bindings not distributed publicly but generated, compiled and consumed on the target platform, it is okay to support non-ABI-portable things like shared_ptr. For reference, https://github.com/triton-inference-server/server/blob/main/docs/customization_guide/inference_protocols.md#java-bindings-for-in-process-triton-server-api also distribute these Java bindings (although do not recommend using them) to these same C++ code: https://github.com/bytedeco/javacpp-presets/tree/master/tritonserver/src/gen/java/org/bytedeco/tritonserver/tritondevelopertoolsserver

So overall, JavaCpp seems to support shared_ptr: bytedeco/javacpp#623

They support it by generating C++ code (with JNI) which is a lot more doable for C++ standard library constructs. And somewhat equivalent to what I said before about generating pure C++ bindings with the Cpp generator in CppSharp.

With the regular CppSharp approach, it only generates C++ code for inline symbols for which there is absolute no other way. We currently support std::string by drilling down into the templates from the standard library implementation and having specific code for that: https://github.com/mono/CppSharp/blob/main/src/Generator/Types/Std/Stdlib.CSharp.cs#L304

Its doable to do the same for other C++ std lib types, but overall its somewhat complicated and error prone. I'd probably switch to the approach of generating supporting C++ code, helper functions to wrap std:: types, which would make it much easier to extend and support all of C++ more easily.

Maybe for CppSharp, the model of a single repo with GitHub Actions - i.e. JavaCpp presets repo: https://github.com/bytedeco/javacpp-presets, reproducbile bindings generation could also be very useful: https://github.com/bytedeco/javacpp-presets

No doubt, this would be great, was always on my mind, but its a lot of work to setup and maintain, so unless someone else is willing to do the dirty work, its not going to happen from my side.

vadimkantorov commented 1 week ago

We currently support std::string by drilling down into the templates from the standard library implementation and having specific code for that:

Maybe then an example/recipe in the docs for adding non-portable support for shared_ptr via some hacks (since C# reference types already have some sort of shared_ptr semantics already) and showcase TypeMap... For the user's particular platform/compiler/libstdc++.

No doubt, this would be great, was always on my mind, but its a lot of work to setup and maintain

Maybe if it's not to support a high-quality bar and just be a place where people contribute single-file, self-contained GitHub Actions workflow files as examples, then the maintenance bar would be lower and some reproducibility would be preserved. And also might be nice if https://github.com/dotnet org or https://dotnetfoundation.org/ sponsored/supported this effort...

They support it by generating C++ code (with JNI) which is a lot more doable for C++ standard library constructs. And somewhat equivalent to what I said before about generating pure C++ bindings with the Cpp generator in CppSharp. With the regular CppSharp approach, it only generates C++ code for inline symbols for which there is absolute no other way.

But yeah, I see what you mean...