Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoking make from node-gyp

I'm using node-gyp to build a native Node.js add-on written in C++ on Linux.

The add-on is dependent on another shared library. This library is not currently built with gyp, it just has a makefile.

If I build the shared library first, then build my add-on specifying a value for 'libraries' in the main target in my binding.gyp file, everything works fine.

However, what I would like to do is build the shared library from source from within the node-gyp process, by invoking make on the shared library's makefile. I tried adding a dependent target using the 'action' property to the add-on's binding.gyp and making the main target dependent on it:

    {
        "target_name": "other_library",
        "type": "none",
        "actions": [
            {
                "action_name": "build_other_library",
                "inputs": [],
                "outputs": [ "/path/to/build/output/libother.so" ],
                "action": [ "make", "-C", "/path/to/makefile" ]
            }
        ]
    }

This doesn't completely work. It is finding the other makefile and make is being launched (I can see this happening with --verbose set), but the makefile is not executing properly.

It seems that GNU make's implicit build rules are being suppressed when the makefile for the shared library runs. This means that .cc and .cpp files are not being compiled to .o files.

I realise that node-gyp is itself generating a set of makefiles for the add-on from the targets in the binding.gyp, and the the shared library's makefile is being spawned from one of these.

Is it inheriting the make settings from node-gyp, including the suppression of built-in rules?

Is there a way around it? (Other than adding explicit build rules to the shared library's makefile)?

(I've tried replacing make with $(MAKE), it made no difference).

EDIT:

Running GNU make on the shared library with -d specified from the shell (i.e. outside node-gyp), the search for an implicit rule for a typical source file looks like this:

   Considering target file `code.o'.
     File `code.o' does not exist.
     Looking for an implicit rule for `code.o'.
     Trying pattern rule with stem `code'.
     Trying implicit prerequisite `code.c'.
     Trying pattern rule with stem `code'.
     Trying implicit prerequisite `code.cc'.
     Trying pattern rule with stem `code'.
     Trying implicit prerequisite `code.C'.
     Trying pattern rule with stem `code'.
     Trying implicit prerequisite `code.cpp'.
     Found prerequisite `code.cpp' as VPATH `../Common/code.cpp'
     Found an implicit rule for `code.o'.

Adding -d to the invocation from within the action block in the node-gyp dependent target, the same source file gets this:

Considering target file `code.o'.
 File `code.o' does not exist.
 Looking for an implicit rule for `code.o'.
 No implicit rule found for `code.o'.

So it does look like implicit build rules are being suppressed (?)

like image 426
dtopham75 Avatar asked Sep 02 '25 02:09

dtopham75


1 Answers

node-gyp sets MAKEFLAGS=-r in its toplevel makefile. -r is the short form of --no-builtin-rules, and by default this is passed down to any sub-makes.

However, you'll be able to re-enable builtin-rules for your sub-make by setting MAKEFLAGS back to their default state in the calling environment.

Without changing your binding action, you can achieve this by prememptively exporting rectified MAKEFLAGS in your makefile, and then reinvoking $(MAKE).

To illustrate, pretend this is your original makefile:

all: foo

foo: foo.o
    $(CC) -o $@ $<

in which you're making a program foo from one source file foo.c (assumed to exist in the working directory) and counting on the builtin rule for %o: %c to compile foo.o from foo.c. So with this makefile the build will fail:

*** No rule to make target 'foo.o', needed by 'foo'. Stop.

Change the makefile like so:

ifneq ($(MAKEFLAGS),w)
all:
    export MAKEFLAGS=-w && $(MAKE)
else
all: foo

foo: foo.o
    $(CC) -o $@ $<

endif

Now the make will recurse if -r is in the MAKEFLAGS and run with MAKEFLAGS=-w instead:

$ node-gyp build
gyp info it worked if it ends with ok
gyp info using [email protected]
gyp info using [email protected] | linux | x64
gyp info spawn make
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
make: Entering directory '/home/imk/develop/scrap/build'
  ACTION binding_gyp_prog_target_build_foo foo
make[1]: Entering directory '/home/imk/develop/scrap'
export MAKEFLAGS=-w && make
make[2]: Entering directory '/home/imk/develop/scrap'
cc    -c -o foo.o foo.c
cc -o foo foo.o
make[2]: Leaving directory '/home/imk/develop/scrap'
make[1]: Leaving directory '/home/imk/develop/scrap'
  TOUCH Release/obj.target/prog.stamp
make: Leaving directory '/home/imk/develop/scrap/build'
gyp info ok 

The -w (short for --print-directory) is the one default option that was operative before node-gyp added -r.

Note that the test ifneq ($(MAKEFLAGS),w) is correct. It is not supposed to be ifneq ($(MAKEFLAGS),-w). If the environment variable MAKEFLAGS contains a make commandline option then the GNU Make special variable MAKEFLAGS will contain only the option character.

like image 127
Mike Kinghan Avatar answered Sep 05 '25 00:09

Mike Kinghan