当前位置: 首页 > article >正文

上层 Makefile 控制下层 Makefile 的方法

在复杂的项目中,通常会将项目划分为多个模块或子项目,每个模块都有自己的 Makefile。上层 Makefile 的作用是协调和控制这些下层 Makefile 的构建过程。下面是几种常见的示例,实现上层 Makefile 对下层 Makefile 的控制。

  1. 直接调用:通过 $(MAKE) 或 make 命令直接调用下层 Makefile

  2. 传递变量:通过环境变量或命令行参数将变量传递给下层 Makefile

  3. 递归构建:通过递归调用 make 命令构建所有模块。

  4. 导入规则:通过 include 指令导入下层 Makefile 的规则和变量。


目录

一个实际例子

1.工程目录结构

2.主目录Makefile内容

3.Service目录的Makefile

4.Client目录的Makefile 

方法 1:直接调用下层 Makefile

示例项目结构

上层 Makefile 示例

下层 Makefile 示例(module1/Makefile)

使用说明

方法 2:传递变量给下层 Makefile

上层 Makefile 示例

下层 Makefile 示例(module1/Makefile)

使用说明

方法 3:使用递归构建

上层 Makefile 示例

下层 Makefile 示例(module1/Makefile)

使用说明

方法 4:使用 include 导入下层 Makefile

上层 Makefile 示例

下层 Makefile 示例(module1/Makefile)

使用说明

总结


一个实际例子

1.工程目录结构

├── ChatRoom
│   ├── Client
│   │   ├── Client.c
│   │   ├── Makefile
│   │   ├── Tcp_Sock.c
│   │   ├── Tcp_Sock.h
│   │   ├── main
│   │   └── obj
│   └── Service
│       ├── Makefile
│       ├── Service.c
│       ├── Tcp_Sock.c
│       ├── Tcp_Sock.h
│       ├── main
│       └── obj
├── Makefile

2.主目录Makefile内容

简单版本:

SUBDIR1 = ChatRoom/Service
SUBDIR2 = ChatRoom/Client
SUBDIRS = $(SUBDIR1) $(SUBDIR2).PHONY: all clean $(SUBDIRS)all: $(SUBDIRS)$(SUBDIRS):$(MAKE) -C $@clean:for dir in $(SUBDIRS); \do \$(MAKE) -C $$dir clean; \done
# 定义模块路径
##################################
########用户配置区#################
MODULE1 = ChatRoom/Client
MODULE2 = ChatRoom/ServiceMODULE1_TARGET = $(MODULE1)/main
MODULE2_TARGET = $(MODULE2)/mainMODULES = $(MODULE1) $(MODULE2)
MODULES_TARGET = $(MODULE1_TARGET) $(MODULE2_TARGET)
##################################
# 默认目标
all: $(MODULES_TARGET)# 构建模块
$(MODULE1_TARGET): $(MODULE1)@echo "正在编译模块:$(MODULE1)"$(MAKE) -C $(MODULE1) all$(MODULE2_TARGET): $(MODULE2)@echo "正在编译模块:$(MODULE2)"$(MAKE) -C $(MODULE2) all# 清理所有模块
clean:$(MAKE) -C $(MODULE1) clean$(MAKE) -C $(MODULE2) cleanrm -f $(MODULE1_TARGET) $(MODULE2_TARGET)# 帮助信息
help:@echo "可用命令:"@echo "  make          编译所有模块"@echo "  make clean    清理所有模块"@echo "  make help     查看帮助信息"

3.Service目录的Makefile

CC = gcc
SCRC = $(wildcard *.c)
OBJS_DIR = ./obj
OBJ = $(SCRC:%.c=$(OBJS_DIR)/%.o)
CFLAGS = -Wall -O2
LDFLAGS = -lpthread
TARGET = main# 默认目标
all: $(TARGET)$(TARGET):$(OBJ)$(CC) $^ -o $@ $(LDFLAGS)
$(OBJS_DIR)/%o:%c@mkdir -p $(OBJS_DIR)$(CC) $< -o $@	$(CFLAGS) -c echo:@echo $(OBJ)clean:rm -rf ./obj.PHONY:clean
.PHONY:echo

4.Client目录的Makefile 

CC = gcc
SCRC = $(wildcard *.c)
OBJS_DIR = ./obj
OBJ = $(SCRC:%.c=$(OBJS_DIR)/%.o)
CFLAGS = -Wall -O2
LDFLAGS = -lpthread
TARGET = main# 默认目标
all: $(TARGET)$(TARGET):$(OBJ)$(CC) $^ -o $@ $(LDFLAGS)
$(OBJS_DIR)/%o:%c@mkdir -p $(OBJS_DIR)$(CC) $< -o $@	$(CFLAGS) -c echo:@echo $(OBJ)clean:rm -rf ./obj.PHONY:clean
.PHONY:echo

方法 1:直接调用下层 Makefile

上层 Makefile 可以通过 $(MAKE) 或 make 命令直接调用下层 Makefile。这种方法简单且灵活。

示例项目结构
project/
├── Makefile         # 上层 Makefile
├── module1/
│   ├── Makefile     # 下层 Makefile
│   └── *.c
├── module2/
│   ├── Makefile     # 下层 Makefile
│   └── *.c
└── main.c
上层 Makefile 示例
# 定义下层模块的路径
MODULE1 = module1
MODULE2 = module2# 定义所有模块
MODULES = $(MODULE1) $(MODULE2)# 默认目标
all: $(MODULES)# 构建模块
$(MODULES):$(MAKE) -C $@# 清理所有模块
clean:$(MAKE) -C $(MODULE1) clean$(MAKE) -C $(MODULE2) cleanrm -f main# 帮助信息
help:@echo "可用命令:"@echo "  make          编译所有模块"@echo "  make clean    清理所有模块"@echo "  make help     查看帮助信息"
下层 Makefile 示例(module1/Makefile
# 定义编译器
CC = gcc# 定义目标文件
TARGET = module1.o# 定义源文件
SRC = module1.c# 编译选项
CFLAGS = -g -Wall# 默认目标
all: $(TARGET)# 生成目标文件
$(TARGET):$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)# 清理
clean:rm -f $(TARGET)
使用说明
  1. 编译所有模块

                上层 Makefile 会依次调用 module1/Makefile 和 module2/Makefile 进行编译。

make
  1. 清理所有模块

    make clean

    上层 Makefile 会调用每个模块的 clean 目标,并清理上层生成的文件。

  2. 查看帮助

    make help

方法 2:传递变量给下层 Makefile

上层 Makefile 可以通过环境变量或命令行参数将变量传递给下层 Makefile

上层 Makefile 示例
# 定义构建类型
BUILD_TYPE ?= Debug# 定义下层模块的路径
MODULE1 = module1
MODULE2 = module2# 定义所有模块
MODULES = $(MODULE1) $(MODULE2)# 默认目标
all: $(MODULES)# 构建模块
$(MODULES):$(MAKE) -C $@ BUILD_TYPE=$(BUILD_TYPE)# 清理所有模块
clean:$(MAKE) -C $(MODULE1) clean$(MAKE) -C $(MODULE2) cleanrm -f main# 帮助信息
help:@echo "可用命令:"@echo "  make          编译所有模块"@echo "  make clean    清理所有模块"@echo "  make help     查看帮助信息"
下层 Makefile 示例(module1/Makefile
# 定义编译器
CC = gcc# 定义目标文件
TARGET = module1.o# 定义源文件
SRC = module1.c# 从环境变量中获取构建类型
ifeq ($(BUILD_TYPE), Debug)CFLAGS = -g -Wall
elseCFLAGS = -O2
endif# 默认目标
all: $(TARGET)# 生成目标文件
$(TARGET):$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)# 清理
clean:rm -f $(TARGET)
使用说明
  1. 编译所有模块(Debug 模式)

make
  1. 编译所有模块(Release 模式)

make BUILD_TYPE=Release
  1. 清理所有模块

make clean

方法 3:使用递归构建

上层 Makefile 可以通过递归调用 make 命令来构建所有下层模块。

上层 Makefile 示例
# 定义模块路径
MODULES = module1 module2# 默认目标
all: $(MODULES)# 构建模块
$(MODULES):@echo "构建模块: $@"$(MAKE) -C $@# 清理
clean:@echo "清理模块..."$(MAKE) -C module1 clean$(MAKE) -C module2 cleanrm -f main# 帮助信息
help:@echo "可用命令:"@echo "  make          编译所有模块"@echo "  make clean    清理所有模块"@echo "  make help     查看帮助信息"
下层 Makefile 示例(module1/Makefile
# 定义编译器
CC = gcc# 定义目标文件
TARGET = module1.o# 定义源文件
SRC = module1.c# 编译选项
CFLAGS = -g -Wall# 默认目标
all: $(TARGET)# 生成目标文件
$(TARGET):$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)# 清理
clean:rm -f $(TARGET)
使用说明
  1. 编译所有模块

make
  1. 清理所有模块

make clean

方法 4:使用 include 导入下层 Makefile

上层 Makefile 可以通过 include 指令导入下层 Makefile 的规则和变量。

上层 Makefile 示例
# 定义模块路径
MODULES = module1 module2# 导入下层 Makefile 的变量和规则
include $(MODULES:%=%.mk)# 默认目标
all: $(MODULES:%=build_%)# 构建模块
build_%:$(MAKE) -C $* all# 清理
clean:$(MAKE) -C module1 clean$(MAKE) -C module2 cleanrm -f main# 帮助信息
help:@echo "可用命令:"@echo "  make          编译所有模块"@echo "  make clean    清理所有模块"@echo "  make help     查看帮助信息"
下层 Makefile 示例(module1/Makefile
# 定义编译器
CC = gcc# 定义目标文件
TARGET = module1.o# 定义源文件
SRC = module1.c# 编译选项
CFLAGS = -g -Wall# 默认目标
all: $(TARGET)# 生成目标文件
$(TARGET):$(CC) $(CFLAGS) -c $(SRC) -o $(TARGET)# 清理
clean:rm -f $(TARGET)
使用说明
  1. 编译所有模块

make
  1. 清理所有模块

make clean

总结

通过上述方法,上层 Makefile 可以灵活地控制下层 Makefile 的构建过程。以下是关键点的总结:

  1. 直接调用:通过 $(MAKE) 或 make 命令直接调用下层 Makefile
  2. 传递变量:通过环境变量或命令行参数将变量传递给下层 Makefile
  3. 递归构建:通过递归调用 make 命令构建所有模块。
  4. 导入规则:通过 include 指令导入下层 Makefile 的规则和变量。

相关文章:

上层 Makefile 控制下层 Makefile 的方法

在复杂的项目中&#xff0c;通常会将项目划分为多个模块或子项目&#xff0c;每个模块都有自己的 Makefile。上层 Makefile 的作用是协调和控制这些下层 Makefile 的构建过程。下面是几种常见的示例&#xff0c;实现上层 Makefile 对下层 Makefile 的控制。 直接调用&#xff1…...

html简易实现推箱子小游戏原理(易上手)

实现效果 使用方向键移动&#xff0c;将橙色箱子推到绿色目标区域&#xff08;黑色块为墙&#xff0c;白色块为可通过区域&#xff0c;蓝球为小人&#xff09; 实现过程 <!DOCTYPE html> <html> <head><title>推箱子小游戏</title><style&g…...

搭建一个Spring Boot聚合项目

1. 创建父项目 打开IntelliJ IDEA&#xff0c;选择 New Project。 在创建向导中选择 Maven&#xff0c;确保选中 Create from archetype&#xff0c;选择 org.apache.maven.archetypes:maven-archetype-quickstart。 填写项目信息&#xff1a; GroupId&#xff1a;com.exampl…...

字符串与栈和队列-算法小结

字符串 双指针 反转字符串(双指针) 力扣题目链接 void reverseString(vector<char>& s) {for (int i 0, j s.size() - 1; i < s.size()/2; i, j--) {swap(s[i],s[j]);} }反转字符串II 力扣题目链接 遍历字符串的过程中&#xff0c;只要让 i (2 * k)&#…...

类似东郊到家的上门按摩预约服务系统小程序APP源码全开源

&#x1f525; 为什么上门按摩正在席卷全国&#xff1f; 万亿蓝海市场爆发 2024年中国按摩市场规模突破8000亿&#xff0c;上门服务增速达65% 90后成消费主力&#xff0c;**72%**白领每月至少使用1次上门按摩&#xff08;数据来源&#xff1a;艾媒咨询&#xff09; 传统痛点…...

Python | 在Pandas中按照中值对箱形图排序

箱形图是可视化数据分布的强大工具&#xff0c;因为它们提供了对数据集内的散布、四分位数和离群值的洞察。然而&#xff0c;当处理多个组或类别时&#xff0c;通过特定的测量&#xff08;如中位数&#xff09;对箱形图进行排序可以提高清晰度并有助于揭示模式。在本文中&#…...

[实战] 二分查找与哈希表查找:原理、对比与C语言实现(附完整C代码)

二分查找与哈希表查找&#xff1a;原理、对比与C语言实现 一、引言 在计算机科学中&#xff0c;高效的数据查找是核心问题之一。本文深入解析两种经典查找算法&#xff1a;二分查找与哈希表查找&#xff0c;从算法原理、时间复杂度、适用场景到完整C语言实现&#xff0c;提供…...

游戏引擎学习第215天

总结并为今天做铺垫 今天的工作内容是解决调试系统中的一个小问题。昨天我们已经完成了大部分的调试系统工作&#xff0c;但还有一个小部分没有完全处理&#xff0c;那就是关于如何层次化组织数据的问题。我们遇到的一个问题是&#xff0c;演示代码中仍有一个尚未解决的部分&a…...

【Redis】redis事物与管道

Redis 事务&#xff08;Transaction&#xff09; 事务概念 事务&#xff1a;是一组操作的集合&#xff0c;是不可分割的工作单元。Redis 事务特点&#xff1a; 一个事务可以一次执行多个命令。所有命令都被顺序化&#xff0c;形成一个队列。所有命令在执行 EXEC 时一次性、顺…...

Django信号使用完全指南示例

推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 **引言:****先决条件:****目录:****1. 什么是Django信号?****2:设置你的Django项目****2.1. 安装Django**2.2. 创建一个Django项…...

DeepSeek BLEU和ROUGE(Recall)的计算

以下是 BLEU Score (Bilingual ​Evaluation ​Understudy)和 ROUGE Score(​Recall-Oriented ​Understudy for ​Gisting ​Evaluation) 的原生Python实现&#xff08;不依赖第三方库&#xff09;&#xff0c;通过分步计算逻辑和示例详细说明。 一、BLEU Score 实现 核心逻辑…...

vulkanscenegraph显示倾斜模型(5.9)-vsg中vulkan资源的编译

前言 上一章深入剖析了GPU资源内存及其管理&#xff0c;vsg中为了提高设备内存的利用率&#xff0c;同时减少内存(GPU)碎片&#xff0c;采用GPU资源内存池机制(vsg::MemoryBufferPools)管理逻辑缓存(VkBuffer)与物理内存(VkDeviceMemory)。本章将深入vsg中vulkan资源的编译(包含…...

今日行情明日机会——20250409

今日行情还需要考虑关税对抗~ 2025年4月8日涨停的主要行业方向分析 1. 军工&#xff08;19家涨停&#xff09; 细分领域&#xff1a;国防装备、航空航天、军民融合。催化因素&#xff1a;国家安全战略升级、国防预算增加、重大军工项目落地预期。 2. 免税&#xff08;15家涨…...

XHR、FetchAxios详解网络相关大片文件上传下载

以下是 XHR(XMLHttpRequest) 与 Fetch API 的全面对比分析,涵盖语法、功能、兼容性等核心差异: 一、语法与代码风格 XHR(基于事件驱动) 需要手动管理请求状态(如 onreadystatechange 事件)和错误处理,代码冗长且易出现回调地狱。 const xhr = new XMLHttpRequest(); x…...

Python基础总结(四)之元组

文章目录 一、元组格式二、元组操作2.1 转换元组 与 列表一样&#xff0c;元组也是序列&#xff0c;唯一的区别在于元组是不能修改的&#xff0c;与字符串一样。 一、元组格式 元组的创建方式很简单&#xff0c;秩序用逗号将元素隔开就能自动创建一个元组 示例&#xff1a; …...

系统分析师(六)-- 计算机网络

概述 TCP/IP 协议族 DNS DHCP 网络规划与设计 逻辑网络设计 物理网络设计 题目 层次化网络设计 网络冗余设计 综合布线系统 IP地址 网络接入技术 其他网络技术应用 物联网...

【前端】【React】useCallback的作用与使用场景总结

一、useCallback 的作用与使用场景总结 useCallback 是 React 提供的一个 Hook&#xff0c;用于缓存函数的引用&#xff0c;避免因为组件重新渲染而导致函数地址发生变化。它返回一个记忆&#xff08;memoized&#xff09;后的回调函数&#xff0c;只有当依赖项发生变化时才会…...

Qwen2.5-VL Technical Report 论文翻译和理解

一、TL&#xff1b;DR Qwen2.5-VL是QwenVL的最新模型&#xff0c;在视觉识别、精准目标定位、稳健文档解析以及长视频理解等方面实现了重大突破引入了动态分辨率处理和绝对时间编码&#xff0c;使其能够处理不同尺寸的图像以及长达数小时的视频&#xff0c;并实现秒级事件定位…...

Foxmail邮件客户端跨站脚本攻击漏洞(CNVD-2025-06036)技术分析

Foxmail邮件客户端跨站脚本攻击漏洞&#xff08;CNVD-2025-06036&#xff09;技术分析 漏洞背景 ‌漏洞编号‌&#xff1a;CNVD-2025-06036 ‌CVE编号‌&#xff1a;待分配 ‌厂商‌&#xff1a;腾讯Foxmail ‌影响版本‌&#xff1a;Foxmail < 7.2.25 ‌漏洞类型‌&#x…...

C语言打印的坑

使用下面的代码buf dprt("data: 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); 明明是一个字节一个字节的打&#xff0c;打出来的数据有些却是4个字节 0xffffffff 0xffffffff 0xffffffff 0x7f 0xffffffff 0x7f0…...

高并发内存池(三):PageCache(页缓存)的实现

前言&#xff1a; 在前两期内容中&#xff0c;我们深入探讨了内存管理机制中在 ThreadCache 和 CentralCache两个层级进行内存申请的具体实现。这两层缓存作为高效的内存分配策略&#xff0c;能够快速响应线程的内存需求&#xff0c;减少锁竞争&#xff0c;提升程序性能。 本期…...

pycharm已有python3.7,如何新增Run Configurations中的Python interpreter为python 3.9

在 PyCharm 中&#xff0c;如果你已经安装了 Python 3.9&#xff0c;并且希望在 Run Configurations 中新增一个 Python 3.9 的解释器&#xff0c;可以按照以下步骤操作&#xff1a; 步骤 1&#xff1a;打开 PyCharm 设置 点击 PyCharm 左上角的 File 菜单。选择 Settings&am…...

Linux驱动开发-网络设备驱动

Linux驱动开发-网络设备驱动 一&#xff0c;网络设备总体结构1.1 总体架构1.2 NAPI数据处理机制 二&#xff0c;RMII和MDIO2.1 RMII接口2.2 MDIO接口 三&#xff0c;MAC和PHY模块3.1 MAC模块3.2 PHY模块 四&#xff0c;网络模型4.1 网络的OSI和TCP/IP分层模型4.1.1 传输层&…...

学习笔记083——Java Stream API

文章目录 1、过滤数据 filter()2、转换元素 map()3、排序 sorted()3.1、自定义排序规则 4、去重 distinct()5、限制元素数量 limit()6、收集结果 collect()6.1、收集为List6.2、收集为Set6.3、转为Map6.4、基本用法&#xff08;注意键冲突会抛异常&#xff09;6.5、处理键冲突&…...

DataEase同比环比

DataEase同比环比 前言术语实现表结构设计DataEase设计创建数据集创建仪表盘最后前言 某大数据项目,需要比较展示今年跟去年的数据,如下图: 说明:比较24,25的产品销量,相同月份做一组,并排放一块 还有更进一步: 说明:比较24,25相同月份,相同产品的销量 直接用DataE…...

RAG 工程基础

RAG 概念 RAG&#xff08;Retrieval - Augmented Generation&#xff09;技术是一种将检索与生成相结合的人工智能技术&#xff0c;旨在利用外部知识源来增强语言模型的生成能力&#xff0c;提高生成内容的质量、准确性和相关性。 具体来说&#xff0c;RAG 技术在处理用户输入的…...

基础算法:滑动窗口_python版本

能使用滑动窗口的题&#xff0c;基本都需要数字为正整数&#xff0c;这样才能保证滑入一个数字总和是增加的(单调性) 一、209. 长度最小的子数组 思路&#xff1a; 已每个位置为右端点&#xff0c;依次加大左端点&#xff0c;最短不满足 sum(num[left,right]) < target的。…...

Qt 之opengl shader language

着色器示例代码 实际运行效果...

PyRoboPlan 库,给 panda 机械臂微分 IK 上大分,关节限位、碰撞全不怕

视频讲解&#xff1a; PyRoboPlan 库&#xff0c;给 panda 机械臂微分 IK 上大分&#xff0c;关节限位、碰撞全不怕 代码仓库&#xff1a;https://github.com/LitchiCheng/mujoco-learning 今天分享PyRoboPlan库&#xff0c;比之前的方式优点在于&#xff0c;这个库考虑了机械…...

GPT - TransformerDecoderBlock

本节代码定义了一个 TransformerDecoderBlock 类&#xff0c;它是 Transformer 架构中解码器的一个基本模块。这个模块包含了多头自注意力&#xff08;Multi-Head Attention&#xff09;、前馈网络&#xff08;Feed-Forward Network, FFN&#xff09;和层归一化&#xff08;Lay…...