多级目录的makefile编写及参数传递

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/AMDDMA/article/details/81840546

目录

一、多级目录结构

二、各级Makefile内容

 三、各级目录下的源码

四、Makefile传参总结


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、其他传递参数形式

        待补充完善。

展开阅读全文

没有更多推荐了,返回首页