目录
makefile使用说明:http://www.cnblogs.com/wang_yb/p/3990952.html
makefile作用:在Linux操作系统下,实现make命令编译整个工程的工具。
一、多级目录结构
当前文档依据以下目录结构描述和书写;涉及到了KO的编译,用户层应用编译makefile的编写和调用。
Makefile共三级,图中[1]为顶级makefile,[2]为第二级,[3]为第三级,即最底层makefile
二、各级Makefile内容
概述:
a.Makefile下,顶级Makefile向下传递参数时,有两种方式命令:
a.i.通过调用来传递参数
make -C $(TOP_DIR)/kernel CURRENT_DIR=$(TOP_DIR)/kernel ko_module
其中:TOP_DIR是工程的顶级目录makefile定义的变量,CURRENT_DIR是顶级Makefile传递给kernel目录下Makefile的参数,在kernel目录的Makefile下可以直接使用,但使用时最好判断一下是否有初值,以免出错,同时若判断没有初值时应予以处理以保证在kernel下输入命令'make'也可以单独执行。ko_module为kernel下的编译对象名。
该方式传输参数时,可以传递多个参数,参数过多时会在make时有过多的打印,不方便查看但方便定位;
a.ii.通过环境变量
环境变量会随着makefile的执行完成而退出,不对当前终端的环境变量有影响
export TOP_DIR=$(shell pwd)
其中:TOP_DIR是顶级Makefile定义的变量,TOP_DIR可以根据工程路径的不同自动确认,编译时文件的路径也应保证是绝对路径,以免出错找不到文件。TOP_DIR在子目录下均可以使用,子目录makefile使用TOP_DIR时应注意转换成当前文件夹路径。
a.iii.通过临时文件
临时文件由用户自行指定:
echo "Something" > /home/firdin/tmp.make
b.子目录的makefile向上层makefile传递参数目前仅掌握了通过环境变量传递参数的方式,如上的第二个描述,反向使用。
2.1、顶级Makefile [1] 的内容
#!/bin/bash
#指定CPU架构
ARCH := x86
#ARCH := arm
#ARCH := powerpc
#指定交叉编译工具
ARM_CROSS_COMPILE := arm-none-eabi-
POWERPC_CROSS_COMPILE := powerpc-fsl-linux-
#已有内核路径
LOCAL_KERNEL := /lib/modules/$(shell uname -r)/build
ARM_KERNEL := /home/firdin/arm_kernel
POWERPC_KERNEL := /home/firdin/ppc_kernel
#当前使用的内核路径
KERNEL_DIR := $(LOCAL_KERNEL)
#依据架构选择当前工程的编译工具
ifeq (,$(ARCH))#默认架构x86
CC := gcc
endif
ifeq (x86,$(ARCH))
CC := gcc
endif
ifeq (powerpc,$(ARCH))
CC := $(POWERPC_CROSS_COMPILE)gcc
endif
ifeq (arm,$(ARCH))
CC := $(ARM_CROSS_COMPILE)gcc
endif
#工程顶级目录
TOP_DIR := $(shell pwd)
#编译用户层的可执行文件
all:
@echo "**************************** Start Make Project *********************************"
$(MAKE) -C $(TOP_DIR)/user TOP_DIR=$(shell pwd) CC=$(CC) ARCH=$(ARCH) all_app
cp $(TOP_DIR)/user/objs/* $(TOP_DIR)/output/ -rf
$(MAKE) -C $(TOP_DIR)/kernel TOP_DIR=$(shell pwd) CC=$(CC) KDIR=$(KERNEL_DIR) ARCH=$(ARCH) ko_module
cp $(TOP_DIR)/kernel/*.ko $(TOP_DIR)/output -rf
@echo "**************************** Make Project Finished ******************************"
#清除工程编译的文件
clean:
$(MAKE) -C $(TOP_DIR)/user TOP_DIR=$(shell pwd) CC=$(CC) ARCH=$(ARCH) clean
$(MAKE) -C $(TOP_DIR)/kernel TOP_DIR=$(shell pwd) CC=$(CC) KDIR=$(KERNEL_DIR) ARCH=$(ARCH) clean
rm $(TOP_DIR)/output/* -rf
2.2、kernel目录下 Makefile [2]的内容
将C文件编译成ko,并接受上层目录的makefile的调用,且可以独立于上层目录执行。由于有独立性,故不适用#include。
#!/bin/bash
#该makefile只适用于单个驱动文件编译
########## 驱动文件名,生成目标名依赖源文件名 ##########
OBJS := drv_module.c
############ 以下参数自动匹配不需要修改 ##########
#根目录选择,该选项目的:使任意目录的Makefile可以独立执行不受上层Makefile影响
#独立使用子目录make时,makefile参数使用默认配置
ifeq (,$(TOP_DIR))
TOP_DIR := $(shell pwd)/..
CURRENT_DIR := $(shell pwd)
else
CURRENT_DIR := $(TOP_DIR)/kernel
endif
#编译完成后输出的ko文件名
KO_TARGET := $(CURRENT_DIR)/$(basename $(OBJS)).ko
#默认架构:x86
ifeq (,$(ARCH))
ARCH := x86
endif
ifeq (,$(CC))
CC := gcc
endif
#指定链接器
LD := ld
#默认内核为本地内核
ifeq (,$(KDIR))
KDIR := /lib/modules/$(shell uname -r)/build
endif
#目标*.o
obj-m := $(basename $(OBJS)).o
ko_module:
@echo "**********************************************Start Make KO-Module **********************************************"
make -C $(KDIR) M=$(CURRENT_DIR) modules ARCH=$(ARCH)
cp $(KO_TARGET) $(TOP_DIR)/output/
@echo "********************************************** Make KO-Module Finished **********************************************"
clean:
make -C $(KDIR) M=$(CURRENT_DIR) clean
2.3、user目录下的Makefile [2]的内容
user下的makefile调用下层的两个makefile,编译两部分的应用,并拷贝到user的objs目录作为缓存,应用可以是二进制可执行程序也可以是静态库等。编译成静态库则其他部分编译时也需要对应的修改和调用。
#!/bin/bash
######### 以下参数传递给源文件作为宏定义使用 ########
#debug开关,用于调试
DEBUG_SW := OFF
#软件版本定义
APP_PART1_VER := V-18.10.13.12
APP_PART2_VER := V-18.10.13.08
############ 以下参数自动匹配不需要修改 ##########
#根目录选择,该选项目的:使任意目录的Makefile可以独立执行不受上层Makefile影响
#独立使用子目录make时,makefile参数使用默认配置
ifeq (,$(TOP_DIR))
TOP_DIR := $(shell pwd)/..
CURRENT_DIR := $(shell pwd)
else
CURRENT_DIR := $(TOP_DIR)/user
endif
#指定运行平台
ifeq (,$(ARCH))#默认架构x86
ARCH := x86
endif
ifeq (,$(CC)) #默认编译器gcc(x86)
CC := gcc
endif
#传递宏定义
ifeq ($(DEBUG_SW),ON)
DEBUG := ON
endif
ifeq ($(DEBUG_SW),OFF)
DEBUG := OFF
endif
#编译执行对象
all_app:
@echo "********************************************** Start Make ALL-Application **************************************"
$(MAKE) -C $(CURRENT_DIR)/app_part_1 CC=$(CC) TOP_DIR=$(CURRENT_DIR) APP_VER=$(APP_PART1_VER) DEBUG=$(DEBUG) ARCH=$(ARCH) app_part1
$(MAKE) -C $(CURRENT_DIR)/app_part_2 CC=$(CC) TOP_DIR=$(CURRENT_DIR) APP_VER=$(APP_PART2_VER) DEBUG=$(DEBUG) ARCH=$(ARCH) app_part2
@echo "********************************************** Make ALL-Application Finished ************************************"
clean:
$(MAKE) -C $(CURRENT_DIR)/app_part_1 TOP_DIR=$(shell pwd) CC=$(CC) ARCH=$(ARCH) clean
$(MAKE) -C $(CURRENT_DIR)/app_part_2 TOP_DIR=$(shell pwd) CC=$(CC) ARCH=$(ARCH) clean
rm $(CURRENT_DIR)/objs/* -rf
2.4、user/app_part_1目录的 Makefile [3]内容
#!/bin/bash
#判断顶级目录是否为空
ifeq (,$(TOP_DIR))
CURRENT_DIR := $(shell pwd)
TOP_DIR := $(shell pwd)/..
else
CURRENT_DIR := $(TOP_DIR)/app_part_1
endif
########### 编译APP1所需源文件,依需修改添加 #############
#源文件位置
OBJS := $(CURRENT_DIR)/src/main.c
OBJS += $(CURRENT_DIR)/src/function_1.c
OBJS += $(CURRENT_DIR)/src/function_2.c
OBJS += $(CURRENT_DIR)/src/function_3.c
#编译头文件路径
APP1_INCLUDE_PATH := -I $(CURRENT_DIR)/include/
#头文件路径增加
#APP1_INCLUDE_PATH += -I $(TOP_DIR)/public_include
#应用依赖库文件
ifeq (x86,$(ARCH))#x86平台依赖库文件
LIBS := $(TOP_DIR)/lib/libtest.a
endif
ifeq (powerpc,$(ARCH))#powerpc依赖库文件
LIBS := $(TOP_DIR)/lib/libtest-ppc.a
endif
ifeq (arm,$(ARCH))#arm依赖库文件
LIBS := $(TOP_DIR)/lib/libtest-arm.a
endif
############ 以下参数自动匹配 ###############
#默认架构和编译器:x86 gcc
ifeq (,$(CC))
CC := gcc
endif
ifeq (,$(ARCH))
ARCH := x86
endif
#依据架构确定生成目标名
ifeq (x86,$(ARCH))
TARGET := $(CURRENT_DIR)/app1
endif
ifeq (powerpc,$(ARCH))
TARGET := $(CURRENT_DIR)/app1-ppc
endif
ifeq (arm,$(ARCH))
TARGET := $(CURRENT_DIR)/app1-arm
endif
#交换软件版本定义,以修改时间为版本号
ifeq (,$(APP_VER))
APP_VER := V-18.11.13.01
endif
#获取编译的时间
GET_DATE := $(shell date +%F)
GET_TIME := $(shell date +%H:%M:%S)
######## Makefile传递给C文件的宏定义 ########
#编译时间
MAKE_DEFINE := -D MAKE_TIME=\"$(GET_DATE)\ $(GET_TIME)\"
#DEBUG模式是否打开 DEBUG_CMD
ifeq ($(DEBUG),ON)
MAKE_DEFINE += -D DEBUG
MAKE_DEFINE += -D LOG_PRINT_EN #log记录时是否打印
endif
#软件版本
MAKE_DEFINE += -D APP_VER=\"$(APP_VER)\"
#大小端定义:IP地址的处理用到
ifeq (powerpc,$(ARCH))
MAKE_DEFINE += -D BIG_ENDIAN
endif
#编译依赖操作系统现有库
PROJECT_LIB := -lpthread
#gcc编译选项
GCC_CHOOSE := -Wall -W -Os -g
app_part1:
@echo "---------------------------------------------- Start Make APP-Part-1 -------------------------------------------"
@echo "app1 Source file Preview:<"$(OBJS)" >Source File List End\n"
$(CC) $(MAKE_DEFINE) $(GCC_CHOOSE) $(APP1_INCLUDE_PATH) -o $(TARGET) $(OBJS) $(LIBS)
cp $(TARGET) $(TOP_DIR)/objs/
@echo "---------------------------------------------- Start Make APP-Part-1 -------------------------------------------"
clean:
rm $(TARGET) -rf
2.5、user/app_part_2目录下的Makefile [3]内容
#!/bin/bash
#顶级目录传参判定
ifeq (,$(TOP_DIR))
CURRENT_DIR := $(shell pwd)
TOP_DIR := $(CURRENT_DIR)/..
else
CURRENT_DIR := $(TOP_DIR)/app_part_2
endif
########### 编译交换的shell所依赖的文件,依需修改添加 ###########
#指定编译shell时所依赖的源文件
OBJS := $(CURRENT_DIR)/src/main.c
OBJS += $(CURRENT_DIR)/src/function_1.c
OBJS += $(CURRENT_DIR)/src/function_2.c
#头文件目录
APP2_INCLUDE_PATH := -I $(CURRENT_DIR)/include
#增加头文件索引路径
#APP2_INCLUDE_PATH += -I $(TOP_DIR)/public_include
#应用依赖库文件
ifeq (x86,$(ARCH))#x86平台依赖库文件
LIBS := $(TOP_DIR)/lib/libtest.a
endif
ifeq (powerpc,$(ARCH))#powerpc依赖库文件
LIBS := $(TOP_DIR)/lib/libtest-ppc.a
endif
ifeq (arm,$(ARCH))#arm依赖库文件
LIBS := $(TOP_DIR)/lib/libtest-arm.a
endif
############ 以下参数依据上层Makefile传递自动匹配 ###########
#默认架构和编译器
ifeq (,$(CC))
CC := gcc
endif
ifeq (,$(ARCH))
ARCH := x86
endif
#生成目标名定义
ifeq (x86,$(ARCH))
TARGET := $(CURRENT_DIR)/app2
endif
ifeq (powerpc,$(ARCH))
TARGET := $(CURRENT_DIR)/app2-ppc
endif
ifeq (arm,$(ARCH))
TARGET := $(CURRENT_DIR)/app2-arm
endif
#工程编译时间,C文件中有该宏定义的使用,必须定义
GET_DATE := $(shell date +%F)
GET_TIME := $(shell date +%H:%M:%S)
MAKE_DEFINE := -D MAKE_TIME=\"$(GET_DATE)\ $(GET_TIME)\"
#DEBUG模式是否打开
ifeq ($(DEBUG),ON)
MAKE_DEFINE += -D DEBUG_BCM_SHELL
endif
#gcc编译选项
GCC_CHOOSE := -Wall -W -Os -g
#版本信息:顶层Makefile若无传参则使用本地Makefile数据
ifeq (,$(APP_VER))
APP_VER := V-18.11.13.01
endif
#版本宏定义传递
MAKE_DEFINE += -D APP_VER=\"$(APP_VER)\"
#大小端定义,显示字符串时需要用到
ifeq ($(ARCH),powerpc)
MAKE_DEFINE += -D BIG_ENDIAN
else
MAKE_DEFINE += -D LITTEN_ENDIAN
endif
app_part2:
@echo "---------------------------------------------- Start Make APP-Part-2 -------------------------------------------"
@echo "app2 Source File Preview:<"$(OBJS)" >Source File List End"
$(CC) $(MAKE_DEFINE) $(GCC_CHOOSE) $(APP2_INCLUDE_PATH) -o $(TARGET) $(OBJS) $(LIBS)
cp $(TARGET) $(TOP_DIR)/objs
@echo "-------------------------------------------- Make APP-Part-2 Finished ------------------------------------------"
clean:
rm $(TARGET) -rf
三、各级目录下的源码
3.1、kernel目录下的示例源码
drv_module.c文件内容示例
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL"); /* Linux许可证 */
MODULE_AUTHOR("Firdin"); /* 作者名 */
#include "drv_module.h"
int dev_init(void)
{
printk("[hello dev Init]:Completed!\n");
return 0;
}
void dev_exit( void)
{
printk("[hello dev Exit]:Exit OK!\n");
}
module_init(dev_init);
module_exit(dev_exit);
drv_module.h文件内容示例:
#ifndef _DRV_BCM5396_MIIM_H
#define _DRV_BCM5396_MIIM_H
/* Nothing */
#endif
3.2、user/app_part_1目录下源码示例
main.c文件示例
#include <stdio.h>
#include "function_1.h"
#include "function_2.h"
#include "function_3.h"
extern void libtest_func();
int main()
{
printf("This is APP-1!Test For Makefile!\nAPP-1 Information List:\n\tAPP1-Version:%s\n\tMake-Time:%s\n",APP_VER,MAKE_TIME);
test_func1();
test_func2();
test_func3();
libtest_func();
#ifdef DEBUG
printf("APP-1 Define Switch '%s' Open!\n","DEBUG");
#endif
#ifdef LOG_PRINT_EN
printf("APP-1 Define Switch '%s' Open!\n","LOG_PRINT_EN");
#endif
#ifdef LITTEN_ENDIAN
printf("This is a litten endian CPU!\n");
#endif
#ifdef BIG_ENDIAN
printf("This is a big endian CPU!\n");
#endif
return 0;
}
function_1.c示例
#include <stdio.h>
#include "function_1.h"
void test_func1()
{
printf("This is print from:\n\
FUNCTION:%s\n\
Line%d\n\
FILE:%s\n",\
__FUNCTION__,__LINE__,__FILE__);
}
function_2.c和function_3.c与function_1.c内容类型,仅修改函数中的数字部分
function_1.h示例
#ifndef _function_1_H_
#define _function_1_H_
void test_func1();
#endif
function_2.h和function_3.h内容与function_1.h内容类似,函数声明处数字不一样。
lib目录下的libtest.a通过以下文件编译而成:
libtest.c内容示例:
#include <stdio.h>
void libtest_func()
{
printf("This is print form Libtes.a!!!\n");
}
编译生成*.o:gcc -c libtest.c
生成库文件:ar crs libtest.a libtest.o
拷贝到对应的目录下。
最后,在顶级目录执行make即可编译整个工程,工程已验证可行!
四、Makefile传参总结
4.1、顶层Makefile向子目录Makefile传递参数
如第二章开始描述
4.2、底层Makefile向顶层Makefile传递参数
通过临时文件:
echo "Something" > /home/firdin/tmp.make
4.3、在make工程时传递参数
执行命令时:make ARCH=x86
4.4、其他传递参数形式
待补充完善。