美文网首页
Makefile 进阶笔记

Makefile 进阶笔记

作者: 天之見證 | 来源:发表于2021-08-02 19:39 被阅读0次

1. 项目结构

├── LICENSE
├── Makefile
├── README.md
├── bin
├── build
│   └── liblcthw.a
├── src
│   └── dbg.h
└── tests
    └── runtests.sh

1. Makefile概览

整体的一个Makefile 如下

CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local

SOURCES=$(wildcard src/**/*.c src/*.c)
OBJECTS=$(patsubst %.c,%.o, $(SOURCES))

TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))

TARGET=build/liblcthw.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

# The Target Build
all: $(TARGET) $(SO_TARGET) tests

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
    ar rcs $@ $(OBJECTS)
    ranlib $@

$(SO_TARGET): $(TARGET) $(OBJECTS)
    $(CC) -shared -o $@ $(OBJECTS)

build:
    @mkdir -p build
    @mkdir -p bin

# The Unit Tests
.PHONY: tests
tests: LDLIBS += $(TARGET)
#tests: LDLIBS += -lm -L./build -llcthw
tests: $(TESTS)
    sh ./tests/runtests.sh

valgrind:
    VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

# The Cleaner
clean:
    rm -rf build $(OBJECTS) $(TESTS)
    rm -f tests/test.log
    find . -name "*.gc*" -exec rm {} \;
    rm -rf `find . -name "*.dSYM" -print`

# The Install
install: all
    install -d $(DESTDIR)/$(PREFIX)/lib/
    install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/


# The checker
BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)'
check:
    @echo Files with potentially dangerous functions
    @egrep $(BADFUNCS) $(SOURCES) || true

2. Makefile头部解析

CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)

OPTFLAGS 可以让使用者按需扩建构建选项

-Wall : 打开所有编译警告

LIBS=-ldl $(OPTLIBS)

用于链接库的选项, 同样也允许其它人使用 OPTFLAGS 变量扩展链接选项。

PREFIX?=/usr/local

设置一个叫做PREFIX的可选变量, 它只在没有PREFIX设置的平台上运行Makefile时有效, 这就是?=的作用

SOURCES=$(wildcard src/**/*.c src/*.c)

通过执行wildcard搜索在src/中所有*.c文件来动态创建SOURCES变量. 需要提供src/**/*.csrc/*.c 以便GNU make能够包含src目录及其子目录的所有此类文件

OBJECTS=$(patsubst %.c,%.o, $(SOURCES))

一旦创建了源文件列表, 可以使用patsubst命令获取*.c文件的SOURCES来创建目标文件的新列表. patsubst把所有%.c扩展为%.o,并将它们赋给OBJECTS

TEST_SRC=$(wildcard tests/*_tests.c)

类似上述的含义,提取测试源文件

TESTS=$(patsubst %.c,%,$(TEST_SRC))

只是将 .c 文件的后缀都去掉了

TARGET=build/liblcthw.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))

设置目标

2.1 扩展参数的使用示例

make PREFIX=/tmp install
make OPTFLAGS=-pthread

3. Makefile构建目标

在没有提供目标时make会默认运行第一个目标. 这里它叫做all

all: $(TARGET) $(SO_TARGET) tests

可以看出, 这里先构建出库文件,再构建出单元测试(tests)

dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all

这个是用来执行"开发者构建"的目标,可以为单一目标修改选项, 例如 CFLAGS 包含类似 Wextra 这样的选项

$(TARGET): CFLAGS += -fPIC

构建 TARGET 库,向目标提供选项来为当前目标修改它们, PIC 表示position-independent code 这样生成出来的代码和绝对地址没有关系,便于加载如内存

$(TARGET): build $(OBJECTS)
    ar rcs $@ $(OBJECTS)
    ranlib $@
    
build:
    @mkdir -p build
    @mkdir -p bin
  1. 构建 build ,再编译所有的 OBJECTS
  2. 运行实际创建 TARGETar 的命令. $@ $(OBJECTS)语法的意思是, 将当前目标的名称放在这里, 并把 OBJECTS 的内容放在后面. 这里 $@ 的值为20行
    $(TARGET) ,它实际上为 build/liblcthw.a. 看起来在这一重定向中它做了很多跟踪工作,它也有这个功能,并且你可以通过修改顶部的 TARGET ,来构建一个全新的库
  3. TARGET 上运行 ranlib 来构建这个库

命令完成后的文件结构如下:

build
├── liblcthw.a
└── liblcthw.so

4. 单元测试

.PHONY: tests

用来标记不是一个真实的目标,只是有个目录/文件叫这个名字,以便 make 忽略

tests: LDLIBS += $(TARGET)
tests: $(TESTS)
    sh ./tests/runtests.sh

构建 TESTS 之后,运行一个 shell 脚本

# runtests.sh
echo "Running unit tests:"

for i in tests/*_tests
do
    if test -f $i
    then
        if $VALGRIND ./$i 2>> tests/tests.log
        then
            echo $i PASS
        else
            echo "ERROR in test $i: here's tests/test.log"
            echo "-----"
            tail tests/tests.log
            exit 1
        fi
    fi
done

echo ""
valgrind:
    VALGRIND="valgrind --log-file=/tmp/valgrind-%p.log" $(MAKE)

上面的 shell 脚本会用到 VALGRIND

5. 清理工具

clean:
    rm -rf build $(OBJECTS) $(TESTS)
    rm -f tests/test.log
    find . -name "*.gc*" -exec rm {} \;
    rm -rf `find . -name "*.dSYM" -print`

6. 安装

install: all
    install -d $(DESTDIR)/$(PREFIX)/lib/
    install $(TARGET) $(DESTDIR)/$(PREFIX)/lib/

上面的2个参数 PREFIX ,DESTDIR 是为了给使用者更多方便以指定路径

7. 检查工具

BADFUNCS='[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)'
check:
    @echo Files with potentially dangerous functions
    @egrep $(BADFUNCS) $(SOURCES) || true

@echo 命令告诉 make 不要打印命令,之需要打印输出

ref: https://learncodethehardway.org/c/ #28

相关文章

  • Makefile 进阶笔记

    1. 项目结构 1. Makefile概览 整体的一个Makefile 如下 2. Makefile头部解析 OP...

  • 笨办法学C 练习28:Makefile 进阶

    练习28:Makefile 进阶 原文:Exercise 28: Intermediate Makefiles 译...

  • Makefile学习笔记

    Makefile学习笔记 学习Makefile的资料 《跟我一起写makefile》 《GUN make manu...

  • Makefile自动化变量

    学习笔记,摘自陈皓的《跟我一起写 Makefile》 Makefile规则 Makefile文件由一系列规则构成。...

  • Makefile笔记

    Makefile 笔记 一、简单的 Makefile 例子 文档目录结构文档目录结构是用户目录HOME下有src ...

  • Makefile学习笔记

    Makefile学习笔记 概述 什么是makefile?或许很多Windows程序员都不知道这个东西,因为那些Wi...

  • Makefile笔记

    1. 调用shell命令 UNAME = $(shell uname -r) 2. 判断语句 KERN ?=$(i...

  • Makefile笔记

    Makefile 简介 makefile用来制定编译的规则及其其它更复杂的操作.并且能实现整个工程的自动化编译,提...

  • JavaScript学习笔记(五)

    慕课网JavaScript进阶篇第9章学习笔记 JavaScript进阶篇—第9章 JavaScript学习笔记(...

  • Makefile学习笔记

    格式:ar rcs libxxx.a xx1.o xx2.o 链接 参数r:在库中插入模块(替换)。当插入的模块...

网友评论

      本文标题:Makefile 进阶笔记

      本文链接:https://www.haomeiwen.com/subject/yopjvltx.html