【问题标题】:Makefile not allowing me to specify directory for object filesMakefile 不允许我为目标文件指定目录
【发布时间】:2016-06-04 17:00:05
【问题描述】:

我的 Makefile 位于当前工作目录中。我正在尝试将我的所有目标文件放在目录./bin/obj 中,并将我的可执行文件放在目录./bin 中。但是,当我按照此处描述的方法进行操作时:How to place object files in separate subdirectory 和其他几个 * 问题,我无法将我的 *.o 文件写入所需的目录;它们是在包含我的 Makefile 的目录中创建的。下面是我的 Makefile 的摘录(点只是更多源文件的规则,为简洁起见省略)。请注意,在我尝试更改输出目录之前,Makefile 一直有效。

CXX=g++
CXXFLAGS=-O0 -march=native -std=c++11 -fopenmp -isystem /usr/local/include/eigen3
LINKFLAGS=-O0 -march=native -std=c++11 -fopenmp -isystem /usr/local/include/eigen3

SRC=src
BIN=bin
OBJ=$(BIN)/obj

BAREBONES=$(SRC)/universal.h $(SRC)/parameters.h
HEADERS=$(wildcard *.h)
ALLOBJS=$(OBJ)/assignDomain.o $(OBJ)/assignDomains.o ...    
all: $(BIN)/ngl.x

$(OBJ)/assignDomain.o: $(BAREBONES) $(SRC)/assignDomain.cpp $(SRC)/Domain.h $(OBJ)/Domain.o $(SRC)/Kingdom.h $(OBJ)/Kingdom.o $(SRC)/Sp.h $(OBJ)/Sp.o
    $(CXX) $(CXXFLAGS) -c $(SRC)/assignDomain.cpp

$(OBJ)/assignDomains.o: $(BAREBONES) $(OBJ)/assignDomain.o $(SRC)/assignDomains.cpp $(SRC)/Domain.h $(OBJ)/Domain.o $(SRC)/Kingdom.h $(SRC)/Sp.h $(OBJ)/Sp.o
    $(CXX) $(CXXFLAGS) -c $(SRC)/assignDomains.cpp

#...more rules...

$(BIN)/ngl.x: $(BAREBONES) $(ALLOBJS) $(wildcard *.h)
    $(CXX) $(ALLOBJS) $(LINKFLAGS) -o $(BIN)/ngl.x

#...more rules...

clean:
    rm -f $(OBJ)/*.o $(OBJ)/*.gch $(BIN)/ngl.x

.phony: all clean

输出如下:

/usr/local/include/eigen3 -c ./src/assignDomain.cpp
g++ -O0 -march=native -std=c++11 -fopenmp -isystem /usr/local/include/eigen3 -c ./src/assignDomains.cpp
g++ -O0 -march=native -std=c++11 -fopenmp -isystem /usr/local/include/eigen3 -c ./src/evict.cpp
g++ ./bin/obj/assignDomain.o ./bin/obj/assignDomains.o /usr/local/include/eigen3 -o ./bin/ngl.x
g++: error: ./bin/obj/assignDomain.o: No such file or directory
g++: error: ./bin/obj/assignDomains.o: No such file or directory

#...same error, for the other rules...

g++: fatal error: no input files
compilation terminated.
Makefile:94: recipe for target 'bin/ngl.x' failed
make: *** [bin/ngl.x] Error 1

【问题讨论】:

    标签: c++ makefile


    【解决方案1】:

    要明确一点:make 中没有内置规则知道如何在一个目录中编译源文件并将目标文件放入不同的目录中。如果你想这样做,你必须编写自己的规则。当您编写自己的规则时,您必须提供 -o 选项:编译器 无法知道您在 makefile 中指定了不同的输出目录,除非你用-o 标志告诉它。编译器不会解析你的 makefile!

    你可以这样写一个模式规则:

    $(OBJ)/%.o : $(SRC)/%.c
            $(CXX) $(CXXFLAGS) -c $< -o $@
    

    那么您不需要任何明确的规则,尽管您确实需要定义先决条件。关于您的 makefile 的其他说明:

    • 让 .o 文件依赖于其他 .o 文件是不正确的。
    • 让可执行文件依赖于头文件是不正确的。

    目标文件列出源文件和头文件作为先决条件。可执行文件列出目标文件(和库,如果有的话)作为先决条件。你应该这样写你的先决条件:

    $(OBJ)/assignDomain.o: $(SRC)/assignDomain.cpp $(BAREBONES) $(SRC)/Domain.h $(SRC)/Kingdom.h $(SRC)/Sp.h
    $(OBJ)/assignDomains.o: $(SRC)/assignDomains.cpp $(BAREBONES) $(SRC)/Domain.h $(SRC)/Kingdom.h $(SRC)/Sp.h
      ...other prerequisites...
    $(BIN)/ngl.x: $(ALLOBJS)
            $(CXX) $^ $(LINKFLAGS) -o $@
    

    【讨论】:

      【解决方案2】:

      您的显式编译规则禁用 Make 了解如何编译子目录中的文件,因此您得到的正是您的规则所说的内容,仅此而已。你没有指定-o 选项,所以你没有告诉g++ 将输出文件放在哪里;所以它遵循其内置默认值,并简单地创建一个文件./a.out (!)。

      最直接的解决方案是不覆盖内置规则。 Make 已经知道如何从具有相同基本名称的.cpp 文件创建.o 文件;您只需要在Makefile 中声明依赖项和标志。

      为了便于阅读,我已将共享依赖项重构为一个单独的变量。

      SHAREDDEPS := $(SRC)/Domain.h $(OBJ)/Domain.o \
          $(SRC)/Kingdom.h \
          $(SRC)/Sp.h $(OBJ)/Sp.o
      
      $(OBJ)/assignDomain.o: $(BAREBONES) $(SRC)/assignDomain.cpp \
              $(SHAREDDEPS) $(OBJ)/Kingdom.o
          # No $(CXX) anything here!
      
      $(OBJ)/assignDomains.o: $(BAREBONES) $(OBJ)/assignDomain.o \
              $(SRC)/assignDomains.cpp $(SHAREDDEPS)
          # Here either!
      

      为了清晰起见,我将依赖项包装在多行中(请注意第一行的最后一个反斜杠),但您应该注意到它们是单个逻辑行,并且 指定依赖项,而不是如何实际编译任何东西。

      尚不清楚依赖的*.o 文件应该如何计算在内;根据您要删除的显式规则,我的猜测是这些实际上并未在编译中使用,因此实际上并不是真正的依赖项。

      【讨论】:

      • 这是让它工作的最后努力,但无论我是否放入它,我都会遇到同样的错误。为了清楚起见,我现在已将其删除。感谢您指出这一点。
      • 嗯?现在您有了一个没有-o 选项的规则。否则,g++ 将不会创建您要求的文件。 (但最简单的解决方案是不覆盖内置规则,只需添加您需要的依赖项和标志。)
      • 原谅我的无知,但我认为我不必写 -o *** 因为规则本身的存在说明了 *.o 要创建什么。我链接的 SO 问题不使用 -o ...
      • 如果你重写 Make 的规则,你必须放入编译器需要的东西。但简单的解决方案是不覆盖 Make 的内置规则。更新了答案。
      • 谢谢。不过,您的代码不包含 -c。为什么是这样? make 怎么知道要编译哪个.cpp 文件呢?