离线下载
PDF版 ePub版

飞龙 · 更新于 2018-11-19 00:00:36

练习28:Makefile 进阶

在下面的三个练习中你会创建一个项目的目录框架,用于构建之后的C程序。这个目录框架会在这本书中剩余的章节中使用,并且这个练习中我会涉及到Makefile便于你理解它。

这个结构的目的是,在不凭借配置工具的情况下,使构建中等规模的程序变得容易。如果完成了它,你会学到很多GNU make和一些小型shell脚本方面的东西。

基本的项目结构

首先要做的事情是创建一个C的目录狂阿基,并且放置一些多续项目都拥有的,基本的文件和目录。这是我的目录:

$ mkdir c-skeleton
$ cd c-skeleton/
$ touch LICENSE README.md Makefile
$ mkdir bin src tests
$ cp dbg.h src/   # this is from Ex20
$ ls -l
total 8
-rw-r--r--  1 zedshaw  staff     0 Mar 31 16:38 LICENSE
-rw-r--r--  1 zedshaw  staff  1168 Apr  1 17:00 Makefile
-rw-r--r--  1 zedshaw  staff     0 Mar 31 16:38 README.md
drwxr-xr-x  2 zedshaw  staff    68 Mar 31 16:38 bin
drwxr-xr-x  2 zedshaw  staff    68 Apr  1 10:07 build
drwxr-xr-x  3 zedshaw  staff   102 Apr  3 16:28 src
drwxr-xr-x  2 zedshaw  staff    68 Mar 31 16:38 tests
$ ls -l src
total 8
-rw-r--r--  1 zedshaw  staff  982 Apr  3 16:28 dbg.h
$

之后你会看到我执行了ls -l,所以你会看到最终结果。

下面是每个文件所做的事情:

LICENSE

如果你在项目中发布源码,你会希望包含一份协议。如果你不这么多,虽然你有代码的版权,但是通常没有人有权使用。

README.md

对你项目的简要说明。它以.md结尾,所以应该作为Markdown来解析。

Makefile

这个项目的主要构建文件。

bin/

放置可运行程序的地方。这里通常是空的,Makefile会在这里生成程序。

build/

当值库和其它构建组件的地方。通常也是空的,Makefile会在这里生成这些东西。

src/

放置源码的地方,通常是.c.h文件。

tests/

放置自动化测试的地方。

src/dbg.h

我将练习20的dbg.h复制到了这里。

我刚才分解了这个项目框架的每个组件,所以你应该明白它们怎么工作。

Makefile

我要讲到的第一件事情就是Makefile,因为你可以从中了解其它东西的情况。这个练习的Makeile比之前更加详细,所以我会在你输入它之后做详细的分解。

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/libYOUR_LIBRARY.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: CFLAGS += $(TARGET)
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/tests.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

要记住你应该使用一致的Tab字符来缩进Makefile。你的编辑器应该知道怎么做,但是如果不是这样你可以换个编辑器。没有程序员会使用一个连如此简单的事情都做不好的编辑器。

头文件