如何编写一个工程文件夹下通用的Makefile

 

新建工程文件夹,在里面新建 bsp、imx6ul、obj 和project 这 3 个文件夹,完成之后如图所示:php

新建的工程根目录文件夹html

其中 bsp 用来存放驱动文件;imx6ul 用来存放跟芯片有关的文件,好比 NXP 官方的 SDK库文件;obj 用来存放编译生成的.o 文件;project 存放 start.S 和 main.c 文件,也就是应用文件;将十二章实验中的 cc.h、fsl_common.h、fsl_iomuxc.h 和 MCIMX6Y2.h 这四个文件拷贝到文件夹 imx6ul 中;将 start.S 和 main.c 这两个文件拷贝到文件夹 project 中。咱们前面的实验中全部的驱动相关的函数都写到了 main.c 文件中,好比函数 clk_enable、led_init 和 delay,这三个函数能够分为三类:时钟驱动、LED 驱动和延时驱动。所以咱们能够在 bsp 文件夹下建立三个子文件夹:clk、delay 和 led,分别用来存放时钟驱动文件、延时驱动文件和 LED 驱动文件,这样main.c 函数就会清爽不少,程序功能模块清晰。工程文件夹都建立好了,接下来就是编写代码了,其实就是将时钟驱动、LED 驱动和延时驱动相关的函数从 main.c 中提取出来作成一个独立的驱动文件 。linux

使用VScode 新建工程,工程名字为“ledc_bsp”。新建文件 imx6ul.h,而后保存到文件夹 imx6ul 中,新建 bsp_led.h 和 bsp_led.c 两个文件,将这两个文件存放到 bsp/led 中,bsp_led.c 里面就两个函数 led_init 和 led_switch,led_init 函数用来初始化LED 所使用的IO,led_switch 函数是控制 LED 灯的打开和关闭,这两个函数都很简单。函数

新建 bsp_clk.h 和 bsp_clk.c 两个文件,将这两个文件存放到 bsp/clk 中,bsp_delay.c 里面就两个函数,delay_short 和 delay。在 main.c 中咱们仅仅留下了 main 函数,至此,本例程跟程序相关的内容就所有编写好了。spa

在工程根目录下新建 Makefile 和 imx6ul.lds 这两个文件,建立完成之后的工程如图所示:code

最终的工程目录htm

 

在文件 Makefile 中输入以下所示内容:blog

1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= bsp 3 4 CC := $(CROSS_COMPILE)gcc 5 LD := $(CROSS_COMPILE)ld 6 OBJCOPY := $(CROSS_COMPILE)objcopy 7 OBJDUMP := $(CROSS_COMPILE)objdump 8 9 INCDIRS := imx6ul \ 10 bsp/clk \ 11 bsp/led \ 12 bsp/delay 13 14 SRCDIRS := project \ 15 bsp/clk \ 16 bsp/led \ 17 bsp/delay 18 19 INCLUDE := $(patsubst %, -I %, $(INCDIRS)) 20 21 SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) 22 CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) 23 24 SFILENDIR := $(notdir $(SFILES)) 25 CFILENDIR := $(notdir $(CFILES)) 26 27 SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) 28 COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o)) 29 OBJS := $(SOBJS) $(COBJS) 30 31 VPATH := $(SRCDIRS) 32 33 .PHONY: clean 34 35 $(TARGET).bin : $(OBJS) 36 $(LD) -Timx6ul.lds -o $(TARGET).elf $^ 37 $(OBJCOPY) -O binary -S $(TARGET).elf $@ 38 $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis 39 40 $(SOBJS) : obj/%.o : %.S 41 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 42 43 $(COBJS) : obj/%.o : %.c 44 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 45 46 clean: 47 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)1 CROSS_COMPILE ?= arm-linux-gnueabihf- 2 TARGET ?= bsp 3 4 CC := $(CROSS_COMPILE)gcc 5 LD := $(CROSS_COMPILE)ld 6 OBJCOPY := $(CROSS_COMPILE)objcopy 7 OBJDUMP := $(CROSS_COMPILE)objdump 8 9 INCDIRS := imx6ul \ 10 bsp/clk \ 11 bsp/led \ 12 bsp/delay 13 14 SRCDIRS := project \ 15 bsp/clk \ 16 bsp/led \ 17 bsp/delay 18 19 INCLUDE := $(patsubst %, -I %, $(INCDIRS)) 20 21 SFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.S)) 22 CFILES := $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c)) 23 24 SFILENDIR := $(notdir $(SFILES)) 25 CFILENDIR := $(notdir $(CFILES)) 26 27 SOBJS := $(patsubst %, obj/%, $(SFILENDIR:.S=.o)) 28 COBJS := $(patsubst %, obj/%, $(CFILENDIR:.c=.o)) 29 OBJS := $(SOBJS) $(COBJS) 30 31 VPATH := $(SRCDIRS) 32 33 .PHONY: clean 34 35 $(TARGET).bin : $(OBJS) 36 $(LD) -Timx6ul.lds -o $(TARGET).elf $^ 37 $(OBJCOPY) -O binary -S $(TARGET).elf $@ 38 $(OBJDUMP) -D -m arm $(TARGET).elf > $(TARGET).dis 39 40 $(SOBJS) : obj/%.o : %.S 41 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 42 43 $(COBJS) : obj/%.o : %.c 44 $(CC) -Wall -nostdlib -c -O2 $(INCLUDE) -o $@ {#content}lt; 45 46 clean: 47 rm -rf $(TARGET).elf $(TARGET).dis $(TARGET).bin $(COBJS) $(SOBJS)

能够看出本验的 Makefile 文件要比前面的实验复杂不少,由于代码 中的 Makefile 代码是一个通用 Makefile,咱们之后全部的裸机例程都使用这个 Makefile。使用时候只要将所须要编译的源文件所在的目录添加到Makefile 中便可,咱们接下来详细分析一下 Makefile 源码:get

第 1~7 行定义了一些变量,除了第 2 行之外其它的都是跟编译器有关的,若是使用其它编译器的话只须要修改第 1 行便可。第 2 行的变量 TARGET 目标名字,不一样的例程确定名字不一同样。编译器

第 9 行的变量 INCDIRS 包含整个工程的.h 头文件目录,文件中的全部头文件目录都要添加到变量INCDIRS 中。好比本例程中包含.h 头文件的目录有imx6ul、bsp/clk、bsp/delay 和bsp/led,因此就须要在变量INCDIRS 中添加这些目录,即:

INCDIRS := imx6ul bsp/clk bsp/led bsp/delay

仔细观察的话会发现第 9~11 行后面都会有一个符号“\”,这个至关于“换行符”,表示本行和下一行属于同一行,通常一行写不下的时候就用符号“\”来换行。在后面的裸机例程中咱们会根据实际状况来在变量 INCDIRS 中添加头文件目录。

第 14 行是变量 SRCDIRS,和变量 INCDIRS 同样,只是 SRCDIRS 包含的是整个工程的全部.c 和.S 文件目录。好比本例程包含有.c 和.S 的目录有 bsp/clk、bsp/delay、bsp/led 和 project,即:

SRCDIRS := project bsp/clk bsp/led bsp/delay

一样的,后面的裸机例程中咱们也要根据实际状况在变量 SRCDIRS 中添加相应的文件目录。

第 19 行的变量 INCLUDE 是用到了函数 patsubst,经过函数 patsubst 给变量 INCDIRS 添加一个“-I”,即:

INCLUDE := -I imx6ul -I bsp/clk -I bsp/led -I bsp/delay

加“-I”的目的是由于 Makefile 语法要求指明头文件目录的时候须要加上“-I”。

第 21 行变量 SFILES 保存工程中全部的.s 汇编文件(包含绝对路径),变量 SRCDIRS 已经存放了工程中全部的.c 和.S 文件,因此咱们只须要从里面挑出全部的.S 汇编文件便可,这里借助了函数 foreach 和函数 wildcard,最终 SFILES 以下:

SFILES := project/start.S

第 22 行变量 CFILES 和变量 SFILES 同样,只是 CFILES 保存工程中全部的.c 文件(包含绝对路径),最终CFILES 以下:

CFILES = project/main.c bsp/clk/bsp_clk.c bsp/led/bsp_led.c bsp/delay/bsp_delay.c

第 24 和 25 行的变量 SFILENDIR 和CFILENDIR 包含全部的.S 汇编文件和.c 文件,相比变量 SFILES 和 CFILES,SFILENDIR 和 CFILNDIR 只是文件名,不包含文件的绝对路径。使用函数 notdir 将 SFILES 和 CFILES 中的路径去掉便可,SFILENDIR 和CFILENDIR 以下:

SFILENDIR = start.SCFILENDIR = main.c bsp_clk.c bsp_led.c bsp_delay.c

第 27 和 28 行的变量 SOBJS 和 COBJS 是.S 和.c 文件编译之后对应的.o 文件目录,默认全部的文件编译出来的.o 文件和源文件在同一个目录中,这里咱们将全部的.o 文件都放到 obj 文件夹下,SOBJS 和 COBJS 内容以下:

SOBJS = obj/start.o

COBJS = obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o

第 29 行变量OBJS 是变量SOBJS 和 COBJS 的集合,以下:

OBJS = obj/start.o obj/main.o obj/bsp_clk.o obj/bsp_led.o obj/bsp_delay.o

编译完成之后全部的.o 文件就所有存放到了 obj 目录下,如图所示:

编译完成后的obj 文件夹

第 31 行的 VPATH 是指定搜索目录的,这里指定的搜素目录就是变量 SRCDIRS 所保存的目录,这样当编译的时候所需的.S 和.c 文件就会在SRCDIRS 中指定的目录中查找。

第 34 行指定了一个伪目标 clean,伪目标前面文章 Makefile 的时候已经讲解过了。

Makefile 文件内容重点工做是找到要编译哪些文件?编译的.o文件存放到哪里?使用到的编译命令和前面实验使用的同样,其实 Makefile 的重点工做就是解决“从哪里来到哪里去的”问题,也就是找到要编译的源文件、编译结果存放到哪里?真正的编译命令很简洁。

连接脚本 imx6ul.lds 的内容和上一篇文章《经过结构体的方式来定义和使用寄存器地址》同样,能够直接使用上一的连接脚本文件。

本文转自小平头电子技术社区:https://www.xiaopingtou.cn/article-104184.html

相关文章
相关标签/搜索