2

There are a few different questions already related to what I'm trying to do, such as this, this, and this. However, I've looked at what is there and what they link to and I'm still not able to get it to work.

I have the following make file (I had some help with this here). It builds .os in a build directory from .cpps in a source directory. I want to adjust this so that if the headers are updated, then it properly re-compiles those so I don't have to make clean the whole thing (most particularly the ones from the INC_DIR1 folder).

CC = g++
CFLAGS = -g -Wall

INC_DIR1 = include
INC_DIR2 = C:/CPPFiles/CPP_Extra_Libraries/armadillo-4.200.0/include
INC_DIR = $(INC_DIR1) $(INC_DIR2)
INCLUDES = $(foreach d, $(INC_DIR), -I$d)

BUILD_DIR = build
SRC_DIR = test

SRC = $(wildcard */*.cpp)

VPATH = $(SRC_DIR)

OBJS = $(addprefix $(BUILD_DIR)/, $(notdir $(SRC:.cpp=.o)))

MAIN = armadillo_extra_functions_test

.PHONY: depend clean

all: $(BUILD_DIR) $(MAIN)
    @echo  compilation complete

$(BUILD_DIR):
    mkdir -p $@

$(BUILD_DIR)/%.o: %.cpp
    $(CC) $(CFLAGS) $(INCLUDES) -c $< -o $@

$(MAIN): $(OBJS) 
    $(CC) $(CFLAGS) $(INCLUDES) -o $(MAIN) $(OBJS)

clean:
    $(RM) *.o *~ $(MAIN) $(BUILD_DIR)/*.o

depend: $(SRC)
    makedepend $(INCLUDES) $^

The latest thing I tried was to remove the depend and makedepend statements and then replace the $(BUILD_DIR/%.o: %.cpp statement with

DEPS = $(patsubst %.o, %.d, $(OBJS))

-include $(DEPS)

$(BUILD_DIR)/%.o: %.cpp
    $(CC) -c $(CFLAGS) $(INCLUDES) $< -o $@
    $(CC) -c $(CFLAGS) -MMD  $< > $(DEPS)

Without the last line of these adjustments, the make file runs, though it will not update headers. However, when I add in the last line I get an error about the $(BUILD_DIR)/%.d files not being there.

Community
  • 1
  • 1
John
  • 395
  • 1
  • 7
  • 23

2 Answers2

2

Basically you need to construct the makefile so that the .cpp files depend on the headers. In this way, changes to the headers will trigger recompilation of the .cpp files. To make this easier, ideally the header dependencies of each .cpp file would be automatically determined.

I recommend using either the GNU autotools approach (great for software you will publish in source form), jam (very simple configuration), or (my current favorite) CMake. All of these essentially automatically generate the makefile from another file (or set of files). They automatically handle the dependency detection and management, too. I would only hand-roll a makefile for self-education or for something with only a handful of source files, or perhaps something weird and complex.

Randall Cook
  • 6,728
  • 6
  • 33
  • 68
  • Your first paragraph describes what I need to do. The autotools approach looks solid, but is probably more than what I need. The other questions I referenced seem to suggest it is possible within a typical make file. I would say I'm probably doing it more for self-education. Right now the test folder and include folders each about about 20 files in them. I don't know if that is too much or not. I might give CMake a try, but I'd rather figure this out within Make first. – John May 16 '14 at 17:02
  • 1
    I understand, @John. One approach that might work is to look for an existing project that does what you want (or something close) and copy its pattern. Or build the makefile up step by step, perhaps with only a few files, all in a single directory, and gradually transform the project structure to match what you are envisioning. – Randall Cook May 16 '14 at 17:10
  • 2
    And it doesn't just stop with header dependencies... there are dependencies on generated code, inter-library dependencies, truly correct cleans, the list goes on and on. I've seen a lot of stubborn people think they can outdo any of the tools mentioned here and they always end up stymied when their makefile fails them. – Andy May 16 '14 at 17:50
1

You were almost right with the -MMD flag, but there is no need to complicate things:

INC_DIR1    :=  include
INC_DIR2    :=  C:/CPPFiles/CPP_Extra_Libraries/armadillo-4.200.0/include
INC_DIRS    :=  $(INC_DIR1) $(INC_DIR2)

BUILD_DIR   :=  build
SRC_DIR     :=  test

SRCS        :=  $(wildcard $(SRC_DIR)/*.cpp)
OBJS        :=  $(SRCS:$(SRC_DIR)/%.cpp=$(BUILD_DIR)/%.o)
DEPS        :=  $(OBJS:.o=.d)

LDLIBS      :=  # -l flags
LDFLAGS     :=  # -L flags

CPPFLAGS    :=  -MMD -MP $(foreach DIR, $(INC_DIRS), -I $(DIR))
CXXFLAGS    :=  -W -Wall

MAIN        :=  armadillo_extra_functions_test

.PHONY: all clean

all:    $(MAIN)
    @echo "compilation complete"

$(MAIN):    $(OBJS)
    $(CXX) $(LDFLAGS) $^ $(LDLIBS) -o $@

clean:
    $(RM) -r $(MAIN) $(BUILD_DIR)

-include $(DEPS)

$(BUILD_DIR):
    mkdir $@

$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp | $(BUILD_DIR)
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ -c $<

Some quick notes :

  • You're using C++. Drop the $(CC) and $(CFLAGS) C variables for their C++ versions: $(CXX) and $(CXXFLAGS) variables.

  • $(CPPFLAGS) is meant for preprocessor flags, such as -MMD, -MP or -I.

  • You need to include the rules GCC created in the dependency files.

Chnossos
  • 9,971
  • 4
  • 28
  • 40
  • Thanks. I'll give this a try when I get back in the office. If it works, I'll accept the answer. – John May 18 '14 at 05:40
  • I made some guesses about your project setup, so feel free to ask me any information if you need to tune this Makefile. – Chnossos May 18 '14 at 09:41
  • I had to change the indentations to tabs to get it to compile. It looks like this compiles correctly and puts `.d` files into the build directory. However, the code will not re-compile the `.cpp` files if I make changes to headers they depend on. I tried adding a simple `std::cout` statement to one of the headers in the `include` directory, re-run make and then re-run the program. Re-running make did not re-compile the `.cpp` files that depend on that header, so no `cout` when running the program. – John May 19 '14 at 21:23
  • 1
    Ok, my bad, I made a mistake. I tried copy/pasting the code and it happens I forgot the `S` from `OBJ` in `DEPS := $(OBJ:.o=.d)` so `DEPS` was empty and nothing was included ! I'm also sorry for the tab characters, but SO translates them to spaces automatically ... I've corrected the code after getting it to work, everything should be OK from now on. – Chnossos May 19 '14 at 22:38
  • No worries on the tabs. Easy fix. I'll try it again tomorrow with that change. – John May 19 '14 at 23:17
  • I re-did what I did above with the `OBJS` in the make file. This time I got a message `build/test_wcov.d:1: *** multiple target patterns. Stop.` on re-running the program where `test_wcov.cpp` was one of the files in the test folder. It's also the first one that was compiled initially. – John May 20 '14 at 14:51
  • I'm wondering, what is your make version ? I use such Makefiles everyday and I never encountered this error. Can you look at [this](http://stackoverflow.com/questions/2100448/multiple-target-patterns-makefile-error) and tell me if it helps ? – Chnossos May 20 '14 at 14:57
  • I had installed the latest versions of MinGW/GCC/Make about a month ago. I'm not sure that link helps. One thing I did notice is that the `build/test_wcov.d` files exist, but there is no `test_wcov.d:1`. Based on that link, the `:1` might be what is causing the target patterns issue. Not sure where that comes from. The only thing I changed was the indentation fix. – John May 20 '14 at 17:07
  • Can you post your full Makefile as it is now ? I tested mine with MinGW 4.8.2 so there might be something else that you can't see just now. – Chnossos May 20 '14 at 17:40
  • I uploaded the whole thing to the link at the end (for now). There is still some weird behavior, like I compile it once, then when I run make clean I get that `test_wcov.d:1` error from above, even without making any changes. https://www.dropbox.com/s/a36umjkx7nziu3p/armadillo_extra_functions.zip – John May 20 '14 at 17:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/54060/discussion-between-chnossos-and-john). – Chnossos May 20 '14 at 18:09