C

the beginning

incompatible implicit declaration of built-in function

编译时警告: incompatible implicit declaration of built-in function ‘exit’

原因: C 语言中, 使用一个没有预先声明的函数称为隐式声明. 隐式声明如果调用成功, 返回类型是 int. 现在 GCC 为一些标准函数提供了内置定义. 如果隐式声明和内置定义不匹配, 就得到上面的警告信息.

解决方法: 在使用前定义函数, 通常可以通过 include 合适的头文件. exit 在 stdlib.h 中定义.

Makefile

Makefile 語法簡介 的笔记

变量宣告

:= 语法

make 会将整个 Makefile 展开后再决定变量的值:

x = foo
y = $(x) bar
x = xyz
# y 的值为 xyz bar

可以用 := 来避开这个问题. := 表示变量的值决定于它在 Makefile 中的位置, 而不是整个 Makefile 展开后最终的值:

x := foo
y := $(x) bar
x := xyz
# y 的值为 foo bar

?= 语法

若变量未定义, 则使用新值, 否则采用原有值:

FOO ?= bar

+= 语法

例:

CFLAGS = -Wall -g
CFLAGS += -O2

此时 CFLAGS 的值就变成 -Wall -g -O2 了.

define 语法

define 的唯一优点是可以使变量直接使用换行. 例:

define foo
uname -a
echo $$SHELL
endef

all:
    $(foo)

上例等同于:

foo = uname -a; echo $$SHELL

all:
    $(foo)

规则

指示 make 如何编译. 主要语法:

target: dependencies
<Tab>Commands

make 在编译时, 若发现 target 比较新, 也就是 dependencies 都比 target 旧, 将不会重新建立 target.

fake 项目

fake 项目 不会建立 target 档案, 为了避免 make 去判断 target 是否是 fake 项目, 建议用 .PHONY 来指定项目为 fake 项目:

.PHONY: clean
clean:
    rm *.o

上例中如果不使用 .PHONY 指定 clean 为 fake 项目, 并且目录中同时存在一个名为 clean 的文件, 则 clean 这个项目会被认为要建立 clean 文件, 但这个项目没有任何 dependencies, 因为 clean 项目会永远被视为 update-to-date, 永远不会被执行.

Commands

每条 Command 会启动一个新的 Shell, 预设为 /bin/sh. 若执行完某条 Command 但传回了错误值, make 就会中断执行.

因为每条 Command 会启动一个新的 Shell, 所以每条法则必须写在同一行. 比如用 if 来进行条件判断时, 可以用 ; 来分隔指令:

all:
    if [ -f foo ]; then rm foo; fi

以下是错误示范:

all:
    cd subdir; $(MAKE)

因为 make 只会检查最后一个指令的传回值, 所以在这条指令中, 即使 subdir 不存在, 仍会继续执行 $(MAKE), 而产生不可预期的结果. 为了避免这个问题, 可以利用 && 来检查指令知否执行成功, 再决定是否执行下条指令. 如:

all:
    cd subdir && $(MAKE)
特别字符

@: 不要显示执行的指令. make 会一行一行将正在执行的 Commands 显示在屏幕上, 可以使用 @ 暂时关闭这个功能. -: 表示即使该行指令出错, 也不会中断执行. 例:

.PHONY: clean
clean:
    @echo "Clean..."
    -rm *.o