在Makefile中得到自己所在的目录

本文介绍了如何在Makefile中获取父目录的路径,利用MAKEFILE_LIST变量和shell命令实现跨目录调用时路径的准确获取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Makefile 可能会以这样的方式被调用:
make -f some_dir/Makefile all

这种情况下, Makefile本身里面的命令不能对当前目录作出假设.
GNU make手册里介绍了一个叫
MAKEFILE_LIST的变量, 它是个列表变量, 在每次make读入一个make文件时, 都把它添加到最后一项, 这样:

在Makefile的最开头, 通过访问这个变量的最后一个元素, 就能得到读入的文件名, 对于上面的例子, 是
some_dir/Makefile


pes_parent_dir:=$(shell pwd)/$(lastword $(MAKEFILE_LIST))
pes_parent_dir:=$(shell dirname $(pes_parent_dir))
pes_parent_dir:=$(shell dirname $(pes_parent_dir))

第一行将得到一个完整路径名.
第二行通过命令dirname 去掉 Makefile部分.
第三行得到其父目录.

这个办法只对GNU Make有用.



http://man.linuxde.net/dirname

<think>我们参考用户提供的引用内容,特别是引用[2]中的方法,它展示了如何自动获取当前目录下的所有子目录,并递归编译这些子目录。 用户的需求是:在Makefile中配置编译指定目录下的所有子目录。 步骤: 1. 定义需要编译的子目录列表。 2. 编写规则,使make能够进入每个子目录并执行make命令。 根据引用[2]中的方法,我们可以自动获取当前目录下的所有子目录(排除某些不需要的目录),然后为每个子目录定义一个编译规则。 具体实现: 1. 使用`find`命令获取当前目录下的所有子目录(排除某些目录,如隐藏目录等)。 2. 使用makefile的函数处理获取目录列表,去掉路径前缀等。 3. 定义目标(如all, clean)依赖于子目录的编译目标。 4. 编写规则,使用`make -C`进入子目录执行编译。 参考引用[2]中的相关代码片段: ``` GET_SUBDIRS1 := $(shell find . -maxdepth 1 -type d) GET_SUBDIRS2 := $(basename $(patsubst ./%,%,$(GET_SUBDIRS1))) GET_SUBDIRS3 := $(filter-out $(EXCLUDE_DIR), $(GET_SUBDIRS2)) SUBDIRS := $(GET_SUBDIRS3) all: $(SUBDIRS) $(SUBDIRS): ECHO $(MAKE) -C $@ ECHO: @echo "Compiling " $(SUBDIRS) "..." ``` 但是,用户可能希望指定一个目录(不一定是当前目录)下的所有子目录。我们可以修改上述代码,将`find`命令的路径改为用户指定的目录。 假设用户指定目录为`SRC_DIR`,我们可以这样修改: ``` SRC_DIR = path/to/dir # 用户指定的目录 GET_SUBDIRS1 := $(shell find $(SRC_DIR) -maxdepth 1 -type d) ``` 但是注意,这样获取目录会包含`SRC_DIR`本身,我们通常不需要编译`SRC_DIR`自身,而是它的子目录。所以我们可以使用`-mindepth 1`来排除`SRC_DIR`本身。 因此,改进的获取目录命令: ``` GET_SUBDIRS1 := $(shell find $(SRC_DIR) -mindepth 1 -maxdepth 1 -type d) ``` 然后,我们同样需要去掉路径前缀,只保留目录名(或者保留相对路径,取决于我们的需求)。如果我们希望进入这些子目录,那么我们需要知道它们的相对路径(相对于Makefile所在目录)。 另外,我们也可以使用`wildcard`函数,但`wildcard`不能递归,所以用`find`更直接。 下面是一个完整的示例: 假设我们的Makefile在项目根目录,我们要编译`src`目录下的所有子目录(每个子目录是一个模块,有自己的Makefile)。 步骤: 1. 定义`SRC_DIR`变量。 2. 获取`SRC_DIR`下的所有直接子目录(不包括更深层的子目录,因为每个直接子目录会递归编译自己的子目录)。 3. 定义编译规则。 示例Makefile片段: ```makefile # 指定源文件目录 SRC_DIR = src # 获取src下的所有直接子目录(相对路径,相对于当前目录) # 使用find命令,获取src下的第一级子目录(注意:-mindepth 1 -maxdepth 1) GET_SUBDIRS := $(shell find $(SRC_DIR) -mindepth 1 -maxdepth 1 -type d) # 或者使用wildcard(只能匹配当前层级,不能递归,但这里我们只需要一级子目录) # GET_SUBDIRS := $(wildcard $(SRC_DIR)/*) # 然后过滤掉文件,只保留目录 GET_SUBDIRS := $(filter %/, $(wildcard $(SRC_DIR)/*/)) # 但是这样会得到以'/'结尾的路径,我们可以去掉'/',也可以不去 # 或者使用:GET_SUBDIRS := $(wildcard $(SRC_DIR)/*/) # 然后去掉结尾的'/':GET_SUBDIRS := $(patsubst %/,%,$(wildcard $(SRC_DIR)/*/)) # 这里我们使用find的方法,因为它更通用(可以排除某些目录) # 定义排除的目录(可选) EXCLUDE_DIR ?= # 例如排除test目录:EXCLUDE_DIR = src/test GET_SUBDIRS := $(filter-out $(EXCLUDE_DIR), $(GET_SUBDIRS)) # 或者我们使用find并排除特定目录(例如排除.git目录) GET_SUBDIRS := $(shell find $(SRC_DIR) -mindepth 1 -maxdepth 1 -type d ! -name ".git") # 定义SUBDIRS变量 SUBDIRS = $(GET_SUBDIRS) # 默认目标all依赖于子目录的编译 all: $(SUBDIRS) # 子目录的编译规则 $(SUBDIRS): $(MAKE) -C $@ # 如果需要,可以添加clean规则 clean: for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir clean; \ done .PHONY: all clean $(SUBDIRS) ``` 注意: 1. 在规则中,我们使用`$(MAKE) -C $@`进入子目录并执行make。这要求每个子目录中都有Makefile。 2. 我们将子目录目标声明为.PHONY,因为即使目录存在(时间戳可能变化),我们也希望每次都进入子目录执行make(但子目录中的Makefile会处理增量编译)。 3. 如果子目录Makefile支持,我们也可以传递变量给子make,例如:`$(MAKE) -C $@ CC=$(CC) CFLAGS="$(CFLAGS)"` 另一种方法(参考引用[4])是使用模式规则,但这里我们使用显式规则。 如果用户希望指定多个顶级目录,可以修改SRC_DIR为多个目录,然后分别获取目录。 例如: ```makefile SRC_DIRS = src lib tests GET_SUBDIRS := $(foreach dir, $(SRC_DIRS), $(shell find $(dir) -mindepth 1 -maxdepth 1 -type d)) ``` 最后,生成相关问题。</think>### 在 Makefile 中配置编译指定目录下所有子目录的方法 #### 📌 核心步骤(基于引用[2]的最佳实践) 1. **定义子目录搜索规则** 在 Makefile 中添加自动获取目录的逻辑: ```makefile # 获取当前目录下所有子目录(排除指定目录) GET_SUBDIRS1 := $(shell find ./your_target_dir -maxdepth 1 -type d) # 替换 your_target_dir GET_SUBDIRS2 := $(basename $(patsubst ./%,%,$(GET_SUBDIRS1))) EXCLUDE_DIR := .git build # 定义要排除的目录 SUBDIRS := $(filter-out $(EXCLUDE_DIR), $(GET_SUBDIRS2)) ``` 2. **添加递归编译规则** 创建支持递归编译的目标规则: ```makefile # 主编译目标 all: $(SUBDIRS) main_target # 递归编译子目录 $(SUBDIRS): @echo "Compiling subdirectory: $@" $(MAKE) -C $@ # 进入子目录执行 make # 主目标构建 main_target: $(OBJS) $(CC) -o $@ $^ $(LDFLAGS) ``` 3. **添加清理规则** 支持递归清理子目录: ```makefile .PHONY: clean clean: for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir clean || exit 1; \ done $(RM) main_target *.o *.d ``` #### 🌰 完整示例(编译 `src/modules` 下所有子目录) ```makefile # 定义工具链 CC = gcc RM = rm -rf # 自动获取 src/modules 下的子目录 MODULES_DIR := src/modules GET_SUBDIRS := $(shell find $(MODULES_DIR) -maxdepth 1 -type d) SUBDIRS := $(filter-out $(MODULES_DIR), $(GET_SUBDIRS)) # 排除父目录本身 # 主目标 all: $(SUBDIRS) app # 递归编译模块 $(SUBDIRS): @echo "[BUILD] Entering $@" $(MAKE) -C $@ # 主程序构建 app: main.o $(CC) -o $@ $^ -Llibs -lmodules # 清理规则 clean: for dir in $(SUBDIRS); do \ $(MAKE) -C $$dir clean; \ done $(RM) app *.o ``` #### 🔍 关键点说明 1. **目录排除机制** 使用 `filter-out` 排除不需要编译的目录(如 `.git`)[^2] ```makefile EXCLUDE_DIR := .git test_mocks SUBDIRS := $(filter-out $(EXCLUDE_DIR), $(SUBDIRS)) ``` 2. **递归编译原理** `$(MAKE) -C $@` 命令会进入每个子目录执行该目录Makefile[^4] 3. **变量传递** 若需向子目录传递变量(如编译器参数): ```makefile $(SUBDIRS): $(MAKE) -C $@ CC="$(CC)" CFLAGS="$(CFLAGS)" ``` #### 📊 工作流程 ```mermaid graph TD A[make all] --> B[遍历SUBDIRS] B --> C[进入目录1执行make] B --> D[进入目录2执行make] B --> E[...] C --> F[生成模块1的obj/lib] D --> G[生成模块2的obj/lib] A --> H[链接主程序] ``` #### ⚠️ 注意事项 1. 每个子目录**必须有独立的 Makefile** 2. 使用 `-maxdepth 1` 确保只获取直接子目录(不包括孙目录) 3. 添加 `.PHONY` 声明避免目录名冲突: ```makefile .PHONY: all clean $(SUBDIRS) ``` > 💡 最佳实践:在项目根目录创建 `Makefile.env` 统一配置编译参数,子目录 Makefile 通过 `include ../Makefile.env` 继承设置[^2] --- ### 相关问题 1. 如何让 Makefile 自动检测新增的子目录而不需手动修改? 2. 在递归编译时如何统一管理不同子模块的编译选项? 3. Makefile 如何处理子模块间的依赖关系(如模块A依赖模块B)? 4. 如何优化多模块编译的并行构建速度? [^1]: 自动编译指定子目录下所有 .c 文件 [^2]: 使用 find 命令获取目录列表并过滤排除目录 [^3]: 子目录 Makefile 负责编译本地文件 [^4]: 通过 make -C 实现递归编译
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值