leveldb的makefile剖析

来源:互联网 发布:域名查询 美橙互联 编辑:程序博客网 时间:2024/05/21 15:14

首先出现在眼前的是这个:

OPT ?= -O2 -DNDEBUG

-O0
-O1
-O2
-O3
编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高 
至于-DNDEBUG我不知道什么意思,麻烦知道的同学告诉我。


-DNDEBUG是定义宏的意思。准确的来说-D是定义宏的意思,定义了NDEBUG这个宏,而在以下的代码当中能够使用#ifndef来判断:
在db/version_set.cc,db/db_bench.cc,doc/bench/db_bench_tree_db.cc,doc/bench/db_bench_sqlite3.cc,table/format.cc 当中都有#ifndef NDEBUG这一行。如果没有定义NDEBUG则不执行下面的代码。
使用这种方式可以通过控制编译参数-D来控制编译的代码量。

然后是这个:

$(shell CC="$(CC)" CXX="$(CXX)" TARGET_OS="$(TARGET_OS)" \./build_detect_platform build_config.mk ./) include build_config.mk

$(shell)是一个makefile的函数,但是它不像其他的函数,它的参数应该就是操作系统Shell的命令。它
和反引号“`”是相同的功能。这就是说,shell函数把执行操作系统命令后的输出作为函数返回。
这个函数做的工作很简单,就是执行了./build_detect_platform这个shell脚本文件。然后生成了./build_config.mk
./build_detect_platform这个shell脚本文件的作用就是检测系统的类型,是IOS还是Linux等等。然后生成的文件./build_config.mk就是符合这个系统的参数。

在build_detect_platform文件当中,有如下的语句:

if test -z "$CC"; then CC=cc fi if test -z "$CXX"; then CXX=g++ fi# Detect OS if test -z "$TARGET_OS"; then TARGET_OS=`uname -s` fi

如果CC,CXX,TARGET_OS为0的话则给它们赋值为cc,g++,操作系统的类型.
随后还有TARGET_OS的操作,

case "$TARGET_OS" in

使用case语句判断操作系统的类型, 随后将编译的参数PLATFORM(平台),COMMON_FLAGS(通用参数),PLATFORM_LIBS(编译时候需要使用的库),PLATFORM_LDFLAGS(链接时候的参数)等等给设定好

我们看include build_config.mk
打开build_config,mk可以发现在这个文件里面包含了make的很多变量,正如其名称一样是build需要的参数
在Makefile使用include关键字可以把别的Makefile包含进来,这很像C语言的#include
在这个include则是把build_config.mk原模原样的放在当前文件的包含位置。

随后在Makefile当中:

CFLAGS += -I. -I./include $(PLATFORM_CCFLAGS) $(OPT)CXXFLAGS += -I. -I./include $(PLATFORM_CXXFLAGS) $(OPT)LDFLAGS += $(PLATFORM_LDFLAGS)    链接的参数LIBS += $(PLATFORM_LIBS)    需要使用的库

接下来的

LIBOBJECTS = $(SOURCES:.cc=.o)

就是把build_config.mk里面的SOURCES里面的c++文件名换成.o文件,并赋值给LIBOBJECTS

MEMENVOBJECTS = $(MEMENV_SOURCES:.cc=.o)

也是同理

TESTUTIL = ./util/testutil.oTESTHARNESS = ./util/testharness.o $(TESTUTIL)

就是简单的赋值

如果用的是苹果系统,那么ar命令和其他系统的命令不同,需要做修改

ifeq ($(PLATFORM), IOS)AR=xcrun arendif

接下来先跳过一大串的代码,直接到

all: $(SHARED) $(LIBRARY)

这里就是makefile默认开始编译的地方,
这条命令说明这个makefile编译生成两个文件:(SHARED)(LIBRARY)
LIBRARY是静态库文件libleveldb.a
SHARED是动态库文件:
在makefile中有一条语句:

ifneq ($(PLATFORM_SHARED_EXT),)

这条语句说如果PLATFORM_SHARED_EXT不为空的话,则执行下面的语句:
查找build_config.mk得到PLATFORM_SHARED_EXT是so
接下去还要判断PLATFORM_SHARED_VERSIONED是否等于true
这个PLATFORM_SHARED_VERSIONED在build_detect_platform当中都是true,除了当检测到TARGET_OS是IOS的情况(此时PLATFORM_SHARED_VERSIONED是空的),就是说当你的系统是IOS时候,
SHARED,SHARED1,SHARED2,SHARED3都是libleveldb.so,而当你的系统是其他系统的时候,SHARED包含了SHARED1 SHARED2 SHARED3
SHARED1是libleveldb.so,SHARED2是libleveldb.so.1,SHARED3是libleveldb.so.1.18
SHARED1(libleveldb.so)和SHARED2(libleveldb.so.1)都是SHARED3(libleveldb.so.1.18)的软链接
而生成SHARED3:

$(CXX) $(LDFLAGS) $(PLATFORM_SHARED_LDFLAGS)$(SHARED2) $(CXXFLAGS) $(PLATFORM_SHARED_CFLAGS) $(SOURCES) -o $(SHARED3) $(LIBS)

这里只要知道(PLATFORMSHAREDLDFLAGS)(SHARED2) 就是-Wl,-soname -Wl,libleveldb.so.1
PLATFORM_SHARED_CFLAGS 就是”-fPIC”这个参数
我们一般用来生成动态库的命令都是g++ -fPIC -shared,在这里两个参数都全了。剩下的重要参数就是SOURCES,就是指要编译进入libleveldb.a的文件
完整命令如下:

g++  -pthread -shared -Wl,-soname -Wl,libleveldb.so.1 -I. -I./include -std=c++0x -fno-builtin-memcmp -pthread -DOS_LINUX -DLEVELDB_PLATFORM_POSIX -DLEVELDB_ATOMIC_PRESENT -O2 -DNDEBUG -fPIC db/builder.cc db/c.cc db/dbformat.cc db/db_impl.cc db/db_iter.cc db/dumpfile.cc db/filename.cc db/log_reader.cc db/log_writer.cc db/memtable.cc db/repair.cc db/table_cache.cc db/version_edit.cc db/version_set.cc db/write_batch.cc table/block_builder.cc table/block.cc table/filter_block.cc table/format.cc table/iterator.cc table/merger.cc table/table_builder.cc table/table.cc table/two_level_iterator.cc util/arena.cc util/bloom.cc util/cache.cc util/coding.cc util/comparator.cc util/crc32c.cc util/env.cc util/env_posix.cc util/filter_policy.cc util/hash.cc util/histogram.cc util/logging.cc util/options.cc util/status.cc port/port_posix.cc -o libleveldb.so.1.18

而静态库很简单:

$(LIBRARY): $(LIBOBJECTS)rm -f $@                                      #首先先把原来的静态库删除掉$(AR) -rs $@ $(LIBOBJECTS)    #重新使用AR生成静态库

接下来makefile需要生成的就是静态库的以来文件(LIBOBJECTS)(LIBOBJECTS) 就是SOURCES的.o 文件,生成它用到了makefile放在最后面的代码:

.cc.o:$(CXX) $(CXXFLAGS) -c $< -o $@.c.o:$(CC) $(CFLAGS) -c $< -o $@

将C++代码和C代码文件编译生成.o文件。
其中,如果系统是IOS的话,那么需要使用IOS下面的编译器:xcrun

ifeq ($(PLATFORM), IOS)# For iOS, create universal object files to be used on both the simulator and# a device.PLATFORMSROOT=/Applications/Xcode.app/Contents/Developer/PlatformsSIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/DeveloperDEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/DeveloperIOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64.cc.o:mkdir -p ios-x86/$(dir $@)xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@mkdir -p ios-arm/$(dir $@)xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@.c.o:mkdir -p ios-x86/$(dir $@)xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/$@mkdir -p ios-arm/$(dir $@)xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/$@xcrun lipo ios-x86/$@ ios-arm/$@ -create -output $@

这一大行代码的作用就是把SOURCES里面的文件编译一下,只不过用到了xcrun
好了,如果你仅仅执行make,那么makefile做的就是生成动态库和静态库,中间会生成.o文件。但是makefile里面还有一个参数check。check是all的超集。如果运行make check那么不仅会生成动态库,静态库,.o文件,还会生成db_bench和leveldbutil可执行文件以及链接生成测试用的可执行文件。

接下来的

TESTS = \arena_test \autocompact_test \bloom_test \c_test \cache_test \coding_test \corruption_test \crc32c_test \db_test \dbformat_test \env_test \fault_injection_test \filename_test \filter_block_test \hash_test \issue178_test \issue200_test \log_test \memenv_test \skiplist_test \table_test \version_edit_test \version_set_test \write_batch_test

这一大串都是测试用的可执行文件的名字。

check: all $(PROGRAMS) $(TESTS)for t in $(TESTS); do echo "***** Running $$t"; ./$$t || exit 1; done

这个循环会把TESTS集合里面的文件一个个拿出来执行一下,就是通过运行./$$t,看看有没有什么问题。如果有问题的话那么退出码为1

makefile首先会生成(PROGRAMS)(TESTS)
就是通过下面:

db_bench: db/db_bench.o $(LIBOBJECTS) $(TESTUTIL)...................write_batch_test: db/write_batch_test.o $(LIBOBJECTS) $(TESTHARNESS)

这一大串的代码

最后有个小的剩余,就是MEMENVLIBRARY = libmemenv.a这个文件
这个文件之所以单独从TESTS这个集合当中拿出来,单独为它生成一个静态库,自然是因为它有一定的重要性,helper/memenv/ 实现了一个简单的完全内存的文件系统.

接下来的clean就不多说了,删除可执行文件,.o文件,动态库和静态库文件还有./build_detect_platform脚本生成的./build_config.mk文件。如果是IOS系统,那么最开始生成.o文件的时候会创建目录ios-x86,ios-arm,也一并删除了:

clean:-rm -f $(PROGRAMS) $(BENCHMARKS) $(LIBRARY) $(SHARED) $(MEMENVLIBRARY) */*.o */*/*.o ios-x86/*/*.o ios-arm/*/*.o build_config.mk-rm -rf ios-x86/* ios-arm/*
0 0