makefile_2

博客介绍了Makefile中不同赋值符号的作用,如“=”“:=”“?=”“+=”。还给出了三个DEMO,包括configurate脚本等bash脚本示例、小型程序makefile典例以及嵌入式makefile demo,涉及静态库生成和链接脚本使用等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

“=” 是最基本的赋值。make会将整个makefile展开后,再决定变量的值。也就是说,变量的值将会是整个makefile中最后被指定的值。

x = foo
y = $(x) bar
x = xyz

      在上例中,y的值将会是 xyz bar ,而不是 foo bar

“:=”表示变量的值决定于它在makefile中的位置,而不是整个makefile展开后的最终值。

x := foo
y := $(x) bar
x := xyz

      在上例中,y的值将会是 foo bar ,而不是 xyz bar 了

?= 是如果没有被赋值过就赋予等号后面的值
+= 是添加等号后面的值

  1. $@ (full target name of the current target)

  2. $? (returns the dependencies that are newer than the current target)

  3. $* (returns the text that corresponds to % in the target)

  4. $< (name of the first dependency)

  5. $^ (name of all the dependencies with space as the delimiter)

DEMO 1

configurate脚本,就是一段bash脚本

# linux系统除了提供位置参数还提供内置参数,内置参数如下: 
# $0 ----脚本名称
# $# ----传递给程序的总的参数数目  
# $? ----上一个代码或者shell程序在shell中退出的情况,如果正常退出则返回0,反之为非0值。   
# $* ----传递给程序的所有参数组成的字符串。(尽量使用$@并将其用双引号括起来)   
# $@----以"参数1" "参数2" ... 形式保存所有参数(传递给脚本/函数的所有参数)
# $n ----表示第几个参数,$1 表示第一个参数,$2 表示第二个参数 ... $0 ----表示当前程序的名称
# $$ ----本程序的(进程ID号)PID   
# $! ----上一个命令的PID

if [ "$1" = "x86" ];then
    echo "copy config.x86 to config";
    cp config.x86 config;
elif [ "$1" = "arm" ];then
    echo "copy config.arm to config"
    cp config.arm config;
else
    echo "Invalid param, usage: [./configure arm ...] or [./configure x86 ...]"
    exit 1
fi

#这是把多余的后面的那几个参数打印一下
if [ "$#" -gt 1 ];then

    i=0
    arglist=

    for arg in "$@"
    do
        if [ $i -ne 0 ];then
            arglist=$arglist" "$arg
        fi
        ((i++))
    done

    echo "arglist:"$arglist

    sed -i "/^PUBLIC_FLAGS.*$/ s/$/$arglist/" config
fi

config.arm文件

ARCH=arm
CROSS_COMPILE := arm-none-linux-gnueabi-

#这里指定了不使用优化,这样就可以使用gdb调试了!
PUBLIC_FLAGS:=-g -O0 -Wall -L$(ROOT_DIR)/libs/arm_linux -DARM_LINUX

#这是使用优化的gcc public flags,这样声称的binary/.so比较小,但不能gdb调试
#PUBLIC_FLAGS := -O2 -L$(ROOT_DIR)/libs/arm_linux -DARM_LINUX -fno-strict-aliasing -ffunction-sections -fdata-sections

CC := $(CROSS_COMPILE)gcc
G++ := $(CROSS_COMPILE)g++
AR  := $(CROSS_COMPILE)ar
LD  := $(CROSS_COMPILE)ld

export CC G++ AR LD

顶层目录makefile,下面这个include如果执行出错会终止,除非前面加上一个 -

include config

ROOT_DIR = $(shell pwd)
OUT_DIR = $(shell pwd)/build

export ROOT_DIR
export OUT_DIR

#用@告诉makefile这条 命令本身 不回显
#make执行时从第一个伪目标开始执行,这里即all
all: clean
    @if [ ! -d $(OUT_DIR) ]; then mkdir $(OUT_DIR);fi
    @$(MAKE) -j4 -C udrv
    @$(MAKE) -j4 -C app
    @echo "***********************************************"
    @echo "**        Build Finished                     **"
    @echo "***********************************************"

modules:
    @if [ ! -d $(OUT_DIR) ]; then mkdir $(OUT_DIR);fi
    @$(MAKE) -C kdrv
    @echo "***********************************************"
    @echo "**        Build Modules Finished             **"
    @echo "***********************************************"

modules_clean:
    @$(MAKE) -C kdrv clean
    @echo "***********************************************"
    @echo "**       Clean Modules Finished              **"
    @echo "***********************************************"

#命令前面有个 - 表示出错也不停止
.PHONY: clean
clean:
    @$(MAKE) -j4 -C udrv clean
    @$(MAKE) -j4 -C app clean
    -rm -rf  $(OUT_DIR)
    @echo "***********************************************"
    @echo "**        Clean Finished                     **"
    @echo "***********************************************"

udrv目录下的makefile

# ?=表示如果外面传进来(此前已经被定义)就用外面的, 否则就用等号后面的
# :=表示等号左边的值类似一个拷贝,当前就使用这个拷贝。后面再改变也和本次使用无关
# =表示等号左边的值类似一个全局变量
ROOT_DIR ?= $(shell pwd)/../
OUT_DIR ?= $(ROOT_DIR)/build
include $(ROOT_DIR)/config

export ROOT_DIR
export OUT_DIR

all: clean
    @if [ ! -d $(OUT_DIR) ]; then mkdir $(OUT_DIR);fi
    $(MAKE) -j4 -C util
    $(MAKE) -j4 -C ufrontend
    $(MAKE) -j4 -C udemux
    $(MAKE) -j4 -C si
    $(MAKE) -j4 -C avm

clean:
    -rm -rf $(OUT_DIR)
    $(MAKE) clean -C util
    $(MAKE) clean -C ufrontend
    $(MAKE) clean -C udemux
    $(MAKE) clean -C si
    $(MAKE) clean -C avm

avm目录下的makefile(avm下面的子目录就不再设makefile了)

# Build configurations

ROOT_DIR ?= $(shell pwd)/../../
OUT_DIR ?= $(ROOT_DIR)/build
include $(ROOT_DIR)/config

export ROOT_DIR
export OUT_DIR

######################################################
# Module library (or Module binary)

MODULE_LIBRARY = libzxdtv.so
MODULE_OBJS += $(patsubst %.c,%.o ,$(wildcard codec/audio_dec/*.c  codec/video_dec/*.c input/ts_dmx/*.c input/pvr_src/*.c))
MODULE_OBJS += $(patsubst %.c,%.o ,$(wildcard output/av_sync/*.c output/audio_out/*.c  output/video_out/*.c player/av_player/*.c))

######################################################
# Header files

MODULE_INCLUDE += -I./output/av_sync -I./output/audio_out  -I./output/video_out -I./player/av_player
MODULE_INCLUDE += -I../udemux/include
MODULE_INCLUDE += -I./ffmpeg

######################################################
# Module flags

#如果要生成binary而不是.so,LDFLAGS空着就好了
LDFLAGS += --shared -fPIC
LIB_FLAGS += -L$(OUT_DIR) -lzxutil -lpthread
LIB_FLAGS += -L./ffmpeg/arm-linux-lib/ -lavcodec -lavformat -lavutil -lswresample
######################################################
# Common dependency process

.PHONY:all clean

%.o: %.c
    $(CC) $(LDFLAGS) $(PUBLIC_FLAGS) $(MODULE_INCLUDE) -c $< -o $@

all : clean $(MODULE_OBJS)
    $(CC) $(MODULE_OBJS) $(PUBLIC_FLAGS) $(LDFLAGS) $(LIB_FLAGS) -o $(OUT_DIR)/$(MODULE_LIBRARY)

clean:
    -rm -f $(LIBS_DIR)/$(MODULE_LIBRARY)
    -rm -f $(MODULE_OBJS)

DEMO 2

小型程序makefile典例~~

若要编译得到so,则MODULE_BIN改为libxx.so,编译.c到.o时候加上-fPIC,链接时加上--shared 即可(此时不要有main()函数),得到的so加上.h文件就可以使用了

MODULE_BIN = maxHeapTest
#MODULE_SRCS = $(shell find . -name "*.c")
MODULE_OBJS += $(patsubst %.c,%.o ,$(wildcard ./*.c))
CC = gcc
OUT_DIR = .
PUBLIC_FLAGS:=-g -O0 -Wall
LDFLAGS = -lm -lz -lpthread
MODULE_INCLUDE = -I.

.PHONY:all clean

%.o: %.c
    $(CC) $(PUBLIC_FLAGS) $(MODULE_INCLUDE) -c $< -o $@

all : clean $(MODULE_OBJS)
    $(CC) $(MODULE_OBJS) $(PUBLIC_FLAGS) $(LDFLAGS) -o $(OUT_DIR)/$(MODULE_BIN)

clean:
    -rm -f $(OUT_DIR)/$(MODULE_BIN)
    -rm -f $(MODULE_OBJS)

DEMO 3

嵌入式makefile demo,以下为顶级makefile

CROSS   =  arm-none-linux-gnueabi-
CFLAGS := -nostdinc -nostdlib -fno-builtin -Wall -O2

INCLUDE = -I$(shell pwd)/include -I$(shell pwd)/start
ROOTDIR = $(shell pwd)/

SUBDIRS = start ucos ff9a device lib app avrnet
SUBOBJS := $(foreach n, $(SUBDIRS), obj/lib$(n).a)

export         CROSS INCLUDE CFLAGS ROOTDIR

#objs := start.o init.o  boot.o
#模式匹配当前目录下所有.c 文件,wildcard为一个预定义的宏
C_sources_files  = $(wildcard *.c)
S_sources_files  = $(wildcard *.S)
#模式匹配,由当前目录下所有.c文件得到对应的.o文件
C_objs          = $(patsubst %.c,%.o,$(C_sources_files))
S_objs          = $(patsubst %.S,%.o,$(S_sources_files))

bootloader.bin: $(S_objs) $(C_objs) $(SUBOBJS)
    #原来的那句,ld命令默认不会给你连接gcc库的
    #${CROSS}ld -Tboot.lds  -o bootloader.elf $^ 
    #连接阶段加入libgcc.a库,否则除法没法用啊
    ${CROSS}ld -Bstatic -Tboot.lds -L /usr/local/arm-linux-gcc4.4.3/lib/gcc/arm-none-linux-gnueabi/4.4.3 \
    -L /usr/local/arm-linux-gcc4.4.3/arm-none-linux-gnueabi/sys-root/usr/lib \
    -o bootloader.elf $^ --start-group -lgcc -lgcc_eh -lgcov -lc --end-group
    ${CROSS}objcopy -O binary -S bootloader.elf $@
    ${CROSS}objdump -D -m arm bootloader.elf > bootloader.dis 
    mv *.o *.bin *.elf *.dis ./obj

%.o:%.c
    ${CROSS}gcc $(CFLAGS) $(INCLUDE) -c -o $@ $<

%.o:%.S
    ${CROSS}gcc $(CFLAGS) $(INCLUDE) -c -o $@ $<

$(SUBOBJS) : $(SUBDIRS)

.PHONY: $(SUBDIRS)
$(SUBDIRS):
    make -C $@

.PHONY:clean
clean:
    for dir in $(SUBDIRS); do \
        make -C $$dir clean; \
    done
    rm -f *.elf *.o *.bin *.dis
    rm -f ./obj/*.elf ./obj/*.bin ./obj/*.dis ./obj/*.o ./obj/*.a

一下为子级makefile, 生成.a静态库

CURDIR = $(shell pwd)

#就为了得到lib<目录名>.a,累死我了..这样TARGET便具有自适应能力了
#<目录名>是当前父目录的名字和shell pwd不一样
TARGET = $(addsuffix .a,$(subst $(ROOTDIR),lib,$(CURDIR)))

#objs := start.o init.o  main.o led.o key.o adc.o Nand.o timer.o uart.o
#模式匹配当前目录下所有.c 文件,wildcard为一个预定义的宏
C_sources_files  = $(wildcard *.c)
S_sources_files  = $(wildcard *.S)

#模式匹配,由当前目录下所有.c文件得到对应的.o文件
C_objs          = $(patsubst %.c,%.o,$(C_sources_files))
S_objs          = $(patsubst %.S,%.o,$(S_sources_files))

$(TARGET):$(S_objs) $(C_objs)
    ${CROSS}ar -r -o $@ $^
    mv *.o *.a ../obj

%.o:%.c
    ${CROSS}gcc $(CFLAGS) $(INCLUDE) -c -o $@ $<
%.o:%.S
    ${CROSS}gcc $(CFLAGS) $(INCLUDE) -c -o $@ $<

.PHONY:clean
clean:
    rm -f *.elf *.o *.bin *.dis *.a

boot.lds链接脚本

SECTIONS
{
    . = 0x33f80000;
    .text :
    {
        ./obj/start.o (.text)
        ./obj/init.o (.text)
        *(.text)
    }
    . = ALIGN(4);
    .rodata :
    {
        *(.rodata*)
    }
    . = ALIGN(4);
    .data :
    {
        *(.data)
    }
    . = ALIGN(4);
    __bss_start = .;
    .bss :
    {
        *(.bss)
        *(COMMON)
    }
    __bss_end = .;
}

在 .c 中可以这么使用链接脚本中的变量

void __clear_bss(void)
{
    extern int __bss_start, __bss_end;
    int *p = &__bss_start;

    for (; p < &__bss_end; p++)
    *p = 0;
}

在 .S 中可以这么使用链接脚本中的变量

ldr r2, =__bss_start
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值