【问题标题】:g++ - not reflecting changes made in source fileg++ - 不反映源文件中所做的更改
【发布时间】:2022-01-11 11:06:50
【问题描述】:

我正在构建一个结构如下的项目:

- Makefile
- main.cpp
- util.h
- subsrc/
  - one.cpp
  - two.cpp

我将 Makefile 设置为输出到构建目录:

all: $(BIN_FILE)

$(BIN_FILE): $(OBJ_FILES)
    mkdir -p $(BIN_DIR)
    g++ $^ -o $@

$(OBJ_DIR)/%.o: %.cpp
    mkdir -p $(OBJ_DIR)
    g++ -c $^ -o $@

$(OBJ_DIR)/subsrc/%.o: subsrc/%.cpp
    mkdir -p $(OBJ_DIR)/subsrc
    g++ -c $^ -o $@

clean:
    rm -rf $(BUILD_DIR)/*

但是,当我手动运行 g++ 时,我也看到了这个问题,所以我认为它与 Makefile 无关。

我可以从干净构建,一切正常。问题是当我更改其中一个 subsrc 文件并尝试通过 make 或自己运行这些命令重新编译时:

g++ -c subsrc/one.cpp -o build/obj/subsrc/one.o
g++ build/obj/main.o build/obj/subsrc/one.o build/obj/subsrc/two.o -o build/bin/prog

如果我这样做,在 one.cpp 中所做的更改不会反映在二进制输出中。如果我重新编译main.cpp(或者,当然,整个项目),它工作正常。这不是 g++ 没有正确覆盖文件的问题,因为即使我在运行上述命令之前rm build/obj/subsrc/one.o 和/或rm build/bin/prog,我仍然看不到更改。这对我来说毫无意义,我也不知道发生了什么。

编辑:我在这里上传了一个最小的可重现示例。 https://github.com/scatter-dev/so_70242118_min_repro

复制说明:

  1. 使用make 或运行上面的 g++ 命令进行构建。
  2. 运行程序以确保它已正确构建。
  3. one.cpp 中更改doWork 函数的输出。保存到磁盘。
  4. 重新运行 make 并注意 one.o 文件已重新编译,prog 已使用链接器重新创建。
  5. 再次运行程序,看到输出没有变化。

在评论者的建议下,我在步骤 1 和步骤 4 之间检查了 one.oprog 的 md5sum,它们确实是相同的。 即使我在重新编译之前删除了one.o,情况仍然如此。是的,我确信 one.cpp 正在保存到磁盘(它的 md5sum 确实发生了变化,而且 make clean && make 将使用新的更改进行编译)。

【问题讨论】:

  • 你怎么知道修改没有反映在二进制文件中?
  • 请显示minimal reproducible example。更改可能在头文件中吗?
  • 一个常见的原因是更改只在编辑器中,并没有保存到磁盘。
  • 您可以在更改前后检查 build/bin/prog 的 md5sum,看看它是否正在更新。对 one.o 和 one.cpp 执行相同的操作。这可能会缩小您对问题发生位置的搜索范围(注意:假设 one.cpp 中没有 __DATE__ 字符串...)
  • @AlanBirtles 我添加了一个最小的可重现示例。更改不在头文件中。

标签: c++ makefile g++


【解决方案1】:

您的问题是您的代码写得很奇怪,因此您的 makefile 不完整。

在你的 main.cpp 中你有:

#include "subsrc/derived.h"

这很好,但在那个标题中你有:

#include "one.cpp"
#include "two.cpp"

这是非常奇怪的。您几乎不想在其他源文件(或头文件)中包含 .cpp 文件。这只是个坏主意。

在这种情况下,您程序的所有内容都包含在main.cpp 中,因此会出现在您的main.o 文件中。链接其他对象(one.otwo.o)是无用且不必要的:它们被忽略了。

但是,在您的 makefile 中,您没有将 one.cpptwo.cpp 列为 main.o 的先决条件,这意味着当您修改这些源文件时,main.o 不会更新,因此不会发生任何变化。如果删除 main.o,则会重新编译它并获得新的行为。

预计到达时间

你有两个选择:

您可以将类的声明放入derived.h 并将doWork() 方法的定义放入.cpp 文件中。看起来像这样:

$ cat main.cpp
#include "subsrc/derived.h"
...

$ cat subsrc/derived.h
#include "../base.h"
class Derived1 : public Base {
public:
    void doWork();
};
class Derived2 : public Base {
public:
    void doWork();
};

$ cat subsrc/one.cpp
#include <iostream>
#include "derived.h"
void Derived1::doWork() {
    std::cout << "I'm Derived1" << std::endl;
}

$ cat subsrc/two.cpp
#include <iostream>
#include "derived.h"
void Derived2::doWork()
{
    std::cout << "I'm Derived2" << std::endl;
}

$ cat Makefile
...
$(OBJ_DIR)/main.o : base.h subsrc/derived.h
$(OBJ_DIR)/subsrc/one.o: base.h subsrc/derived.h
$(OBJ_DIR)/subsrc/two.o: base.h subsrc/derived.h

或者您可以按照您的方式内联头文件中的所有内容(但如果这些文件包含类声明,您真的不想使用.cpp 扩展名来命名这些文件)。或者您可以只拥有一个derived.h,然后将one.cpptwo.cpp 完全丢弃。

但你需要在 makefile 中添加先决条件:如果你保留多个头文件,那么main.o 必须依赖它们。

【讨论】:

  • 这是有道理的,但是写这个的正确方法是什么?如果我更改 derived.h 以声明类而不是包含它们,我会得到不完整的类型错误,这是有道理的,但我只想使用在基类中定义的函数,所以我不应该定义完整的类型。另外,如果我对derived.h进行更改,会不会出现同样的问题?
  • 是的,如果您希望 makefile 工作,您必须列出先决条件。如果你愿意,有一些方法可以让 make 自动生成这些依赖项。
  • 关于derived.h的内容,我想你想要的是main.cpp包含base.h,然后one.cpptwo.cpp也包含base.h,就是这样它。你根本不需要derived.h
  • 好吧,这又回到了我最初创建 derived.h 的全部原因 - 我不知道如何在 main.cpp 中创建派生类的实例,除非我包含整个类不知何故。即使我声明它们是从Base 派生的,它仍然会抱怨类型不完整,即使无论如何都没有明确定义的构造函数,而且我只使用Base 中定义的函数。
  • 感谢编辑!必须在每个派生类中声明 doWork() 似乎很麻烦,但我看到它会起作用。我想我会就此提出另一个问题,因为我最初的问题现在已经得到回答。但我计划有几个这样的派生类,并且我希望尽可能保持代码 DRY,所以我很想对此进行更深入的讨论。
猜你喜欢
  • 2013-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-11
  • 2019-03-31
  • 1970-01-01
  • 2021-01-12
相关资源
最近更新 更多