首先出现在眼前的是这个:
OPT ?= -O2 -DNDEBUG
-O0
-O1
-O2
-O3
编译器的优化选项的4个级别,-O0表示没有优化,-O1为缺省值,-O3优化级别最高
至于-DNDEBUG我不知道什么意思,麻烦知道的同学告诉我。
然后是这个:
$(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.o
TESTHARNESS = ./util/testharness.o $(TESTUTIL)
就是简单的赋值
如果用的是苹果系统,那么ar命令和其他系统的命令不同,需要做修改
ifeq ($(PLATFORM), IOS)
AR=xcrun ar
endif
接下来先跳过一大串的代码,直接到
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 [email protected] #首先先把原来的静态库删除掉
$(AR) -rs [email protected] $(LIBOBJECTS) #重新使用AR生成静态库
接下来makefile需要生成的就是静态库的以来文件(LIBOBJECTS)(LIBOBJECTS) 就是SOURCES的.o 文件,生成它用到了makefile放在最后面的代码:
.cc.o:
$(CXX) $(CXXFLAGS) -c $< -o [email protected]
.c.o:
$(CC) $(CFLAGS) -c $< -o [email protected]
将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/Platforms
SIMULATORROOT=$(PLATFORMSROOT)/iPhoneSimulator.platform/Developer
DEVICEROOT=$(PLATFORMSROOT)/iPhoneOS.platform/Developer
IOSVERSION=$(shell defaults read $(PLATFORMSROOT)/iPhoneOS.platform/version CFBundleShortVersionString)
IOSARCH=-arch armv6 -arch armv7 -arch armv7s -arch arm64
.cc.o:
mkdir -p ios-x86/$(dir [email protected])
xcrun -sdk iphonesimulator $(CXX) $(CXXFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/[email protected]
mkdir -p ios-arm/$(dir [email protected])
xcrun -sdk iphoneos $(CXX) $(CXXFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/[email protected]
xcrun lipo ios-x86/[email protected] ios-arm/[email protected] -create -output [email protected]
.c.o:
mkdir -p ios-x86/$(dir [email protected])
xcrun -sdk iphonesimulator $(CC) $(CFLAGS) -isysroot $(SIMULATORROOT)/SDKs/iPhoneSimulator$(IOSVERSION).sdk -arch i686 -arch x86_64 -c $< -o ios-x86/[email protected]
mkdir -p ios-arm/$(dir [email protected])
xcrun -sdk iphoneos $(CC) $(CFLAGS) -isysroot $(DEVICEROOT)/SDKs/iPhoneOS$(IOSVERSION).sdk $(IOSARCH) -c $< -o ios-arm/[email protected]
xcrun lipo ios-x86/[email protected] ios-arm/[email protected] -create -output [email protected]
这一大行代码的作用就是把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/*