你如何告诉 Make 查看单独的对象目录?



最后更新:

多亏了@Useless,问题解决了!

$(EXET): $(OBJS)更改为$(EXET): $(OSRC)

<小时 />

更新:2020 年 9 月 29 日

按照@MadScientist的建议,我编辑并从Makefile中删除了不必要的部分。但是,行OSRC := $(addprefix $(ODIR)/, $(OBJS))似乎没有任何影响,因为*.o对象文件现在已编译到工作区文件夹,而不是专用./obj文件夹中,这是我的源问题。

#############################################################
## THIS IS A TEST PROJECT                                  ##
#############################################################
vpath %.cpp src
vpath %.h hdr
#############################################################
## BUILD TASKS                                             ##
#############################################################
HDIR            := hdr
#SRCS           := src
ODIR            := obj
EXET            := a
OBJS            := main.o mainhdr.o testcode.o
OSRC            := $(addprefix $(ODIR)/, $(OBJS))
CXX             := g++
CXXFLAGS        := -I$(HDIR) -g -Wall -std=c++17
## BUILD DIRECTIVE:
all: $(EXET)
$(EXET): $(OBJS)
$(CXX) $(CXXFLAGS) $^ -o $@
$(ODIR)/%.o: %.cpp
$(CXX) -c $(CXXFLAGS) $^ -o $@

#############################################################
## CLEAN TASK                                              ##
#############################################################
.PHONY: clean
clean:
rm -r $(EXET) *.o

原始问题:

我一直在拔头发,试图让 Make 从特定目录链接预构建的对象,但无济于事。我让我的 Makefile 将目标文件编译到不同的目录中,但在那之后我基本上卡住了。

#############################################################
## THIS IS A TEST PROJECT                                  ##
#############################################################
.SUFFIXES:
.SUFFIXES: .o .h .cpp
vpath %.cpp src
vpath %.h hdr
vpath %.o out
%.h : %.cpp
#############################################################
## BUILD TASKS                                             ##
#############################################################
HDIR            := hdr
#SRCS            := src #UNUSED
ODIR            := obj
EXE             := a
OBJS            := main.o mainhdr.o testcode.o
## This line added in reference to this thread:
## https://stackoverflow.com/questions/9178285/how-can-makefile-use-separate-directories-for-source-code-and-binaries
## But it doesn't seem to work either
OSRC            := $(addprefix $(ODIR)/, $(OBJS))
CPP             := g++
CPPFLAGS        := -I$(HDIR) -c -g -Wall -std=c++17
## BUILD DIRECTIVE:
all: $(EXE)
## PROBLEM: Doesn't seem to work when prefixed with the output dir:
#$(EXE): $(ODIR)/$(OBJS)
$(EXE): $(OBJS)
$(CPP) -o %@ %^
## This compiles the objects into the output dir just fine.
%.o: %.cpp
#       $(CPP) $(CPPFLAGS) $< -o $@
$(CPP) $(CPPFLAGS) $< -o $(ODIR)/$@
#############################################################
## CLEAN TASK                                              ##
#############################################################
.PHONY: clean
clean:
rm -r $(EXE) $(ODIR)/*.o
## End of Makefile ##########################################
到目前为止

调试工作:

  1. 我将.o文件类型添加到.SUFFIXES:但我怀疑它是否做任何事情。Make 似乎只关心标头和源文件。

  2. 我做了更多的研究,后来我添加了这部分:

## This line added in reference to this thread:
## https://stackoverflow.com/questions/9178285/how-can-makefile-use-separate-directories-for-source-code-and-binaries
## But it doesn't seem to work either
OSRC            := $(addprefix $(ODIR)/, $(OBJS))

但是,它似乎根本没有任何效果。制作也找不到对象目录。

  1. 尽管它有效,但这行不知何故困扰着我:
$(CPP) $(CPPFLAGS) $< -o $(ODIR)/$@

难道不应该以某种方式修复它以与$(addprefix)指令保持一致吗?


来自 GNU Make 的快速参考,以防我错过了什么:

$@ target
%< first prerequisite
$^ prerequisites

以下是简单的测试代码:

// cat hdr/mainhdr.h
#ifndef MAINHDR_H
#define MAINHDR_H
#include "testcode.h"
#define BIGCONST 777
void testFunc();
#endif
// cat hdr/testcode.h
#ifndef TESTCODE_H
#define TESTCODE_H
#include <iostream>
#include <string>
#define SMALLCONST 25
class NameTag {
private:
std::string f;
std::string l;
public:
NameTag(std::string first="", std::string last="");
void setFirstN(std::string );
void setLastN(std::string );
std::string getFirstN() const;
std::string getLastN() const;
void operator=(const NameTag &);
};
#endif
// cat src/main.cpp
#include "mainhdr.h"
int main () {
testFunc();
return 0;
}
// cat src/mainhdr.cpp
#include "mainhdr.h"
void testFunc() {
std::cout << "THIS IS A TEST STRING!n";
std::cout << "BIGCONST = " << BIGCONST << std::endl;
std::cout << "SMALLCONST = " << SMALLCONST << std::endl;
NameTag someDude("Good", "Dude");
std::cout << someDude.getFirstN() << " " << someDude.getLastN() << std::endl;
}
// cat src/testcode.cpp
#include "testcode.h"
NameTag::NameTag(std::string first, std::string last) {
f = first;
l = last;
}
void NameTag::setFirstN(std::string first) {
f = first;
}
void NameTag::setLastN(std::string last) {
l = last;
}
std::string NameTag::getFirstN() const {
return f;
}
std::string NameTag::getLastN() const {
return l;
}
void NameTag::operator=(const NameTag &right) {
f = right.f;
l = right.l;
}

这里有许多问题,一些真正的问题和一些没有用的东西。

.SUFFIXES:
.SUFFIXES: .o .h .cpp

这些行是无用的,因为您使用的是模式规则。 仅当您编写后缀规则时才使用.SUFFIXES

vpath %.o out

这是没有用的。 您永远不应该使用 vpath 来查找目标。 您只能使用它来查找源文件。 此外,您似乎从未在生成文件的其他任何地方使用out

%.h : %.cpp

我不确定这是想做什么,但它实际上什么也没做。

OBJS := main.o mainhdr.o testcode.o
OSRC := $(addprefix $(ODIR)/, $(OBJS))

这很好,但它毫无用处,因为您从未在 makefile 中的其他任何地方实际使用该变量OSRC

CPP             := g++
CPPFLAGS        := -I$(HDIR) -c -g -Wall -std=c++17

仅供参考,makefile 中的常见做法是对C++编译器使用CXX,而不是CPPCPP(通常的makefile用语)是指C预处理器。 当然,这只是一个建议。

## PROBLEM: Doesn't seem to work when prefixed with the output dir:
#$(EXE): $(ODIR)/$(OBJS)

当然这行不通。 所以$(ODIR)obj$(OBJS)main.o mainhdr.o testcode.o.这意味着:

$(ODIR)/$(OBJS)

显然扩展到这个:

obj/main.o mainhdr.o testcode.o

这显然不是你想要的。 上面的版本,使用addprefix将目录附加到变量中的每个单词前面,而不仅仅是将其放在整个变量扩展的开头,这就是您想要的。

$(EXE): $(OBJS)
$(CPP) -o %@ %^

你把OSRC设置为正确的事情,但你仍然在这里使用$(OBJS),所以它对你没有帮助。

另外,显然你的意思是这里的$@$^而不是%@%^.

## This compiles the objects into the output dir just fine.
%.o: %.cpp
$(CPP) $(CPPFLAGS) $< -o $(ODIR)/$@

不,它没有。 我的意思是,是的,它确实将对象编译到输出目录中,但不是"很好"。 任何时候你有一个配方构建了一些不完全$@的其他文件,那个配方是错误的。 在这里,你的食谱是$(ODIR)/$@,而不是$@,所以这绝对是错误的。

你想这样写:

$(ODIR)/%.o: %.cpp
$(CPP) $(CPPFLAGS) $< -o $@

另外请注意,IMO 将像-c这样的输出控制标志放入CPPFLAGS(或使用标准名称,CXXFLAGS)是一种糟糕的做法。 您应该将这样的标志直接放入配方中,而不是在变量中。

通常,明智的做法是在链接行和编译行中包含$(CXXFLAGS)变量,以便看到与链接器相关的任何编译器标志。 如果变量也包含-c,则不起作用。