【问题标题】:Makefile from one source Directory trying to access a cpp file from other Directory来自一个源目录的 Makefile 试图从其他目录访问 cpp 文件
【发布时间】:2017-08-25 17:09:48
【问题描述】:

我在为位于 2 个不同目录中的 cpp 文件创建对象时遇到问题,如下所示:

- Project-xyz
  - Hello
    - Hello.cpp
  - World
    - Main.cpp
    - World.cpp
    - Makefile

为了更清楚,这里是代码的外观(只是一个虚拟代码)。

你好/Hello.h

#ifndef HELLO_H
#define HELLO_H

void HelloPrint();

#endif // HELLO_H

你好/Hell.cpp

#include <iostream>
#include "Hello.h"

void HelloPrint()
{
  std::cout <<"Hello" << std::endl;
}

world/World.h

 #ifndef WORLD_H
 #define WORLD_H 

 void WorldPrint();

 #endif // WORLD_H

world/World.cpp

#include <iostream>
#include "World.h"

void WorldPrint()
{
 std::cout <<"World!" << std::endl;
}

世界/Makefile 使用here 中给出的生成文件。哪个工作正常。但它不会创建任何 obj 文件。

# set non-optional compiler flags here
CXXFLAGS += -std=c++11 -Wall -Wextra -pedantic-errors

# set non-optional preprocessor flags here
# eg. project specific include directories
CPPFLAGS +=

# find cpp files in subdirectories
SOURCES := $(shell find . -name '*.cpp')
SOURCES += $(shell find ../hello -name '*.cpp')

# find headers
HEADERS := $(shell find . -name '*.h')

OUTPUT := HelloWorld

# Everything depends on the output
all: $(OUTPUT)

# The output depends on sources and headers
$(OUTPUT): $(SOURCES) $(HEADERS)
    $(CXX) $(CXXFLAGS) $(CPPFLAGS) -o $(OUTPUT) $(SOURCES)

clean:
     $(RM) $(OUTPUT)

我正在寻找一个使用 makefile 的解决方案,它在特定目录中生成 obj 文件,如 here 所示。

现在我正在尝试使用下面的 make 文件为 world/objdir/hello.o 中的 hello/hello.cpp 文件生成 obj 文件。这是行不通的。 如何让它工作?

世界/Makefile

GCC=g++
CPPFLAGS=-c -Wall
LDFLAGS=
OBJDIR=objdir
OBJ=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(wildcard *.cpp)))
TARGET=HelloWorld

.PHONY: all clean

all: $(OBJDIR) $(TARGET)

$(OBJDIR):
    mkdir $(OBJDIR)

$(OBJDIR)/%.o: %.cpp
    $(GCC) $(CPPFLAGS) -c $< -o $@
$(TARGET): $(OBJ)
    $(GCC) $(LDFLAGS) -o $@ $^
clean:
    @rm -f $(TARGET) $(wildcard *.o)
    @rm -rf $(OBJDIR)

当我尝试运行 make 时,它​​会抛出以下错误。

g++ -c -Wall -c main.cpp ../hello/Hello.cpp -o objdir/main.o
g++: fatal error: cannot specify -o with -c, -S or -E with multiple files
compilation terminated.
make: *** [objdir/main.o] Error 4

它无法为 hello/Hello.cpp 文件生成 obj 文件,因此抛出此错误。

感谢 makefile Masters 的任何帮助! :)

【问题讨论】:

  • -o 必须在链接“阶段”中使用:link: $(GCC) -o $(TARGET) $(OBJ)。顺便说一句,您的项目中没有 main
  • 对不起,我错过了添加。这只是一个简单的代码,我试图解释这个问题。在这里 #include "../hello/Hello.h" #include "World.h" int main() { HelloPrint();世界打印(); }

标签: c++ makefile


【解决方案1】:

添加 vpath %.cpp ../hello 以及 @gaoithe 的解决方案已经解决了这个问题。

vpath %.cpp ../hello
GCC=g++
CPPFLAGS=-c -Wall
LDFLAGS=
OBJDIR=objdir
OBJ=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(wildcard *.cpp)))
OBJ+=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(notdir $(wildcard ../hello/*.cpp))))
TARGET=HelloWorld

.PHONY: all clean

all: $(OBJDIR) $(TARGET)

$(OBJDIR):
    mkdir $(OBJDIR)

$(OBJDIR)/%.o: %.cpp
    $(GCC) $(CPPFLAGS) -c $< -o $@
$(TARGET): $(OBJ)
    $(GCC) $(LDFLAGS) -o $@ $^
clean:
    @rm -f $(TARGET) $(wildcard *.o)
    @rm -rf $(OBJDIR)

现在

$ ls objdir/
 Hello.o  main.o   World.o  

专家需要评估新的 makefile 并确认这是否是一个可扩展的解决方案?

我们非常感谢任何有关优化/增强当前 makefile 以及帮助减少构建时间的意见。

【讨论】:

    【解决方案2】:

    您不能一次编译多个 c 或 cpp 文件并为它们指定一个目标文件。另请参阅此问题Compile multiple C source fles into a unique object file 以下命令会给您错误:

    g++ -c -Wall -c main.cpp ../hello/Hello.cpp -o objdir/main.o
    

    以下编译为目标文件,在当前目录中为您提供 Main.o 和 Hello.o 和 World.o:

    g++ -I./hello -c -Wall -c world/Main.cpp hello/Hello.cpp world/World.cpp
    

    您可以逐个编译每个 cpp 文件并为每个文件生成目标文件,然后将它们链接在一起。这就是构建具有多个源代码文件的任何大型项目的方式。手动执行此操作:

    cd world
    g++ -I../hello -c -Wall -c Main.cpp -o objdir/Main.o
    g++ -c -Wall -c ../hello/Hello.cpp -o objdir/Hello.o
    g++ -c -Wall -c World.cpp -o objdir/World.o
    # link:
    g++ objdir/Main.o objdir/Hello.o objdir/World.o -o HelloWorld
    

    来自人 gcc

       -c  Compile or assemble the source files, but do not link.  The linking stage simply is not done.  The ultimate output is in the form of an object file for each source file.
    
       -o file
           Place output in file file.  This applies to whatever sort of output is being produced, whether it be an executable file, an object file, an assembler file or preprocessed C code.
    

    回答:我的意思是不直接回答 :-P 但这有点棘手。以下是您需要在 Makefile 中查看的内容:

    # you want to add reference to hello as include dir
    #  for compiling Main.cpp, In CPPFLAGS add -I ../hello
    
    # the OBJ list is missing a reference to files in hello directory:
    OBJ=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(wildcard *.cpp)))
    OBJ+=$(addprefix $(OBJDIR)/, $(patsubst %.cpp, %.o, $(notdir $(wildcard ../hello/*.cpp))))
    
    # no change needed for the %.cpp 
    # new rule needed to allow it to find hello dir:
    
    $(OBJDIR)/%.o: 
        $(GCC) $(CPPFLAGS) -c $< -o $@
    $(OBJDIR)/%.o: ../hello/%.cpp
        $(GCC) $(CPPFLAGS) -c $< -o $@
    

    阅读:https://www.gnu.org/software/make/manual/html_node/File-Name-Functions.html

    【讨论】:

    • 感谢您的意见。我尝试了您建议的更改,但仍然出现以下错误。 mkdir objdir g++ -c -Wall -c World.cpp -o objdir/World.o g++ -c -Wall -c main.cpp -o objdir/main.o make: *** No rule to make target 'objdir/Hello .o',HelloWorld' 需要。停止。这基本上意味着 Hello.o 仍然没有在 **"world/objdir" ** 目录下生成。还有其他建议吗?我很乐意尝试并回来。
    • 检查 $(OBJDIR)/%.o: ../hello/%.cpp 规则。你有它好吗?是否有 ../hello/Hello.cpp 文件?
    【解决方案3】:

    你想访问当前目录之上的目录很奇怪,因为你不能让文件名包含..

    你需要这个来编译多个具有相似名称的文件,例如:在World目录中你可能有另一个名为Hello/Hello.cpp的文件,那么你必须同时编译../Hello/Hello.cppHello/Hello.cpp。尽管它们是两个不同的文件,但无法生成具有不同名称的目标文件。您可以将 .. 替换为 UPPERDIRECTORY 之类的东西,但是如果您有另一个文件要编译名称为 UPPERDIRECTORY/Hello/Hello.cpp,那么您就完蛋了...

    无论如何,这是一个“临时修复”:

    CXX=g++
    CPPFLAGS=-Wall
    LDFLAGS=
    OBJDIR=objdir
    TARGET=HelloWorld
    
    # PLACE ALL NEEDED DIRECTORIES HERE
    # List of directories that contains all needed cpp files
    MODULES=. ../Hello
    
    # Get all cpp source file names
    SRCS=$(foreach module, $(MODULES), $(wildcard $(module)/*.cpp))   #*/weird SO comment syntax
    
    # Convert them to object file names + $(OBJDIR) prefix
    OBJS=$(SRCS:%.cpp=$(OBJDIR)/%.o)
    
    # Get all directories of $(OBJS) so we can create object files with same name
    # but locate in different dirs, for example:
    #             Hello.cpp -> objdir/Hello.o                      (need to create objdir/)
    #       Hello/Hello.cpp -> objdir/Hello/Hello.o                (need to create objdir/Hello/)
    #    ../Hello/Hello.cpp -> objdir/UPPERDIRECTORY/Hello/Hello.o (need to create objdir/UPPERDIRECTORY/Hello/)
    ALL_OBJ_DIRS=$(subst ..,UPPERDIRECTORY, $(sort $(dir $(OBJS))))
    
    .PHONY: all clean
    
    all: $(TARGET)
    
    # Rule to make all needed object directories
    $(ALL_OBJ_DIRS):
        @mkdir -p $@
    
    # Rule to make object files
    $(OBJDIR)/%.o: %.cpp | $(ALL_OBJ_DIRS)
    # We need to replace .. with UPPERDIRECTORY in .o (output) path,
    # but keep .cpp (input) path as it is
        $(CXX) -c $(CPPFLAGS) $< -o $(subst ..,UPPERDIRECTORY,$@)
    
    # Rule to make target file
    $(TARGET): $(OBJS)
    # We need to replace .. with UPPERDIRECTORY in all $(OBJS) paths
        $(CXX) $(subst ..,UPPERDIRECTORY,$^) $(LDFLAGS) -o $@
    
    clean:
        @rm -f $(TARGET)
        @rm -rf $(OBJDIR)
    

    如果我是你,我会将Makefile 放在所有 cpp 文件的顶部以避免..

    project/
      src/
        Makefile
        Hello/
          Hello.h
          Hello.cpp
        World/
          World.h
          World.cpp
          main.cpp
    

    编辑:您可以在目标文件名中将/ 替换为_,因此现在您可以在目标文件名中包含..

    GCC=g++
    CPPFLAGS=-Wall
    LDFLAGS=
    OBJDIR=obj
    TARGET=HelloWorld
    
    # Modules: directories that contains all needed cpp files
    MODULES=. ../Hello
    # Get all cpp source file names
    SRCS_WITHCURRENTDIR=$(foreach module, $(MODULES), $(wildcard $(module)/*.cpp))   #*/weird SO comment syntax
    SRCS=$(SRCS_WITHCURRENTDIR:./%=%) # remove ./
    # Convert them to object file names + $(OBJDIR) prefix
    OBJS=$(SRCS:%.cpp=$(OBJDIR)/%.o)
    OBJ_PREFIX=OBJ
    
    .PHONY: all clean
    
    all: $(OBJDIR) $(TARGET)
    
    $(OBJDIR):
        @mkdir -p $@
    
    $(OBJDIR)/%.o: %.cpp
        $(GCC) -c $(CPPFLAGS) $< -o $(addprefix $(OBJDIR)/$(OBJ_PREFIX),$(subst /,_,$(patsubst $(OBJDIR)/%,%,$@)))
    
    $(TARGET): $(OBJS)
        $(GCC) $(addprefix $(OBJDIR)/$(OBJ_PREFIX),$(subst /,_,$(patsubst $(OBJDIR)/%,%,$^))) $(LDFLAGS) -o $@
    
    clean:
        @rm -f $(TARGET)
        @rm -rf $(OBJDIR)
    

    make clean all给了

    g++ -c -Wall main.cpp -o obj/OBJmain.o
    g++ -c -Wall World.cpp -o obj/OBJWorld.o
    g++ -c -Wall ../Hello/Hello.cpp -o obj/OBJ.._Hello_Hello.o
    g++ obj/OBJmain.o obj/OBJWorld.o obj/OBJ.._Hello_Hello.o  -o HelloWorld
    

    obj/ 中的 3 个目标文件:OBJmain.oOBJWorld.oOBJ.._Hello_Hello.o

    【讨论】:

    • 谢谢 Tntxtnt。我完全同意你关于我被搞砸的观点! :) 只是为了说明为什么在“Hello”目录下不可能有一个makefile。基本上我正在使用 Apache thrift 自动生成一些代码,它会在“Hello”目录和“World”目录中生成一些代码,我将使用生成的“Hello.cpp”文件。所以我肯定不能在“Hello”目录中有一个makefile。但是我在项目的父目录中有一个 Makefile。哪个反过来应该递归调用各个子文件夹中的makefile。
    • 这有助于我以模块化方式构建代码,我不需要从顶层进行构建。相反,我可以在我的本地目录中构建。如果你有更好的方法来解决这个问题。我愿意接受你的建议。我会尝试您建议的解决方案并返回。
    • 我发现了类似的情况here。您可能需要在不同的目录结构中依赖单个 makefile。
    • 我认为你不能使用vpath 编译同名文件。如果没有这样的名称冲突,那很好。第二,虽然我认为您可以将输出 .o../Hello/Hello.o 重命名为例如 OBJ.._Hello_Hello.o(将 / 替换为 _ 并添加前缀 OBJ),以便您可以编译具有相同名称的文件。跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 2011-05-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    相关资源
    最近更新 更多