创建基本的Makefile

本文介绍了如何将shell脚本编译过程转化为基础Makefile,展示了Makefile的基本结构,包括目标、依赖和命令,强调了模块化和效率的重要性,以及如何逐步完善Makefile以提高可维护性。

创建基本的Makefile


下一篇:练习3 Makefile之简单介绍,上一篇:练习1 理解编译的基本过程目录首页

温故知新

经过以上练习,我们大致了解了,源码编译的基本流程,有的人可能会说对于练习1只有一个原文件一行命令g++ main.cpp就结束了,
是的,日常几乎没有人去细扣这些编译的细节,正因为这样,我们才更应该去了解!

练习1的最后,我们使用了一个shell脚本去执行繁杂的操作,其实这也就是Makefile的初衷了,Makefile本质上也是shell,它的command中就是支持shell命令的,当然使用Makefile可以更加通用化,不然每人写一个编译脚本,

大家还要去理解,那不烦死了。那么接下来让我们将练习1中的shell脚本过渡成一个Makefile。在这个过程中,我们将创建一个更灵活、可维护和通用的构建系统。


下一篇:练习3 Makefile之简单介绍,上一篇:练习1 理解编译的基本过程目录首页

优化准备

还是这个main.cpp源文件

/**
@Author  : nickdecodes
@Email   : 
@Usage   : 
@Filename: main.cpp
@DateTime: 2023/10/07 17:29
@Software: vscode
**/

#include <iostream>
using namespace std;


int main(int argc, char const *argv[]) {
    cout << "hello world" << endl;
    return 0;
}

直接编译还记得吧,g++ main.cpp

还是这个shell脚本compile.sh

# !/bin/bash

# 源文件和目标可执行文件的名称
SOURCE_FILE="main.cpp"
TARGET_EXECUTABLE="a.out"

# 编译器和编译选项
CXX="g++" 
CXXFLAGS="-Wall -g" 

# 预处理(Preprocessing)
echo "1. Preprocessing: $SOURCE_FILE -> ${SOURCE_FILE%.cpp}.i"
$CXX -E $SOURCE_FILE -o ${SOURCE_FILE%.cpp}.i

# 编译(Compiling)
echo "2. Compiling: ${SOURCE_FILE%.cpp}.i -> ${SOURCE_FILE%.cpp}.s"
$CXX $CXXFLAGS -S ${SOURCE_FILE%.cpp}.i -o ${SOURCE_FILE%.cpp}.s

# 汇编(Assembling)
echo "3. Assembling: ${SOURCE_FILE%.cpp}.s -> ${SOURCE_FILE%.cpp}.o"
$CXX $CXXFLAGS -c ${SOURCE_FILE%.cpp}.s -o ${SOURCE_FILE%.cpp}.o

# 链接(Linking)
echo "4. Linking: ${SOURCE_FILE%.cpp}.o -> $TARGET_EXECUTABLE"
$CXX $CXXFLAGS ${SOURCE_FILE%.cpp}.o -o $TARGET_EXECUTABLE

# 清理临时文件
# echo "5. Cleaning up temporary files"
# rm -f ${SOURCE_FILE%.cpp}.i ${SOURCE_FILE%.cpp}.s ${SOURCE_FILE%.cpp}.o ${TARGET_EXECUTABLE}

echo "Compilation completed!"

下一篇:练习3 Makefile之简单介绍,上一篇:练习1 理解编译的基本过程目录首页

创建Makefile文件

接下来我们新建一个文件,名字叫做makefile 或者Makefile,在这里我们使用Makefile

touch Makefile

下一篇:练习3 Makefile之简单介绍,上一篇:练习1 理解编译的基本过程目录首页

转化compile.sh内容

一个基本的 Makefile 由以下形式的格式构成,Makefile称之为规则

target ... : prerequisites ...
    recipe
    ...
    ...

其中

  • target - 目标文件, 可以是 Object File, 或者是可执行文件, 也可以是要执行的操作的名称,
  • prerequisites - 生成 target 所需要的文件或者目标, 通常取决于多个文件。我们称之为依赖
  • recipe - 配方是一个Shell命令(任意的shell命令),用于构建目标 , makefile中的命令必须以 [tab] 开头

那么,规则解释了如何以及何时重新制作作为特定规则的目标的某些文件。 make根据创建或更新targetprerequisites执行命令。规则还可以解释如何以及何时执行操作。

这次我们主要是展示一下makefile的功能,如果只是写一个最基础的,我们直接将compile.sh的内容转化一下就好啦,转化之后内容如下

main : main.cpp
    g++ -Wall -g main.cpp

这就是最简单的makefile了, 接下来我们可以在相同目录下执行make, 你就可以看到编译输出,以及编译结果啦

不过这只是一个非常简略的Makefile,还有很多地方需要优化。


下一篇:练习3 Makefile之简单介绍,上一篇:练习1 理解编译的基本过程目录首页

完善Makefile内容

上面我们所有写的Makefile着实太过精简,根本就体现不出Makefile的全貌,接下来我们就对当下的文件情况,去进一步完善一下,中间可能会有一些不理解的地方,放心后面会一一介绍的

首先我们把之前的Makefile备份一下,就叫做Makefile_simple

cp Makefile Makefile_simple

我暂时不会直接说该怎么写,我们就作为一个开发者而言,看有什么地方需要优化!

  1. 首先g++ -Wall -g我们现在是硬编码去写的,一旦出现更改,我们就得改一次,现在我们就只有一个main.cpp也还好,也就改一次就完事了,但是要是多了怎么办,复制粘贴大法吗?大家一定会想到模块化对吧!

    那么怎么去模块化呢,我们在compile.sh中也说明了,g++是指编译器、-Wall -g是指编译参数,👌那我们就命名两个变量就可以了,其它地方就使用命名的变量,万一改变值,也就是在定义的地方改变一次就可以了,实现如下

    CXX = g++ # 编译命令
    CXXFLAGS = -Wall -g # 编译参数
    
    main: main.cpp
        $(CXX) $(CXXFLAGS) main.cpp
    

    其中注释是像shell一样以#开头的文本

    $(CXX) 是引用变量的格式

    看来Makefile人家也想到了模块化,我们直接就可以使用相应的语法了,这样我们通过思考去深刻记忆也是更加深刻的

  2. 接下来我们看看还有什么地方看着不太舒服呢?不知道main.cpp能不能继续抽离出来呢,同样多文件的情况下,改起来也是挺麻烦的,那我们在优化一下吧

    CXX = g++ # 编译命令
    CXXFLAGS = -Wall -g # 编译参数
    OBJS = main.o # 生成对象
    
    # 链接过程
    main : $(OBJS)
        $(CC) $(CFLAGS) $(OBJS) -o test
    
    # 编译过程
    main.o: main.cpp
        $(CXX) -c $(CXXFLAGS) main.cpp
    

    现在我们将编译和链接拆解开了,有人肯定说这不是改复杂了吗?其实这是方便理解的一个过渡,不然接下来肯定更加不理解了,你不信我们就一步到位看看

    CXX = g++ # 编译命令
    CXXFLAGS = -Wall -g # 编译参数
    OBJS = main.o # 生成对象
    
    all:
        $(CXX)  -c $(CXXFLAGS) *.cpp # 编译所有.cpp文件省去链接文件过程
        $(CXX) $(CXXFLAGS) $(OBJS) -o test # 直接生成对象文件
    

    这下简单了吧,连main.cpp都不用写了,其实就是一种shell的缺省匹配*.cpp是匹配所有后缀为.cpp的文件

  3. 至此,好像也没什么了吧!但是细心的一定会发现,当你执行一次make之后,再次执行make,会提示你不会做任何更改。有的人肯定会觉得这么反人类吗?还不允许重复执行了?其实人家Makefile底层有判断是否需要执行或者更新的逻辑,相比来说,其实是更加注重了效率,不过对于我们就想看效果的,我们也可以加一些清理操作,顺便清理一下生成的垃圾,然后在从头编译就好了

    CXX = g++ # 编译命令
    CXXFLAGS = -Wall -g # 编译参数
    OBJS = main.o # 生成对象
    
    .PHONY: clean # 伪目标 通常用于clean操作
    
    all:
       $(CXX)  -c $(CXXFLAGS) *.cpp # 编译所有.cpp文件省去链接文件过程
       $(CXX) $(CXXFLAGS) $(OBJS) -o main # 直接生成对象文件
        
    clean:
       rm -rf *.o main
    

    这样我们可以执行make clean就可以清除生成的文件,想要再次生成就在执行一遍make,是不是很方便


下一篇:练习3 Makefile之简单介绍,上一篇:练习1 理解编译的基本过程目录首页

<think>好的,我现在需要帮助用户了解如何在IAR中创建Makefile文件。首先,用户可能对IAR Embedded Workbench的构建流程不太熟悉,尤其是如何从IDE生成或手动创建Makefile。我需要回忆一下IAR的相关功能和文档。 IAR通常使用自己的项目文件(.ewp)来管理项目配置,但用户可能需要Makefile来自动化构建流程,或者与其他工具集成。首先,我需要确认IAR是否支持直接生成Makefile。根据以往的经验,IAR可能不直接提供生成Makefile的功能,但用户可以通过导出构建命令或使用命令行工具来创建自定义的Makefile。 接下来,我需要考虑步骤。用户可能需要先设置项目,然后在构建过程中获取编译器、链接器等命令的参数。例如,在IAR中构建项目时,可以在输出窗口查看详细的命令,这些信息可以用来编写Makefile。另外,IAR提供了命令行工具,如iccarm(用于ARM的编译器),ilinkarm(链接器),这些需要在Makefile中正确调用。 然后,我需要考虑Makefile的结构。通常,Makefile需要定义源文件、目标文件、编译选项、链接选项等。对于多文件项目,可能需要处理依赖关系。用户可能还需要了解如何指定头文件路径、库路径等,这些在IAR项目中配置的参数都需要转换到Makefile中。 此外,用户可能关心如何优化生成的Makefile,比如减小可执行文件的大小,这可能涉及到编译器选项,如引用[2]提到的-ffunction-sections和-Wl,-gc-sections,但需要确认IAR的编译器是否支持类似的选项。例如,IAR的编译器可能有不同的参数来实现相同的功能,如--split_sections,这需要查阅IAR的文档。 还有,用户可能需要处理多目录结构,如何组织源文件和目标文件,这在Makefile中需要正确设置路径。引用[3]提到汇编阶段生成目标文件,所以在Makefile中需要处理不同源文件的编译步骤,确保每个.c或.s文件都被正确编译为.o文件,然后链接成最终的可执行文件。 另外,用户可能需要清理中间文件的步骤,类似于示例中的clean目标,这需要确保rm命令正确删除生成的文件,同时注意IAR生成的文件扩展名可能不同,比如.obj而不是.o。 最后,我需要验证这些步骤是否正确,是否有遗漏,比如是否需要处理依赖关系自动生成,或者是否需要包含特定的库文件。可能需要建议用户参考IAR的官方文档,确认编译器、链接器的命令行选项,以及项目配置中的各种路径和宏定义。 总结一下,步骤大致是:配置IAR项目,获取构建命令参数,编写Makefile基本结构,定义变量,设置编译和链接规则,处理多文件和目录,添加清理规则,测试并优化。同时,需要注意IAR特有的编译工具和选项,确保Makefile的正确性。</think>在IAR Embedded Workbench中创建Makefile主要分为手动创建和自动导出两种方式。以下是详细的步骤指南: ### 一、手动创建Makefile步骤 1. **获取编译参数** - 在IAR IDE中右击项目选择`Options` - 在`C/C++ Compiler`和`Linker`配置页查看所有参数 - 构建项目后,在`Build`输出窗口复制完整命令行参数[^3] 2. **编写基础结构** ```makefile # 工具链定义 CC = iccarm LD = ilinkarm TARGET = output.elf # 源文件列表 SRCS = main.c system.c drivers/gpio.c OBJS = $(SRCS:.c=.o) # 编译选项 CFLAGS = --cpu=cortex-m4 -Ohz --dlib_config=full # 链接选项 LDFLAGS = --config stm32f4xx.icf --map $(TARGET).map all: $(TARGET) $(TARGET): $(OBJS) $(LD) $(LDFLAGS) $^ -o $@ %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ clean: rm -f $(OBJS) $(TARGET) *.map ``` 3. **多目录处理** ```makefile # 添加头文件路径 CFLAGS += -Iinc -Idrivers/inc # 设置目标文件输出目录 OBJ_DIR = obj OBJS = $(addprefix $(OBJ_DIR)/, $(SRCS:.c=.o)) $(OBJ_DIR)/%.o: %.c | $(OBJ_DIR) $(CC) $(CFLAGS) -c $< -o $@ $(OBJ_DIR): mkdir -p $(OBJ_DIR)/drivers ``` ### 二、自动导出方法 1. 通过`Project > Generate Makefile`导出(部分版本支持) 2. 生成的Makefile需要手动调整: - 替换路径变量 - 添加clean规则 - 优化构建规则[^2] ### 三、关键优化技巧 1. **代码段优化** ```makefile CFLAGS += --split_sections # 类似GCC的-ffunction-sections LDFLAGS += --redirect _Printf=__NoPrintf # 消除未使用库函数 ``` 2. **并行构建** ```makefile MAKEFLAGS += -j4 # 根据CPU核心数设置 ``` 3. **依赖自动生成** ```makefile DEPDIR = .deps CFLAGS += -MMD -MF $(DEPDIR)/$*.d -include $(wildcard $(DEPDIR)/*.d) ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值