haxscramper / hcparse

High-level nim bindings for parsing C/C++ code
https://haxscramper.github.io/hcparse-doc/src/hcparse/libclang.html
Apache License 2.0
37 stars 2 forks source link

Generated wrappers should support both static and dynamic linking #23

Open haxscramper opened 2 years ago

haxscramper commented 2 years ago

const
  libgit2LinkMode* {.strdefine.} = "dynlib"
  libgit2Lib* {.strdefine.} =  
    when defined(windows): "libgit2.dll"
    elif defined(macosx):  "libgit2.dylib"
    else:                  "libgit2.so"

import std/[macros]

macro git2Proc*(a: untyped): untyped =
  result = a
  result.addPragma(ident"importc")
  when libgit2LinkMode == "dynlib":
    result.addPragma(nnkExprColonExpr.newTree(
      ident"dynlib", newLit(libgit2Lib)))

  elif libgit2LinkMode in ["static", "dlink"]:
    # Default dynamic or static linking, handled by user via `{.passl.}`
    # etc.
    discard

  else:
    {.error: "Invalid libgit2 link mode specified" &
      " expected 'dynlib', 'static' or 'dlink', but got " &
      libgit2LinkMode.}

Use gitProc for wrapping procedures instead of current git annotations.

haxscramper commented 2 years ago

Implemented in https://github.com/haxscramper/hcparse/commit/da4d2794ea5a36d3ec08db4d4011a325924bd9e3 by allowing full override of the bind expressions. Not closing for now, maybe there would be more fixups/ideas for this

haxscramper commented 2 years ago

https://github.com/nim-lang/rfcs/issues/58

Linux is "fundamentally broken", right, but I think it still makes sense to allow for this mode of dynamic library interfacing as well, because it makes sense to give this decision power to the users.

haxscramper commented 2 years ago

Dynlib version pattern feature should also be supported when using constant/define to specify library location. dynlib can call into regular proc, so this functionality can even be exposed to the library caller via an API (import hlibgit2/libgit2_config with subsequent hlibgit2SetDlPath or something similar).

Not sure how good this really is, but certainly better than hardcoding allowed versions of the library and then repeating the same thing thousand times over all wrapped procs.

haxscramper commented 2 years ago

Each library should define --<libraryName>LinkMode = "static" | "dlopen" | "dlink" for static, dynlib and built-in dynamic linking respectively.

haxscramper commented 2 years ago

After trying out conan for package management (#20 and #27) I think that

(1) if nim package wants to properly use C++ dynamic libraries that are installed via conan it is highly likely that regular dynamic linking would be more useful. LD_LIBRARY_PATH already exists, and there is no need to reinvent shared library handling. For example, hcparse itself requires boost/1.76.0 to run, so I write a dependency configuration for this -

[requires]
boost/1.76.0

and run conan install . --build=missing, which provides me with all the necessary installed libraries (I don't really have to worry which specific OS/distro I'm running on). After I've done so, nim still picks up system-installed libraries, so I have to do source activate_run.sh (for bash-compatible shells), or E:LD_LIBRARY_PATH=(str:join ":" (jq "[ .dependencies[].lib_paths[] ]" conanbuildinfo.json | from-json)) (for elvish shell that I use) in order to configure LD_LIBRARY_PATH.

I don't know how it is supposed to work on Windows, but given the usual approach of just copying the shared libraries to the adjacent folder and claiming it "just works", this can be easily replicated. Alternatively, conan provides an activate_run.ps1, so windows would work as well.

There is a question of distributing the dynamic libraries for the dependencies, but this is not exactly hcparse's problem - package author is free to select whatever approach they deem necessary. Probably conan has some solutions for that as well.

(2) Making nim reuse the same dynamic library handling approach as any other compiled programming language is a sensible goal that at least worth considering (aka I don't really care that "every scripting language uses the dlopen mechanism" because nim is not a scripting language). It is not a problem to support dynlib as well, considering all the configuration is now compacted into a single macro.

(3) I want to create binaries that can be inspected using default tools like ldd etc

haxscramper commented 2 years ago

The build process is a little more complex compared to using dynlib of course - I need to set LIBRARY_PATH when building and LD_LIBRARY_PATH when loading the app (dynlib allows to either hardcoded, or make application perform the search via dynlib: findLib(), so if you want you can reimplement a dynamic linker for your library)

https://github.com/nim-lang/Nim/issues/12751 I guess @appendenv was created to do something similar - @appendenv "LIBRARY_PATH" "$config/lib".

haxscramper commented 2 years ago

After some hands-on experience with dynlib and using libraries form a "non-standard" location I think it would be most appropriate for hcparse to default to the regular dynamic linking rather than dynlib. It would be a trivial switch to change the behavior, but I would prefer to choose the most flexible solution.

In addition, there should be some additional documentation, like using LIBRARY_PATH, LD_LIBRARY_PATH, conan package management, linking with self-built libraries and so on

haxscramper commented 2 years ago

Not sure if dynlib can even work with C++, need to find more examples on this one