【问题标题】:How do you tell Make to look into a separate object dir?你如何告诉 Make 查看一个单独的对象目录?
【发布时间】:2020-09-30 01:21:45
【问题描述】:

最终更新:

感谢@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))

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

  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;
}

【问题讨论】:

  • 您的EXET 仍然依赖于OBJS,而不是依赖于您添加前缀的OSRC。如果没有任何东西依赖于OSRC,它永远不会被使用。
  • @无用 谢谢!那解决了!太美了!

标签: c++ makefile


【解决方案1】:

这里有很多问题,一些是真正的问题,一些只是没有用的东西。

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

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

vpath %.o out

这没有用。您永远不应该使用 vpath 来查找目标。您只能使用它来查找源文件。此外,您似乎从未在 makefile 的其他任何地方使用 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,那将无法正常工作。

【讨论】:

  • 我用新编辑的 Makefile 更新了原始帖子。问题仍然存在,因为对象被编译到工作区文件夹中,而不是预期的 ./obj 目录。有什么建议吗?
  • 问题解决了!感谢您的广泛解释和建议!
猜你喜欢
  • 1970-01-01
  • 2012-11-11
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 1970-01-01
  • 2011-07-30
相关资源
最近更新 更多