本教程为纯干货教程系列,内容原创android 系统源码编译,持续更新,欢迎关注转发
废话不多说,干货。 上图显示cmake是一个跨平台编译工具。 当公司的一套代码运行在多个平台上时,例如:android、ios、windows、linux可以同时用cmake编译。 事实上,不同的平台需要配置不同的编译环境。 比如android需要配置ndk,ios需要配置xcode。 该配置稍后会继续输出。 欢迎关注。 明天我们来谈谈基本命令。
本文内容由简单到复杂,最后是一个小项目的完整示例。 本系列教程从本文的基本技巧开始。 欢迎继续关注
cmake句型和shell句型类似,都支持大小写
命令名称([可选])
项目
项目的基本设置
project( [LANGUAGES] [...])
project(
[VERSION [.[.[.]]]]
[LANGUAGES ...])
实施例1
project(test C)
实施例2
# 项目信息
project( demo1
VERSION 1.2.3
DESCRIPTION "项目描述,比如:本项目非常牛逼"
HOMEPAGE_URL "项目地址,比如:https://github.com/XXX/YYY"
LANGUAGES CXX
)
放
set命令是变量的形参,可以内置到cmake中,也可以自定义
设定系统自带的变量
set( CMAKE_CXX_FLAGS "-std=c++11" )//以flags的形式的实现通知使用版本
set(CMAKE_CXX_STANDARD 11)//以命令形式通知使用版本
设置自定义变量
set(THIRD_PART "${CMAKE_CURRENT_SOURCE_DIR}/src/third/include")//设置头文件的搜索路径
include_directories(${THIRD_PART})
添加定义
添加预处理的标志显然就是添加宏定义
add_definitions(-DFOO -DBAR ...)
添加方式有两种:
例子:
例如,您的代码中可能有这样的内容:
#ifdef TEST
//TODO if
#else
//TODO else
#endif
那么为什么不改变代码而是通过编译后的脚本来控制宏,它使用了这个命令:add_definitions,如下所示
add_definitions("-DTEST")
这时,TEST宏就会在预处理阶段与源代码结合起来。
包含目录
指定包含的头文件的搜索路径
include_directories([AFTER|BEFORE] [SYSTEM] dir1 [dir2 ...])
注意:如果路径中有空格,需要用双冒号括起来 aux_source_directory
为某个目录下的所有源代码文件设置一个变量名,以便以后所有源代码文件都可以替换为变量名。 例如,可以与add_library一起使用生成静态库,方便指定子构建系统的完整库类型和名称。
aux_source_directory( )
组合使用命令示例:
aux_source_directory(. DIR_LIB_SRC)
add_library(mylib STATIC ${DIR_LIB_SRC})
分析:将该目录下的所有源代码文件设置为变量DIR_LIB_SRC,然后创建动态库。
添加库
将源代码编译到指定的库中(类型+名称)
add_library( [STATIC | SHARED | MODULE]
[EXCLUDE_FROM_ALL]
[source1] [source2 ...])
生成库的目的和作用命令的组合使用例1.1:外部提供库文件的Android将源代码编译成动态库
add_library(
# Specifies the name of the library.
libDemo
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/jni/test1.cpp
src/main/jni/test2.cpp
src/main/jni/test3.cpp
)
例1.2:外部提供库文件的Android将源代码编译成动态库并与aux_source_directory结合使用
aux_source_directory(./src/main/jni custom)
add_library(
# Specifies the name of the library.
libDemo
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${custom}
)
例1.3:外部提供库文件的非Android示例
它本质上与android示例相同,属于子构建系统。 区别在于:android没有顶层构建直接与jni通信,非android有顶层构建通过add_subdirectory添加子构建系统
aux_source_directory(. SUBLIB1_SRC)
add_library(subLib1 ${SUBLIB1_SRC})
示例 2:结合 target_link_libraries 执行顶级构建
add_library和target_link_libraries组合起来构建一个顶层编译系统,一般是为了向外界提供库文件,比如android的建立
aux_source_directory(./ TOP_SRCS)
add_library(libraryLink SHARED ${TOP_SRCS})
target_link_libraries(
libraryLink
)
添加子目录
多编译系统,即多个CMakeList.txt,非顶层构建都是子构建系统。 子构建系统通过add_library生成构建库文件android 系统源码编译,顶层构建如何与子构建系统关联使用这个命令。
将子目录添加到构建中。
add_subdirectory(source_dir [binary_dir]
[EXCLUDE_FROM_ALL])
例子:
add_subdirectory(./subLib1)
注意:该命令唯一需要的选项是子编译系统目录,子编译系统目录,而不是子编译系统生成的库名target_link_libraries
链接所有库、子编译系统和库文件
例子:
cmake_minmum_required(VERSION 3.4.1)
project(linkDemo CXX)
//1. 添加子编译系统
add_subdirectory(./sublib)
//2. 顶级编译系统
aux_source_directory(./ TOP_SRCS)
add_executable(linkDemo ${TOP_SRCS})
//3. 第三方库
set(THIRD_PART "${CMAKE_CURRENT_SOURCE_DIR}/src/third/include")
include_directories(${THIRD_PART})
message("third part include path = ${THIRD_PART}")
message("thrid part libs path = ${THIRD_PART}/libs/XXX.a")
//4. 链接:子系统+顶级系统+第三方库
target_link_libraries(
linkDemo
sublib
"${THIRD_PART}/libs/XXX.a"
)
本文为VX同名公众号原创,欢迎关注
抱歉,这周真的很忙。 。 。 废话不多说,直接进入正题。
前言
所谓分析思路无非就是找到程序的入口点,按照执行流程一步步阅读分析,或者从自己感兴趣的部分开始。这个系列还不是对Lua的全面分析,但只选取其中一部分进行分析。 为此,我们要找到自己感兴趣的部分lua源码编译,并有针对性地进行分析。
因为Lua的官方文档比较完整,所以我们在浏览完官方资料之后就比较容易开始分析。
所谓源代码分析,就是从源代码中重构出可执行程序,调试调试,看执行过程,看主要数据结构和算法,程序的运行状态等,最后品味设计(问题背景、原因等、优缺点),仅此而已。
我们先看一下源代码的布局。
代码布局
可以从Makefile开始,但是这里我们使用的是Windows+VisualStudio,所以这一步可以省略。
查看从官网下载的源码包,提供了以下文件:
./lua-5.3.0/
Makefile README
/doc/
contents.html logo.gif lua.1
lua.css luac.1 manual.css
manual.html osi-certified-72x60.png
readme.html
/src/
lapi.c lapi.h lauxlib.c lauxlib.h
lbaselib.c lbitlib.c lcode.c lcode.h
lcorolib.c lctype.c lctype.h ldblib.c
ldebug.c ldebug.h ldo.c ldo.h
ldump.c lfunc.c lfunc.h lgc.c
lgc.h linit.c liolib.c llex.c
llex.h llimits.h lmathlib.c lmem.c
lmem.h loadlib.c lobject.c lobject.h
lopcodes.c lopcodes.h loslib.c lparser.c
lparser.h lprefix.h lstate.c lstate.h
lstring.c lstring.h lstrlib.c ltable.c
ltable.h ltablib.c ltm.c ltm.h
lua.c lua.h lua.hpp luac.c
luaconf.h lualib.h lundump.c lundump.h
lutf8lib.c lvm.c lvm.h lzio.c
lzio.h Makefile
第一件事实际上是阅读 README。 不用说,其实专业的做法可以是阅读Makefile。 这里,为了防止引入其他不相关的知识,使用了简单的技术。
根据说明查看doc/readme.html文件中的[BuildingLuaonothersystems]部分,我们发现它介绍了lua可执行程序的大致组成和依赖关系,如下:
library:
lapi.c lcode.c lctype.c ldebug.c
ldo.c ldump.c lfunc.c lgc.c
llex.c lmem.c lobject.c lopcodes.c
lparser.c lstate.c lstring.c ltable.c
ltm.c lundump.c lvm.c lzio.c
lauxlib.c lbaselib.c lbitlib.c lcorolib.c
ldblib.c liolib.c lmathlib.c loslib.c
lstrlib.c ltablib.c lutf8lib.c loadlib.c
linit.c
interpreter:
library, lua.c
compiler:
library, luac.c
根据这个简单的依赖关系,我用VS2015构建了相应的项目进行调试。 虽然官方数据集中也提供了现成的VS项目下载链接:
TODO:这里应该有一个下载链接^_^
因为我们分析的重点是编译原理和虚拟机部分,而不是相关库的实现部分。 因此,我们应该从lua.c或者luac.c开始,虽然从文件列表中我们也可以看到上面有几个重要的文件:
llex.clopcodes.clparser.clvm.c
至于从哪里开始更好,这是一个意见问题。 这里我还是用官方的资料来帮助选择。
官方数据集的 wiki 中提供了一个页面。 本页描述了该文件的用途、编程约定、模块结构等。
为了防止这个页面失效,我还是无耻的把下面的翻译复制粘贴过来给大家提供翻译。 网上也有一些翻译,但是省略了一些我认为翻译时有用的信息。 一些简单的我就不翻译了。
Lua源码模块结构、实用功能模块、基本数据类型实现模块
lobject.c - Lua 对象的一些通用函数。 包括:
lstring.c-stringtable(保存Lua处理的所有字符串)
lfunc.c - 用于操作原型和闭包的辅助函数。
ltable.c - Luatables(哈希)
句子分析和代码生成相关模块
ldump.c - 用于保存预编译的Lua代码块:
处理Lua字节码执行的模块
lvm.c-Lua虚拟机:
ldo.c-Lua函数调用和堆栈管理。处理函数调用(luaD_call/luaD_pcall)、堆栈增长、解释器处理等。
ltm.c - 标签方法。 实现了查询对象元方法的功能。
标准库的实现模块
lbaselib.c - (base functions)
lstrlib.c - string
ltablib.c - table
lmathlib.c - math
loslib.c - os
liolib.c - io
loadlib.c - package
ldblib.c - debug
以下模块定义了CAPIlua和luac程序感兴趣模块的实现模块
认识了Lua的模块结构之后,我们需要注意分析的模块就下来了:
其实后期如果有时间我会再分析一下Lua的垃圾收集器,不过最近太忙了。
从Makefile查看模块之间的依赖关系
以下摘录了lua-5.3.0/src/Makefile的重要部分
LUA_A= liblua.a
CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o
lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o
ltm.o lundump.o lvm.o lzio.o
LIB_O= lauxlib.o lbaselib.o lbitlib.o lcorolib.o ldblib.o liolib.o
lmathlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o loadlib.o linit.o
BASE_O= $(CORE_O) $(LIB_O) $(MYOBJS)
LUA_T= lua
LUA_O= lua.o
LUAC_T= luac
LUAC_O= luac.o
根据我们感兴趣的模块,大致有以下目标文件:
lua.o
luac.o
llex.o
ldo.o
lstate.o
lparser.o
opcodes.o
lvm.o
lcode.o
转储文件
lundump.o
ltm.o
lobject.o
lstring.o
ltable.o
lfunc.o
(我不得不抱怨 Markdown 编辑器的 bug,并以某种方式手动将其他条目加粗......我根本没有那样写!!!)
虽然还是不少,但比较重要的都用斜体标出。 大致按照这个顺序来分析。 虽然那些也可以直接看源文件中引用的头文件,但是那样太麻烦了。 不过,有时候Lua源码并没有把include命令写在文件的底部,而是写在中间的某个地方,这就更操蛋了。 看Makefile更方便更专业。 我们继续看看Makefile中上述目标文件的依赖关系:
lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h
luac.o: luac.c lprefix.h lua.h luaconf.h lauxlib.h lobject.h llimits.h
lstate.h ltm.h lzio.h lmem.h lundump.h ldebug.h lopcodes.h
llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldo.h
lobject.h lstate.h ltm.h lzio.h lmem.h lgc.h llex.h lparser.h lstring.h
ltable.h
ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h
lparser.h lstring.h ltable.h lundump.h lvm.h
lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h
lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h
lstring.h ltable.h
lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h
ldo.h lfunc.h lstring.h lgc.h ltable.h
lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h
lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h
llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h
ltable.h lvm.h
lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h
llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h
ldo.h lgc.h lstring.h ltable.h lvm.h
这里简单介绍一下Makefile句型:引号左边的.o是编译时生成的目标文件,逗号左边是生成目标文件所需的源文件。 上面的Makefile代码中,等号右边是变量,等号左边是变量的值。
关于Makefile:Makefile是GNU/Linux系统中用于手动创建的DSL,由gnumake使用,类似于Android开发中的.gradle文件。 然而,VS 和 Qt 也都使用自己的 Makefile 格式和工具。 VS有nmake,Qt有qmake,跨平台有cmake,这里就不赘述了,自己看文档即可。
现在lua源码编译,我们可以更有针对性地查看源文件了。
关于下一期
下一期我会介绍一下Lua源码中的编程约定(虽然我还是一个搬运工+翻译家,但是这里的翻译就靠运气了,翻译质量应该问题不大)。 以上问题比较无聊,又缺一不可,所以只能忍了。 预测,明天更新第三弹。
参考
模块化结构简介