通用Makefile模板与编译Linux的脚本

前言:

由于工作,编译应用层程序、编译动态&静态库、编译Linux驱动模块,因此需要一个强大的Makefile的模板,只需要通过开关方便控制编译方向。
该模板参考了网上许多的Makefile模板,也参考了Linux内核中的Makefile,修改为适应工程使用的Makefile模板,分享出来供大家参考使用。测试可用,若需要使用,需要修改部分参数。
后面附带一个用于Linux源码下的内核配置(.config)、编译内核(zImage)、编译设备树(.dtb)、编译内核模块(.ko)、清除配置的Shell脚本。需要执行某个功能时,指定交叉编译器的命令不可注释(条件),取消注释可实现该功能的命令,其他行功能的命令继续注释。

设置开机自动将交叉编译器的路径添加进环境变量里
# 1 打开/etc/profile文件
$ sudo vim /etc/profile

# 2 将下面的内容添加到/etc/profile文件最后一行
export PATH=$PATH:/XXX/gcc-linaro-arm-linux-gnueabihf-4.7-2012.11-20121123_linux/bin/

# 3 在当前bash环境下读取并执行/etc/profile中的命令
$ source /etc/profile

# 4 检测检查编译器是否可用
$ arm-linux-gnueabihf-gcc -v

1. 编译应用层程序的Makefile模板

# 目标文件的运行平台: ARM x86 MIPS RISC
RUN_ARCH := ARM

# 生成指定的目标文件名,目标文件的拷贝路径
TARGET_FILE  := testsizeof
TARGET_PATH  := ~/nfs/

# 编译工具
ifeq ($(RUN_ARCH), ARM)
	export CROSS := arm-linux-gnueabihf-
else ifeq ($(RUN_ARCH), x86)
	export CROSS := 
endif
# CROSS = arm-linux-gnueabihf-
export CC       = $(CROSS)gcc
export CXX      = $(CROSS)g++
export AR       := $(CROSS)ar
export AS       := $(CROSS)as
export STRIP    := $(CROSS)strip
export CPP      = $(CC) -E
export OBJCOPY  = $(CROSS)objcopy
export OBJDUMP  = $(CROSS)objdump
export NM       = $(CROSS)nm
export LD       = $(CROSS)ld

# 源文件,自动找所有.c和.cpp文件,并将目标定义为同名.o文件
SOURCE  := $(wildcard *.c) $(wildcard *.cpp)
OBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))

# 编译参数
LIBS    := -L/mnt/hgfs/Share_Ubuntu/Temp_File/test4/Lib/lib
LDFLAGS := -ltest
DEFINES :=
INCLUDE := -I /mnt/hgfs/Share_Ubuntu/Temp_File/test4/Lib/include/
CFLAGS  := -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H


# 下面的基本上不需要做任何改动了
.PHONY : everything objs clean veryclean rebuild
everything : $(TARGET_FILE)
all : $(TARGET_FILE)
objs : $(OBJS)
rebuild: veryclean everything
clean :
	rm -rf *.o $(TARGET_FILE)
veryclean : clean
	rm -rf $(TARGET_FILE)

# 编译.c/.cpp文件为.o文件
$(OBJS): $(SOURCE)
	@echo "\n **** Top Dir **** .c --> .o **** "
	$(CXX) $(CXXFLAGS) -o $@ -c $<
	@echo " **** Top Dir **** .c --> .o **** done **** \n"

# 编译.o文件为.elf文件
$(TARGET_FILE) : $(OBJS)
	@echo " **** Top Dir **** .o --> .elf **** "
	$(CXX) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)
	@echo " **** Top Dir **** .o --> .elf **** done **** \n"
# 拷贝目标文件
	cp $(TARGET_FILE) $(TARGET_PATH)

2. 编译动态库/静态库的Makefile模板

# 当前的目录(编译前)结构为:tree Lib/
Lib/
├── bin
├── include
│   └── 1.h
├── lib
├── Makefile
└── src
    ├── b.c
    ├── c.c
    └── Makefile

# 编译后的目录结构为:tree Lib/
Lib/
├── bin
│   ├── b.o
│   └── c.o
├── include
│   └── 1.h
├── lib
│   ├── libtest.a
│   └── libtest.so
├── Makefile
└── src
    ├── b.c
    ├── c.c
    └── Makefile

# 相应解释:
# 编译将src/目录下的.c/.cpp文件编译为bin/目录下的.o文件,再将bin/目录下的.o文件集成为
# lib/目录下的.so/.a文件

# 该项目的顶级目录和src目录下各有一个Makefile文件:
# 顶级目录下的Makefile为总的Makefile,负责驱动src目录下的Makefile和生成库文件;
# src目录下的Makefile为子目录的Makefile,负责将.c和.cpp文件编译为相应的.o文件;
  1. 顶级目录的Makefile文件内容:
# 生成链接库,包含: DynamicLib(动态库, 也称共享库) StaticLib(静态库)
LIB_NAME    := libtest
DYNAMIC_LIB := $(LIB_NAME).so
STATIC_LIB  := $(LIB_NAME).a

# 目标文件的拷贝路径
TARGET_PATH  := /usr/lib/testlib/

# 目标文件的运行平台: ARM x86 MIPS RISC PowerPC
RUN_ARCH := ARM

# 编译工具
ifeq ($(RUN_ARCH), ARM)
	export CROSS := arm-linux-gnueabihf-
else ifeq ($(RUN_ARCH), x86)
	export CROSS :=
endif

export CC       := $(CROSS)gcc
export CXX      := $(CROSS)g++
export AR       := $(CROSS)ar cru# cru
export AS       := $(CROSS)as
export STRIP    := $(CROSS)strip
export CPP      := $(CC) -E
export OBJCOPY  := $(CROSS)objcopy
export OBJDUMP  := $(CROSS)objdump
export NM       := $(CROSS)nm
export LD       := $(CROSS)ld
export RANLIB	:= $(CROSS)ranlib

# 工程顶级目录,
TOP_DIR     := $(shell pwd)
SOURCE_DIR  := $(TOP_DIR)/src
INCLUDE_DIR := $(TOP_DIR)/include
BIN_DIR     := $(TOP_DIR)/bin
LIB_DIR     := $(TOP_DIR)/lib


LIBS	:=# -lName
LDFLAGS	:=# -L Path/Lib/
DEFINES	:=# -DXXX_XXX_H
INCLUDE	:= -I $(INCLUDE_DIR)
CFLAGS	:= -fPIC -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
SHARE	:= -shared
STATIC	:= -static



# 源文件,自动寻找src/目录下所有.c和.cpp文件,去除相应的路径后,并将定义为bin/目录下同名.o文件
SOURCE_HAVE_DIR  := $(wildcard src/*.c) $(wildcard src/*.cpp)
SOURCE_NOT_DIR   := $(notdir $(SOURCE_HAVE_DIR))
OBJS             := $(patsubst %.c, bin/%.o, $(patsubst %.cpp, bin/%.o, $(SOURCE_NOT_DIR)))


all : $(LIB_DIR)/$(DYNAMIC_LIB) $(LIB_DIR)/$(STATIC_LIB)


# 编译.c/.cpp文件为.o文件
$(OBJS): $(SOURCE_HAVE_DIR)
	@echo "\n **** Top Dir **** .c/.cpp --> .o **** "
	make -C $(SOURCE_DIR) TOP_DIR=$(TOP_DIR) RUN_ARCH=$(RUN_ARCH)
	@echo " **** Top Dir **** .c/.cpp --> .o **** Done **** \n"

# 生成动态库
$(LIB_DIR)/$(DYNAMIC_LIB): $(OBJS)
	@echo " **** Top Dir **** .o --> .so **** "
	$(CXX) $(CXXFLAGS) $(SHARE) -o $@ $^ $(LDFLAGS) $(LIBS)
#	$(STRIP) $(LIB_DIR)/$(DYNAMIC_LIB)
	@echo " **** Top Dir **** .o --> .so **** Done **** \n"

# 生成静态库
$(LIB_DIR)/$(STATIC_LIB): $(OBJS)
	@echo " **** Top Dir **** .o --> .a **** "
	$(AR) $@ $^
	$(RANLIB) $@
	@echo " **** Top Dir **** .o --> .a **** Done **** \n"
	
#	sudo mkdir -p $(TARGET_PATH)
#	sudo cp -rf $(INCLUDE_DIR) $(TARGET_PATH)
#	sudo cp -rf $(LIB_DIR) $(TARGET_PATH)

.PHONY: clean

clean:
	rm -rf $(LIB_DIR)/*.so $(LIB_DIR)/*.a
	make -C src  clean

  1. src目录下的Makefile文件内容:
# TOP_DIR 可以为根目录传递给子目录的参数
ifeq ($(TOP_DIR), )
	TOP_DIR     := $(shell pwd)/..
	SOURCE_DIR  := $(shell pwd)
	INCLUDE_DIR := $(TOP_DIR)/include/
	BIN_DIR     := $(TOP_DIR)/bin
else
	SOURCE_DIR  := $(TOP_DIR)/src
	INCLUDE_DIR := $(TOP_DIR)/include/
	BIN_DIR     := $(TOP_DIR)/bin
endif

# 编译工具选择,RUN_ARCH 可以为根目录传递给子目录的参数
ifeq ($(RUN_ARCH), )
	export CROSS := arm-linux-gnueabihf-
else ifeq ($(RUN_ARCH), ARM)
	export CROSS := arm-linux-gnueabihf-
else ifeq ($(RUN_ARCH), x86)
	export CROSS :=
endif

export CC       := $(CROSS)gcc
export CXX      := $(CROSS)g++
export AR       := $(CROSS)ar cru
export AS       := $(CROSS)as
export STRIP    := $(CROSS)strip
export CPP      := $(CC) -E
export OBJCOPY  := $(CROSS)objcopy
export OBJDUMP  := $(CROSS)objdump
export NM       := $(CROSS)nm
export LD       := $(CROSS)ld
export RANLIB	:= $(CROSS)ranlib


LIBS	:=# -lname
LDFLAGS	:=# -L ../lib/
DEFINES	:=
INCLUDE	:= -I $(INCLUDE_DIR)
CFLAGS	:= -fPIC -g -Wall -O3 $(DEFINES) $(INCLUDE)
CXXFLAGS:= $(CFLAGS) -DHAVE_CONFIG_H
SHARE	:= -shared -o



# 源文件,自动找所有.c和.cpp文件,并将目标定义为同名.o文件
SOURCE  := $(wildcard *.c) $(wildcard *.cpp)
OBJS    := $(patsubst %.c, $(BIN_DIR)/%.o, $(patsubst %.cpp, $(BIN_DIR)/%.o, $(SOURCE)))


all: $(OBJS)

# 将.cpp程序编译为相应的.o文件
$(BIN_DIR)/%.o: $(SOURCE_DIR)/%.cpp
	@echo " **** SRC Dir **** C Plus Plus Compiling $< : "
	$(CXX) -c $(CXXFLAGS) $< -o $@
	@echo " Compiling Done \n"

# 将.c程序编译为相应的.o文件
$(BIN_DIR)/%.o: $(SOURCE_DIR)/%.c
	@echo " **** SRC Dir **** C Language Compiling $< : "
	$(CXX) -c $(CXXFLAGS) $< -o $@
	@echo " Compiling Done \n"

.PHONY: clean

clean:
	rm -rf $(BIN_DIR)/*.o

3. 编译Linux驱动模块的Makefile模板

# 目标文件的文件名,目标文件的拷贝路径
SOURCE_FILE  := ltc
TARGET_PATH  := ~/nfs/

# 要生成的模块名
obj-m = $(SOURCE_FILE).o
# Linux源码根目录
KDIR := /XXXX/linux
# 编译器路径
COMPILER_PATH := /opt/XXXX/bin/arm-linux-gnueabihf-
# 目标文件的运行平台: ARM x86 MIPS RISC
RUN_ARCH := ARM
# 当前路径,即驱动程序的位置
PWD := $(shell pwd)
# 编译的中间文件
RM_FILE := *.o .*.cmd *.mod.c .tmp_versions *.mod.o *.symvers *.order *Module.markers


# 下面的基本上不需要做任何改动了
default:
	make ARCH=$(RUN_ARCH) CROSS_COMPILE=$(COMPILER_PATH) -C $(KDIR) M=$(PWD) modules
	rm -rf $(RM_FILE)
	cp -rf $(SOURCE_FILE).ko $(TARGET_PATH)
clean:
	rm -rf $(RM_FILE) *.ko

4. 编译Linux内核的Shell脚本

4.1 编译的环境搭建
# 编译Linux内核,在编译之前安装制作uImage需要的工具u-boot-tools、uboot-mkimage
sudo apt-get install u-boot-tools
sudo apt-get install uboot-mkimage
4.1 编译内核的Shell脚本
#!/bin/sh

# 指定交叉编译器
export CROSS_COMPILE=/opt/XXXX/bin/arm-linux-gnueabihf-

# 清空之前的Linux配置,复位配置文件 ====一般该功能为屏蔽状态
#make ARCH=arm socfpga_defconfig

# 图形化配置(.config)
make ARCH=arm menuconfig

# 编译Linux内核(zImage)
#make ARCH=arm uImage LOADADDR=0x8000

# 编译设备树(.dtb)
#make ARCH=arm dtbs

# 编译内核模块(.ko)
#make ARCH=arm modules

# 将相应路径下的内核模块加载进内核中
#sudo make ARCH=arm INSTALL_MOD_PATH=/XXX/Path/ modules_install

5. 编译工具链的介绍

GCC原名为GNU C Compiler(GNU C语言编译器),只能处理C语言。但其很快扩展,变得可处理C++,后来又扩展为能够支持更多编程语言,如Fortran、Pascal、Objective -C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。

# 用交叉编译工具链来介绍编译工具链

# ls /opt/gcc-linaro-arm-linux-gnueabihf/bin/
arm-linux-gnueabihf-addr2line     arm-linux-gnueabihf-gfortran
arm-linux-gnueabihf-ar            arm-linux-gnueabihf-gprof
arm-linux-gnueabihf-as            arm-linux-gnueabihf-ld
arm-linux-gnueabihf-c++           arm-linux-gnueabihf-ld.bfd
arm-linux-gnueabihf-c++filt       arm-linux-gnueabihf-ldd
arm-linux-gnueabihf-cpp           arm-linux-gnueabihf-ld.gold
arm-linux-gnueabihf-ct-ng.config  arm-linux-gnueabihf-nm
arm-linux-gnueabihf-elfedit       arm-linux-gnueabihf-objcopy
arm-linux-gnueabihf-g++           arm-linux-gnueabihf-objdump
arm-linux-gnueabihf-gcc           arm-linux-gnueabihf-pkg-config
arm-linux-gnueabihf-gcc-4.7.3     arm-linux-gnueabihf-pkg-config-real
arm-linux-gnueabihf-gcc-ar        arm-linux-gnueabihf-ranlib
arm-linux-gnueabihf-gcc-nm        arm-linux-gnueabihf-readelf
arm-linux-gnueabihf-gcc-ranlib    arm-linux-gnueabihf-size
arm-linux-gnueabihf-gcov          arm-linux-gnueabihf-strings
arm-linux-gnueabihf-gdb           arm-linux-gnueabihf-strip

# 可以看出该交叉编译工具链的命名为:arm-linux-gnueabihf-XX
# 
# XX最常用的为:
# gcc:用于编译C语言。(“C语言编译器”)
# g++:用于编译C++。(“C++编译器”)
# 
# 部分XX代表的含义如下:
# addr2line:把程序地址转换为文件名和行号。
# ar:建立、修改、提取归档文件。(江湖人称“库管理器”)
# as:主要用来编译C编译器gcc输出的汇编文件。
# ld:把.o文件链接成.elf文件。(江湖人称“链接器”)
# nm:列出目标文件中的符号。
# objcopy:文件格式转换。(江湖人称“文件格式转换转换器”)
# objdump:显示一个或多个目标文件的信息,主要用于反编译。(江湖人称“反汇编器”)
# ranlib:产生归档文件索引,并将其保存到这个归档文件中。
# readelf:显示.elf文件格式可执行文件的信息。(江湖人称“elf文件工具”)
# size:列出目标文件每一段的大小以及总体的大小。
# strings:打印某个文件的可打印字符串。
# strip:丢弃目标文件中的全部或特定符号,减小文件体积。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值