Program compiled on Linux not getting code in #ifdef section - c++

I have a cross platform program I am working on that has several files with
#ifdef WIN32
...inclues
#endif
#ifdef LINUX
..inlcudes
#endif
when I went to compile on LINUX I keep getting errors about not finding functions and such, but if i comment out the #ifdef LINUX blocks (still keeping the includes uncommented) it works, so it seams as if my define is not working properly.
This is my make file (file names changed):
CC = gcc
CPP = g++
CFLAGS = -DLINUX $(INCLUDES) -c -g -O0 -Wall -fpic
INCLUDES = -I.
LFLAGS += -fpic -shared
LFLAGS += -lpthread
CFILES = a.c b.c c.c d.c e.c f.cpp g.cpp h.cpp i.cpp
##all: $(SFILES:.s=.s.o) $(CFILES:.c=.o) $(CFILES:.cpp=.o)
## $(CPP) $(INCLUDES) $(LFLAGS) -o libclient.so.1.0 $(CFILES:.c=.o) $(CFILES:.cpp=.o)
all: $(SFILES:.s=.s.o) $(CFILES:.c=.o)
$(CPP) $(INCLUDES) $(LFLAGS) -o libclient.so.1.0 $(CFILES:.c=.o)
%.o : %.c
$(CPP) -c $(CFLAGS) $< -o $#
%.o : %.cpp
$(CPP) -c $(CFLAGS) $< -o $#
clean:
rm *.o libclient.so.1.0

Your make file actually does work for c files, but not for c++ files.
This is because you invoke your custom compile command line for c compilation but your cpp files are going directly into what is effectively your link command line which does not specify compilation flags.

First make sure you're using the proper command, so copy paste the output from Makefile and execute that from your shell (by hand).
Maybe your define is undefined along the way so, here are two approaches to find out what's wrong:
1) include #warning statement(s) within your ifdef to see if it is really a missing define:
#warning "before the define"
#ifdef LINUX
#warning "here goes the linux define"
..inlcudes
#endif
if you compile the code and don't see the warning then indeed you miss the define somewhere.
2) Check the preprocessor output. To do so send the preprocessor (use cpp not g++) output to stdout by using the -E flag. (cpp -E ....)
By looking at output you can see all code included, so you can track down in detail what code your compiler gets. I find that method of last resort usually giving most insight into
weird problems.

Change you ifdefs to
#ifdef WIN32
...inclues
#endif
#ifdef __linux__
..inlcudes
#endif
If the ifdef'ed content is UNIX rather than Linux specific use __unix__

You should be using a construct like this:
#ifdef _WIN32
// Windows specific #includes
#else
// Unix specific #includes
#endif
rather than setting yourself up to work only on Windows and Linux. The differences between Linux and other Unix systems are not worth worrying about till someone complains, but don't make life harder for yourself in advance.

Related

GCC - Multiple precompiled headers and specific paths

Background
I have a large Makefile project I'm working on which I'd like to tidy up a bit. It builds several dozen sub-projects, each of which contains roughly 100 .cpp and .h files. I've set it up so that it's able to build debug and release builds for multiple operating systems (Linux, OSX/Mac, QNX, etc) and for multiple architectures (x86/i386, x64/amd64, armhf, arm64/aarch64) in parallel. This is because it's a massive project, and the only way to make it build quickly is in parallel with multiple tool chains.
I have a master rule that all projects obey that stores the intermediate objects (ie: .o files) in temporary directories when building. So, building test.c for Linux, arm64, release mode; would build the object file in the following sub-directory in the present working directory:
.tmp/Linux/arm64/release
Issues
This feature works without issue in my builds, but with this setup, I can't seem to properly use pre-compiled headers (ie: .GCH files) with GCC. With my setup, I have a stdafx.h/stdafx.cpp pair. Using GCC, I can create a stdafx.h.gch file easily enough. However, the project only seems to use it (which accelerates the build) if the file is in the same path as the source files. If the precompiled header is inn the intermediate object path (ie: .tmp/Linux/arm64/release) it doesn't get detected or used. Even if I explicitly add the include path to the intermediate objects path which would contain the gch file, it fails. Including the full path to the file name itself results in it being treated as an invalid linker script, and is ignored.
So, my first workaround was to make a rule to force all OS/arch builds to wait on initial pre-compiled header generation, rather than build a gch on a per-OS/arch basis. However, if I build the gch with release mode settings and attempt to make a debug build, I get the following warning:
warning: stdafx.h.gch: created with -gnone, but used with -gdwarf-2
First, I don't know if that has severe consequences for my build, and second, different operating systems might pass different compile time define flags for the gch generation, so this isn't a "one size fits all" use case, as far as I can see.
Question
How can I work around this so that the precompiled header is in a location other than the $PWD and it can be detected by GCC? I'm currently using gcc v5.3.1.
Thank you.
Here is an MVCE for your problem
scenario:
main.c
#include <hw.h>
#include <stdio.h>
int main(void)
{
puts(HW);
return 0;
}
hw.h
#ifndef HW_H
#define HW_H
#define HW "Hello World"
#endif
Makefile
srcs := main.c
objs := $(addprefix tmp/,$(srcs:.c=.o))
pch := tmp/hw.h.gch
CPPFLAGS += -I.
.PHONY: all clean
all: hw
tmp:
mkdir -p tmp
tmp/%.o: %.c | $(pch)
gcc -c $(CPPFLAGS) -o $# $<
$(pch): hw.h | tmp
gcc -c $(CPPFLAGS) -o $# $<
ifdef ENFORCE_PCH
echo "#error Debug." >> $^
endif
hw: $(objs)
gcc -o $# $^
clean:
sed -i '/^#error/d' hw.h
rm -fr hw tmp
This project outputs its intermediate files in tmp. The .o files go there
and so does the PCH hw.h.gch.
Build and run it:
$ make && ./hw
mkdir -p tmp
gcc -c -I. -o tmp/hw.h.gch hw.h
gcc -c -I. -o tmp/main.o main.c
gcc -o hw tmp/main.o
Hello World
So far so good. But did it actually use the PCH? Let's see:
$ make clean
sed -i '/^#error/d' hw.h
rm -fr hw tmp
$ make ENFORCE_PCH=true
mkdir -p tmp
gcc -c -I. -o tmp/hw.h.gch hw.h
echo "#error Debug." >> hw.h
gcc -c -I. -o tmp/main.o main.c
In file included from main.c:1:0:
./hw.h:5:2: error: #error Debug.
#error Debug.
^
Makefile:15: recipe for target 'tmp/main.o' failed
make: *** [tmp/main.o] Error 1
No it didn't. We know that because, with ENFORCE_PCH defined, we have
tacked an #error directive to the end of hw.h after generating the
good tmp/hw.h.gch. So if the former is subsequently #include-ed anywhere
instead of the latter, the build breaks. Which it just did.
And that is just as it should be. GCC manual 3.21 Using Precompiled Headers,
para. 3:
A precompiled header file is searched for when #include is seen in the compilation.
As it searches for the included file (see Search Path) the compiler looks for a
precompiled header in each directory just before it looks for the include file in
that directory. The name searched for is the name specified in the #include with ‘.gch’
appended. If the precompiled header file can't be used, it is ignored.
So, given include search path ., the directive #include <hw.h> will cause
gcc to check for the PCH ./hw.h.gch before using ./hw.h, and as there is
no ./hw.h.gch, it will use ./hw.h.
It might appear, from the documentation just quoted, that adding tmp to the include search path - CPPFLAGS += -Itmp -I. - should cause
tmp/hw.h.gch to be used in preference to./hw.h. But in fact it makes no difference.
The documentation omits a crucial qualification. The second sentence ought to read:
As it searches for the included file (see Search Path) the compiler looks for a
precompiled header in each directory just before it looks for the include file in
that directory and will use the precompiled header for preference if the include file
is found.
To be found and used, a PCH has to be a sibling of the matching header. And on consideration
this is what you'd want. Otherwise, a a/foo.h.gch without a matching sibling header might be
found and used thanks to -Ia when there is a b/foo.h.gch, with a matching b/foo.h, that
could be found and used thanks to a later -Ib. Clearly, the latter is the sounder choice.
With this insight it's not hard to see a solution: if you really want to compile and use
a PCH that's not a sibling of its source header, make sure to give it a phony matching header
that is a sibling. You can arrange this as you see fit, e.g.
Makefile (fixed)
srcs := main.c
objs := $(addprefix tmp/,$(srcs:.c=.o))
pch := tmp/hw.h.gch
# Seek headers in tmp first...
CPPFLAGS += -Itmp -I.
.PHONY: all clean
all: hw
tmp:
mkdir -p tmp
tmp/%.o: %.c | $(pch)
gcc -c $(CPPFLAGS) -o $# $<
$(pch): hw.h | tmp
# Make phony header in tmp...
echo "#error You should not be here" > $(basename $#)
gcc -c $(CPPFLAGS) -o $# $<
ifdef ENFORCE_PCH
echo "#error Debug." >> $^
endif
hw: $(objs)
gcc -o $# $^
clean:
sed -i '/^#error/d' hw.h
rm -fr hw tmp
See that the PCH is used from tmp:
$ make clean
sed -i '/^#error/d' hw.h
rm -fr hw tmp
$ make ENFORCE_PCH=true && ./hw
mkdir -p tmp
# Make phony header in tmp...
echo "#error You should not be here" > tmp/hw.h
gcc -c -Itmp -I. -o tmp/hw.h.gch hw.h
echo "#error Debug." >> hw.h
gcc -c -Itmp -I. -o tmp/main.o main.c
gcc -o hw tmp/main.o
Hello World
If you force the compiler to -include tmp/pch.h or -include-pch tmp/pch.h.gch, the guard block in pch.h will prevent it from being included again.
# Makefile
SOURCES := main.cpp
OBJECTS := $(SOURCES:%.cpp=tmp/%.o)
PCH_H := tmp/pch.h
PCH := $(PCH_H).gch
$(PCH) : *.h
$(COMPILE.cpp) -x c++-header src/pch.h -o $#
$(OBJECTS) : tmp/%.o : src/%.cpp $(PCH)
$(COMPILE.cpp) -include $(PCH_H) $< -o $#
Notes:
gcc searches for precompiled header called pch.h.gch
clang searches for precompiled header called pch.h.pch or .gch
clang requires either -include pch.h or -include-pch pch.h.pch
gcc always searches for precompiled headers and does not support -include-pch

Linking C from C++ in OS X Mavericks

Having transitioned to OS X Mavericks and XCode 5.0.1, I can no longer gracefully link compiled C files (output from gcc) to a C++ project (output from g++).
The offending pair of commands produced from my makefile are:
gcc `pkg-config --cflags glib-2.0` -g -Wall -O3 `pkg-config --cflags flann` -c -o vec.o vec.c
g++ `pkg-config --cflags glib-2.0` -g -Wall -O3 -stdlib=libstdc++ -lstdc++ layoutquality.cpp vec.o `pkg-config --libs glib-2.0` -L/usr/local/Cellar/flann/1.8.4/lib -lflann -o layoutquality
To which the linker complains:
Undefined symbols for architecture x86_64:
"load_dmat(char const*)", referenced from:
_main in layoutquality-I8HOqy.o
ld: symbol(s) not found for architecture x86_64
Where load_dmat is just a function in the file vec.c . If I replace the gcc with g++ in the first line, then everything compiles and links fine, but clang says:
clang: warning: treating 'c' input as 'c++' when in C++ mode, this behavior is deprecated
Is there an inoffensive, non-deprecated way of compiling and linking these? Linking with g++ together with object files from gcc worked fine before I upgraded to OS X Mavericks and the new command line tools. Any insight into what changed and how to go forward would be great, thanks.
Adding a "-x c" (without quotes) before "vec.c" should fix it.
If you compile multiple .c/.cpp files in the same line you can use "-x c" or "-x c++" before each list of C or C++ filenames to switch context appropriately. For example:
g++ -x c alpha.c beta.c -x c++ gamma.cpp
Here's an example Makefile that let us use C++ code/function in a C program.
CC=clang
CXX=clang++
CFLAGS=-Wall -g
CXXFLAGS=-Wall -g -std=c++11 -I.
DEPS=CPP.h
OBJ=main.o CPP.o
RM=rm -f
# compile only, C source
%.o: %.c
$(CC) -c -o $# $< $(CFLAGS)
# compile only, C++ source
%.o: %.cpp $(DEPS)
$(CXX) -c -o $# $< $(CXXFLAGS)
# link
main: $(OBJ)
$(CXX) -o $# $^ $(CXXFLAGS)
clean:
$(RM) $(OBJ)
As you can see, we generate our objects separately using CXXFLAGS and CFLAGS in two separate calls to the compiler. In the context of Mac using Xcode's clang, clang (CC) and clang++ (CXX) are actually the same thing. Only the differing flags matter. I am just being pedantic by stating the definitions of CC and CXX in the above example Makefile.
Once the object files are generated, we are good to go to link them together.
Note however that you have to do one extra step to make your C++ code usable by the C program.
In CPP.h in this example, you have to explicitly use extern "C" to specify linkage for your C++ code for use by C.
For example, like this:
#ifdef __cplusplus
extern "C" {
#endif
double timesTwo(double number);
#ifdef __cplusplus
}
#endif
The preprocessor macros #ifdef __cplusplus and #endif are to make sure that our header file won't cause C-mode compilation errors and is only in-effect during C++-mode compilation.
This complete example comprises only 4 files.
Makefile
main.c
CPP.h
CPP.cpp
The Makefile source and CPP.h are explained above.
For a complete understanding, I am including main.c and CPP.cpp here as well.
main.c:
#include <stdio.h>
#include "CPP.h"
int main()
{
printf("Running main.c\n");
double ans = timesTwo(3.0);
printf("The answer from our C++ timesTwo function, when given 3.0, is %f\n", ans);
return 0;
}
CPP.cpp:
#include "CPP.h"
double timesTwo(double number)
{
return 2 * number;
}
I trust that this explanation and example clarifies how we can set up our Makefile, specify the #ifdef __cplusplus preprocessor macro and extern "C" linkage declaration to allow C++-to-C interop, and with no erroneous clang warning when we run make.
Most probably you are a victim of Name mangling. To avoid name mangling in C++, use extern "C" around declarations, like:
#ifdef __cplusplus
extern "C" {
#endif
void load_dmat(char const*);
#ifdef __cplusplus
}
#endif

Projecting the makefile for debug/release configurations

Need your help with projecting the makefile. So, what I have and what I want to get. I need to build two executables. Source code is common. Preprocessor macros control differences for both exes, for example, in main.cpp:
#if PROJECT_TYPE==FTP
std::cout << "FTP" << std::endl;
#else
std::cout << "SFTP" << std::endl;
#endif
Differences aren't only in source code but also in additional libraries that used for linking:
FTP_LDFLAGS= -static-libstdc++ -static-libgcc -s -L $(ROOT_DIR)/lib/Release -l:libboost_thread-mgw46-mt-1_49.a -l:libcommon.a \
-l:libfile.a -l:libfilesearcher.a -l:libftpclient.a -l:libftplib.a -l:libdbclient.a -l:libsqlite3.a -l:libscheduler.a -l:libws2_32.a
SFTP_LDFLAGS= -static-libstdc++ -static-libgcc -s -L $(ROOT_DIR)/lib/Release -l:libboost_thread-mgw46-mt-1_49.a -l:libcommon.a \
-l:libfile.a -l:libfilesearcher.a -l:libdbclient.a -l:libsqlite3.a -l:libscheduler.a -l:libsftpclient.a -l:libws2_32.a -l:libssh.dll
Also I want to create debug/release build targets with own CXXFLAGS and output directories, so in result to get following:
TARGETS= ftp_auto_backup sftp_auto_backup
Screenshot of dirs tree here.
I want use implicit rule for compiling, smth like this:
%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $< -o $(O_DIR)/$#
But how can I specify separate O_DIR for each target int $(TARGETS) and for each build configuration? My main problem is in this step. I can paste all the makefile that I have at this moment, but it still won't build anything. Just in case here it is: http://pastebin.com/jjB5Ld1s
Thanks in advance, guys!
There are many ways to do this. The simplest seems to be by means of recursive Make. (I am not among those who think that this is always a bad idea.)
.PHONY: release
release: VERSION=Release
.PHONY: debug
debug: VERSION=Debug
release debug:
#$(MAKE) -S ftp_auto_backup sftp_auto_backup VERSION=$(VERSION)
Now to make the executables, given VERSION. We could handle the two project types with another layer of recursion, but we can do without it (and I won't go into detail about preprocessor macros and compiler/linker flags, since you seem to know about them already-- I'll spell them out if you like):
OBJ_LIST = foo.o bar.o baz.o
ftp_auto_backup: PROJECT_TYPE=FTP
ftp_auto_backup: $(addprefix $(VERSION)/ftp_auto_backup/obj/, $(OBJ_LIST))
sftp_auto_backup: PROJECT_TYPE=SFTP
sftp_auto_backup: $(addprefix $(VERSION)/sftp_auto_backup/obj/, $(OBJ_LIST))
ftp_auto_backup sftp_auto_backup:
#g++ -Wall $^ -o $# with various flags
$(VERSION)/ftp_auto_backup/obj/%.o: %.cc
#g++ -Wall -c $< -o $# with various flags
$(VERSION)/sftp_auto_backup/obj/%.o: %.cc
#g++ -Wall -c $< -o $# with various flags

building the same file for different targets with different options

I have one "helper" file included in two "main" files which are built into two executables with the same makefile. I have debug print statements in the helper file. I want the print statements to actually be printed in one executable, but not the other. Is there a way to do it? Right now I have the following, and I was hoping to compile with DEBUG_PRINT defined for one executable but not the other, but I don't see how.
main1.cpp:
#include "helper.h"
...
main2.cpp:
#include "helper.h"
...
helper.cpp:
#ifdef DEBUG_PRINT
cout << "here is a debug message" << endl;
#endif
Makefile:
build: main1 main2
main1: main1.o helper.o
g++ -g -o main1 main1.o helper.o
main2: main2.o helper.o
g++ -g -o main2 main2.o helper.o
%.o: %.cpp
gcc -g -c $<
You will need two different object files (main1-helper.o and main2-helper.o) and target-specific compiler flags:
main1: CXXFLAGS=-DDEBUG_PRINT
%.o: %.cpp
gcc $(CXXFLAGS) -g -o $# -c $<
Note: This leaves you with the problem of generating main1-helper.o from helper.o. There are a few ways to solve this; however, you might be more comfortable using automake from the start instead of rolling your own solutions.

Updating a legacy C makefile to include a C++ source file

I work in a computational biophysics lab. I am not a programmer, although I do get paid to act like one. Here's my problem: the main product of the lab is a ginormous (50+ source files) C program. I need to get our lab's program to work with another lab's toolkit, which just so happens to be in the form of a series of C++ libraries (.a files). I can get the main library for our program to compile using the following makefile:
CC = gcc
#CC = icc
CFLAGS = -g -Wall
#CFLAGS = -xT -openmp -I/opt/local/include -I/usr/local/include -I/opt/GDBM/include
#CFLAGS = -O3 -g -Wall -I/opt/GDBM/include -fopenmp
LIB = mcce.a
AR = ar
ARFLAGS = rvs
SRC = all.c ddvv.c geom_3v_onto_3v.c ins_res.c strip.c\
app.c del_conf.c geom_apply.c line_2v.c vdotv.c\
avv.c del_prot.c geom_inverse.c load_all_param.c vector_normalize.c\
avvvv.c del_res.c geom_move.c load_param.c vector_vminusv.c\
cpy_conf.c det3.c geom_reset.c mxm4.c vector_vplusv.c\
cpy_prot.c det4.c geom_roll.c new_prot.c vector_vxv.c\
cpy_res.c dll.c get_files.c param_get.c param_exist.c\
db_close.c dvv.c iatom.c param_sav.c\
db_open.c free_strings.c ins_conf.c plane_3v.c pdbline2atom.c\
premcce.c init.c load_pdb.c write_pdb.c rotamers.c assign_rad.c get_connect12.c\
surfw.c vdw.c vdw_conf.c shuffle_n.c cmp_conf.c sort_conf.c sort_res.c id_conf.c\
energies.c assign_crg.c coulomb.c coulomb_conf.c\
get_vdw0.c get_vdw1.c relax_water.c relax_h.c monte.c monte2.c ran2.c\
relaxation.c collect_connect.c torsion.c vdw_fast.c hbond_extra.c swap.c quick_e.c\
check_tpl.c zip.c del_dir.c make_matrices.c\
mem_position.c probe.c add_membrane.c load_pdb_no_param.c ga_engine.c rotamers_ga.c compute_patches.c
OBJ = $(SRC:.c=.o)
HEADER = mcce.h
$(LIB): $(OBJ)
$(AR) $(ARFLAGS) $(LIB) $(OBJ)
$(OBJ): $(HEADER)
.c.o:
$(CC) $(CFLAGS) -c $*.c
clean:
rm -f *.o mcce.a
The executable itself then compiles with this makefile:
CC = gcc -g -O3
#CC = icc -xT -static-intel -L/opt/local/lib -L/usr/local/lib
mcce: mcce.c lib/mcce.h lib/mcce.a
# $(CC) -o mcce mcce.c mcce.a /opt/GDBM/lib/libgdbm.a -lm -lz -openmp; cp mcce bin
$(CC) -o mcce mcce.c lib/mcce.a -lgdbm -lm -lz -fopenmp; cp mcce bin
I can get a standalone version of the other lab's code to compile using this other makefile:
OEDIR = ../..
INCDIR = $(OEDIR)/include
LIBDIR = $(OEDIR)/lib
INCS = -I$(INCDIR)
LIBS = -L$(LIBDIR) \
-loezap \
-loegrid \
-loefizzchem \
-loechem \
-loesystem \
-loeplatform \
-lz \
-lpthread -lm
CXX = /usr/bin/c++
RM = rm -f
CXXFLAGS = -m64 -W -Wall -O3 -fomit-frame-pointer -ffast-math
LFLAGS = -m64 -s
TEXT2HEX = ../text2hex
PROGRAMS = other_labs_code
.SUFFIXES: .cpp
.SUFFIXES: .o
.cpp.o:
$(CXX) $(CXXFLAGS) $(INCS) -c $<
.SUFFIXES: .txt
.SUFFIXES: .itf
.txt.itf:
$(TEXT2HEX) $< InterfaceData > $#
all: $(PROGRAMS)
clean:
$(RM) $(PROGRAMS)
$(RM) ii_files core a.out *.itf
$(RM) *.o
other_labs_code.o: other_labs_code.cpp other_labs_code.itf
other_labs_code: other_labs_code.o
$(CXX) other_labs_code.o $(LFLAGS) -o $# $(LIBS)
I know that I have to change the paths of the various libs and stuff, but other than that, how do I combine all of these makefiles into one working product? Also, since some of the source files that go into compiling my program's main library (mcce.a) are going to need to be able to call functions from the C++ source file, it's the library's makefile that I need to modify, right?
I know extremely little about makefiles, so even if someone can just point me in the direction of a tutorial that covers this kind of problem (writing a makefile for a many source file C and C++ program), that may be sufficient.
For bonus points, the C++ FAQ says that:
You must use your C++ compiler when compiling main() (e.g., for static initialization)
Your C++ compiler should direct the linking process (e.g., so it can get its special libraries)
I don't exactly know what those things are supposed to mean, but assuming that I did, are there any other important points like that I should be aware about when combining C and C++?
Preparing the code
C programs cannot just use C++ symbols. Unless the authors of the C++ code arranged for that. This is because some features that C++ offers, such as function overloading (having several functions of the same name but with different formal arguments) demand that the function name be mangled in some way. Else the linker would see the same symbol defined several times. C compilers don't understand this name mangling and therefore cannot use C++ symbols. There are, generally, two possible solutions.
Declare and define all C++ symbols that the C code wants to use within extern "C" { ... } blocks and let your C++ tools handle the linking. The C code does not need to be changed in this case.
Compile the C code with the (exact same) C++ compiler as the C++ code. Fix the C++ compiler's complaints of the C code as they arise. Depending on project size and coding style, this may or may not be a lot of work.
Preparing a master Makefile
I personally try to avoid becoming intimate with other people's Makefiles, especially if they are subject to change or complex. So, assuming generating a Makefile that orchestrates the bits you already have (as opposed to writing one Makefile incorporating everything) is okay, I'd start out with something similar to this:
I'm assuming that
One of the above-mentioned options has been implemented
The code for mcce.a lies in subdirectory mcce/lib/
other_labs_code.cpp lies in other_labs_code/
The main function you want to use lies in ./mystuff.c
the following top-level Makefile may get you started
CXX = c++
CXXFLAGS = -m64 # From other_labs_code/Makefile
LDFLAGS = -m64 -L<path to OEDIR> # From other_labs_code/Makefile
LIBS = -lgdbm -lm -lz # From mcce/lib/Makefile
LIBS += -loezap \ # From other_labs_code/Makefile
-loegrid \
-loefizzchem \
-loechem \
-loesystem \
-loeplatform \
-lpthread
mystuff: mystuff.c mcce/lib/mcce.a other_labs_code/other_labs_code.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) -o $# $^ $(LIBS)
mcce/lib/mcce.a:
cd mcce/lib/ && $(MAKE) CC="$(CXX) -m64" mcce.a
other_labs_code/other_labs_code.o:
cd other_labs_code/ && $(MAKE) other_labs_code.o
Makefile: mcce/lib/Makefile other_labs_code/Makefile
echo "Warning: `pwd`/$# is out of date" >&2
This Makefile will employ the existing sub-project Makefiles to do the compilations. If the sub-project Makefiles have a newer timestamp than this Makefile, potentially rendering it obsolete, then this will be warned about. The linking basically works by combining the required libraries of both sub-projects. I've removed duplicates. The compiler switches are basially those of the original authors since compiling is delegated to the sub-projects. The code both sub-projects generate must be for the same platform. If your compiler is gcc/g++ then either -m64 is the default and therefore redundant in the second project or should be added to the first project. I have illustrated injecting it into the first project without changing their Makefile (using GNU make). NB: This example also causes the first project to be compiled with the C++ compiler.
An extern "C" {...} block located in a C or C++ header file that C code wants to include should look like this
/* inclusion guard etc */
#if defined(__cplusplus)
extern "C" {
#endif
/* C declarations */
#if defined(__cplusplus)
}
#endif
/* inclusion guard etc */
Minor points
In the first posted Makefile, I suggest changing the bottom part to
.c.o:
$(CC) $(CFLAGS) -c -o $# $<
clean:
rm -f $(OBJ) mcce.a
.PHONY: clean
which is a tiny bit cleaner.
The second Makefile is broken. The bottom rule links the binary and then copies it to a directory named bin, if it exists, else a copy of the file is created and named `bin'. If the linking fails, that fact is not propagated to the caller, i.e. the error is ignored. The bottom rule should read
mcce: mcce.c lib/mcce.h lib/mcce.a
$(CC) -o $# mcce.c lib/mcce.a -lgdbm -lm -lz -fopenmp
cp mcce bin/
i.e. the link command should be on its own line and that `bin' is supposed to be a directory should be made explicit.
http://www.gnu.org/software/make/manual/make.html#Introduction