So I'm going to publish this small project on GitHub and I thought I'd better do a makefile for it. The source code consists of

  • 1 Java Class
  • 1 Native JNI module written in C
  • 1 stdlib wrapper object written in C (for LD_PRELOAD)

The purpose of the Makefile is to compile the two shared objects from the C-Source.

Here it is

CFLAGS=-nostartfiles -fpic -shared -ldl -D_GNU_SOURCE -Wall -Werror
OUTFILES = $(OUT)/bind.so $(OUT)/bindsetter.so

#Two locations needed to get all the JNI header files
JNI_INCLUDE=/usr/lib/jvm/java-8-oracle/include \

#Add the includes to the compiler flags
CFLAGS+=$(foreach d, $(JNI_INCLUDE), -I$d)

all: $(OUTFILES)

$(OUTFILES): $(OUT)/%.so: %.c $(JNI_AUTO_HEADER)
    $(CC) $(CFLAGS) -c $< -o $@

$(JNI_AUTO_HEADER): ClientBind.java
    javac ClientBind.java -d $(OUT) -h $(OUT)

.PHONY: clean
    rm bld/* -rf

So some questions: CC=gcc is this a bit presumptuous? CFLAGS - well these all assume GCC too. What about inflicting -Werror on people - on my computer there are no warnings as it stands.

I hate this: JNI_INCLUDE=/usr.... that is seriously installation dependent. What's a better way of doing this?

I have this header file JNI_AUTO_HEADER that javac can generate from the .java source, so therefore I consider it a build artifact which the makefile is responsible for. Do you agree? Also I've made all of the .so files dependent on JNI_AUTO_HEADER but hey, there's only two of them so I guess it's not worth worrying about.

Also I'm not sure if there's a better way of doing this in my source file:

// This include generated in the build directory at compile time
#include "bld/com_daftdroid_droidlib_bind_ClientBind.h"

(without getting into a much more complex build process)

Any other comments welcome.

  • CFLAGS conventionally control the compilation. Linking options, that is

    -nostartfiles -shared -ldl

    are better be separated into LDFLAGS.

  • -Werror is always a good idea. You don't want your users to run potentially incorrect code, do you?

  • I recommend to make $(OUT) directory automatically.

    $(OUTFILES): | $(OUT)
        mkdir $(OUT)

    See order-only dependencies for details.

  • JNI_AUTO_HEADER is a build artifact, and yes make is responsible for it. I see no reason for confusion.

  • As for #include "bld/com_daftdroid_droidlib_bind_ClientBind.h", consider adding

    -include $(JNI_AUTO_HEADER)

    to CFLAGS, or passing it via -D option (see this discussion for details)

  • I don't know the correct way to figure out Java include paths; maybe java.home is a good place to start? You'd need a small Java app to print it though.

  • \$\begingroup\$ LDFLAGS turned out to be crucial I needed to do $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) so the -ldl was specified after the output file or it wouldn't link at all. (error introduced while refactoring for this post). I'm normally used to CFLAGS for all the compilation units and LDFLAGS "at the end" and I hadn't considered that in this case, each unit is linked individually. \$\endgroup\$ – Rodney Aug 2 '17 at 8:24
  • \$\begingroup\$ -include $(JNI_AUTO_HEADER) was a good find. Note you can get away without including this header at all, unless you add -Wmissing-prototypes. Thanks for taking the time to review my Makefile! \$\endgroup\$ – Rodney Aug 2 '17 at 8:27

I've got some better logic for the JNI include paths.

  • Test to see if the user has already set JNI_INCLUDE
  • If not, make a best guess based on JAVA_HOME

The logic is now


#If the user hasn't supplied JNI_INCLUDE path
#then use a default based on JAVA_HOME

ifndef JAVA_HOME
$(error JAVA_HOME not set)


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.