Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

using nbind / node-gyp with lot's of extra libraries - how to tell node-gyp where all sources are? #35

Closed
subwaystation opened this Issue Nov 9, 2016 · 52 comments

Comments

Projects
None yet
3 participants
@subwaystation
Copy link

subwaystation commented Nov 9, 2016

Take a C++ framework, e.g. https://github.com/vgteam/vg/tree/master/src having lots of dependencies when building.
Now I would like to e.g. nbind all classes in file https://github.com/vgteam/vg/tree/master/src/vg.cpp. The problem is that when executing
"npm run -- node-gyp configure build" the following happens:

../src/vg.hpp:11:10: fatal error: 'omp.h' file not found
#include <omp.h>
^
1 error generated.

This error does not occur if I build the project as specified in the wiki. Following this issue http://stackoverflow.com/questions/25990296/how-to-include-omp-h-in-os-x I have to state that gcc 4.9.3 via homebrew is installed and globally available. Maybe node-gyp does not use gcc / g++?
Is there anyway to pass all the source files necessary for the build to node-gyp or how do I proceed here? (I have a CMakeLists.txt file containing over 3000 lines of sources, can I pass this to node-gyp? Or would node-gyp be able to do this automatically?)

All the best.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 9, 2016

You should probably compile vg separately into a library first, and then link your Node.js addon with it. This will be some work but you can look at libui-node for reference. If you git clone and npm install it, you'll see it loads precompiled binary libraries and then links the nbind-based addon with them. The binding.gyp file there shows how to do it on Windows, Linux and OS X.

Any kind of notes or a tutorial would be very welcome if you get it working.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 9, 2016

If you simply install vg globally, it should be quite easy to configure compiler and linker flags in binding.gyp to use it. libui-node has some extra complexity because it installs the external binaries together with the npm package, which is easier for others using the package.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 9, 2016

Yeah I already compiled it, as was instructed by the vg wiki.
How would I install it globally?
Could you please give me some hints about the compiler configuration and linker flags?
Basically I have to transfer what it says in the vg makefile intobinding.gyp, right?

I will sort out a detailed description, as soon as it works ;)

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 9, 2016

I looked into the binding.gyp of libui-node and I am not quite sure how to interpret it.
Where does the variable module_root_dir point to (Especially on Mac OS)? Does this differ from OS to OS?
Is -lui the library on Mac OS corresponding to libui.lib on WIN and libui.so on Linux?
If I have compiled whole vg, what files do I have to add here? The object files ending with .o?

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 9, 2016

So out of every *.o file I have created after compiling vg I create a library for each object file as given here: http://www.adp-gmbh.ch/cpp/gcc/create_lib.html. I would like to create a shared library, right? Can I put several object files into one lib?
Then I will have to specify all libs in the binding.gyp file, right? Can I put them to e.g. /usr/lib to make them available globally?

So far so good the theory, but I am not quite getting how I can then (n)bind to these libraries.
Here is what I am thinking of, but it would be some work to do:
Every class and corresponding functions or global functions I want to use via nbind I will have to 'reimplement' again.
Small example, lets stick to your given Greeter example:

  1. construction of library file out of hello.cc --> libhello.a
  2. create new file hello.cc (is the same name possible? - else hello1.cc) as follows:

#include

void sayHey(std::string name) {
sayHello(name);
}

#include "nbind/nbind.h"

NBIND_GLOBAL() {
function(sayHello);
}

That should work, right?

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 9, 2016

Looking at vg's Makefile it seems it should produce a file libvg.a. That file should contain all the code you need (it's an archive of all of vg's relevant .o files in a form the C++ compiler understands).

It seems like you'll have quite a few classes and methods to bind. Looking at the class VG there's several methods like normalize and has_node that should be possible to bind without additional code (for example break_cycles will however require binding also the Edge class).

In theory you should be able to create a new .cpp file like:

#include "vg.hpp"
#include "nbind/api.h"

namespace vg {
  // This inherits methods from VG for directly binding them when possible.
  class JS_VG : public VG {
  public:
    // Anything taking a callback will have to be wrapped!
    for_each_node(nbind::cbFunction lambda) { /* your code here */ }

    // Alternatively you can give your new implementation a new name.
    js_for_each_node(nbind::cbFunction lambda) { /* your code here */ }
  };
} // namespace

#include "nbind/nbind.h"

// You can rename your JS_VG reimplementation back to VG on the JavaScript side.
NBIND_CLASS(vg :: JS_VG, VG) {
  // You don't have to reimplement this, just tell nbind that it exists.
  method(normalize);

  // This C++ method is overloaded so we need to tell nbind which arguments it takes
  // (otherwise the C++ compiler cannot distinguish between overloads).
  multimethod(has_node, args(Node *));

  // If we want to bind several overloads, they need to be renamed uniquely
  // because nbind doesn't yet support overloading by type on the JavaScript side.
  multimethod(has_node, args(id_t), "has_node_id");

  // We've overloaded this with a new implementation.
  multimethod(for_each_node, args(nbind::cbFunction));

  // Alternatively if we renamed it uniquely, we can just rename it back here
  // and have nbind autodetect the arguments. Actually we can use camelCase
  // which is more common in JavaScript.
  method(js_for_each_node, "forEachNode");
}

Note that I didn't really check the syntax of the above very carefully, but hopefully it will get you started...

I think as long as you don't need to call your new C++ methods (reimplemented for nbind purposes) from other classes, you can put your new class declaration in the .cpp file and when required as an argument or return type from other classes, just forward declare it like:

class JS_VG;
@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 9, 2016

It seems vg doesn't have an install option in its Makefile so maybe you should just copy libvg.a inside your own project and use it from there. You could ask people in the vg project how you're supposed to use it as a library, or look at how its command line tool is linked with the library.

You could look for command line flags in gcc docs if you're targeting Linux or OS X. On OS X the default compiler is probably clang which should be compatible with gcc command line flags.

libui-node is a pretty good reference how to put the compiler flags in your binding.gyp.

I think you should start with a really simple C++ application that will just call a single method from vg and print something. When you get the compilation step working, it should be easier to get your nbind bindings working afterwards. For the basic compilation, the people maintaining vg should also be able to help you. They'll probably be happy that someone is working on Node.js bindings.

@adamnovak

This comment has been minimized.

Copy link

adamnovak commented Nov 10, 2016

Hello, I'm from the vg project and vaguely understand the build system. Unfortunately I know nothing about nbind or Gyp, but I'd like to get node bindings working if we can.

We pull in a lot of libraries as Git submodules, because our target environment is academic clusters and other places where users don't have access to a package manager. As part of our Makefile, we manually copy library headers into vg/include and their compiled libraries into vg/lib, and then compile and link against those. All the vg sources and headers are comingled in vg/src, except for some which are generated using Protobuf and live in vg/cpp. Most of the vg code goes into libvg.a which lands in vg/lib next to the dependency libraries. The final binary lands in vg/bin.

To build its dependencies, vg relies on a bunch of recursive Make calls. Some of the dependencies (SDSL-lite) demand to be installed using their own shell scripts.

We've never actually written any code to install vg globally; we've just been adding it to our PATHs manually. Projects that depend on vg generally include it as a Git submodule, build it with recursive Make, and just point to its includes and libraries inside the submodule.

Is it possible to use nbind without getting gyp to be able to build vg and all its dependencies? Is it possible to configure gyp for vg alone, and to call make or other scripts on the bundled dependencies? Or to get this to work, do we have to repackage all the dependencies?

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 10, 2016

@adamnovak nbind will work as long as it's possible to link a new project against vg by passing command line flags to the compiler, something like gcc ... -lvg. So ideally if vg can be compiled into a library so that a single test program test.cpp that calls vg can be easily linked with it, the same compiler flags when placed in a binding.gyp file should allow defining Node.js bindings for it.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 10, 2016

@subwaystation It seems you should copy libvg.a and all the .hpp files from vg's src directory to your own project's directory (also create the subdirectories subcommand and unittest and copy the hpp files from those too just in case). Then create a new file test.cpp and try to get it to compile. ultrabubble_eval seems like a good starting point.

Once that works, try:

export CXX=echo
make

Now it should print the necessary gcc command line flags. They need to go in cflags (arguments from the first command that compiles an individual source file) and ldflags (arguments from the last command that links everything together) in binding.gyp.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

I was able to compile a small example test.cpp making use of the vg library.
My file setup was the following:
../git/vg/
../git/vg_compile_test
../git/nbind-testing

In the attachment one can find the vg_compile_test folder including MAKEFILE and compiler output for MAC OS.
vg_compile_test.zip
Thanks for all the tips!

@jjrv However, when trying to build with node-gyp, I ran into the following problem:

CXX(target) Release/obj.target/nbind/JS_VG.o
In file included from ../JS_VG.cpp:1:
../../vg/src/vg.hpp:11:10: fatal error: 'omp.h' file not found
#include <omp.h>
^
1 error generated.

The omp library is being shipped with g++ 4.9.3 as far as I know.
Do I have to specify which g++ to use in the binding.gyp?
Maybe node-gyp is using clang as default on MAC OS? Still, g++ points to /usr/local/bin/g++ being the correct g++ compiler on my machine (I have used this one to compile vg
in the first place).

Here is the current version of my binding.gyp that I have derived from the compiler output:

{
    "targets": [
        {
            "includes": [
                "auto.gypi"
            ],
            "sources": [
                "JS_VG.cpp"
            ],
            "conditions": [
                ["OS=='mac'", {
                    "xcode_settings": {
                        "OTHER_CFLAGS": [
                            "-c",
                            "-msse4.1",
                            "-fopenmp",
                            "-frtti",
                            "-std=c++11",
                            "-ggdb",
                            "-g",
                            "-I../vg/",
                            "-I../vg/include/"
                        ],
                        "OTHER_LDFLAGS": [
                            "-03",
                            "-msse4.1",
                            "-fopenmp",
                            "-std+c++11",
                            "ggdb",
                            "-g",
                            "-I../vg/",
                            "-I../vg/include",
                            "-lm",
                            "-lpthread",
                            "-ly",
                            "-lbz2",
                            "-lsnappy",
                            "-ldivsufsort",
                            "-ldivsufsort64",
                            "-ljansson",
                            "-L../vg/src/",
                            "-L../vg/lib/"
                        ]
                    },
                }],
            ]
        }
    ],
    "includes": [
        "auto-top.gypi"
    ]
}
@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Or ist node-gyp not able to grasp my -fopenmp CFLAG?

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

It should use whatever compiler is defined in the environment variable CXX. You can also set that at the top level of binding.gyp like this:

"make_global_settings": [
  ["CXX",  "/usr/local/bin/g++"],
  ["LINK", "/usr/local/bin/g++"]
]
@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Then I run into the following:

 CXX(target) Release/obj.target/nbind/JS_VG.o
g++: error: unrecognized command line option '-stdlib=libc++'
g++: error: unrecognized command line option '-stdlib=libc++'
make: *** [Release/obj.target/nbind/JS_VG.o] Error 1

Although, I did not even specify this command line option in binding.gyp.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Try removing "OTHER_LDFLAGS": ["-stdlib=libc++"] from src/nbind.gypi inside nbind. It's needed for clang...

Not sure how gcc will work without it, hopefully it uses an stdlib compatible with C++11 anyway.

Also, I think -03 in LDFLAGS should be -O3 and -std+c++11 should be -std=c++11 or you can just leave them out, they're already in that default nbind.gypi file.

ggdb in OTHER_LDFLAGS should be -ggdb.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

I did comment them out. So I got rid of one line of
g++: error: unrecognized command line option '-stdlib=libc++'
However, still one is there.

You are right about the flags, I just removed them from my binding.gyp.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Where else could node-gyp 'hide' it's parameters?

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Did you remove it also from OTHER_CPLUSPLUSFLAGS in nbind.gypi?

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Yes, I did:

                "OTHER_CPLUSPLUSFLAGS": [
                    "-O3",
                    "-std=c++11",
#                   "-stdlib=libc++"
                ],
#               "OTHER_LDFLAGS": ["-stdlib=libc++"]
@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Seems like node-gyp adds it here and here. CLANG_CXX_LIBRARY can be used to change the argument, but I can't find a setting to remove the parameter entirely.

node-gyp also adds parameters Emscripten doesn't like, so there's a script bin/emcc inside nbind to remove them. You could probably create a new compiler script to get rid of the unwanted arguments in the same way:

#!/bin/sh

skip=0

for arg; do
    shift
    [ "$skip" = "1" ] && skip=0 && continue
    case "$arg" in
        -stdlib=*) ;;
        *) set -- "$@" "$arg" ;;
    esac
done

/usr/local/bin/g++ $@

Just make that executable and point CXX to it. You can remove any other undesirable parameters by adding them in that case statement. If they come in pairs like -arch x86_64 then you can do -arch) skip=1 ;; to remove both. See the existing emcc script.

It's a dirty hack but should get the job done. I'm sorry it gets so messy...

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Well, better messy than not working :)

Sorry to bother you again, but now I am stuck here:

  CXX(target) Release/obj.target/nbind/JS_VG.o
In file included from ../JS_VG.cpp:1:0:
../../vg/src/vg.hpp:17:18: fatal error: gssw.h: No such file or directory
 #include "gssw.h"
                  ^
compilation terminated.

I thought "-L<../vg/lib/", would have taken care of this (there at least the gssw.a file is located; adding "../vg/lib/libgssw.a" in binding-gyp under OTHER_LDFLAGS does not solve the issue). But as it turns out, gssw.h is located in ../git/vg/deps/gssw/src/gssw.h.
However, in the MAKEFILE when using vg as a lib for my code, I did not have to specify this location which is confusing me, as I think about it. Maybe you can get something from the MAKEFILE (uploaded above)?

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

I just found out, that gssw.h is also located at ../git/vg/include/gssw.h. Which I added in my binding.gyp as "-I../vg/include". So the compiler actually should be aware of the file. My confusion is now growing even more....

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Gyp "relativizes" paths (I can't remember how it handles when there's no space between the I and .. though), so that ../vg/include should probably be relative from the directory where binding.gyp is located. Try export CXX=echo before compiling and see the -I path that the compiler actually receives as an argument.

The compiler is actually executed in a new build subdirectory below where binding.gyp is located so if the compiler receives the same -I../vg/include as in the config file (I saw that in compile_output in the zip you uploaded), you should either add an extra ../ in the beginning or try to get rid of that space after -I.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Putting -I../../vg/include kind of solved the issue. Indeed, I observed, that the compiler directory is in build.
However, when export CXX=echo was run, this pops up:

  CXX(target) Release/obj.target/nbind/JS_VG.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1 -DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION -I/Users/heumos/.node-gyp/7.0.0/include/node -I/Users/heumos/.node-gyp/7.0.0/src -I/Users/heumos/.node-gyp/7.0.0/deps/uv/include -I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan -I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11 -stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF ./Release/.deps/Release/obj.target/nbind/JS_VG.o.d.raw -c -o Release/obj.target/nbind/JS_VG.o ../JS_VG.cpp
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/common.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1 -DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION -I/Users/heumos/.node-gyp/7.0.0/include/node -I/Users/heumos/.node-gyp/7.0.0/src -I/Users/heumos/.node-gyp/7.0.0/deps/uv/include -I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan -I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11 -stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF ./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/common.o.d.raw -c -o Release/obj.target/nbind/node_modules/nbind/src/common.o ../node_modules/nbind/src/common.cc
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/reflect.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1 -DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION -I/Users/heumos/.node-gyp/7.0.0/include/node -I/Users/heumos/.node-gyp/7.0.0/src -I/Users/heumos/.node-gyp/7.0.0/deps/uv/include -I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan -I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11 -stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF ./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/reflect.o.d.raw -c -o Release/obj.target/nbind/node_modules/nbind/src/reflect.o ../node_modules/nbind/src/reflect.cc
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1 -DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION -I/Users/heumos/.node-gyp/7.0.0/include/node -I/Users/heumos/.node-gyp/7.0.0/src -I/Users/heumos/.node-gyp/7.0.0/deps/uv/include -I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan -I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11 -stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF ./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o.d.raw -c -o Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o ../node_modules/nbind/src/v8/Buffer.cc
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1 -DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION -I/Users/heumos/.node-gyp/7.0.0/include/node -I/Users/heumos/.node-gyp/7.0.0/src -I/Users/heumos/.node-gyp/7.0.0/deps/uv/include -I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan -I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11 -stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF ./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o.d.raw -c -o Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o ../node_modules/nbind/src/v8/Binding.cc
  SOLINK_MODULE(target) Release/nbind.node
g++: error: Release/obj.target/nbind/JS_VG.o: No such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/common.o: No such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/reflect.o: No such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o: No such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o: No such file or directory
make: *** [Release/nbind.node] Error 1

For some reason, no object files for nbind are created. Do I have to adjust something especially for g++ maybe?

If I do not export CXX=echo, then the error is still the same:

  CXX(target) Release/obj.target/nbind/JS_VG.o
In file included from ../JS_VG.cpp:1:0:
../../vg/src/vg.hpp:17:18: fatal error: gssw.h: No such file or directory
 #include "gssw.h"
                  ^
compilation terminated.
@adamnovak

This comment has been minimized.

Copy link

adamnovak commented Nov 11, 2016

With CXX=echo it's not actually building anything, just announcing
the options it will use. So the complaints about missing .o files
(which would be created by the compiler, which isn't being run) are to
be expected. But it also means that -I../../vg/include didn't really
solve the problem, because you still have it without CXX=echo.

You could try an absolute path to vg/include. But that will only work
on your system.

On Fri, Nov 11, 2016 at 10:02 AM, subwaystation
notifications@github.com wrote:

Putting -I../../vg/include kind of solved the issue. Indeed, I observed,
that the compiler directory is in build.
However, when export CXX=echo was run, this pops up:

CXX(target) Release/obj.target/nbind/JS_VG.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1
-DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION
-I/Users/heumos/.node-gyp/7.0.0/include/node
-I/Users/heumos/.node-gyp/7.0.0/src
-I/Users/heumos/.node-gyp/7.0.0/deps/uv/include
-I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan
-I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7
-arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11
-stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF
./Release/.deps/Release/obj.target/nbind/JS_VG.o.d.raw -c -o
Release/obj.target/nbind/JS_VG.o ../JS_VG.cpp
CXX(target) Release/obj.target/nbind/node_modules/nbind/src/common.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1
-DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION
-I/Users/heumos/.node-gyp/7.0.0/include/node
-I/Users/heumos/.node-gyp/7.0.0/src
-I/Users/heumos/.node-gyp/7.0.0/deps/uv/include
-I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan
-I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7
-arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11
-stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF
./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/common.o.d.raw
-c -o Release/obj.target/nbind/node_modules/nbind/src/common.o
../node_modules/nbind/src/common.cc
CXX(target) Release/obj.target/nbind/node_modules/nbind/src/reflect.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1
-DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION
-I/Users/heumos/.node-gyp/7.0.0/include/node
-I/Users/heumos/.node-gyp/7.0.0/src
-I/Users/heumos/.node-gyp/7.0.0/deps/uv/include
-I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan
-I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7
-arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11
-stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF
./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/reflect.o.d.raw
-c -o Release/obj.target/nbind/node_modules/nbind/src/reflect.o
../node_modules/nbind/src/reflect.cc
CXX(target) Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1
-DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION
-I/Users/heumos/.node-gyp/7.0.0/include/node
-I/Users/heumos/.node-gyp/7.0.0/src
-I/Users/heumos/.node-gyp/7.0.0/deps/uv/include
-I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan
-I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7
-arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11
-stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF
./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o.d.raw
-c -o Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o
../node_modules/nbind/src/v8/Buffer.cc
CXX(target) Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o
-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1
-DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE
-D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION
-I/Users/heumos/.node-gyp/7.0.0/include/node
-I/Users/heumos/.node-gyp/7.0.0/src
-I/Users/heumos/.node-gyp/7.0.0/deps/uv/include
-I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan
-I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7
-arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11
-stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF
./Release/.deps/Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o.d.raw
-c -o Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o
../node_modules/nbind/src/v8/Binding.cc
SOLINK_MODULE(target) Release/nbind.node
g++: error: Release/obj.target/nbind/JS_VG.o: No such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/common.o: No
such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/reflect.o: No
such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o: No
such file or directory
g++: error: Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o: No
such file or directory
make: *** [Release/nbind.node] Error 1

For some reason, no object files for nbind are created. Do I have to adjust
something especially for g++ maybe?

If I do not export CXX=echo, then the error is still the same:

CXX(target) Release/obj.target/nbind/JS_VG.o
In file included from ../JS_VG.cpp:1:0:
../../vg/src/vg.hpp:17:18: fatal error: gssw.h: No such file or directory
#include "gssw.h"
^
compilation terminated.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Changing all necessary parameters to absolute paths like -I/Users/my_user_name/git/vg/include results in the same errors / outputs.

@adamnovak

This comment has been minimized.

Copy link

adamnovak commented Nov 11, 2016

And there's definitely a "gssw.h" in /Users/my_user_name/git/vg/include?

On Fri, Nov 11, 2016 at 10:19 AM, subwaystation
notifications@github.com wrote:

Changing all necessary parameters to absolute paths like
-I/Users/my_user_name/git/vg/include results in the same errors / outputs.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the thread.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

Yes there is:

ls /Users/my_user_name/git/vg/include
BandedSmithWaterman.h   Repeats.h       dynamic.hpp     hts_internal.h      raptor2         ssw_cpp.h
BedReader.h     SmithWatermanGotoh.h    files.h         htslib          rnglib.hpp      stream.hpp
CandidateList.hpp   Subgraph.hpp        filevercmp.h        internal.h      rocksdb         support.h
DAG.hpp         Variant.h       filevercmp.o        join.h          sdsl            tabix.hpp
DetectSuperBubble.hpp   algorithms.h        gcsa.h          kseq.h          sha1.hpp        utils.h
Fasta.h         cdflib.hpp      gfakluge.hpp        lcp.h           snappy-c.h      var.hpp
Graph.hpp       config.h        globalDefs.hpp      lru_cache.h     snappy-sinksource.h vcfheader.hpp
IndelAllele.h       convert.h       google          mt19937ar.h     snappy-stubs-public.h   version.h
IntervalTree.h      dbg.h           gpatInfo.hpp        multichoose.h       snappy.h        vg.pb.h
LargeFileSupport.h  disorder.h      gssw.h          multipermute.h      sonLib          vg_git_version.hpp
LeftAlign.h     divsufsort.h        hash_map_set.hpp    path_graph.h        sparsehash      xg.hpp
Mosaik.h        divsufsort64.h      helperDefs.hpp      pdflib.hpp      split.h
Region.h        dynamic         hfile_internal.h    progress_bar.hpp    ssw.h

Just to make sure, I did not a bad typo or something, here is my current binding.gyp:

{
"make_global_settings": [
  ["CXX",  "/Users/my_user_name/git/nbind-testing/node_modules/nbind/bin/g++"],
  ["LINK", "/Users/my_user_name/git/nbind-testing/node_modules/nbind/bin/g++"]
],
    "targets": [
        {
            "includes": [
                "auto.gypi"
            ],
            "sources": [
                "JS_VG.cpp"
            ],
            "conditions": [
                ["OS=='mac'", {
                    "xcode_settings": {
                        "OTHER_CFLAGS": [
                            "-c",
                            "-msse4.1",
                            "-fopenmp",
                            "-frtti",
                            "-ggdb",
                            "-g",
                            "-I/Users/my_user_name/git/vg/",
                            "-I/Users/my_user_name/git/vg/include"
                        ],
                        "OTHER_LDFLAGS": [
                            "-msse4.1",
                            "-fopenmp",
                            "-ggdb",
                            "-g",
                            "-I/Users/my_user_name/git/vg/",
                            "-I/Users/my_user_name/git/vg/include",
                            "-lm",
                            "-lpthread",
                            "-ly",
                            "-lbz2",
                            "-lsnappy",
                            "-ldivsufsort",
                            "-ldivsufsort64",
                            "-ljansson",
                            "-L</Users/my_user_name/git/vg/src",
                            "-L</Users/my_user_name/git/vg/lib",
                        ]
                    },
                }],
            ]
        }
    ],
    "includes": [
        "auto-top.gypi"
    ]
}
@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

@adamnovak In your MAKEFILE example of https://github.com/glennhickey/ultrabubble_eval you added all the ../../vg/lib/libgssw.a manually. Is this missing or necessary in the binding.gyp?

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Adding the .a anywhere shouldn't affect whether it finds the .h. One thing you can do is make the paths absolute, do CXX=echo and run the command manually in the build directory. By directly running the command and making changes, usually you can eventually get it to work. It also takes nbind out of the equation and becomes just a problem of getting the compiler flags right in one single case.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Another thing, the result you pasted...

-DNODE_GYP_MODULE_NAME=nbind -DUSING_UV_SHARED=1 -DUSING_V8_SHARED=1 -DV8_DEPRECATION_WARNINGS=1 -D_DARWIN_USE_64_BIT_INODE=1 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DBUILDING_NODE_EXTENSION -I/Users/heumos/.node-gyp/7.0.0/include/node -I/Users/heumos/.node-gyp/7.0.0/src -I/Users/heumos/.node-gyp/7.0.0/deps/uv/include -I/Users/heumos/.node-gyp/7.0.0/deps/v8/include -I../node_modules/nan -I../node_modules/nbind/include -Os -gdwarf-2 -mmacosx-version-min=10.7 -arch x86_64 -Wall -Wendif-labels -W -Wno-unused-parameter -std=c++11 -stdlib=libc++ -fno-rtti -fno-threadsafe-statics -O3 -std=c++11 -MMD -MF ./Release/.deps/Release/obj.target/nbind/JS_VG.o.d.raw -c -o Release/obj.target/nbind/JS_VG.o ../JS_VG.cpp

I don't see any -I flags referencing vg in there at all. That might be why it fails. Try OTHER_CPLUSPLUSFLAGS instead of OTHER_CFLAGS.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

That did the trick ;)

Now I am hanging here:

  CXX(target) Release/obj.target/nbind/JS_VG.o
In file included from ../../vg/src/position.hpp:6:0,
                 from ../../vg/src/path.hpp:16,
                 from ../../vg/src/gssw_aligner.hpp:14,
                 from ../../vg/src/vg.hpp:20,
                 from ../JS_VG.cpp:1:
/Users/heumos/git/vg/include/xg.hpp: In constructor 'xg::XG::XG()':
/Users/heumos/git/vg/include/xg.hpp:264:10: warning: 'xg::XG::end_marker' will be initialized after [-Wreorder]
     char end_marker;
          ^
/Users/heumos/git/vg/include/xg.hpp:90:12: warning:   'size_t xg::XG::seq_length' [-Wreorder]
     size_t seq_length;
            ^
/Users/heumos/git/vg/include/xg.hpp:54:5: warning:   when initialized here [-Wreorder]
     XG(void) : start_marker('#'),
     ^
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/common.o
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/reflect.o
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/v8/Buffer.o
  CXX(target) Release/obj.target/nbind/node_modules/nbind/src/v8/Binding.o
  SOLINK_MODULE(target) Release/nbind.node
ld: library not found for -lsnappy
collect2: error: ld returned 1 exit status
@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

So theoretically snappy is available:

ls /Users/heumos/git/vg/lib
lib3edgeconnected.a     libprotobuf-lite.dylib      libraptor2.a            libvcfh.a
libdivsufsort.a         libprotobuf-lite.la     librocksdb.a            libvcflib.a
libdivsufsort64.a       libprotobuf.10.dylib        libsdsl.a           libvg.a
libgcsa2.a          libprotobuf.a           libsnappy.1.dylib       libxg.a
libgfakluge.a           libprotobuf.dylib       libsnappy.a         pkgconfig
libgssw.a           libprotobuf.la          libsnappy.dylib         ssw.h
libhts.a            libprotoc.10.dylib      libsnappy.la            ssw_cpp.h
libpinchesandcacti.a        libprotoc.a         libsonlib.a
libprotobuf-lite.10.dylib   libprotoc.dylib         libssw.a
libprotobuf-lite.a      libprotoc.la
@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

And added under "OTHER_LDFLAGS".

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

Try removing the < from -L</Users/my_user_name/git/vg/lib. I think it's meant for variables, but that path is a normal string.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

It's compiling now, thank you very much @jjrv.

If I could get the following example working, I would be very happy:
JS_VG.ccp:

#include "../vg/src/vg.hpp"
#include "nbind/api.h"

using namespace vg;

#include "nbind/nbind.h"

NBIND_CLASS(VG) {

  // Add an empty constructor
  construct<>();

  // Check if the graph is empty
  method(empty);

  // Generate hash out of graph
  method(hash);
}

And the corresponding vg.js:

var nbind = require('nbind');
var lib = nbind.init().lib;

var emptyGraph = new lib.VG();

console.log(emptyGraph.hash());
console.log(emptyGraph.empty());

Output for node vg.js:

module.js:600
  return process.dlopen(module, path._makeLong(filename));
                 ^

Error: dlopen(/Users/heumos/git/nbind-testing/build/Release/nbind.node, 1): Symbol not found: __ZN2vg2VG4hashEv
  Referenced from: /Users/heumos/git/nbind-testing/build/Release/nbind.node
  Expected in: flat namespace
 in /Users/heumos/git/nbind-testing/build/Release/nbind.node
    at Object.Module._extensions..node (module.js:600:18)
    at Module.load (module.js:490:32)
    at tryModuleLoad (module.js:449:12)
    at Function.Module._load (module.js:441:3)
    at Module.require (module.js:500:17)
    at require (internal/module.js:20:19)
    at initNode (/Users/heumos/git/nbind-testing/node_modules/nbind/dist/nbind.js:140:15)
    at /Users/heumos/git/nbind-testing/node_modules/nbind/dist/nbind.js:114:13
    at findCompiledModule (/Users/heumos/git/nbind-testing/node_modules/nbind/dist/nbind.js:78:13)
    at find (/Users/heumos/git/nbind-testing/node_modules/nbind/dist/nbind.js:92:13)

I would assume it is a binding problem which I can't figure out. Or a C++ problem.
As I recently started with both C++ and JavaScript I am having some trouble seeing through.

As soon as I am able to run your given, more complex example on the more top of this issue, I will make a point by point tutorial for binding vg with nbind.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 11, 2016

I think it's because libvg.a is a static library, and the compiled Node.js addon is a bundle. Somehow the linker thinks it has found everything but then when Node loads the addon, the contents of libvg.a are not available...

@jjrv

This comment has been minimized.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 11, 2016

So I tried this surrounding all the library stuff:

                            "Wl,--whole-archive",
                            "-lm",
                            "-lpthread",
                            "-ly",
                            "-lbz2",
                            "-lsnappy",
                            "-ldivsufsort",
                            "-ldivsufsort64",
                            "-ljansson",
                            "-L/Users/heumos/git/vg/src",
                            "-L/Users/heumos/git/vg/lib",
                            "-Wl,--no-whole-archive"

But g++ does not recognize the option...
SOLINK_MODULE(target) Release/nbind.node g++: error: unrecognized command line option '--whole-archive' make: *** [Release/nbind.node] Error 1

I even tried -Wl,-whole-archive or -whole-archive or --whole-archive leading all to the same error. I did take a look into the ld manual it it clearly states --whole-archive. Weird.

Another theory: Could the upper error (Symbol not found, ...) have been occurred, because I did not specify all the static libraries one by one? Because when you take a look at the COMPILE_OUTPUT file when I use vg as a library in C++ they are part of the build process in the MAKEFILE.

@adamnovak

This comment has been minimized.

Copy link

adamnovak commented Nov 11, 2016

You're missing a "-" on the first "-Wl".

I also notice you have no "-lvg" in your list here; that's the library you
want wholely included. I think that libvg.a has all the code from all the
libraries, at least if you built vg itself with "make static".

On Fri, Nov 11, 2016 at 3:24 PM, subwaystation notifications@github.com
wrote:

So I tried this surrounding all the library stuff:

                        "Wl,--whole-archive",
                        "-lm",
                        "-lpthread",
                        "-ly",
                        "-lbz2",
                        "-lsnappy",
                        "-ldivsufsort",
                        "-ldivsufsort64",
                        "-ljansson",
                        "-L/Users/heumos/git/vg/src",
                        "-L/Users/heumos/git/vg/lib",
                        "-Wl,--no-whole-archive"

But g++ does not recognize the option...
SOLINK_MODULE(target) Release/nbind.node
g++: error: unrecognized command line option '--whole-archive'
make: *** [Release/nbind.node] Error 1

I even tried -Wl,-whole-archive or -whole-archive or --whole-archive
leading all to the same error. I did take a look into the ld manual it it
clearly states --whole-archive.

Another theory: Could the upper error (Symbol not found, ...) have been
occurred, because I did not specify all the static libraries one by one?
Because when you take a look at the COMPILE_OUTPUT file when I use vg as
a library in C++ they are part of the build process in the MAKEFILE.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#35 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AE0_X-FthXULLOxqsfGbpp175txKyJTXks5q9PlDgaJpZM4KthLO
.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 12, 2016

Yeah, sorry about the -, it is a little bit late already....
Still, I could not get g++ run with -Wl,-whole-archive.

However, the -lvg brought me on the right track, thanks!
I had to add all the other libraries like -lsdsl, -lprotobuf and so forth.
And it is finally working!! 👍
Thanks guys, now I can sleep in peace :)

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 12, 2016

@jjrv I would still like to get your little bit more complex example to run:

#include "../vg/src/vg.hpp"
#include "nbind/api.h"

namespace vg {
  // This inherits methods from VG for directly binding them when possible.
  class JS_VG : public VG {
    // Anything taking a callback will have to be wrapped!
    static void for_each_node(nbind::cbFunction lambda) { /* your code here */ }

    // Alternatively you can give your new implementation a new name.
    static void js_for_each_node(nbind::cbFunction lambda) { /* your code here */ }
  };
} // namespace

#include "nbind/nbind.h"

// You can rename your JS_VG reimplementation back to VG on the JavaScript side.
NBIND_CLASS(vg :: JS_VG, VG) {
  // You don't have to reimplement this, just tell nbind that it exists.
  method(normalize);

  // This C++ method is overloaded so we need to tell nbind which arguments it takes
  // (otherwise the C++ compiler cannot distinguish between overloads).
  multimethod(has_node, args(Node *));

  // If we want to bind several overloads, they need to be renamed uniquely
  // because nbind doesn't yet support overloading by type on the JavaScript side.
  multimethod(has_node, args(id_t), "has_node_id");

  // We've overloaded this with a new implementation.
  multimethod(for_each_node, args(nbind::cbFunction));

  // Alternatively if we renamed it uniquely, we can just rename it back here
  // and have nbind autodetect the arguments. Actually we can use camelCase
  // which is more common in JavaScript.
  method(js_for_each_node, "forEachNode");
}

But there seem to be some issues:

In file included from ../JS_VG.cpp:15:0:
../JS_VG.cpp: In constructor 'BindInvokerVG<Bound>::BindInvokerVG()':
../JS_VG.cpp:24:30: error: 'Node' was not declared in this scope
   multimethod(has_node, args(Node*));
                              ^
../node_modules/nbind/include/nbind/nbind.h:62:87: note: in definition of macro 'multimethod'
 #define multimethod(name, args, ...) nbind::BindDefiner<Bound> :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## __VA_ARGS__)
                                                                                       ^
../JS_VG.cpp:24:25: note: in expansion of macro 'args'
   multimethod(has_node, args(Node*));
                         ^
../JS_VG.cpp:24:30: note: suggested alternatives:
   multimethod(has_node, args(Node*));
                              ^

Furthermore, I don't see, what JavaScript callbacks inside my C++ code are useful for. Do I get better performance? Or can I access JavaScript Code from within C++?

@adamnovak

This comment has been minimized.

Copy link

adamnovak commented Nov 12, 2016

I think you may be missing a using namespace vg;. It can't tell that you
mean vg::Node and not gcsa::Node.

I think the Javascript callback thing is just a necessary piece of
translation machinery. You need code that tells VG code how to call
Javascript code, so that it when, from Javascript, you call methods in vg
that take callbacks, they can actually call you back.

On Fri, Nov 11, 2016 at 4:21 PM, subwaystation notifications@github.com
wrote:

@jjrv https://github.com/jjrv I would still like to get your little bit
more complex example to run:

#include "../vg/src/vg.hpp"
#include "nbind/api.h"

namespace vg {
// This inherits methods from VG for directly binding them when possible.
class JS_VG : public VG {
// Anything taking a callback will have to be wrapped!
static void for_each_node(nbind::cbFunction lambda) { /* your code here */ }

// Alternatively you can give your new implementation a new name.
static void js_for_each_node(nbind::cbFunction lambda) { /* your code here */ }

};
} // namespace

#include "nbind/nbind.h"
// You can rename your JS_VG reimplementation back to VG on the JavaScript side.NBIND_CLASS(vg :: JS_VG, VG) {
// You don't have to reimplement this, just tell nbind that it exists.
method(normalize);

// This C++ method is overloaded so we need to tell nbind which arguments it takes
// (otherwise the C++ compiler cannot distinguish between overloads).
multimethod(has_node, args(Node *));

// If we want to bind several overloads, they need to be renamed uniquely
// because nbind doesn't yet support overloading by type on the JavaScript side.
multimethod(has_node, args(id_t), "has_node_id");

// We've overloaded this with a new implementation.
multimethod(for_each_node, args(nbind::cbFunction));

// Alternatively if we renamed it uniquely, we can just rename it back here
// and have nbind autodetect the arguments. Actually we can use camelCase
// which is more common in JavaScript.
method(js_for_each_node, "forEachNode");
}

But there seem to be some issues:

In file included from ../JS_VG.cpp:15:0:
../JS_VG.cpp: In constructor 'BindInvokerVG::BindInvokerVG()':
../JS_VG.cpp:24:30: error: 'Node' was not declared in this scope
multimethod(has_node, args(Node_));
^
../node_modules/nbind/include/nbind/nbind.h:62:87: note: in definition of macro 'multimethod'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:24:25: note: in expansion of macro 'args'
multimethod(has_node, args(Node_));
^
../JS_VG.cpp:24:30: note: suggested alternatives:
multimethod(has_node, args(Node_));
^
../node_modules/nbind/include/nbind/nbind.h:62:87: note: in definition of macro 'multimethod'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:24:25: note: in expansion of macro 'args'
multimethod(has_node, args(Node_));
^
In file included from /Users/heumos/git/vg/include/files.h:28:0,
from /Users/heumos/git/vg/include/gcsa.h:28,
from ../../vg/src/vg.hpp:18,
from ../JS_VG.cpp:1:
/Users/heumos/git/vg/include/support.h:357:8: note: 'gcsa::Node'
struct Node
^
In file included from ../../vg/src/gssw_aligner.hpp:11:0,
from ../../vg/src/vg.hpp:20,
from ../JS_VG.cpp:1:
/Users/heumos/git/vg/include/vg.pb.h:173:7: note: 'vg::Node'
class Node : public ::google::protobuf::Message /* @@protoc_insertion_point(class_definition:vg.Node) / {
^
/Users/heumos/git/vg/include/vg.pb.h:173:7: note: 'vg::Node'
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:62:76: error: parse error in template argument list
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:24:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(Node
));
^
../JS_VG.cpp: In instantiation of 'BindInvokerVG::BindInvokerVG() [with Bound = vg::JS_VG]':
../JS_VG.cpp:18:1: required from here
../node_modules/nbind/include/nbind/nbind.h:56:27: error: no matching function for call to 'nbind::BindDefinervg::JS_VG::method(const char [10], void (vg::VG::)(int))'
#define method(name, ...) definer.method(#name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:20:3: note: in expansion of macro 'method'
method(normalize);
^
../node_modules/nbind/include/nbind/nbind.h:56:27: note: candidates are:
#define method(name, ...) definer.method(#name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:20:3: note: in expansion of macro 'method'
method(normalize);
^
In file included from ../node_modules/nbind/include/nbind/nbind.h:8:0,
from ../JS_VG.cpp:15:
../node_modules/nbind/include/nbind/BindDefiner.h:180:15: note: template<class ReturnType, class ... Args, class ... Policies> nbind::BindDefiner& nbind::BindDefiner::method(const char
, ReturnType ()(Args ...), Policies ...) [with ReturnType = ReturnType; Args = {Args ...}; Policies = {Policies ...}; Bound = vg::JS_VG]
BindDefiner &method(
^
../node_modules/nbind/include/nbind/BindDefiner.h:180:15: note: template argument deduction/substitution failed:
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:56:27: note: mismatched types 'ReturnType (
)(Args ...)' and 'void (vg::VG::)(int)'
#define method(name, ...) definer.method(#name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:20:3: note: in expansion of macro 'method'
method(normalize);
^
In file included from ../node_modules/nbind/include/nbind/nbind.h:8:0,
from ../JS_VG.cpp:15:
../node_modules/nbind/include/nbind/BindDefiner.h:202:15: note: template<class MethodType, class ... Policies> nbind::BindDefiner& nbind::BindDefiner::method(const char
, MethodType Bound::, Policies ...) [with MethodType = MethodType; Policies = {Policies ...}; Bound = vg::JS_VG]
BindDefiner &method(
^
../node_modules/nbind/include/nbind/BindDefiner.h:202:15: note: template argument deduction/substitution failed:
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:56:27: note: mismatched types 'vg::JS_VG' and 'vg::VG'
#define method(name, ...) definer.method(#name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:20:3: note: in expansion of macro 'method'
method(normalize);
^
../node_modules/nbind/include/nbind/nbind.h:62:151: error: no matching function for call to 'nbind::BindDefinervg::JS_VG::Overloaded::multimethod(nbind::BindDefinervg::JS_VG&, const char [9], , const char [12])'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: candidates are:
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
In file included from ../node_modules/nbind/include/nbind/nbind.h:8:0,
from ../JS_VG.cpp:15:
../node_modules/nbind/include/nbind/BindDefiner.h:218:15: note: template<class ReturnType, class ... Policies> static void nbind::BindDefiner::Overloaded::multimethod(nbind::BindDefiner&, const char
, ReturnType ()(Args ...), Policies ...) [with ReturnType = ReturnType; Policies = {Policies ...}; Args = {unsigned int}; Bound = vg::JS_VG]
static void multimethod(
^
../node_modules/nbind/include/nbind/BindDefiner.h:218:15: note: template argument deduction/substitution failed:
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:62:151: note: mismatched types 'ReturnType (
)(unsigned int)' and 'bool (vg::VG::)(const vg::Node&)'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: mismatched types 'ReturnType (
)(unsigned int)' and 'bool (vg::VG::)(vg::Node)'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: mismatched types 'ReturnType ()(unsigned int)' and 'bool (vg::VG::)(vg::id_t) {aka bool (vg::VG::)(long long int)}'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: could not resolve address from overloaded function '& vg::VG::has_node'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
In file included from ../node_modules/nbind/include/nbind/nbind.h:8:0,
from ../JS_VG.cpp:15:
../node_modules/nbind/include/nbind/BindDefiner.h:230:15: note: template<class ReturnType, class ... Policies> static void nbind::BindDefiner::Overloaded::multimethod(nbind::BindDefiner&, const char
, ReturnType (Bound::)(Args ...), Policies ...) [with ReturnType = ReturnType; Policies = {Policies ...}; Args = {unsigned int}; Bound = vg::JS_VG]
static void multimethod(
^
../node_modules/nbind/include/nbind/BindDefiner.h:230:15: note: template argument deduction/substitution failed:
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:62:151: note: mismatched types 'vg::JS_VG
' and 'vg::VG_'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: mismatched types 'vg::JS_VG_' and 'vg::VG_'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: mismatched types 'vg::JS_VG_' and 'vg::VG_'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: could not resolve address from overloaded function '& vg::VG::has_node'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
In file included from ../node_modules/nbind/include/nbind/nbind.h:8:0,
from ../JS_VG.cpp:15:
../node_modules/nbind/include/nbind/BindDefiner.h:242:15: note: template<class ReturnType, class ... Policies> static void nbind::BindDefiner::Overloaded::multimethod(nbind::BindDefiner&, const char_, ReturnType (Bound::)(Args ...) const, Policies ...) [with ReturnType = ReturnType; Policies = {Policies ...}; Args = {unsigned int}; Bound = vg::JS_VG]
static void multimethod(
^
../node_modules/nbind/include/nbind/BindDefiner.h:242:15: note: template argument deduction/substitution failed:
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:62:151: note: types 'ReturnType (vg::JS_VG::)(unsigned int) const' and 'bool (vg::VG::)(const vg::Node&)' have incompatible cv-qualifiers
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: types 'ReturnType (vg::JS_VG::)(unsigned int) const' and 'bool (vg::VG::)(vg::Node
)' have incompatible cv-qualifiers
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: types 'ReturnType (vg::JS_VG::)(unsigned int) const' and 'bool (vg::VG::)(vg::id_t) {aka bool (vg::VG::)(long long int)}' have incompatible cv-qualifiers
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../node_modules/nbind/include/nbind/nbind.h:62:151: note: could not resolve address from overloaded function '& vg::VG::has_node'
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:28:3: note: in expansion of macro 'multimethod'
multimethod(has_node, args(id_t), "has_node_id");
^
../JS_VG.cpp:8:17: error: 'static void vg::JS_VG::for_each_node(nbind::cbFunction)' is private
static void for_each_node(nbind::cbFunction lambda) { /* your code here / }
^
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:62:123: error: within this context
#define multimethod(name, args, ...) nbind::BindDefiner :: template Overloaded args :: multimethod(definer, #name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:31:3: note: in expansion of macro 'multimethod'
multimethod(for_each_node, args(nbind::cbFunction));
^
../JS_VG.cpp:11:17: error: 'static void vg::JS_VG::js_for_each_node(nbind::cbFunction)' is private
static void js_for_each_node(nbind::cbFunction lambda) { /
your code here / }
^
In file included from ../JS_VG.cpp:15:0:
../node_modules/nbind/include/nbind/nbind.h:56:49: error: within this context
#define method(name, ...) definer.method(#name, &Bound::name, ## VA_ARGS)
^
../JS_VG.cpp:36:3: note: in expansion of macro 'method'
method(js_for_each_node, "forEachNode");
^
make: *
* [Release/obj.target/nbind/JS_VG.o] Error 1

Furthermore, I don't see, what JavaScript callbacks inside my C++ code are
useful for. Do I get better performance? Or can I access JavaScript Code
from within C++?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#35 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AE0_X5LHfLOHwsLq-XFlFn6MwaWu6vMEks5q9QamgaJpZM4KthLO
.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 12, 2016

@subwaystation that code was meant to illustrate nbind syntax and probably has several issues.

First of all, to pass vg::Node objects around, you'll need:

namespace vg {
  // This may be needed depending on whether the class has members
  // that cannot be used without a wrapper.
  class JS_Node : public Node {
  public:
    // Stuff goes here
  };
} // namespace

// This lets nbind understand references to Node objects.
// You can put methods here that don't need wrappers,
// but if you need the JS_Node class it might be cleaner to put them all there.
NBIND_CLASS(vg :: Node, _Node) {}

NBIND_CLASS(vg :: JS_Node, Node) {
  inherit(vg :: Node);
  // More stuff goes here
}

You don't have to put the stuff in at first, but might need it to do something meaningful with the Node object from JavaScript.

Again haven't tested if the above compiles.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 12, 2016

Callbacks currently need handling because nbind will give you an object nbind::cbFunction and you can call it with whatever you like:

std::string myString;
std::vector<std::string> myVector;
lambda.call<int>(myString, myVector)` 

That passes a string and an array of strings to a JavaScript callback, and casts its return value to int. When the vg api wants a particular kind of C++ callback, you need to create one calling the nbind::cbFunction you received, so that it's possible to call the vg api from JavaScript giving it a JavaScript function as a callback.

It might be possible to automatize this as well, so nbind would detect the required callback type and generate a wrapper automatically. I'd need to see if the C++ template syntax allows this.

Another issue that you'll run into is that nbind doesn't handle std::set at all yet. I should probably add it since apparently even Node.js 0.12 supports sets...

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 12, 2016

You'll run into #36 and #37. I'll try to do the next release fixing them in a week.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 17, 2016

@jjrv Shall I add the tutorial here, or where would be the best place to put it?

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 17, 2016

@subwaystation Easiest is to just copy&paste it here in this issue or do a PR putting it in the doc/ directory. If you already have a blog somewhere, you could also publish it there. I should eventually add a website for the project and add the tutorial there, but for now I'll add a link from the main readme.

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 17, 2016

Tutorial Example of node binding to the vg library on MAC OS 10.11 using g++.

In order for this example to work, your project structure should look like the following:

../git/vg
../git/project
  • Prearrangements:

Create JS_VG.cpp and fill it with whatever functionality and bindings you require. In the following example I am binding an empty constructor and two functions.

#include "../vg/src/vg.hpp"
#include "nbind/api.h"

#include "nbind/nbind.h"


namespace vg {

NBIND_CLASS(VG) {

  // Add an empty constructor
  construct<>();

  // Check if the graph is empty
  method(empty);

  // Generate hash out of graph
  method(hash);
}

}

Then we have to add relevant scripts to our `package.json', which should look like the following:

{
  "scripts": {
    "autogypi": "autogypi",
    "node-gyp": "node-gyp"
  },
  "dependencies": {
    "autogypi": "^0.2.2",
    "nbind": "^0.3.5",
    "node-gyp": "^3.4.0"
  }
}

Although, verify that the program versions do match the ones on your machine. I left emscripten out here, because at it's current state it is not able to handle multithreading well enough in order to be applicable to vg.
Now install nbind, autogypi and node-gyp locally:

npm install --save nbind autogypi node-gyp

As nbind was designed to work with clang on MAC OS the following adjustments to the nbind module have to be made:

  1. Create g++ under ..git/project/node-modules/nbind/bin/:
#!/bin/sh

# This script calls g++ (the homebrew C++ compiler on MAC OS) with given command line
# arguments. It removes the "-stdlib=libc++" flag (and its parameter), which node-gyp
# will add on OS X hosts causing a compiler error.

skip=0

for arg; do
    shift
    [ "$skip" = "1" ] && skip=0 && continue
    case "$arg" in
        -stdlib=*) ;;
        *) set -- "$@" "$arg" ;;
    esac
done

/usr/local/bin/g++ $@

Don't forget to make it executable via:

chmod +x ..git/project/node-modules/nbind/bin/g++
  1. Open ..git/project/node-modules/nbind/src/nbind.gypi. Here comment out "-stdlib=libc++" under OTHER_CPLUSPLUSFLAGS and the whole line "OTHER_LDFLAGS": ["-stdlib=libc++"].

Now we are ready for:

npm run -- autogypi --init-gyp -p nbind -s JS_VG.cpp

Among other files a binding.gyp is created. Note that we have to point our C++ executable to our ../node_modules/nbind/bin/g++. This is reflected under make_global_settings. Furthermore, we have to add all the vg related compilation parameters in the file, finally looking like this:

{
"make_global_settings": [
  ["CXX",  "../node_modules/nbind/bin/g++"],
  ["LINK", "../node_modules/nbind/bin/g++"]
],
    "targets": [
        {
            "includes": [
                "auto.gypi"
            ],
            "sources": [
                "JS_VG.cpp"
            ],
            "conditions": [
                ["OS=='mac'", {
                    "xcode_settings": {
                        "OTHER_CPLUSPLUSFLAGS": [
                            "-c",
                            "-msse4.1",
                            "-fopenmp",
                            "-frtti",
                            "-ggdb",
                            "-g",
                            "-I../../vg/",
                            "-I../../vg/include"
                        ],
                        "OTHER_LDFLAGS": [
                            "-msse4.1",
                            "-fopenmp",
                            "-ggdb",
                            "-g",
                            "-I../../vg/",
                            "-I../../vg/include",
                            "-lvg",
                            "-lsdsl",
                            "-lxg",
                            "-lvcflib",
                            "-lgssw",
                            "-lrocksdb",
                            "-lhts",
                            "-lgcsa2",
                            "-lprotobuf",
                            "-lraptor2",
                            "-lgfakluge",
                            "-lsupbub",
                            "-lpinchesandcacti",
                            "-l3edgeconnected",
                            "-lsonlib",
                            "-lm",
                            "-lpthread",
                            "-ly",
                            "-lbz2",
                            "-lsnappy",
                            "-ldivsufsort",
                            "-ldivsufsort64",
                            "-ljansson",
                            "-L../vg/src",
                            "-L../vg/lib"
                        ]
                    },
                }],
            ]
        }
    ],
    "includes": [
        "auto-top.gypi"
    ]
}

Feel free to add parameters for any other OS under conditions.
We are ready for the compilation step now :)

npm run -- node-gyp configure build

If everything went fine, then createvg.js and fill it with:

var nbind = require('nbind');
var lib = nbind.init().lib;

var emptyGraph = new lib.VG();

console.log(emptyGraph.hash());
console.log(emptyGraph.empty());

Then run it with node:

node vg.js

And it should print out a hash value and true, because the graph is indeed empty.
For your convenience you can find JS_VG.cpp, vg.js, binding.gyp and package.json, g++ and nbind.gypi in the following ZIP:
project.zip

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 17, 2016

@subwaystation Wow, thank you! I suppose you'll give permission to add it in the repository and later on a project website? How do I credit you, CC-BY license with author "subwaystation" and a link to your Github profile?

@subwaystation

This comment has been minimized.

Copy link
Author

subwaystation commented Nov 17, 2016

@jjrv Well, thank you! Without your help and @adamnovak I wouldn't have come that far ;)
Sure, put it wherever you like it. CC-BY sounds great, thanks for the linking.

@jjrv

This comment has been minimized.

Copy link
Member

jjrv commented Nov 18, 2016

Thanks again, I've put the tutorial in doc/vg-tutorial.md and added a link from a new readme section Binding external libraries.

@jjrv jjrv closed this Nov 18, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.