Creating 2 versions of same lib using GNU make - makefile

I need to create 2 different versions of same lib (.a & .so files), using different compilation flags.
for instance,
the following debug flag should be enabled in only one of the builds:
#ifdef ENABLE_NEW_FEATURE
//some code
#endif
the lib Makefile is being called from outside project with many executables, some of them need to have the new feature and some of them not.
i wonder how can i use Makefile to create 2 different versions of same lib without suffering race condition in case of running make -j.

The usual way is to use several directories for the objects. Then you may use something like:
SOURCES=f1.cpp f2.cpp
all: libmylib-d.a libmylib-o.so
libmylib-d.a: CFLAGS=-Dd
libmylib-d.a: $(SOURCES:%.cpp=obj-d/%.o)
ar -cru $# $^
libmylib-o.so: CFLAGS=-Do -fPIC
libmylib-o.so: $(SOURCES:%.cpp=obj-o/%.o)
g++ -shared -o $# $^
obj-d/%.o: %.cpp | obj-d
$(CXX) $(CFLAGS) -c -o $# $<
obj-o/%.o: %.cpp | obj-o
$(CXX) $(CFLAGS) -c -o $# $<
obj-o obj-d:
mkdir $#

Related

What are the automatic rules make is using to build objects files without being specified?

I have an old project with several C++ source and header files in a directory which are built in cygwin using GCC and a makefile. On editing the makefile to move all the temporary and output files into a sub-directory, the rules to build the object files didnt have any effect.
$(BIN): $(OBJ)
$(CXX) $(LDFLAGS) -o $(BIN) $^
%.o: %.c
$(CXX) $(INC) $(CXXFLAGS) $# -c -o $<
The following makefile still builds the output binary without having any rules to build the object files from source code.
INC=-I.
CXX=g++
CXXFLAGS=-std=c++11 -Wall -Wextra -Wconversion -pedantic -O2 -g
LDFLAGS=
CLEAN_FILES=*.o *.out *.stackdump *.exe *.gcno *.gcda
BIN=app
SRC=$(wildcard *.cpp)
OBJ=$(SRC:%.cpp=%.o)
all: $(BIN)
$(BIN): $(OBJ)
.PHONY: clean
clean:
rm -f $(CLEAN_FILES)
What is this automatic behavior called?
Two notes regarding built-in rules:
In large projects (which will have their owner rules for compiling, linking, ....), it might be easier to start with no built-in rules (make -r or make --no-builtin-rules).
Many built-in rules have hooks, via variables, that allow configuration changes. For example:
%.o: %.c:
$(COMPILE.c) $(OUTPUT_OPTION) $<
...
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
Where opssible to customize the command by modifying "CFLAGS", "CPPFLAGS", etc. Common examples will be make "CFLAGS=-O -g" to get optimized debug program, without having to redefine all the rules.
The set of rules that make knows about without you having to define them are called built-in rules. The manual lists most of them but not all of them. You can run make -p -f/dev/null to see a list of them all.

How to use the include directive in a makefile for a specific target

I want to use the include directive only for a specific target. I do not want to run the other makefiles when the target is not needed because it means the makefiles are generated needlessly.
So is there a way to conditionally use the include directive, which is conditional on a target? Or somehow to make the include directive a prerequisite of a target.
Here's what I have so far:
# Flags
INCDIR = $(CURDIR)/include
CFLAGS = -Wall -Wno-overflow -Wno-uninitialized -pedantic -std=c99 -I$(INCDIR) -O3
LFLAGS = -flat_namespace -dynamiclib -undefined dynamic_lookup
# Directory names
# Set vpath search paths
vpath %.h include
vpath %.c src
vpath %.o build
vpath %.d build
# Get files for the core library
CORE_FILES = $(wildcard src/*.c)
CORE_OBJS = $(patsubst src/%.c, build/%.o, $(CORE_FILES))
CORE_DEPS = $(CORE_OBJS:.o=.d)
# Core library target linking
core : $(CORE_OBJS) | bin
$(CC) $(LFLAGS) -o bin/libcbitcoin.2.0.dylib $(CORE_OBJS)
# Include header prerequisites (How to do only for "core" target?)
include $(CORE_DEPS)
# Makefiles for header dependencies.
$(CORE_DEPS): build/%.d: src/%.c | build
rm -f $#; \
$(CC) -I$(INCDIR) -MM $< -MT '$(#:.d=.o) $#' > $#
# Objects depend on directory
$(CORE_OBS) : | build
# Create build directory
build:
mkdir build
# Create bin directory
bin:
mkdir bin
# Core Compilation
$(CORE_OBJS): build/%.o: src/%.c
$(CC) -c $(CFLAGS) $< -o $#
# Depencies require include/CBDependencies.h as a prerequisite
build/CBOpenSSLCrypto.o: include/CBDependencies.h
# Crypto library target linking
crypto : build/CBOpenSSLCrypto.o -lcrypto -lssl | bin
$(CC) $(LFLAGS) -o bin/libcbitcoin-crypto.2.0.dylib build/CBOpenSSLCrypto.o -lcrypto -lssl
# Crypto library compile
build/CBOpenSSLCrypto.o: dependencies/crypto/CBOpenSSLCrypto.c
$(CC) -c $(CFLAGS) $< -o $#
#Clean
clean:
rm -f $(CORE_OBJS) $(CORE_DEPS) build/CBOpenSSLCrypto.o
As you should be able to tell I do not need to include the ".d" files for "crypto" but I do for "core" (default goal).
Thank you for any help.
Make is not a procedural language, so treating it as one goes against the grain; your makefiles will be difficult to scale, and it can lead to subtle bugs.
There's a better way by Tom Tromey that's clean, efficient and scalable. The trick is to realize that you can build the dependency file in the same step as the object file. The dependencies simply tell Make when the object is due to be rebuilt; you don't need them when you first build the object, because Make knows that the object must be built. And if the dependencies change, that can only be because something in the source or the old dependencies has changed, so again Make knows that the object must be rebuilt. (This is not obvious, so it may take a little cogitation.)
$(CORE_OBJS): build/%.o: src/%.c
$(CC) -c $(CFLAGS) $< -o $#
$(CC) -MM -MF build/$*.d $<
-include build/*.d
There's one more hitch: if you alter the code so as to remove a dependency -- and also remove that file -- you won't be able to rebuild, because the old dependency list will still demand a file which can no longer be found. The sophisticated solution is to process the dependency file so as to make each prerequisite (e.g. header) a target in its own right, with no commands, so that it can be assumed to be rebuilt when needed:
$(CORE_OBJS): build/%.o: src/%.c
$(CC) -c $(CFLAGS) $< -o $#
$(CC) -MM -MF build/$*.d $<
#cp build/$*.d build/$*.P
#sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
-e '/^$$/ d' -e 's/$$/ :/' < build/$*.P >> build/$*.d;
#rm build/$*.P
A cruder method, but almost as foolproof, is to put in catch-all rules for headers and sources:
$(CORE_OBJS): build/%.o: src/%.c
$(CC) -c $(CFLAGS) $< -o $#
$(CC) -MM -MF build/$*.d $<
%.cc %.h:
EDIT:
To break down the new commands:
The -MM option tells gcc to produce a make rule for the object file, instead of preprocessing or compiling. The default is to send the rule to wherever it would send preprocessed output, which will usually be stdout.
The -MF option, used with -MM, specifies the output file. So -MM -MF build/$*.d will put the rule where we want it.
So the following two commands are (almost always) equivalent:
$(CC) -MM -MF build/$*.d $<
$(CC) -MM $< > build/$*.d
(I've left out the -I$(...) and the possibility of using the -MMD option, because both get a little complicated and are not really the point of the question.)
You can use MAKECMDGOALS.
ifeq (core,$(MAKECMDGOALS))
include $(CORE_DEPS)
endif
You could of course, use ifneq (,$(findstring core,$(MAKECMDGOALS))) if there was a possibility of more than one target.
Note: this is a 'quick and dirty' solution -- I agree with Beta that you shouldn't make this a common practice (this could get messy if you did it in a lot of makefiles...).
John
I can't help breaking the guidelines for what is a good answer.
My answer to the original question is in my opinion, no you cannot include rules that are dependant on the target -all rules are processed before targets are considered. This is a limitation of make (I guess). Then again, good point, there is MAKECMDGOALS, but is this not just a hack in the make utility itself?
The answer from Beta is reasonable and orthodox enough, but you can't call it clean even if it is the best that can be done. It won't work if make has not processed the particular target before and the appropriate build/*.d dependency file is not sitting there.

Compiling for Multiple Targets

I want to compile my program both to linux and windows using g++ and mingw respectively. The only difference between the compilations is the compiler to use and output file name.
A single make command should produce both output files. What is the best way to achieve this with as little duplications in the makefile as possible?
How about this:
linux-name: CC:=g++
windows-name: CC:=mingw
linux-name windows-name:
$(CC) whatever -o $#
EDIT:
What I wrote above is only the new part of the makefile; I assumed that the rest of the makefile was implied. To be more explicit:
all: linux-name windows-name
linux-name: CC:=g++
windows-name: CC:=mingw
linux-name windows-name: foo.o bar.o baz.o SomethingElse
$(CC) $(CCFLAGS) whatever $^ -o $#
%.o: %.cc
$(CC) $(CFLAGS) -I$(INC_DIR) whatever -c $< -o $#
SomethingElse:
build somehow

Makefile include file for same targets

I have a large project using recursive Make. Almost all the Makefiles are the same though. I'm basically building all the object files into the same directory like this:
$(OBJ)/%.o: %.c
$(COMPILE) ${INCLUDES} -c $< -o $#
$(OBJ)/%.o: %.cpp
${CXX} ${INCLUDES} ${FLAGS} -c -fPIC $< -o $#
Is it possible to put these targets in an include file so I don't have to put the same lines in every Makefile?
include I've only used for shared variables and when I tested this using include it did not work.

VPATH not working with Makefile Rule

I have added another rule to a Makefile to attempt to build a C shared library that uses SWIG to wrap the functions for Java using JNI
The additional rule looks like this (basically lifted from one of the SWIG java examples)
java: $(program_C_SRCS)
$(SWIG) -java $(SWIGOPT) $(INTERFACEPATH)
$(CC) -c $(CFLAGS) $(JAVACFLAGS) $(program_C_SRCS) $(ISRCS) $(CPPFLAGS) $(JAVA_INCLUDE)
$(JAVALDSHARED) $(CFLAGS) $(program_C_OBJS) $(IOBJS) $(JAVA_DLNK) $(LDFLAGS) -o $(JAVA_LIBPREFIX)$(TARGET)$(JAVASO)
javac *.java
problem I have is that my VPATH doesn't seem to work with the *.c files anymore
I noticed that this rule causes all the .c files to compiled as one call to gcc rather than a separate call to gcc for the compilation of each .o file
my previous rules for compilation without any JNI stuff look like this:
.PHONY: all clean
all: $(program_DEBUG_NAME) $(program_RELEASE_NAME)
# original rule to build library in src dir (no longer inc. in all)
$(program_NAME): $(program_C_OBJS)
$(LINK.c) -shared -Wl,-soname,$# $^ -o $#
# new rules to build debug/release libraries and place them in relevant build
# dirs
$(program_DEBUG_NAME): $(DEBUG_OBJS)
$(DEBUG_LINK.c) -shared -Wl,-soname,$# $^ -o $(BUILD_DIR)/debug/$#
$(program_RELEASE_NAME): $(RELEASE_OBJS)
$(RELEASE_LINK.c) -shared -Wl,-soname,$# $^ -o $(BUILD_DIR)/release/$#
# rule to build object files (replaces implicit rule)
$(BUILD_DIR)/debug/%.o: %.c
$(DEBUG_LINK.c) $< -c -o $#
$(BUILD_DIR)/release/%.o: %.c
$(RELEASE_LINK.c) $< -c -o $#
and these work with VPATH no problem
my VPATH statement looks like this:
VPATH = ../../pulse_IO/src ../../../g2/src
Look at your rule:
java: $(program_C_SRCS)
...
$(CC) -c $(CFLAGS) $(JAVACFLAGS) $(program_C_SRCS) ...
...
Suppose program_C_SRCS is foo.c, and the path is somewhere/foo.c. Without VPATH, this rule doesn't run at all because Make can't find foo.c. With VPATH, Make finds it, and knows that the real prereq is somewhere/foo.c. But you have $(program_C_SRCS) in your rule:
java: somewhere/foo.c
...
$(CC) -c $(CFLAGS) $(JAVACFLAGS) foo.c ...
...
This fails because there is no foo.c (locally).
Try this:
java: $(program_C_SRCS)
...
$(CC) -c $(CFLAGS) $(JAVACFLAGS) $^ ...
...
(The use of automatic variables like $^ is the reason your previous rules worked.)

Resources