目录
1. Makefile
1.1. PHONY 的作用
单词 phony (即 phoney) 的意思是: 伪造的, 假的。来自 collins 的解释是:
If you describe something as phoney, you disapprove of it because it is false rather than genuine.
那么, 在 Makefile 中, .PHONY
后面的 target 表示的也是一个伪造的 target, 而不是真实存在的文件 target, 注意 Makefile 的 target 默认是文件。
举个例子
Makefile 内容如下:
clean:
rm -f test.txt
该目录下只有 test.txt
时, 执行 make, 得到结果如下:
rm -f test.txt
在该目录下 test.txt
和 clean
两个文件, 这时执行 make 命令, 得到结果如下:
make: "clean" 是最新的。
可以看到 make 会将 clean
文件当成目标文件, 而执行失败
如果将 Makefile 修改为如下内容:
.PHONY:clean
clean:
rm -f test.txt
然后再执行 make, 得到结果如下:
rm -f test.txt
即该 makefile 并没有将 clean
当成是目标文件, 所以执行成功
小结:
.PHONY: clean
o means the word "clean" doesn't represent a file name in this Makefile;
o means the Makefile has nothing to do with a file called "clean"
为什么需要.PHONY?
栗子:
如果make后,如果新建一个:touch clean文件,再执行make clean时,将不会执行rm命令,因为make clean和文件clean重名问题。为了避免出现这个问题,需要在Makefile里增加:.PHONY: clean
Makefile中非常三个变量:$@--目标文件,$^--所有的依赖文件,$<--第一个依赖文件。
1、创建hello.c文件
#include <stdio.h>
int main()
{
printf(".PHONY 测试!\n");
}
2、编写Makefile
$(CC) = gcc
all:hello test //hello和test为两目标文件
hello :hello.c
$(CC) -o $@ $<
test :hello.c
$(CC) -o $@ $<
clean:
rm hello
3、执行编译和删除命令
# make
# make clean
4、在当前目录下创建clean文件,此时执行make clean
报log如下,没有执行删除hello操作
make clean
make: “clean”是最新的。
5、在Makefile修改如下,此时顺利执行
$(CC) = gcc
.PHONY:clean #增加
all:hello test
hello :hello.c
$(CC) -o $@ $<
test :hello.c
$(CC) -o $@ $<
clean:
rm hello
总结:
为了解决问题,我们将目标clean定义成伪目标。也就是添加:.PHONY:clean
当一个目标被声明为伪目标后,make在执行规则时不会去试图去查找隐含规则来创建它。这样就提高了make的执行效率,也不用担心由于目标和文件名重名了。
1.2. golang Makefile
.PHONY: all build clean run check cover lint docker help
BIN_FILE=hello
all: check build
build:
@go build -o "${BIN_FILE}"
clean:
@go clean
rm --force "xx.out"
test:
@go test
check:
@go fmt ./
@go vet ./
cover:
@go test -coverprofile xx.out
@go tool cover -html=xx.out
run:
./"${BIN_FILE}"
lint:
golangci-lint run --enable-all
docker:
@docker build -t leo/hello:latest .
help:
@echo "make 格式化 go 代码 并编译生成二进制文件"
@echo "make build 编译 go 代码生成二进制文件"
@echo "make clean 清理中间目标文件"
@echo "make test 执行测试 case"
@echo "make check 格式化 go 代码"
@echo "make cover 检查测试覆盖率"
@echo "make run 直接运行程序"
@echo "make lint 执行代码检查"
@echo "make docker 构建 docker 镜像"
1.2.1. @
是什么意思
make 通常会把命令执行显示出来 加 @
会隐藏
比如 @echo "hello world"
不加 @
输出
echo "hello world"
hello world
加了 @
就只输出
hello wolrd
1.2.2. build multiple platform executables
go-executable-build.bash
:
#!/usr/bin/env bash
package=$1
if [[ -z "$package" ]]; then
echo "usage: $0 <package-name>"
exit 1
fi
package_split=(${package//\// })
package_name=${package_split[-1]}
platforms=("windows/amd64" "windows/386" "darwin/amd64")
for platform in "${platforms[@]}"
do
platform_split=(${platform//\// })
GOOS=${platform_split[0]}
GOARCH=${platform_split[1]}
output_name=$package_name'-'$GOOS'-'$GOARCH
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
env GOOS=$GOOS GOARCH=$GOARCH go build -o $output_name $package
if [ $? -ne 0 ]; then
echo 'An error has occurred! Aborting the script execution...'
exit 1
fi
done
./go-executable-build.bash github.com/mholt/caddy/caddy
1.3. 报错解决
1.3.1. Makefile missing separator. Stop.
有几个原因会引起这样的结果:
- Makefile 的命令行, 开头必须用 tab 键;
- 编码方式引起的原因, 需设定 UTF-8;
2. Linux 之简单调用 so 库及 Makefile 用法
1.test.h
#include <stdio.h>
//函数指针
typedef int (*ADD)(int,int);
2.test.c
#include <stdio.h>
int add(int a, int b){
printf(".PHONY 测试! \n");
return (a + b);
}
3. 测试程序 main.c
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "test.h"
int main(){
void *handle=dlopen("./libtest.so",RTLD_LAZY);
ADD add=NULL;
*(void **)(&add)=dlsym(handle,"add");
int result=add(2,5);
printf("a + b = %d\n",result);
return 0;
}
4.Makefile
#$@--目标文件 (main), $^--所有的依赖文件 (main.c), $<--第一个依赖文件。
CC := gcc
.PHONY:all clean
# test
SOURCE := $(wildcard *.c) #把*.c 赋值给 SOURCE
OBJS := $(patsubst %.c,%.o,$(SOURCE)) #把*.c 替换成*.o
all: libtest.so main
libtest.so: test.o
@echo $@ $^ "11111"
$(CC) -shared -o $@ $^ #编译-shared 生成 so 共享库
test.o: test.c
@echo $@ $^ "2222"
$(CC) -c -fPIC -o $@ $^ #-c 只编译不链接; PIC: Position Independent Code
main: main.c
@echo $@ $^ "3333"
$(CC) -o $@ $^ -ldl #不需指定 libtest.so 进行编译, 执行会在指定目录加载 so
./main
#ldd main
clean:
rm *.so *.o main