如何编写模式规则来分隔对象和源文件的位置?

时间:2016-08-25 13:02:39

标签: c++ c makefile

假设我有一个目录结构,如:

./Header
./Srcs
./makefile

文件夹./Header/的内容是两个头文件:

那么header1.h

#ifndef HEADER1_H
#define HEADER1_H
#include <stdio.h>

void func1();

#endif

header2.h

#ifndef HEADER2_H
#define HEADER2_H
#include <stdio.h>

void func2();

#endif

./Srcs/我有以下srcs:

src1.c

#include <header1.h>

void func1() {
   printf("func1()\n");
}

src2.c

#include <header2.h>

void func2() {
   printf("func2()\n");
}

的main.c

#include <header1.h>
#include <header2.h>

int main(int argc, char** argv) {
   func1();
   func2();
   return 0;
}

最后 makefile 如下所示:

CC=gcc
INCLUDE=-I./Header/
SRC_DIR=./Srcs/
SRC_LIST=$(addprefix $(SRC_DIR), main.c src1.c src2.c)
OBJ_LIST=$(addsuffix .o, $(basename $(SRC_LIST)))
OUTPUT=test

%.o : %.c
    $(CC) -c $< -o $@ $(INCLUDE)

all : $(OBJ_LIST)
    $(CC) -o $(OUTPUT) $(OBJ_LIST)

clean : $(OUTPUT) $(OBJ_LIST)
    rm -r $(OUTPUT) $(OBJ_LIST)

执行makefile:

gcc -c Srcs/main.c -o Srcs/main.o -I./Header/
gcc -c Srcs/src1.c -o Srcs/src1.o -I./Header/
gcc -c Srcs/src2.c -o Srcs/src2.o -I./Header/
gcc -o test ./Srcs/main.o ./Srcs/src1.o ./Srcs/src2.o

具体来说,由于我使用的模式规则,所有的对象文件都是在文件夹./Srcs/中生成的,我想要实现的是将所有目标文件放到最终输出的目录中在我的具体例子中,最终输出将与makefile所在的目录相同。

如何编写makefile以达到这个目标?

2 个答案:

答案 0 :(得分:0)

如果您想将构建输出放在与源相同的结构中,但是在构建目录中:

#PROJDIR is the folder where your Makefile is
PROJDIR := $(realpath $(CURDIR)/..)

#SOURCEDIR is the root folder where your sources sub-folder ares
SOURCEDIR := $(PROJDIR)/Sources

#BUILDDIR is the root folder where your objects will be
BUILDDIR := $(PROJDIR)/Build

#DIRS are the sub-folder, e.g. Sources/Folder1 and Build/Folder1
DIRS = Folder0 Folder1 Folder2

#Here we create a list of source and builder folders with full path
SOURCEDIRS = $(foreach dir, $(DIRS), $(addprefix $(SOURCEDIR)/, $(dir)))
TARGETDIRS = $(foreach dir, $(DIRS), $(addprefix $(BUILDDIR)/, $(dir)))

#We allow GNU make to search for the sources in all the sources folders
VPATH = $(SOURCEDIRS)

#We define a function to generate a Build/Folder1/%.o: %.c rule for each sub-folder
define generateRules
$(1)/%.o: %.c
    @echo Building $$@
    $(CC) -c  -o $$@ $$< 
endef

#Finally we generate the rule for each build folder
$(foreach targetdir, $(TARGETDIRS), $(eval $(call generateRules, $(targetdir))))

或者如果您想将所有构建项目放在构建目录中:

#PROJDIR is the folder where your Makefile is
PROJDIR := $(realpath $(CURDIR)/..)

#SOURCEDIR is the root folder where your sources sub-folder ares
SOURCEDIR := $(PROJDIR)/Sources

#BUILDDIR is the root folder where your objects will be
BUILDDIR := $(PROJDIR)/Build

#DIRS are the sub-folder, e.g. Sources/Folder1 and Build/Folder1
DIRS = Folder0 Folder1 Folder2

#Here we create a list of source with full path
SOURCEDIRS = $(foreach dir, $(DIRS), $(addprefix $(SOURCEDIR)/, $(dir)))

#We allow GNU make to search for the sources in all the sources folders
VPATH = $(SOURCEDIRS)

$(BUILDDIR)/%.o: %.c
    $(CC) -o $@ $<

答案 1 :(得分:0)

  

我想要实现的是将所有目标文件放入最终输出所在的目录中,在我的具体示例中,最终输出将位于makefile所在的同一目录中。

如果您将自己限制在标准make,但是您已经承诺使用GNU make,并且您已经使用了相应的功能,则会有点麻烦:模式规则。你想要的核心是这样的:

SRC_DIR = ./Srcs
OUTPUT_DIR = .
OBJ_LIST = $(addprefix $(OUTPUT_DIR)/, main.o src1.o src2.o)
OUTPUT = $(OUTPUT_DIR)/test

all : $(OUTPUT)

$(OUTPUT) : $(OBJ_LIST)
    $(CC) -o $@ $(OBJ_LIST)

# Here's the key:
$(OUTPUT_DIR)/%.o : $(SRC_DIR)/%.c
    $(CC) -c $< -o $@ $(INCLUDE)

clean :
    rm -rf $(OUTPUT) $(OBJ_LIST)

.PHONY: all clean

附加说明:

  • 除非另行指定,否则文件中的第一个目标是默认目标。这通常应该是构建整个项目的项目,通常命名为all

  • 您希望能够构建的每个目标都应命名为 目标。 all目标通常将所有期望的特定目标指定为依赖关系;它不应该直接构建它们。

  • clean目标(无论名称)不应该依赖于构建要清理的文件的目标!如果确实如此,那么在没有首次建造的情况下就无法进行清洁。

  • 至少在GNU make中,建议标记实际上并未构建名为PHONY的文件的人工目标,如上所示。

  • GNU make从.c构建.o的标准配方比您更灵活:

    $(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
  • GNU make将.o链接到程序中的标准方法也比你的更灵活:
    $(CC) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) -o $@
  • 如果您特别想要将构建的文件放入顶级源目录,那么您可以通过删除所有$(OUTPUT_DIR)内容来大大简化这些事情。