代码之家  ›  专栏  ›  技术社区  ›  TomáÅ¡ Zato

如何为临时目录中生成的obj文件创建makefile目标?

  •  0
  • TomáÅ¡ Zato  · 技术社区  · 4 年前

    我正在尝试再次学习使用makefiles。我的目录结构如下:

    build
      |- Makefile
      |- Project files, like visual studio project
    tmp
      |- $(os name)_$(compiler)
        |- *.o and any other intermediate files
    bin
      |- $(os name)_$(compiler)
        |- the compiled binary executable
        |- debug info, if any
    src
      |- *.cpp, *.h
    

    所以我做了这个 Makefile 经过一番谷歌搜索和试验:

    CXX      := g++
    CXXFLAGS := -std=c++17 -Wall
    LDFLAGS  := -lpthread
    ASSUMED_OS := unknownOS
    SRC_DIR := ../src
    
    EXTENSION :=
    
    # OS detection and extension assignment for windows redacted for brevity
    # ... detect OS, set extension to .exe if windows
    
    COMPILE_PATH := $(ASSUMED_OS)_gcc
    TMP_DIR := ../tmp/$(COMPILE_PATH)
    
    TARGET_DIR := ../bin/$(COMPILE_PATH)
    TARGET := $(TARGET_DIR)/MyProject$(EXTENSION)
    
    # $(wildcard *.cpp /xxx/xxx/*.cpp): get all .cpp files from the current directory and dir "/xxx/xxx/"
    SRCS := $(wildcard $(SRC_DIR)/*.cpp)
    # $(patsubst %.cpp,%.o,$(SRCS)): substitute all ".cpp" file name strings to ".o" file name strings
    OBJS := $(patsubst %.cpp,%.o,$(SRCS))
    
    all: $(TARGET)
    
    # Compile all obj files into an executable
    $(TARGET): $(OBJS)
        @mkdir -p $(@D)
        $(CXX) -o $@ $^ $(LDFLAGS)
    # Compile all CPP files in separate obj files
    %.o: %.cpp
        @mkdir -p $(@D)
        $(CXX) $(CXXFLAGS) -c $< -o $@
    clean:
        rm -rf $(TARGET) ../*.o
    
    .PHONY: all clean
    

    这是有效的,但它创造了 .o 源旁边的文件。

    所以我试着改变 OBJS 变量,并替换路径:

    OBJS := $(patsubst %.cpp,%.o,$(SRCS))
    # Substitute src path for tmp directory path
    OBJS := $(patsubst $(SRC_DIR)/%,$(TMP_DIR)/%,$(OBJS))
    

    但后来我得到了一个错误:

    make: *** No rule to make target '../tmp/linux_gcc/Job.o', needed by '../bin/linux_gcc/MyProject'.  Stop.
    

    所以我尝试创建一个不同的目标,我认为它可以映射应该在 ../tmp/linux_gcc 到源名称 ../src :

    # $(wildcard *.cpp /xxx/xxx/*.cpp): get all .cpp files from the current directory and dir "/xxx/xxx/"
    SRCS := $(wildcard $(SRC_DIR)/*.cpp)
    # $(patsubst %.cpp,%.o,$(SRCS)): substitute all ".cpp" file name strings to ".o" file name strings
    OBJS := $(patsubst %.cpp,%.o,$(SRCS))
    # Substitute src path for tmp directory path
    OBJS := $(patsubst $(SRC_DIR)/%,$(TMP_DIR)/%,$(OBJS))
    all: $(TARGET)
    
    # Compile all obj files into an executable
    $(TARGET): $(OBJS)
        @mkdir -p $(@D)
        $(CXX) -o $@ $^ $(LDFLAGS)
    # Compile all CPP files in separate obj files
    $(OBJS): $(SRCS)
        @mkdir -p $(@D)
        $(CXX) $(CXXFLAGS) -c $< -o $@
    

    但当我这样做的时候,make总是编译相同的 cpp 每个的文件 .o 文件:

    g++ -std=c++17 -Wall -c ../src/Job.cpp -o ../tmp/linux_gcc/Job.o
    g++ -std=c++17 -Wall -c ../src/Job.cpp -o ../tmp/linux_gcc/Worker.o
    g++ -std=c++17 -Wall -c ../src/Job.cpp -o ../tmp/linux_gcc/WorkManager.o
    

    我想我已经很接近了,但我该怎么做呢?我认为在compile命令中 $(CXX) $(CXXFLAGS) -c $< -o $@ 我需要别的东西 $< 输入文件,但我不确定是什么。

    0 回复  |  直到 4 年前
        1
  •  0
  •   Beta    4 年前

    在您的第一个生成文件中:

    SRC_DIR := ../src
    ...
    SRCS := $(wildcard $(SRC_DIR)/*.cpp)
    ...
    OBJS := $(patsubst %.cpp,%.o,$(SRCS))
    ...
    %.o: %.cpp
        @mkdir -p $(@D)
        $(CXX) $(CXXFLAGS) -c $< -o $@
    

    是的,这个 “在源旁边创建.o文件”。 如果你有消息来源 foo.cpp ,变量 OBJS 将包含 ../src/foo.o ,规则将建立它。

    在第二次尝试中,您更改了对象,但没有更改规则,因此Make knows构建的唯一方法 ../tmp/linux_gcc/Job.o 来自 ../tmp/linux_gcc/Job.cpp ,它不存在。

    在你的第三次尝试中,你有正确的想法,但你的规则中有一个缺陷:

    $(OBJS): $(SRCS)
        @mkdir -p $(@D)
        $(CXX) $(CXXFLAGS) -c $< -o $@
    

    变量 $(OBJS) 是对象的列表, $(SRCS) 是一个来源列表,所以 每个 对象依赖于 全部的 来源。以及自动变量 $< 展开到先决条件列表中的第一项,在本例中为 ../src/Job.cpp ,所以无论您选择哪个对象,Make都会尝试通过编译该源代码来构建它。

    这是一份 static pattern rule :

    $(OBJS): $(TMP_DIR)/%.o: $(SRC_DIR)/%.cpp
        @mkdir -p $(@D)
        $(CXX) $(CXXFLAGS) -c $< -o $@