【问题标题】:Using GNU Make with subdirectories使用带有子目录的 GNU Make
【发布时间】:2015-08-11 18:34:55
【问题描述】:

我想知道在带有子目录的项目中使用 Make 有哪些不同的方法,它们的优点/缺点是什么,但从来没有看到好的总结或食谱。

我在研究中主要看到了“递归”和“单个 makefile”方法,但还有其他方法吗?

我还假设不仅有一种“递归”或“单个 makefile”方法,而且有几种,所以有人可以总结一下吗?

对于我的特殊情况,我想要一个如下所示的目录架构:

.
├── build
│   ├── *.d
│   ├── *.o
|   ├── subdir1
|   │   ├── *.d
|   │   └── *.o
|   └── subdir2
|       ├── *.d
|       ├── *.o
|       └── subdir3
|           ├── *.d
|           └── *.o
├── include
│   ├── *.h
│   └── *.h
├── Makefile
└── src
    ├── *.c
    ├── *.h
    ├── subdir1
    │   ├── *.c
    │   └── *.h
    └── subdir2
        ├── *.c
        ├── *.h
        └── subdir3
            ├── *.c
            └── *.h

我应该选择哪种解决方案?可能是允许同名的源文件?

【问题讨论】:

  • 反对递归 make 的基本论点在 Recursive Make Considered Harmful(pdf) 论文中列出。 This pagePainless non-recursive Make 文章一样讨论了实现非递归 make 系统。我相信Multi-Architecture Builds 文章也有一些相关的事情要说。
  • 递归和单makefile几乎涵盖了它,但当然你可以混合使用它们——让主makefile调用src/Makefile,然后它自己处理整个src/树。跨度>
  • 两种方式都有争论。使用非递归 make 的启动时间可能会很昂贵(当我第一次构建 glibc 时,需要将近一个小时才能启动)。提倡非递归的论文使这个问题不予考虑或完全忘记提及它。鉴于此,这个问题是题外话。

标签: makefile gnu-make


【解决方案1】:

您的项目设置非常基础,您的 Makefile 也应该如此:

SRC_DIR := src
BLD_DIR := build

SRC := $(shell find $(SRC_DIR) -name "*.c")
OBJ := $(SRC:$(SRC_DIR)/%.c=$(BLD_DIR)/%.o)
DEP := $(OBJ:.o=.d)

CPPFLAGS := -MMD -MP # enable auto-dependency generation
CFLAGS   := -Wall -W -pedantic

.PHONY: all clean

all: $(OBJ)

clean:
    $(RM) -r $(BLD_DIR)

.SECONDEXPANSION:
$(BLD_DIR)/%.o: $(SRC_DIR)/%.c | $$(@D)/ # First check that the destination directory exists
    $(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<

%/:
    mkdir -p $* # -p flag necessary for recursive directory creation

ifeq "$(MAKECMDGOALS)" ""
-include $(DEP)
endif

这里的想法是使用 find 命令递归地列出源文件,为 make 提供适当的模式规则以在正确的位置进行编译,并将正确的预处理器文件传递给编译器以启用自动依赖生成。


使用 GIT Bash shell 和以下目录结构在 Windows 8.1 下使用 GNU Make 4.1 进行测试:

.
├── Makefile
└── src
    ├── test.c
    ├── test1.c
    └── subdir1
        └── test.c

【讨论】:

  • 非常感谢您的回答,因为它可以解决我的问题。我对这种方法的唯一担心是我没有在某些特定的地方定义我的源文件,而是只是把所有的东西都拿走了。有时这是不可取的。
  • 列出源文件的方式最终取决于您,您可以手动完成,但即使您确实只需要列出特定文件,我的技术也可以让您编写一个小的单个 Makefile并且您需要更新的是SRC,因为每个项目结构更改都已涵盖,而您的解决方案需要为每个目录创建一个文件,并且更改您的不同包含在整个文件系统中。对我来说,我的 Makefile 更加模块化,甚至比您的答案所宣传的所有内容都要小。
【解决方案2】:

在阅读Recursive Make Considered Harmful 之后,我想出了一个非常简单和模块化的方法来实现这一点,通过在所有子目录中包含相互包含并包含在主 makefile 中的文件:

CXX         := gcc

SRCDIR          := src
OBJDIR          := build

# These lines are needed to set immediate evaluation for
# these variables, instead of deferred evaluation which is unsuitable.
SRCS            :=
SUBDIRS         :=
CFLAGS          :=
LDFLAGS         :=

include $(SRCDIR)/module.mk

OBJS            := $(addprefix $(OBJDIR)/, $(SRCS:.c=.o))

SRCS            := $(addprefix $(SRCDIR)/, $(SRCS))

DEPS            := $(OBJS:.o=.d)

TMPS            := $(OBJS) $(OBJS:.o=.d)

CFLAGS          += -MD
debug: CFLAGS   += -g -g3 -ggdb
CFLAGS          += $(addprefix -I./$(SRCDIR)/, $(SUBDIRS))

LDFLAGS         += -lsomelib
debug: LDFLAGS      += -g -g3 -ggdb

NAME            := yolo

all: $(NAME)

debug:  re

-include $(DEPS)

$(OBJDIR)/%.o: $(SRCDIR)/%.c
    $(COMPILE.c) $(OUTPUT_OPTION) $<

$(NAME): $(OBJS)
    @$(CXX) $(OBJS) -o $(NAME) $(LDFLAGS)

$(OBJS): | $(OBJDIR)

$(OBJDIR):
    @mkdir -p $(OBJDIR)
    @for dir in $(SUBDIRS);         \
    do                  \
        mkdir -p $(OBJDIR)/$$dir;   \
    done

clean:
    rm -rf $(TMPS)

fclean: clean
    rm -rf $(NAME)
    rm -rf $(OBJDIR)

re: fclean all

.PHONY: all clean fclean re

在每个子目录中,都有一个 module.mk 文件(我可以给它起任何名字,但这看起来很酷)。

对于源代码:

SRCS        :=  main.c file1.c file2.c

SUBDIRS     +=  subdir1 subdir2

include $(SRCDIR)/subdir1/module.mk
include $(SRCDIR)/subdir2/module.mk

对于一级子目录:

THIS_DIR_L0 := subdir1

MOD_SRC     :=  file3.c file4.c

SRCS        += $(addprefix $(THIS_DIR_L0)/, $(MOD_SRC))

SUBDIRS     += $(THIS_DIR_L0)/subdir3

include $(SRCDIR)/$(THIS_DIR_L0)/subdir3/module.mk

对于 2 级子目录(更深一层):

THIS_DIR_L1 := subdir3

MOD_SRC     :=  file5.c file6.c

SRCS        += $(addprefix $(THIS_DIR_L0)/$(THIS_DIR_L1)/, $(MOD_SRC))

等等……

设置起来非常简单,我发现它非常模块化,并且不使用递归生成文件。在你的目录结构中创建库和东西也不会很复杂。

谁有更好的想法请告诉我。

【讨论】:

    猜你喜欢
    • 2021-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-10
    • 1970-01-01
    相关资源
    最近更新 更多