leveldb的makefile剖析

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

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/*
时间: 2024-10-05 02:42:54

leveldb的makefile剖析的相关文章

tiny210——uboot移植之Makefile剖析篇

这篇东东早就写好了,一直没时间发出来,现在终于有时间发一下了记录总结uboot的学习历程,好像够详细了,以后忘了也可以再温习回来嘛有些特殊字符显示得乱掉了 Makefile追踪技巧: 技巧1:可以先从编译目标开始顺藤摸瓜地分析,先不要关注具体细节,着重关注主要的代码结构和编译过程 技巧2:追踪分析时要通过文本或者其他途径暂时记录重要的线索 技巧3:将主要的Makefile文件中export出来的变量以及include的文件提取出来,看看include的文件大致是些什么文件,当看到一些来历不明的变

LevelDB源码剖析

LevelDB的公共部件并不复杂,但为了更好的理解其各个核心模块的实现,此处挑几个关键的部件先行备忘. Arena(内存领地) Arena类用于内存管理,其存在的价值在于: 提高程序性能,减少Heap调用次数,由Arena统一分配后返回到应用层. 分配后无需执行dealloc,当Arena对象释放时,统一释放由其创建的所有内存. 便于内存统计,如Arena分配的整体内存大小等信息. 1 class Arena { 2 public: 3 Arena(); 4 ~Arena(); 5 6 // R

[Leveldb源码剖析疑问]-block_builder.cc之Add函数

Add函数是给一个Data block中添加对应的key和value,函数源码如下,其中有一处不理解: L30~L34是更新last_key_的,不理解这里干嘛不直接last_key_ = key.ToString(); 写成 // Update state last_key_.resize(shared); last_key_.append(key.data() + shared, non_shared); assert(Slice(last_key_) == key); 是有什么其他原因吗?

2017年6月笔记

react native webview加载本地html资源文件的解决方案 https://blog.jeepeng.com/2017/03/16/react-native-webview-load-from-device-local-file-system/?utm_source=tuicool&utm_medium=referral 消息中间件(一)分布式系统事务一致性解决方案大对比,谁最好使? http://blog.csdn.net/lovesomnus/article/details/

erlang.mk和makefile语法剖析

1. makefile 基本规则: 1. 所有的源文件没有被编译过,则对各个源文件进行编译并进行链接,生成最后的可执行程序; 2. 每一个在上次执行make之后修改过的源代码文件在本次执行make时将会 被重新编译; 3. 头文件在上一次执行make之后被修改.则所有包含此头文件的源文件在本次执行 make 时将会被重新编译. 2. 基本格式: TARGET... : PREREQUISITES... COMMAND ... 2.1 target(目标)通常是最后需要生成的文件名或者为了实现这个

LevelDb 剖析之五 (LevelDb的一些动态操作&lt;二&gt;)

一.根据Key读取记录: LevelDb读取记录流程 LevelDb首先会去查看内存中的Memtable,如果Memtable中包含key及其对应的value,则返回value值即可:如果在Memtable没有读到key,则接下来到同样处于内存中的Immutable Memtable中去读取,类似地,如果读到就返回,若是没有读到,那么只能万般无奈下从磁盘中的大量SSTable文件中查找.因为SSTable数量较多,而且分成多个Level,所以在SSTable中读数据是相当蜿蜒曲折的一段旅程.总的

leveldb单元测试之宏定义源码剖析

前言 leveldb 是一个库,没有 main() 函数入口, 故非常难理清其中的代码逻辑.但好在库中有非常多的单元测试代码,帮助读者理解其中的各个模块的功能.然而,测试代码个人觉得一开始看时非常费解,特别是其中非常复杂的宏定义让人陷于云里雾里一般.研究 leveldb 的时间也有一段时间了,但一直都不想也不愿去弄懂.今天算是拿出破釜沉舟的勇气弄懂了这部分的原理,不能让谷歌大神辛苦写的测试代码没有发挥出它应有的价值.其实单元测试对于开发的作用非常重要的,这是毋庸置疑的.但我一直都没有去了解这部分

GDAL源码剖析(一)(转载)

GDAL源码剖析(一) GDAL 前言:一直在使用和研究GDAL的相关东西,发现网上对GDAL的内容倒是不少,但是很少有系统的介绍说明,以及内部的一些结构说明,基于这些原因,将本人的一些粗浅的理解放在此处,形成一个系列,暂时名为<GDAL源码剖析>(名称有点大言不惭,欢迎大家口水吐之,板砖拍之),供大家交流参考,有什么错误之处,望大家不吝指正,本系列对于GDAL的使用均是在Windows平台下,对于Linux平台下的不在此系列讨论范围之内.此外,转载本博客内容,请注明出处,强烈鄙视转载后不注明

工程管理之makefile与自动创建makefile文件过程

(风雪之隅 http://www.laruence.com/2009/11/18/1154.html) Linux Makefile自动编译和链接使用的环境 想知道到Linux Makefile系统的真相么,想知道Linux Makefile系统中藏有的内在奥义么,只有我来给大家全面讲解介绍Linux Makefile系统作为Linux下的程序开发人员,大家一定都遇到过Linux Makefile,用make命令来编译自己写的程序确实是很方便.一般情况下,大家都是手工写一个简单Linux Mak