diff --git a/OS/Openthos/专题分析/Soong构建系统.md b/OS/Openthos/专题分析/Soong构建系统.md new file mode 100644 index 0000000..82c2699 --- /dev/null +++ b/OS/Openthos/专题分析/Soong构建系统.md @@ -0,0 +1,102 @@ +在Android7.0之前,使用的是GNU Make。从Android7.0开始,引入Soong。 + +#### Android.bp文件的格式 + +Android.bp的设计原则是简约,它不包含条件或控制流语句,所有的复杂性都是由go语言写的内在逻辑来控制的。 + +##### 模块Modules + +模块类型详见:[module type](https://ci.android.com/builds/submitted/6066704/linux/latest/view/soong_build.html)。模块是由模块类型+一组键值对组成的。格式如下: + +``` +cc_binary { + name: "gzip", + srcs: ["src/test/minigzip.c"], + shared_libs: ["libz"], + stl: "none", +} +``` + +- 模块名 + + cc_binary用于生成可以在设备上跑的二进制。 + + filegroup包含一个文件的列表,用于其它模块里属性的引用(使用`:`语法),或者在包之间导出文件。 + +- 属性 + + `name`属性是必须的,它表示模块的名称。其值在所有模块里必须是唯一的。 + + `srcs`,表示用来编译C/C++模块的源文件列表。也可以使用语法`:module`来引用其它模块的输出。 + + `shared_libs`,表示其它模块的列表,这些模块将被动态链接到本模块中。 + + `stl`,选项要使用的STL库。可能的值是"libc++", "libc++_static", "libstdc++"或"none"。 + +##### 类型Types + +变量和属性是强制类型的,变量的类型由第一次赋值时自动设定,属性的类型由模块类型静态设定。支持的类型有: + +- 布尔型(true, false) +- 整型(int) +- 字符串(string) +- 字符串列表(["string1","string2"]) +- Maps ({key1: "value1", key2: ["value2"]}) + +Maps可以包含任何类型的值,包括嵌套maps。 + +##### Globs + +Glob模式是包含通配符的匹配模式。`*`可以匹配多个字符,`**`可以匹配多个路径。 + +##### 变量Variables + +变量的作用域为文件的剩余部分,以及子bp文件里。变量是不可变的,但有一个例外:可以使用`+=`来追加一个值,但仅限于它们被引用前。 + +##### 注释Comments + +支持`/* */`和`//`。 + +##### 操作符Operators + +可以使用`+`操作符追加字符串,字符串列表或maps。也可以用`+`给整数求和。两个map执行`+`操作将使它们各自拥有对方的键值。 + +##### 条件判断Conditionals + +大多数情况下,条件判断都可以写成map里的一个属性,由顶层的属性来决定使用哪一个的值。 + +例如,为了支持不同架构: + +``` +cc_library { + ... + srcs: ["generic.cpp"], + arch: { + arm: { + srcs: ["arm.cpp"], + }, + x86: { + srcs: ["x86.cpp"], + }, + }, +} + +``` + + + +##### 规范化Formatter + +``` +bpfmt -w . # 递归地重新格式化当前目录下的所有的Android.bp文件 +``` + +规范的格式包括: + +- 四个空格的缩进 +- 多元素列表中每个元素后的新行 +- 列表和maps里的尾随逗号 + +#### 参考文档 + +- [Soong build system](https://source.android.com/setup/build) \ No newline at end of file diff --git a/OS/Openthos/启动过程.md b/OS/Openthos/启动过程.md index 78f7b2c..e2c189d 100644 --- a/OS/Openthos/启动过程.md +++ b/OS/Openthos/启动过程.md @@ -54,7 +54,7 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶 如果当前运行的程序名是modprobe,则执行`modprobe_main`,应该是用来加载内核模块的。 - 如果当前运行的程序名是ueventd,则执行`ueventd_main`,进行设备节点的创建和权限设定。 + 如果当前运行的程序名是ueventd,则执行`ueventd_main`,进行设备节点的创建和权限设定。对于ueventd_main,首先把umask修改为0;然后使用InitKernelLogging函数初始化内核的Log系统,这样ueventd就能通过内核的Log系统输出到串口了;然后注册selinux的回调函数用于打印Log;CreateDeviceHandler使用parser.ParseConfig来解析rc文件,。 如果当前运行的程序名是watchdogd,则执行`watchdogd_main`,用于系统出问题时重启系统。 @@ -162,13 +162,23 @@ init是第一个进程,也叫root进程或所有进程的父进程。在此阶 2. 加入一些事件和Action - am.QueueEventTrigger("early-init")用于触发early-init事件。 + am.QueueEventTrigger("early-init")用于把early-init事件放到事件队列里。 am.QueueBuiltinAction用于添加Action,第一个参数是要执行的command,第二个参数是trigger。 - am.QueueEventTrigger("init")用来触发所有的boot actions。 + am.QueueEventTrigger("init")用来把init事件放到事件队列里,init事件的作用触发所有的boot actions。 - bootmode是个全局静态变量,用来标示启动模式,android的启动模式分为正常开机流程和关机充电流程。 + bootmode是个全局静态变量,用来标示启动模式,android的启动模式分为正常开机流程和关机充电流程。如果bootmode为"charge"则是关机充电流程,则把事件charge放到事件队列里;否则就是就是正常开机流程,这会把事件late-init放到事件队列里。 + +3. 事件队列里Action的执行过程 + + Action的执行次序是early-init, init, late-init。 + + 在early-init里,首先是设置init进程及其子进程的oom_adj,然后禁用键盘中断,然后是创建一些目录挂载一些设备,最后启动uenventd服务。uenventd在/sbin/uenventd,是指向init的软链接,在init的main函数里可见最终调用的是ueventd_main函数。 + + 在init里干了大部分的初始化工作。 + + 在late-init里是把更多的Action加到了事件队列里。 ##### init进程第二阶段之服务处理 @@ -207,7 +217,7 @@ Zygote是一个虚拟机进程,由init进程启动。Zygote预加载以及初 zygote程序是app_process64,zygote_secondary程序是app_process32。两者的参数和属性基本相同。 -- 在init.cpp的main里触发late-init,在init.rc的late-init里触发zygote-start,在init.rc的zygote-start里启动zygote进程和zygote_secondary进程。 +- 在init.cpp的main里把late-init加入运行队列,在init.rc的late-init里把zygote-start加入运行队列,在init.rc的zygote-start里启动zygote进程和zygote_secondary进程。 - 在init/builtins.cpp的builtin_functions里定义了start命令对应的函数do_start,do_start函数最终是调用对应server的Start方法,Start方法主要是fork一个新进程然后执行对应server的二进制文件。 @@ -296,7 +306,7 @@ Zygote启动系统服务,即ZygoteInit类里的startSystemServer方法。 从整体来看,一个rc文件是由若干个段组成的。一个段只能是Actions或Server其中之一: - Actions,由trigger和它下面的一堆Commands组成。 -- Servers,由一堆Options组成。 +- Servers,由一堆Options组成。需要注意的是,Servers的启动也是在Actions里指定的(使用class_start命令或者start命令). 注意:Actions和Servers不能重名。如果重名了,后出现的定义将会作为错误而忽略掉。 @@ -338,43 +348,21 @@ name是程序名,pathname是程序所在的路径,argument是程序的参数 Options是Services的修改器,它们影响init进程如何运行service以及何时运行service。 -- console,此服务需要一个console。默认设备为`/dev/console`。也可自己指定,如`console /dev/tty0`,其中`/dev/`是可忽略的,即等同于`console tty0`。 - -- ctitical,此服务是一个设备关键服务。如果在4分钟内退出4次,设备将会重启进入recovery模式。 - -- disabled,此服务无法通过它所属的类型(class)来启动,必须通过它的名字来启动。这就意味着启动一个类型的服务时这种服务是不会被启动的,也就是它被disable掉了。 - -- setenv,在启动的进程里设置环境变量。 - -- socket \ \ \ [\ [\]] - - 创建一个unix域socket,它的名字叫/dev/socket/\,并将它的fd传给启动的进程。 - -- file,打开一个文件并将它的fd传给启动的进程。 - -- user,在执行服务前改变用户(user),默认的用户是root。 - -- group,在执行服务前改变组名。 +- animation,将会包含所有启动和关闭animation所必须的服务。 - capabilities,在执行服务的时候设置capabilities. -- seclabel,在执行服务前改变为seclabel。 - -- oneshot,当服务退出后不要重启它。 - - class,为服务指定所属的类型。在同一个类型里的服务可以同时打开或关闭。如不使用此选项明确指定服务所属的类型,则默认在default里。 -- animation,将会包含所有启动和关闭animation所必须的服务。 +- console,此服务需要一个console。默认设备为`/dev/console`。也可自己指定,如`console /dev/tty0`,其中`/dev/`是可忽略的,即等同于`console tty0`。 -- onrestart当服务重启的时候执行一条命令。 +- critical,此服务是一个设备关键服务。如果在4分钟内退出4次,设备将会重启进入recovery模式。 -- writepid,当子进程forks的时候将其pid写到给定文件中。 +- disabled,此服务无法通过它所属的类型(class)来启动,必须通过它的名字来启动。这就意味着启动一个类型的服务时这种服务是不会被启动的,也就是它被disable掉了。 -- priority,调度service进程的优先级。 +- file,打开一个文件并将它的fd传给启动的进程。 -- namespace,当forking一个服务的时候,进入一个新的PID或挂载namespace。 - -- oom_score_adjust,设置子进程的变量值。 +- group,在执行服务前改变组名,默认的组是root。 - memcg.swappiness,设置子进程的变量值。 @@ -382,8 +370,30 @@ Options是Services的修改器,它们影响init进程如何运行service以及 - memcg.limit_in_bytes,设置子进程的变量值。 +- namespace,当forking一个服务的时候,进入一个新的PID或挂载namespace。 + +- oneshot,当服务退出后不要重启它。如果一个程序不是守护进程,应该使用这个选项。 + +- onrestart当服务重启的时候执行一条命令。 + +- oom_score_adjust,设置子进程的变量值。 + +- priority,调度service进程的优先级。 + +- seclabel,在执行服务前改变为seclabel。 + +- setenv,在启动的进程里设置环境变量。 + - shutdown,设置关机的时候此service进程的行为。默认是关机进程通过传递SIGTERM和SIGKILL来杀死此服务。 +- socket \ \ \ [\ [\]] + + 创建一个unix域socket,它的名字叫/dev/socket/\,并将它的fd传给启动的进程。 + +- user,在执行服务前改变用户(user),默认的用户是root。 + +- writepid,当子进程forks的时候将其pid写到给定文件中。 + ##### Triggers Triggers就是个字符串,用来匹配特定事件和触发一个action。分为事件触发器和权限触发器。 @@ -552,11 +562,10 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分 > Create a symbolic link at _path_ with the value _target_ `sysclktz ` -> Set the system clock base (0 if system clock ticks in GMT) +> 设置系统时钟基础(0代表GMT,GMT的意思是格林尼治标准时间)。 `trigger ` -> Trigger an event. Used to queue an action from another -> action. +> 触发一个事件。用于从其它动作里把一个动作加到队列里。 `umount ` > Unmount the filesystem mounted at that path. @@ -580,11 +589,19 @@ Triggers就是个字符串,用来匹配特定事件和触发一个action。分 > immediately. `write ` + > 把写到里。里的属性将会展开。 ##### Imports -解析init配置文件,扩展当前配置。 +`import ` + +> 解析init配置文件,扩展当前配置。如果path是目录,此目录下的所有文件都将作为配置文件被解析,但这样的解析不是递归的,下级目录将不会被解析。 +> +> import是个关键字(keyword)而不是命令(command),它不会是Action的一部分,它本身就是个独立的段(section),它的执行逻辑如下: +> +> - 如果是一个文件则解析这个文件,如果是一个目录则按字母序解析这个目录下的所有文件 +> - 如果文件里依然有import语句,则继续解析之(这意味着import不管出现在文件的什么位置,它总是最后执行的) #### 参考文档 diff --git a/OS/Openthos/编译过程.md b/OS/Openthos/编译过程.md index 63e2b13..c80017c 100644 --- a/OS/Openthos/编译过程.md +++ b/OS/Openthos/编译过程.md @@ -56,76 +56,74 @@ lunch命令定义在build/envsetup.sh里,用来让用户选择编译设备与 ### make -Android.mk和Android.bp都会转化成ninja文件,ninja才是真正的编译配置文件。 +- make命令定义在build/envsetup.sh里。 -Android.mk收集生成out/build-openthos_x86_64.ninja。Android.bp收集生成out/soong/build.ninja.d,进而生成out/soong/build.ninja。out/combined-openthos_x86_64.ninja用于将如上两个文件组织起来。 + ``` + function make() + { + _wrap_build $(get_make_command) "$@" + } + ``` -#### 构建的起点 +- 依据`get_make_command`函数的定义,$(get_make_command)的值是"build/soong/soong_ui.bash --make-mode"。 -在`build/core/main.mk`,如没有指定编译目标,则默认编译目标是droid。droid的目标是编译出整个系统的镜像,对于openthos来说就会编译出openthos的镜像。 +- 在`_wrap_build`函数可见,第二行"$@"已经执行完所有的编译过程了,其它部分只不过是输出一些统计信息而已。 -droid依赖droid_targets,droid_targets依赖blueprint_tools, apps_only, droidcore, dist_files。blueprint_tools是编译工具;apps_only会编译出不含user, userdebug和eng参数的应用程序;droidcore用来构建整个系统,它依赖了更多的目标,本身不进行任何处理;dist_files用来复制文件到out/dist目录,默认的编译目标是不会产生out/dist目录的,out/dist目录用于存放为多种分发而准备的包。 +- build/soonn/soong_ui.bash。首先执行build/soong/cmd/microfactory/microfactory.bash,定义了两个工具函数,`getoutdir`用来查找out目录,`build_go`用来编译所需的二进制(参数1是所要编译的二进制的名字,参数2是包名);然后用build_go来编译soong_ui,最后执行的是`out/soong_ui $@`完成构建的过程。 -droidcore依赖files, systemimage和一堆$(INSTALLED_*)。files依赖了更多的目标,本身不进行任何处理;systemimage用来生成system.img,将被挂载为/system;其它依赖作用如下: +- 函数build_go的执行逻辑:如果$mf_bin存在且$mf_version的值等于$mf_version_file里保存的值,说明$mf_bin存在且是最新的,此时它就是$mf_cmd;否则说明$mf_bin不存在或它的版本不是最新的,这时就需要构建新的$mf_cmd,它的值为`go run $mf_src/microfactory.go`。`$mf_cmd ...`那行是真正干活的,-s用来指示microfactory的源码目录,-b用来指示microfactory二进制的位置,-pkg-path是从包前缀到文件路径的映射,-trimpath用于从记录的源文件路径中删除前缀,-o用于指定输出文件的名称。 -- $(INSTALLED_BOOTIMAGE_TARGET) :生成boot.img -- $(INSTALLED_RECOVERYIMAGE_TARGET):生成recovery.img -- $(INSTALLED_VBMETAIMAGE_TARGET) -- $(INSTALLED_USERDATAIMAGE_TARGET) :生成userdata.img -- $(INSTALLED_CACHEIMAGE_TARGET) :生成cache.img -- $(INSTALLED_BPTIMAGE_TARGET) -- $(INSTALLED_VENDORIMAGE_TARGET) -- $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) -- $(INSTALLED_FILES_FILE) :生成installed-files.txt文件,记录了当前镜像中已安装的文件列表。 -- $(INSTALLED_FILES_FILE_VENDOR) -- $(INSTALLED_FILES_FILE_SYSTEMOTHER) +#### 构建的过程 -files依赖的目标如下: - -- $(modules_to_install) :编译当前配置下所有需要被安装的模块 -- $(INSTALLED_ANDROID_INFO_TXT_TARGET):生成android-info.txt文件,记录当前build配置的设备信息。 +构建的起点在`build/core/main.mk`,可以看到整个文件的结构如下: ``` -49 include $(BUILD_SYSTEM)/config.mk # 定义基于配置和宿主信息的变量 -86 include $(BUILD_SYSTEM)/cleanbuild.mk # 定义删除编译结果的函数和目标 -133 include $(BUILD_SYSTEM)/definitions.mk # 定义编译过程中用到的变量和宏 -138~283 # 检查TARGET_BUILD_VARIANT变量有效性 -根据make参数决定编译目标 -419~423 # 加载所有子目录下的Android.mk -从Android.md中筛选出TARGET_BUILD_VARIANT目标 -包含Makefile -930~1183 定义一些目标 +ifndef KATI + ... +else # KATI + ... +endif # KATI ``` +由于没有定义KATI,所以走的是ifndef流程。MAKECMDGOALS是make执行时后面的参数,这意味着不管执行什么目标最终都执行的都是run_soong_ui这个目标。这样编译的控制权就交到了soong手上,后面的事就跟make没有关系了。 + +soong_ui代码在`build/soong/cmd/soong_ui/main.go`,真正干活的是main函数里的最后一句: + +``` +build.Build(buildCtx, config, build.BuildAll) +``` + +build.Build在`ui/build/build.go`,从中可以看到执行soong的流程: + +1. `runMakeProductConfig`在ui/build/make.go,用来配置编译参数。在这里可以看到[第一段输出](# 第一段输出) + +2. `runSoongBootstrap`和`runSoong`在ui/build/soong.go,分别运行的是`./bootstrap.bash`和`soong`两个命令。在这里可以看到[第二段输出](# 第二段输出)和[第三段输出](# 第三段输出) + + `./bootstrap.bash`的最后一句会执行`build/blueprint/bootstrap.bash $@`,如不带参数则生成minibp,如带参数则生成一个基于Blueprint的构建系统。 + +3. `runKati`在ui/build/kati.go,运行的是ckati命令。把所有的Android.mk文件生成ninja文件。 + +4. `createCombinedBuildNinjaFile`在ui/build/build.go,在out/combined-openthos_x86_64.ninja文件里用include关键字把前两步生成的ninja文件包含进来。 + +5. `runNinja`在ui/build/ninja.go,运行ninja命令执行构建过程,所使用的配置文件就是第四步生成的combined-openthos_x86_64.ninja。在这里看到[第四段输出](# 第四段输出) + #### 第一段输出 -在`build/core/dumpvar.mk`,输出的是关于编译环境的若干变量。 - -包含的次序main.mk --> config.mk --> dumpvar.mk +输出的是关于编译环境的若干变量。 #### 第二段输出 在`build/soong/build.ninja.in`,编译生成`out/soong/.bootstrap`下的文件。源文件在build/blueprint。 -包含的次序main.mk --> - #### 第三段输出 搜集所有的`Android.bp`文件生成build.ninja。 #### 第四段输出 -编译生成`out/soong/.bootstrap`下的文件。 - -#### 第五段输出 - -看起来是包含更多的文件,包括`*.h *.cpp Android.bp`等。 - -#### 第六段输出 - 详细的编译过程 -#### 第七段编译目标oto_img +##### 第四段之生成oto_img 在bootable/newinstaller/Android.mk @@ -224,4 +222,6 @@ unsquashfs system.sfs [理解安卓build系统](https://www.ibm.com/developerworks/cn/opensource/os-cn-android-build/) -[Android.bp及其工具链](http://note.qidong.name/2017/08/android-blueprint/) \ No newline at end of file +[Android.bp及其工具链](http://note.qidong.name/2017/08/android-blueprint/) + +[Android Soong build系统介绍](https://www.jianshu.com/p/80013a768a45) \ No newline at end of file diff --git a/Software/builtin命令/source.md b/Software/builtin命令/source.md new file mode 100644 index 0000000..1f3d0a6 --- /dev/null +++ b/Software/builtin命令/source.md @@ -0,0 +1,5 @@ +``` +source [arguments] # 在当前shell下执行filename里的命令 + # 返回filename里最后一条命令的返回值。如果filename不可读则错误。 +``` + diff --git a/Software/tmux.md b/Software/tmux.md index e7e2422..f53b6d7 100644 --- a/Software/tmux.md +++ b/Software/tmux.md @@ -21,6 +21,9 @@ ctrl + b c # 创建新窗口 & # 关闭当前窗口 数字键 # 切换到指定窗口 +l # 切换到上一个窗口 +f # 依据窗口编号向前切换 +n # 住所窗口编号向后切换 ``` diff --git a/Software/vim.md b/Software/vim.md index a1862de..97d079d 100644 --- a/Software/vim.md +++ b/Software/vim.md @@ -141,6 +141,7 @@ V # 选择,以行为单位 w //移动到下个单词的第一个字符 e //移动到下个单词的最后一个字符 + b # 移到上一个单词 0 //移动到行首 ``` diff --git a/Software/编译工具/Makefile.md b/Software/编译工具/Makefile.md index 570f5a7..4402dd2 100644 --- a/Software/编译工具/Makefile.md +++ b/Software/编译工具/Makefile.md @@ -49,7 +49,8 @@ $(findstring) $(firstword) $(patsubst PATTERN,REPLACEMENT,TEXT) # 将TEXT中符合PATTERN的单词替换为PEPLACEMENT -$(sort) +$(sort ) + # 给字符串中的单词按升序排序,返回排序后的字符串。 $(strip) $(subst) $(wildcard ) diff --git a/Software/编译工具/go.md b/Software/编译工具/go.md new file mode 100644 index 0000000..57ede99 --- /dev/null +++ b/Software/编译工具/go.md @@ -0,0 +1,35 @@ +``` +go [arguments] +``` + +#### 命令 + +``` +clean +doc +env +fix +fmt +get +install +list +test +tool +version +vet +``` + +##### build + +``` +go build [-o output] [build flags] [packages] # 编译包和依赖 +# 选项 +``` + +##### run + +``` +run [build flags] [arguments] + # 编译和运行Go程序。关于[build flags]请查询"go-build"。 +``` + diff --git a/Software/编译工具/make.md b/Software/编译工具/make.md index 12de230..0e0acf8 100644 --- a/Software/编译工具/make.md +++ b/Software/编译工具/make.md @@ -39,12 +39,12 @@ -o file -O [type] -p, --print-data-base --q +-q, --question # 不运行任何命令,也不打印任何东西,只返回一个退出的状态。如果编译目标已经更新则返回0,否则返回其它数字。 -r -R -s -S --t +-t, --touch # 更新文件的日期,从而不执行它所依赖的命令。这实际上就是假装命令已经执行过了,以便make将来调用的时候欺骗它。 --trace -v -w diff --git a/Software/编译工具/ninja.md b/Software/编译工具/ninja.md new file mode 100644 index 0000000..6581875 --- /dev/null +++ b/Software/编译工具/ninja.md @@ -0,0 +1,16 @@ +一个构建工具,它的作用和make命令是一样的。 + +#### ninja文件介绍 + +``` +# 规则(rule) +rule + command = gcc ... + description = + +# build语句 +build : + +# subninja和include,都是用来引入其它的ninja文件。不同点是,subninja引入的子模块可以使用父模块的变量,include引入的子模块不可以使用交模块中的变量。 +``` +