Makefile C/C++ with function depending of environment - c++

I am struggling with the following problem :
I would like to use tput in my Makefile for color but it needs the environment variable $TERM ( and the echo alternative with \033[0;31m is not portable and will no behave the same way on all computers ).
When on an environment that does not have the variable, make fails and the program is not compiled.
I already tried searching for environment variable testing in a Makefile but with no success so far.
This is an example of what I would like to do :
define my_color
#tput setaf $2
#tput bold
#echo $1
#tput sgr0
endef
CC = gcc
CFLAGS = -Wall -Wextra
SRCS = my_super_file.c
OBJS = $(SRCS:.c=.o)
NAME = super_programm
all: $(NAME)
$(NAME): $(OBJS)
$(call my_color, " Compiling $<", 2)
$(CC) -o $(NAME) $(OBJS)
clean:
rm -rf $(OBJS)
fclean: clean
rm -rf $(NAME)
re: fclean all
.PHONY: all re clean fclean
a more complete example here : https://github.com/Hellfire01/Makefile/blob/master/Makefile
both will work just fine any computer but not on my server
What I am trying to do is have only one Makefile capable of working on both my computer and my server, I tried looking at the function ifeq but I did not succeed in getting it to work.
Any help will be greatly appreciated thank you for reading.

I think this makefile illustrates what you want:
have_term := $(shell echo $$TERM)
ifdef have_term
define my_color =
#tput setaf $2
#tput bold
#echo $1
#tput sgr0
endef
else
my_color = #echo $1
endif
all:
$(call my_color, "Hello world", 2)
If TERM is defined in the environment then make prints Hello World in
green, otherwise in plain white.
See 7.2 Syntax of Conditionals

Assuming the issue here is just that you want make to ignore errors/failures from tput when it can't operate correctly and continue you can do that by sticking a leading - on the tput lines (the same as the leading #).
So either #-tput ... or -#tput ....
This will cause make to see (and output) the errors but won't fail the target because of them.
From Errors in the GNU Make Manual:
To ignore errors in a recipe line, write a ‘-’ at the beginning of the line’s text (after the initial tab). The ‘-’ is discarded before the line is passed to the shell for execution.
For example,
clean:
-rm -f *.o

#- won't work, you'll get lots of error messages.
Something like this would work, but you could defintely write this more elegantly.
have_term := $(TERM)
ifeq ($(TERM),dumb)
have_term=
endif
ifdef have_term
color_blue = #tput setaf 6
color_end = #tput sgr0
else
color_blue =
color_end =
endif

Related

LibreOffice C++ API activation ('soffice') monopolizes terminal

I am using Ubuntu 20.04.4 LTS and have just installed the package libreoffice-dev.
The intention is to run the C++ DocumentLoader sample noted here. (The 3 files constituting the sample may be viewed here.)
The instructions are mostly embedded in the sample Makefile. Here is what happens:
The soffice command opened a new LibreOffice window - but did not actually finish (ie: no 'ready' message).
Q1: Is this expected behaviour?
It's catch-22. I'm prevented from running make DocumentLoader.run (or any) command but if I manually close the LibreOffice window (whereupon terminal bursts back into life) I am able to compile (but not successfully run) the program - as follows:
daz#daz-NUC8i7BEH:~/mpa/LO-SDK$ soffice "--accept=socket,host=localhost,port=2083;urp;StarOffice.ServiceManager"
daz#daz-NUC8i7BEH:~/mpa/LO-SDK$ make DocumentLoader.run
cd /home/daz/libreoffice7.3_sdk/LINUXexample.out/bin && DocumentLoader -env:URE_MORE_TYPES=file://"/usr/lib/libreoffice/program/types/offapi.rdb" "/home/daz/libreoffice7.3_sdk/matt/test.odt"
DocumentLoader.cxx has started. nCount=1
Error: cannot establish a connection using 'uno:socket,host=localhost,port=2083;urp;StarOffice.ServiceManager':
loading component library </libmergedlo.so> failed /build/libreoffice-SshRy0/libreoffice-7.3.1~rc3/cppuhelper/source/shlib.cxx:311
make: *** [Makefile:68: DocumentLoader.run] Error 1
daz#daz-NUC8i7BEH:~/mpa/LO-SDK$
Q2: Have I just made a procedural error?
Here is the actual Makefile ... [%.run: is Makefile:66]
# Builds the C++ DocumentLoader example of the SDK.
# From here: https://api.libreoffice.org/examples/cpp/DocumentLoader/
#PRJ=../../.. Original
PRJ=$(OO_SDK_HOME)
SETTINGS=$(PRJ)/settings
include $(SETTINGS)/settings.mk
include $(SETTINGS)/std.mk
# Define non-platform/compiler specific settings
COMPONENT_NAME=DocumentLoader
OUT_COMP_INC = $(OUT_INC)/$(COMPONENT_NAME)
OUT_COMP_GEN = $(OUT_MISC)/$(COMPONENT_NAME)
OUT_COMP_OBJ=$(OUT_OBJ)/$(COMPONENT_NAME)
CXXFILES = DocumentLoader.cxx
OBJFILES = $(patsubst %.cxx,$(OUT_SLO_COMP)/%.$(OBJ_EXT),$(CXXFILES))
ENV_OFFICE_TYPES=-env:URE_MORE_TYPES=$(URLPREFIX)$(OFFICE_TYPES)
# Targets
.PHONY: ALL
ALL : \
CppDocumentLoaderExample
include $(SETTINGS)/stdtarget.mk
$(OUT_COMP_OBJ)/%.$(OBJ_EXT) : %.cxx $(SDKTYPEFLAG)
-$(MKDIR) $(subst /,$(PS),$(#D))
$(CC) $(CC_FLAGS) $(CC_INCLUDES) -I$(OUT_COMP_INC) $(CC_DEFINES) $(CC_OUTPUT_SWITCH)$(subst /,$(PS),$#) $<
$(OUT_BIN)/DocumentLoader$(EXE_EXT) : $(OUT_COMP_OBJ)/DocumentLoader.$(OBJ_EXT)
-$(MKDIR) $(subst /,$(PS),$(#D))
-$(MKDIR) $(subst /,$(PS),$(OUT_COMP_GEN))
ifeq "$(OS)" "WIN"
$(LINK) $(EXE_LINK_FLAGS) /OUT:$# /MAP:$(OUT_COMP_GEN)/$(basename $(#F)).map \
$< $(CPPUHELPERLIB) $(CPPULIB) $(SALHELPERLIB) $(SALLIB)
else
$(LINK) $(EXE_LINK_FLAGS) $(LINK_LIBS) -o $# $< \
$(CPPUHELPERLIB) $(CPPULIB) $(SALHELPERLIB) $(SALLIB) $(STDC++LIB)
ifeq "$(OS)" "MACOSX"
$(INSTALL_NAME_URELIBS_BIN) $#
endif
endif
CppDocumentLoaderExample : $(OUT_BIN)/DocumentLoader$(EXE_EXT)
#echo --------------------------------------------------------------------------------
#echo The example loads the "$(QM)test.odt$(QM)" document in the DocumentLoader example directory.
#echo If you want to load your own document, please use:
#echo $(SQM) $(SQM)DocumentLoader -env:URE_MORE_TYPES="$(QM)<fileurl_office_types_rdb>$(QM)" "$(QM)filename$(QM)" [connection_url]
#echo -
#echo Use the following command to execute the example!
#echo -
#echo $(MAKE) DocumentLoader.run
#echo -
#echo NOTE: This example does not use the new UNO bootstrap mechanism, it uses still a socket
#echo $(SQM) $(SQM)connection. The example use the defaultBootstrap_InitialComponentContext method and provides
#echo $(SQM) $(SQM)the additional office types via the UNO environment variable -env:URE_MORE_TYPES=...
#echo $(SQM) $(SQM)Before you can run this example you have to start your office in listening mode.
#echo -
#echo $(SQM) $(SQM)soffice "$(QM)--accept=socket,host=localhost,port=2083;urp;StarOffice.ServiceManager$(QM)"
#echo --------------------------------------------------------------------------------
%.run: $(OUT_BIN)/DocumentLoader$(EXE_EXT)
# cd $(subst /,$(PS),$(OUT_BIN)) && $(basename $#) $(ENV_OFFICE_TYPES) $(subst \\,/,$(subst /,$(PS),"$(OO_SDK_HOME)/examples/cpp/DocumentLoader/test.odt"))
cd $(subst /,$(PS),$(OUT_BIN)) && $(basename $#) $(ENV_OFFICE_TYPES) $(subst \\,/,$(subst /,$(PS),"/home/daz/libreoffice7.3_sdk/matt/test.odt"))
.PHONY: clean
clean :
-$(DELRECURSIVE) $(subst /,$(PS),$(OUT_COMP_INC))
-$(DELRECURSIVE) $(subst /,$(PS),$(OUT_COMP_GEN))
-$(DELRECURSIVE) $(subst /,$(PS),$(OUT_COMP_OBJ))
-$(DEL) $(subst \\,\,$(subst /,$(PS),$(OUT_BIN)/DocumentLoader*))

c++ makefile using gcc - generate source file list from a list of subfolders

So I have some rules that work:
# Folders
SRCDIR = src
SUBFOLDERS = test test/test2
OBJDIR = obj
# Just for me so I can read my own makefile :o
_TARGET = $#
_DEPEND = $<
# Get sources from /src and /src/test/ and /src/test/test2/
SOURCES = $(wildcard $(SRCDIR)/*.cpp ))
SOURCES = $(wildcard $(SRCDIR)/test/*.cpp ))
SOURCES = $(wildcard $(SRCDIR)/test/test2/*.cpp ))
OBJECTS = $(addprefix $(OBJDIR)/, $(patsubst src/%, %, $(SOURCES:.cpp=.o))
# Main target 'make debug'
debug: debugging $(OBJECTS)
#echo building: gcc $(OBJECTS) -o out.exe
# Compiling each file
$(OBJDIR)/%.o: $(SRCDIR)/%.cpp
g++ -c $(_DEPEND) -o $(_TARGET)
debugging:
#echo $(SOURCES)
#echo $(OBJECTS)
Note: I had to hand-copy this code so there may be some errors, but hopefully its clear what I am trying to do.
Lets say I want to add some more source file sub folders: src/another, src/andanother. Now I have to add more SOURCES = ... lines to do this. Is there a way to do this in a rule. I have been tinkering with it but I can't come up with rules that work yet. What I want is that I add a new folder to SUBFOLDERS list and my code picks up the .cpp files automatically.
note I don't want to use things like $(shell find ... ) because windows find sucks and I want this to be as portable as possiblt windows/Linux.
You can use make's foreach.
like:
SOURCES = $(foreach dir,${SUBFOLDERS},$(wildcard ${dir}/*.cpp))
more info:
https://www.gnu.org/software/make/manual/html_node/Foreach-Function.html

Using $(filter) in GNU makefiles static pattern rules

Let's say I have this GNU makefile:
SOURCE = source1/a source2/b source3/c
BUILD = build/a build/b build/c
BASE = build/
$(BUILD): $(BASE)%: %
install $< $#
So basically I have files in the directories source1, source2 and source3, which I want to regardlessly put into the build directory.
I want to accomplish this with a single static pattern rule. My naïve approach would be this:
$(BUILD): $(BASE)%: $(filter *%, $(SOURCE))
install $< $#
This doesn't work. I know that filters are also denoted with percentage signs.
$(BUILD): $(BASE)%: $(wildcard */$(notdir %))
install $< $#
This doesn't work either. But even if, this would still be non-satisfactory, as I might want to touch a new file source1/b, which would mess everything up.
How can I use the $(filter) function in GNU makefile static pattern rules? Or is there another approach to do accomplish this?
The hierarchy now looks like this:
source1/
a
source2/
b
source3/
c
build/
I want it to look like this:
source1/
a
source2/
b
source3/
c
build/
a
b
c
After reading a thousand Stackoverflow questions and the GNU make tutorial for hours, I finally got it working:
SOURCE = source1/a source2/b source3/c
TEST = a b c
BUILD = build/a build/b build/c
BASE = build/
PERCENT = %
.SECONDEXPANSION:
$(BUILD): $(BASE)%: $$(filter $$(PERCENT)/$$*,$$(SOURCE))
install $< $#
It's kinda hacky, but I'm happy with it. If anybody got any better idea, please let me know.
You can do this with the VPATH variable:
SOURCE = source1/a source2/b source3/c
BUILD = build/a build/b build/c
BASE = build/
VPATH = source1 source2 source3
$(BUILD): $(BASE)%: %
install $< $#
Or:
SOURCE = source1/a source2/b source3/c
BASE = build/
BUILD = $(addprefix $(BASE), $(notdir $(SOURCE)))
VPATH = $(dir $(SOURCE))
$(BUILD): $(BASE)%: %
install $< $#

GNU make ifeq comparison not working

I am trying to have a command executed depending on the current target from a list of targets (currently only one entry in that list) before another makefile is executed.
i have this:
$(LIBS):
ifeq ($#,libname)
my command here
endif
$(MAKE) -C ./lib/$#
the problem is, that the ifeq does not get executed even if the target name is correct. Using an $(info $#) shows exactly the libname but the expression is not evaluated as true.
I thought maybe there is a problem with expansion of the automatic variable in a conditional so i tried using an eval like this:
$(LIBS):
$(eval CURRENT_LIB := $#)
ifeq ($(CURRENT_LIB),libname)
my command here
endif
$(MAKE) -C ./lib/$#
info shows that the variable now equals exactly the libname but the ifeq does not get excuted.
When i enter something like ifeq (libname,libname) it works so the statement is working, but the comparison between variable and text does not evaluate to true even if the two are equal and it should work.
GNU make version is 4.1
What am i missing here?
Complete Makefile:
CC := g++
CFLAGS := -v -std=c++0x -pthread -Wall -g -O3
OBJ := mycode.o
OBJ += moreobjects.o
#more objects in here
LIBS = libname
.PHONY: libs $(LIBS)
SRC = $(OBJ:%.o=%.cpp)
DEPFILE := .depend
mytarget: libs $(OBJ)
$(CC) $(CFLAGS) -o $# $(OBJ)
-include $(DEPFILE)
%.o: %.cpp
$(CC) $(CFLAGS) -c $<
$(CC) -MM -std=c++11 $(SRC) > $(DEPFILE)
libs: $(LIBS)
$(LIBS):
$(eval CURRENT_LIB := $#)
ifeq ($(CURRENT_LIB),libname)
./lib/$(CURRENT_LIB)/configure
endif
$(MAKE) -C ./lib/$#
.PHONY: clean_all
clean_all: clean
$(foreach dir,$(LIBS),$(MAKE) clean -C ./lib/$(dir))
.PHONY: clean
clean:
rm -rf mytarget $(OBJ) $(DEPFILE)
Thank You very much!
What you are trying to do cannot work. From the documentation:
Conditionals control what 'make' actually "sees" in the makefile, so they cannot be used to control recipes at the time of execution.
The way I put it is that conditionals are evaluated at the time the Makefile is read. So make reads your Makefile, your conditional is false and it is removed.
"Normal" conditionals, don't work in a recipe with GNU make. Unfortunately that's true.
Workaround:
But you can use Functions for Conditionals and The eval Function instead. A small example for error detection from my build system. Here I use the eval and if function.
Snippet from Makefile.xu, which use eval and if:
xubuild_check_objects:
$(eval RESULT = $(call xu-obj_func-valid,$(xu-src-c-y)))
#echo 'echo nothing (dummy)' >/dev/null
$(if $(filter n,$(RESULT)), \
$(error XUbuild: error: xubuild_check_objects: xu-obj->xu-src-c-y override detected! Cannot build target! Aborting),)
NOTE:
#echo 'echo nothing (dummy)' >/dev/null
This here is required. When no command will be executed after eval, you get a make: Nothing to be done for 'xubuild_check_objects message.
Project Makefile (root directory) with error:
# XUbuild system (MUST included first)
include Makefile.xu
# This produces an error and aborts the build
xu-src-c-y = test.c
all: xubuild_check_objects
Error output:
$ make
Makefile.xu:104: *** XUbuild: error: xubuild_check_objects: xu-obj->xu-src-c-y override detected! Cannot build target! Aborting. Stop.
$
Project Makefile (root directory) without error:
# XUbuild system (MUST included first)
include Makefile.xu
# My build system is happy with this:
xu-src-c-y += test.c
all: xubuild_check_objects
Output (no error):
$ make
$
The full error detection from my build system simplified. I removed the most Makefile.xu "code" and only post the error detection here. (I don't explain my whole build system in this answer):
# XUbuild generic object
xu-obj := :xu-obj
xu-obj_del := :
# XUbuild object types
xu-obj_nop := n
xu-obj_yes := y
xu-obj_module := m
xu-obj_src := s
xu-obj_flag := f
# XUbuild source priority
xu-obj_src_head := 0
xu-obj_src_normal := 1
# XUbuild source types
xu-obj_src_a := 0
xu-obj_src_c := 1
xu-obj_src_cpp := 2
# XUbuild object functions
xu-obj_func-get = $(filter $(xu-obj)%,$(1))
xu-obj_func-remove = $(filter-out $(xu-obj)%,$(1))
xu-obj_func-valid = $(if $(call xu-obj_func-get,$(1)),y,n)
# XUbuild source objects
xu-src := $(xu-obj)$(xu-obj_del)
# XUbuild C source
xu-src-c := $(xu-src)$(xu-obj_src_normal)$(xu-obj_del)$(xu-obj_src_c)$(xu-obj_del)
xu-src-c- := $(xu-src-c)$(xu-obj_nop)
xu-src-c-y := $(xu-src-c)$(xu-obj_yes)
XUBUILD_PHONY += xubuild_check_objects
xubuild_check_objects:
$(eval RESULT = $(call xu-obj_func-valid,$(xu-src-c-y)))
#echo 'echo nothing (dummy)' >/dev/null
$(if $(filter n,$(RESULT)), \
$(error XUbuild: error: xubuild_check_objects: xu-obj->xu-src-c-y override detected! Cannot build target! Aborting),)
.PHONY: $(XUBUILD_PHONY)

undefined reference to `main' with makefile on linux, with boost and mpi

I am not very familiar with make; i've always used and modified this highly ancient makefile I inherited from another project, modifying it as necessary. Up until now, it has functioned perfectly, compiling together projects with 20-50 files, even within sub-directories, determining and building all the dependencies properly.
Today I am running a very simple test code to determine if BOOST.MPI will work on a cluster I recently got access to. I am trying to compile a 1-file test case to make sure that the library works. This is also my first time building and linking to boost on this system.
I have boost installed in my user directory: ~/boost, and I've run the appropriate bjam to ensure that BOOST.MPI is present. I don't believe my problem lies there. As you'll see in a moment, however, I had to include -lboost_mpi and -lboost_serialization to get as far as I did.
The makefile seems to compile, and assemble just fine producing the intermediary .a file. It hangs up on the linking step with a surprising error:
Creating dependency for "boosttest.cpp"
Compiling "boosttest.cpp"
Assembling ag...
ar: creating ./libag.a
Linking bt1...
/usr/lib/gcc/x86_64-redhat-linux/4.4.5/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: ld returned 1 exit status
make: *** [bt1] Error 1
The only source code I've written (copy/pasted right out of the Boost.MPI implementation page):
boosttest.cpp:
#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <iostream>
int main(int argc, char* argv[])
{
boost::mpi::environment env(argc, argv);
boost::mpi::communicator world;
std::cout << "I am process " << world.rank() << " of " << world.size()<< "." << std::endl;
return 0;
}
Now for the part where I get lost. This is the makefile I've modified. I mostly understand what parts of it are doing, and it is the only makefile I've ever seen (although as mentioned I'm not very experienced with them) to produce .d files containing a list of all dependent libraries.
Makefile:
PROJECT = boosttest
# The names of the sub-projects that the executable depends on. These names are used later on to define targets, location, libraries, etc.
DEPS =
# The names of the sub-projects that the test executable depends on.
TEST_DEPS =
EXE_DEPS =
LOCAL_O_FILES = boosttest.o
LOCAL_TEST_O_FILES =
LOCAL_LIB_NAME = ag
LOCAL_LIB_DIR = .
LOCAL_TEST_LIB_NAME =
LOCAL_TEST_LIB_DIR =
LOCAL_INCLUDE_ROOT = .
EXE_NAME = BT
MPIC_DIR = /usr/local/mvapich2-1.6-gcc/
#these assume our local path and bin are set up properly since I don't know where the compilers are
CC = $(MPIC_DIR)/bin/mpicc
CCC = $(MPIC_DIR)/bin/mpicxx
F77 = $(MPIC_DIR)/bin/mpif77
CLINKER = $(MPIC_DIR)/bin/mpicc
CCLINKER = $(MPIC_DIR)/bin/mpicxx
FLINKER = $(MPIC_DIR)/bin/mpif90
F90 = $(MPIC_DIR)/bin/mpif90
F90LINKER = $(MPIC_DIR)/bin/mpif90
MAKE = make --no-print-directory
SHELL = /bin/sh
#PROF = -ggdb -O2
PROF = -O2
ADDITIONAL_LIBS = -lboost_mpi -lboost_serialization
SED = $(shell which sed)
GREP = $(shell which grep)
# Warnings will depend on the GCC version -- if it's 4, have "-Wdeclaration-after-statement -Wunused-value -Wunused-variable"
#GCC_MAJOR_VERSION = $(shell $(CCC) --version | $(GREP) "\(egcs\|gcc\)" | $(SED) "s/[^0-9]*\([0-9]\).*/\1/")
#WARNINGS = -Wno-import $(shell if test $(GCC_MAJOR_VERSION) -eq 4; then echo -Wunused-value -Wunused-variable; fi)
#GCC_INSTALL_DIR = /turing/software-linux/mpich-eth
GCC_INSTALL_DIR = $(MPIC_DIR)
GCC_INCLUDE_DIR = $(GCC_INSTALL_DIR)/include/
GCC_LIB_DIR = $(GCC_INSTALL_DIR)/lib/
BOOST_DIR = ~/boost
BOOST_LIB_DIR = ~/boost/stage/lib
# Expand SRCDIR so that it turns into "-I <srcdir>" for each listed directory
INCLUDE_DIRS = -I $(OBJC_ROOT)/include $(foreach dep, $(UNIQUE_DEPS), -I$($(dep)_INCLUDE_ROOT)) -I $(LOCAL_INCLUDE_ROOT) -I $(BOOST_DIR) -I $(GCC_INCLUDE_DIR)
#C_FLAGS = -DROM $(PROF) $(WARNINGS) $(INCLUDE_DIRS)
C_FLAGS = -DROM $(PROF) $(INCLUDE_DIRS)
L_FLAGS = $(foreach dir, $(OBJC_ROOT) $(DEP_LIB_DIRS), -L$(dir)) -L $(BOOST_LIB_DIR) -L $(GCC_LIB_DIR) $(PROF) $(ADDITIONAL_LIBS)
TEST_L_FLAGS = $(foreach dir, $(OBJC_ROOT) $(TEST_DEP_LIB_DIRS), -L$(dir)) -L/usr/lib -Xlinker --whole-archive $(TEST_REQUIRED_LIBS) -Xlinker --no-whole-archive
REQUIRED_LIBS = $(foreach dep, $(DEPS) LOCAL, $(if $($(dep)_LIB_NAME), -l$($(dep)_LIB_NAME)))
TEST_REQUIRED_LIBS = $(foreach dep, $(TEST_DEPS) LOCAL LOCAL_TEST, $(if $($(dep)_LIB_NAME), -l$($(dep)_LIB_NAME)))
.SUFFIXES:
.SUFFIXES: .d .cc .cpp .c .o
ASSEMBLE_TARGETS = $(foreach dep,$(DEPS),$(dep)_LIB_NAME)
ASSEMBLE_TEST_TARGETS = $(foreach dep,$(TEST_DEPS),$(dep)_LIB_NAME)
CLEAN_TARGETS = $(foreach dep, $(UNIQUE_DEPS), $(dep)_CLEAN)
LOCAL_D_FILES = $(LOCAL_O_FILES:.o=.d)
LOCAL_TEST_D_FILES = $(LOCAL_TEST_O_FILES:.o=.d) $(TEST_RUNNER:.o=.d)
DEP_LIB_DIRS = $(foreach dep, $(DEPS) LOCAL, $($(dep)_LIB_DIR))
TEST_DEP_LIB_DIRS = $(foreach dep, $(TEST_DEPS) LOCAL LOCAL_TEST, $($(dep)_LIB_DIR))
UNIQUE_ASSEMBLE_TARGETS = $(sort $(ASSEMBLE_TARGETS) $(ASSEMBLE_TEST_TARGETS))
UNIQUE_DEPS = $(sort $(DEPS) $(TEST_DEPS))
LOCAL_LIB = $(if $(LOCAL_LIB_NAME), $(LOCAL_LIB_DIR)/lib$(LOCAL_LIB_NAME).a)
LOCAL_TEST_LIB = $(if $(LOCAL_TEST_LIB_NAME), $(LOCAL_TEST_LIB_DIR)/lib$(LOCAL_TEST_LIB_NAME).a)
EXE_TARGETS = $(foreach dep,$(EXE_DEPS),$(dep)_EXE_NAME)
INSTALL_TARGETS = $(foreach dep,$(EXE_DEPS),$(dep)_INSTALL)
export PROJECTS += $(PROJECT)
# PHONY targets get remade even if there exists an up-to-date file with the same name.
.PHONY: default clean compile assemble link submit
# Targets for mortals
default: link
clean: $(CLEAN_TARGETS) LOCAL_CLEAN
compile: $(DEPS) $(LOCAL_D_FILES) $(LOCAL_O_FILES)
assemble: compile $(ASSEMBLE_TARGETS) $(LOCAL_LIB)
link: assemble $(EXE_TARGETS) $(EXE_NAME)
install: $(INSTALL_TARGETS) $(if $(EXE_NAME), LOCAL_INSTALL)
submit: link
qsub $(PROJECT).qsub
# Targets for make
# Invoking sub projects
$(UNIQUE_DEPS): MAKE_TARGET = $(if $(filter %_TEST, $#), compile-tests, compile)
$(UNIQUE_DEPS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($#_DIR) && $(MAKE) $(MAKE_TARGET))
# First, remove the _LIB_NAME attached by ASSEMBLE_TARGETS, a
$(UNIQUE_ASSEMBLE_TARGETS): DEP_NAME = $(#:_LIB_NAME=)
$(UNIQUE_ASSEMBLE_TARGETS): MAKE_TARGET = $(if $(filter %_TEST, $(DEP_NAME)), assemble-tests, assemble)
$(UNIQUE_ASSEMBLE_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) $(MAKE_TARGET))
# First, remove the _EXE_NAME attached by EXE_TARGETS, a
$(EXE_TARGETS): DEP_NAME = $(#:_EXE_NAME=)
$(EXE_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) link)
$(CLEAN_TARGETS): DEP_NAME = $(#:_CLEAN=)
$(CLEAN_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) clean)
$(INSTALL_TARGETS): DEP_NAME = $(#:_INSTALL=)
$(INSTALL_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) install)
#Local stuff
# The rule to change either a '.c' or a '.m' to a '.o'
#%.o : %.c %.d %.cc %.cpp
.cc.o .cpp.o .c.o:
#echo "Compiling \"$<\""
#$(CCC) -c $(C_FLAGS) $< -o $#
.cc.d .cpp.d .c.d :
#echo "Creating dependency for \"$<\""
# #$(CCC) $(WARNINGS) $(INCLUDE_DIRS) -MM $< -o $#
# This foogly hack because gcc seems to have issues with emitting the correct target/dependency names of
# files in sub-dirs of the current dir (specifically, it doesn't add the sub-dir to the target
# name, and insists on adding the directory to the dependencies) which ends up breaking dependencies...)
#dependLine=$$( $(CCC) $(C_FLAGS) $(INCLUDE_DIRS) -MM $< ); \
dirName=$$( dirname $< | $(SED) "s/\//\\\\\//g" ); \
dependLine=$$( echo $${dependLine} | $(SED) "s/ $${dirName}\// /g" ); \
oFile=$$( echo $${dependLine} | $(SED) "s/:.*//" ); \
dependencies=$$( echo $${dependLine} | $(SED) "s/.*://" ); \
echo $${oFile} $${oFile%.o}.d: $${dependencies} | $(SED) "s/ \\\//g" > $#
$(UNIQUE_ASSEMBLE_TARGETS): DEP_NAME = $(#:_LIB_NAME=)
$(UNIQUE_ASSEMBLE_TARGETS): MAKE_TARGET = $(if $(filter %_TEST, $(DEP_NAME)), assemble-tests, assemble)
$(UNIQUE_ASSEMBLE_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) $(MAKE_TARGET))
# First, remove the _EXE_NAME attached by EXE_TARGETS, a
$(EXE_TARGETS): DEP_NAME = $(#:_EXE_NAME=)
$(EXE_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) link)
$(CLEAN_TARGETS): DEP_NAME = $(#:_CLEAN=)
$(CLEAN_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) clean)
$(INSTALL_TARGETS): DEP_NAME = $(#:_INSTALL=)
$(INSTALL_TARGETS):
$(if $(findstring $(DEP_NAME), $(PROJECTS)),,cd $($(DEP_NAME)_DIR) && $(MAKE) install)
#Local stuff
# The rule to change either a '.c' or a '.m' to a '.o'
#%.o : %.c %.d %.cc %.cpp
.cc.o .cpp.o .c.o:
#echo "Compiling \"$<\""
#$(CCC) -c $(C_FLAGS) $< -o $#
.cc.d .cpp.d .c.d :
#echo "Creating dependency for \"$<\""
# #$(CCC) $(WARNINGS) $(INCLUDE_DIRS) -MM $< -o $#
# This foogly hack because gcc seems to have issues with emitting the correct target/dependency names of
# files in sub-dirs of the current dir (specifically, it doesn't add the sub-dir to the target
# name, and insists on adding the directory to the dependencies) which ends up breaking dependencies...)
#dependLine=$$( $(CCC) $(C_FLAGS) $(INCLUDE_DIRS) -MM $< ); \
dirName=$$( dirname $< | $(SED) "s/\//\\\\\//g" ); \
dependLine=$$( echo $${dependLine} | $(SED) "s/ $${dirName}\// /g" ); \
oFile=$$( echo $${dependLine} | $(SED) "s/:.*//" ); \
dependencies=$$( echo $${dependLine} | $(SED) "s/.*://" ); \
echo $${oFile} $${oFile%.o}.d: $${dependencies} | $(SED) "s/ \\\//g" > $#
$(LOCAL_LIB): compile $(ASSEMBLE_TARGETS)
#echo Assembling $(LOCAL_LIB_NAME)...
#ar rs $(LOCAL_LIB) $(LOCAL_O_FILES)
# Create the executable
$(EXE_NAME): assemble
#echo Linking $(EXE_NAME)...
#$(CCC) -o $(EXE_NAME) $(L_FLAGS)
# Erase all object files, the dependencies file, the core file and the executable, then rebuild everything
LOCAL_CLEAN:
#echo Cleaning $(PROJECT)...
#rm -f $(LOCAL_O_FILES) $(LOCAL_TEST_O_FILES) $(LOCAL_LIB) $(LOCAL_TEST_LIB) $(EXE_NAME) $(TEST_EXE_NAME) $(LOCAL_D_FILES) $(LOCAL_TEST_D_FILES) $(TEST_RUNNER) $(TEST_RUNNER:.o=.d) core*
ifeq (,$(findstring clean,$(MAKECMDGOALS)))
-include $(LOCAL_O_FILES:.o=.d) $(LOCAL_TEST_O_FILES:.o=.d)
endif
The paths to my mpicxx compiler (needed for the cluster, and for those unfamiliar, it wraps gcc) are correct, as evidenced by the fact that it does compile, it just won't link. Similarly, my boost library paths seem functional, the -lboost_mpi flag gets caught properly.
At this point, my question has extended to be for my own education based on how this makefile works/intends to work. Ultimately the entirely obvious/simple command:
mpicxx boosttest.cpp -L ~/boost/stage/lib/ -lboost_mpi -lboost_serialization -o test
however I want to get a proper makefile going as once I get past this test case verifying Boost.MPI's equivalent of a hello world, I'll be moving on to projects involving my full source code, 20+ files with cross dependencies, etc. Any help or intuition would be greatly appreciated.
Based on the suggestion of #Oktalist above, I was able to find something that works, at least for the single-file case, but I can't see why it won't work when I extend this to multiple files. I'm still at a loss why this worked in the past but not now, but perhaps I changed something I wasn't tracking.
The correction is near the end, in this block:
# Create the executable
$(EXE_NAME): assemble
#echo Linking $(EXE_NAME)...
#$(CCC) $(L_FLAGS) $(LOCAL_LIB) -o $(EXE_NAME)