1

I am just getting started with makefiles, and I have written one for my project that has two targets. If I run a new make everything works fine. But if I don't clean after that, and I edit and save one source file, for example, and then I run make again, my project only compiles the source file I edited and then makes no attempt to link my sources into a new executable.

In other words: The first time I run make I get $(CC) -c <sourcefile> for each source, and then $(CC) -o <sourceobject1> <sourceobject2>... and everything works. But if I then go and edit a sourcefile, and try to make again, I just get $(CC) -c <editedsourcefile>, and no linking after that! At least make is not trying to compile all my source files again, but why isn't it linking me a new executable? Is this the expected behavior?

Bonus question: Is there a nicer way to move my object files into a subdirectory than sticking mv *.o at the top of my target rule? I have looked at this answer, but I didn't understand what was going on and couldn't figure out how to adapt it to my case with two targets.

Here is my makefile:

CC = clang++
OPTFLAGS = -O3 -flto -m64
CFLAGS = -Wall -c $(OPTFLAGS)
LFLAGS = -Wall $(OPTFLAGS)
MAKE = make
RM = \rm -f
TARGET-DIR = build
OBJ-DIR = $(TARGET-DIR)/obj
OBJS = $(addprefix $(OBJ-DIR)/, MCTS.o NodePool.o Node.o FastMath.o \
       Board.o Patterns.o FastSet.o FastHash.o FastHash2.o FastHashMap.o)
GTP-OBJS = $(addprefix $(OBJ-DIR)/, GtpPlayer.o)
GTP-TARGET = $(TARGET-DIR)/go-gtp
TEST-OBJS = $(addprefix $(OBJ-DIR)/, TestsMain.o MCTSTests.o NodePoolTests.o \
        NodeTests.o FastMathTests.o BoardTests.o FastSetTests.o \
        FastHashTests.o FastHash2Tests.o FastHashMapTests.o)
TEST-TARGET = $(TARGET-DIR)/go-test

.PHONY : all 
all : $(GTP-TARGET) $(TEST-TARGET)

$(GTP-TARGET) : $(GTP-OBJS) $(OBJS)
    mv *.o $(OBJ-DIR)
    $(CC) $(LFLAGS) $(GTP-OBJS) $(OBJS) -o $(GTP-TARGET)

$(OBJ-DIR)/GtpPlayer.o : GtpPlayer.hpp GtpPlayer.cpp mcts/MCTS.hpp
    $(CC) $(CFLAGS) GtpPlayer.cpp

$(OBJ-DIR)/MCTS.o : mcts/MCTS.hpp mcts/MCTS.cpp mcts/NodePool.hpp
    $(CC) $(CFLAGS) mcts/MCTS.cpp

$(OBJ-DIR)/NodePool.o : mcts/NodePool.hpp mcts/NodePool.cpp mcts/Node.hpp
    $(CC) $(CFLAGS) mcts/NodePool.cpp

$(OBJ-DIR)/Node.o : mcts/Node.hpp mcts/Node.cpp mcts/FastMath.hpp board/Board.hpp
    $(CC) $(CFLAGS) mcts/Node.cpp

$(OBJ-DIR)/FastMath.o : mcts/FastMath.hpp mcts/FastMath.cpp
    $(CC) $(CFLAGS) mcts/FastMath.cpp

$(OBJ-DIR)/Board.o : board/Board.hpp board/Board.cpp board/Patterns.hpp struct/FastSet.hpp struct/FastHash.hpp
    $(CC) $(CFLAGS) board/Board.cpp

$(OBJ-DIR)/Patterns.o : board/Patterns.hpp board/Patterns.cpp  struct/FastHash2.hpp struct/FastHashMap.hpp
    $(CC) $(CFLAGS) board/Patterns.cpp

$(OBJ-DIR)/FastSet.o : struct/FastSet.hpp struct/FastSet.cpp
    $(CC) $(CFLAGS) struct/FastSet.cpp

$(OBJ-DIR)/FastHash.o : struct/FastHash.hpp struct/FastHash.cpp
    $(CC) $(CFLAGS) struct/FastHash.cpp

$(OBJ-DIR)/FastHash2.o : struct/FastHash2.hpp struct/FastHash2.cpp
    $(CC) $(CFLAGS) struct/FastHash2.cpp

$(OBJ-DIR)/FastHashMap.o : struct/FastHashMap.hpp struct/FastHashMap.cpp
    $(CC) $(CFLAGS) struct/FastHashMap.cpp

$(TEST-TARGET) : $(TEST-OBJS) $(OBJS)
    mv *.o $(OBJ-DIR)
    $(CC) $(LFLAGS) $(TEST-OBJS) $(OBJS) -o $(TEST-TARGET)

$(OBJ-DIR)/TestsMain.o : test/TestsMain.cpp test/MCTSTests.hpp test/NodePoolTests.hpp \
          test/NodeTests.hpp test/FastMathTests.hpp test/BoardTests.hpp \
          test/FastSetTests.hpp test/FastHashTests.hpp test/FastHash2Tests.hpp test/FastHashMapTests.hpp
    $(CC) $(CFLAGS) test/TestsMain.cpp

$(OBJ-DIR)/MCTSTests.o : test/MCTSTests.hpp test/MCTSTests.cpp mcts/MCTS.hpp
    $(CC) $(CFLAGS) test/MCTSTests.cpp

$(OBJ-DIR)/NodePoolTests.o : test/NodePoolTests.hpp test/NodePoolTests.cpp
    $(CC) $(CFLAGS) test/NodePoolTests.cpp

$(OBJ-DIR)/NodeTests.o : test/NodeTests.hpp test/NodeTests.cpp
    $(CC) $(CFLAGS) test/NodeTests.cpp

$(OBJ-DIR)/FastMathTests.o : test/FastMathTests.hpp test/FastMathTests.cpp
    $(CC) $(CFLAGS) test/FastMathTests.cpp

$(OBJ-DIR)/BoardTests.o : test/BoardTests.hpp test/BoardTests.cpp
    $(CC) $(CFLAGS) test/BoardTests.cpp

$(OBJ-DIR)/FastSetTests.o : test/FastSetTests.hpp test/FastSetTests.cpp
    $(CC) $(CFLAGS) test/FastSetTests.cpp

$(OBJ-DIR)/FastHashTests.o : test/FastHashTests.hpp test/FastHashTests.cpp
    $(CC) $(CFLAGS) test/FastHashTests.cpp

$(OBJ-DIR)/FastHash2Tests.o : test/FastHash2Tests.hpp test/FastHash2Tests.cpp
    $(CC) $(CFLAGS) test/FastHash2Tests.cpp

$(OBJ-DIR)/FastHashMapTests.o : test/FastHashMapTests.hpp test/FastHashMapTests.cpp
    $(CC) $(CFLAGS) test/FastHashMapTests.cpp

.PHONY : clean
clean : 
    $(RM) $(OBJ-DIR)/*.o

.PHONY : distclean
distclean :
    $(MAKE) clean
    $(RM) $(GTP-TARGET) $(TEST-TARGET)

.PHONY : again
again:
    $(MAKE) distclean
    $(MAKE) $(GTP-TARGET)
    $(MAKE) $(TEST-TARGET)

.PHONY : tar
tar: 
    tar cfv $(GTP-TARGET).tar $(GTP-OBJS) $(OBJS)
    tar cfv $(TEST-TARGET).tar $(TEST-OBJS) $(OBJS)

Example output:

$ make
clang++ -Wall -c -O3 -flto -m64 GtpPlayer.cpp
clang++ -Wall -c -O3 -flto -m64 mcts/MCTS.cpp
clang++ -Wall -c -O3 -flto -m64 mcts/NodePool.cpp
clang++ -Wall -c -O3 -flto -m64 mcts/Node.cpp
clang++ -Wall -c -O3 -flto -m64 mcts/FastMath.cpp
clang++ -Wall -c -O3 -flto -m64 board/Board.cpp
clang++ -Wall -c -O3 -flto -m64 board/Patterns.cpp
clang++ -Wall -c -O3 -flto -m64 struct/FastSet.cpp
clang++ -Wall -c -O3 -flto -m64 struct/FastHash.cpp
clang++ -Wall -c -O3 -flto -m64 struct/FastHash2.cpp
clang++ -Wall -c -O3 -flto -m64 struct/FastHashMap.cpp
mv *.o build/obj
clang++ -Wall -O3 -flto -m64 build/obj/GtpPlayer.o build/obj/MCTS.o build/obj/NodePool.o build/obj/Node.o build/obj/FastMath.o build/obj/Board.o build/obj/Patterns.o build/obj/FastSet.o build/obj/FastHash.o build/obj/FastHash2.o build/obj/FastHashMap.o -o build/go-gtp
clang++ -Wall -c -O3 -flto -m64 test/TestsMain.cpp
clang++ -Wall -c -O3 -flto -m64 test/MCTSTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/NodePoolTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/NodeTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/FastMathTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/BoardTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/FastSetTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/FastHashTests.cpp
clang++ -Wall -c -O3 -flto -m64 test/FastHash2Tests.cpp
clang++ -Wall -c -O3 -flto -m64 test/FastHashMapTests.cpp
mv *.o build/obj
clang++ -Wall -O3 -flto -m64 build/obj/TestsMain.o build/obj/MCTSTests.o build/obj/NodePoolTests.o build/obj/NodeTests.o build/obj/FastMathTests.o build/obj/BoardTests.o build/obj/FastSetTests.o build/obj/FastHashTests.o build/obj/FastHash2Tests.o build/obj/FastHashMapTests.o build/obj/MCTS.o build/obj/NodePool.o build/obj/Node.o build/obj/FastMath.o build/obj/Board.o build/obj/Patterns.o build/obj/FastSet.o build/obj/FastHash.o build/obj/FastHash2.o build/obj/FastHashMap.o -o build/go-test

Now if I edit and save Board.cpp for example and run make again, I get this:

$ make
clang++ -Wall -c -O3 -flto -m64 board/Board.cpp

What happened to the linking command? I want a new target!

Community
  • 1
  • 1
Imran
  • 12,950
  • 8
  • 64
  • 79
  • 4
    Why are you moving the `*.o` files around? What if you try your makefile without that moving? (Obviously you'd have to change several things to make it work.) That's definitely a nonstandard way to do things. Solutions I've seen to the "I want my `.o` files somewhere else" problem usually involve telling the compiler to generate them directly in that place, rather than moving them around in the makefile (which is bound to confuse make). – Greg Hewgill Jul 04 '11 at 00:41
  • 1
    Not related, but you can simplify most of your rules into `$(OBJ_DIR)/%.0: test/%.hpp test/%.cpp`. – Maxpm Jul 04 '11 at 01:39
  • 1
    if you add `-o $@` to your compiler commands, they'll automatically emit objects into your object directory (because your targets expect it: `$(OBJ-DIR)/FastHash2.o`). That will let you remove the `mv` command that I think is giving you extra trouble. I'm not entirely sure how to fix the linker problem, but I think moving the entire Makefile to list only the dependencies will help make that easier to spot. – sarnold Jul 04 '11 at 01:44
  • @Greg I am moving my object files and not telling the compiler to output them somewhere else because this is my first makefile and I don't know what I'm doing! :) – Imran Jul 04 '11 at 01:56
  • @Maxpm and @sarnold I will try those rules to simplify things. – Imran Jul 04 '11 at 01:57

2 Answers2

2

It won't link because the newer obj files are not in OBJ_DIR but in wherever they are before they are moved (current directory?). Make looks at the executable and the object files and correctly concludes that the executable is newer than the object files in OBJ_DIR.

Make the compiler generate them in OBJ_DIR in the first place (use -o) or move the mv command to each of the rules generating the object files (not recommended) so that the after running the rule, the obj file in OBJ_DIR is updated.

lmz
  • 1,560
  • 1
  • 9
  • 19
  • Thanks for explaining exactly why my method was failing. I simply didn't know the command to output the object files to the directory I wanted. – Imran Jul 04 '11 at 02:20
1

Outputting my object files to the desired directory with $(CC) -o $@ (and removing the mv *.o commands in my target rules) solves my problem. Everything links as expected now.

Thanks to Greg and sarnold who explained how to do this in the comments.

My makefile now looks like this:

CC = clang++
CFLAGS = -Wall -O3 -flto -m64
MAKE = make
RM = \rm -f
TARGET-DIR = build
OBJ-DIR = $(TARGET-DIR)/obj
OBJS = $(addprefix $(OBJ-DIR)/, MCTS.o NodePool.o Node.o FastMath.o \
       Board.o Patterns.o FastSet.o FastHash.o FastHash2.o FastHashMap.o)
GTP-OBJS = $(addprefix $(OBJ-DIR)/, GtpPlayer.o)
GTP-TARGET = $(TARGET-DIR)/go-gtp
TEST-OBJS = $(addprefix $(OBJ-DIR)/, TestsMain.o MCTSTests.o NodePoolTests.o \
        NodeTests.o FastMathTests.o BoardTests.o FastSetTests.o \
        FastHashTests.o FastHash2Tests.o FastHashMapTests.o)
TEST-TARGET = $(TARGET-DIR)/go-test

.PHONY : all 
all : $(GTP-TARGET) $(TEST-TARGET)

$(GTP-TARGET) : $(GTP-OBJS) $(OBJS)
    $(CC) $(CFLAGS) $^ -o $@

$(OBJ-DIR)/GtpPlayer.o : GtpPlayer.cpp
    $(CC) -c $(CFLAGS) $< -o $@

$(OBJ-DIR)/%.o : mcts/%.cpp
    $(CC) -c $(CFLAGS) $< -o $@

$(OBJ-DIR)/%.o : board/%.cpp
    $(CC) -c $(CFLAGS) $< -o $@

$(OBJ-DIR)/%.o : struct/%.cpp
    $(CC) -c $(CFLAGS) $< -o $@

$(TEST-TARGET) : $(TEST-OBJS) $(OBJS)
    $(CC) $(CFLAGS) $^ -o $@

$(OBJ-DIR)/%.o : test/%.cpp
    $(CC) -c $(CFLAGS) $< -o $@

.PHONY : clean
clean : 
    $(RM) $(OBJ-DIR)/*.o

.PHONY : distclean
distclean :
    $(MAKE) clean
    $(RM) $(GTP-TARGET) $(TEST-TARGET)

.PHONY : again
again:
    $(MAKE) distclean
    $(MAKE) $(GTP-TARGET)
    $(MAKE) $(TEST-TARGET)

.PHONY : tar
tar: 
    tar cfv $(GTP-TARGET).tar $(GTP-OBJS) $(OBJS)
    tar cfv $(TEST-TARGET).tar $(TEST-OBJS) $(OBJS)
Community
  • 1
  • 1
Imran
  • 12,950
  • 8
  • 64
  • 79