首页
关于
友链
其它
统计
壁纸
更多
留言
Search
1
cgroup--(4)cgroup v1和cgroup v2的详细介绍
6,730 阅读
2
修改Linux Kernel defconfig的标准方法
6,581 阅读
3
Android系统之VINTF(1)manifests&compatibility matrices
6,167 阅读
4
使用git生成patch和应用patch
3,720 阅读
5
c语言的__attribute__
3,205 阅读
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
登录
Search
标签搜索
Rust
shell
Linux
c
uboot
Vim
vintf
Linux驱动
Android
device_tree
git
DEBUG
arm64
链表
数据结构
IDR
内核
ELF
gcc
ARM
adtxl
累计撰写
381
篇文章
累计收到
17
条评论
首页
栏目
默认分类
文章收集
学习总结
算法
环境配置
知识点
入门系列
vim
shell
Git
Make
Android
Linux
Linux命令
内存管理
Linux驱动
Language
C++
C
Rust
工具
软件工具
Bug
COMPANY
页面
关于
友链
其它
统计
壁纸
留言
搜索到
381
篇与
的结果
2021-03-01
[转载]make编译过程-Android10.0编译系统(三)
版权声明:本文为CSDN博主「IngresGe」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/yiranfeng/article/details/1090840821. 概述上一节,我们理清了编译环境初始化的过程,环境变量已经加载,并配置了编译目标,接下来执行一个make命令我们就能够进行编译。make之后是怎么完成编译的,这个很有意思,我们一起往下探讨。2. Android系统的编译历程Android7.0 Google引入了soong构建系统,用来逐步替代GNU make的编译,因此在Android10.0 上,make执行后,我们走的是soong构建环境。Android系统的编译历程:3.Soong编译系统家族成员从下图可知,mk文件被编译成了 out/build-aosp\_arm.ninja和out/build-aosp\_arm-package.ninja,bp文件被编译成了out/soong/build.ninja,这三个ninja文件又被合并成out/combined-aosp\_arm.ninja,最终通过ninja工具来编译out/combined-aosp\_arm.ninja完成最终的编译。4. make的流程图soong构建的流程图如下图所示:5. make()执行完make命令后,会调用envsetup.sh的make()函数进行处理。function make() { _wrap_build $(get_make_command "$@") "$@" }从get_make_command()可以看出,make后,真正然后执行编译的入口是:build/soong/soong_ui.bashfunction get_make_command() { # If we're in the top of an Android tree, use soong_ui.bash instead of make if [ -f build/soong/soong_ui.bash ]; then # Always use the real make if -C is passed in for arg in "$@"; do if [[ $arg == -C* ]]; then echo command make return fi done echo build/soong/soong_ui.bash --make-mode else echo command make fi }6. soong_ui.bash6.1 soong_ui.bash调用栈soong_ui.bash执行过程:source microfactory.bash,得到一些函数命令, 例如:soong_build_go编译/build/soong/cmd/soong_ui/main.go,生成 out/soong_ui这个可执行程序执行命令:out/soong_ui --make-mode ,执行了make命令,会把"build/make/core/main.mk" 加到构建环境中,同时启动kati、blueprint-soong、ninja的编译。接下来根据调用栈的流程,来详细分析编译的过程。6.2 [build/soong/soong_ui.bash]soong_ui.bash 用来配置一些资源环境,得到一些函数命令,例如:soong_build_go,最终回退到根目录,执行out/soong_ui --make-mode进行真正的构建。# Save the current PWD for use in soong_ui export ORIGINAL_PWD=${PWD} export TOP=$(gettop) source ${TOP}/build/soong/scripts/microfactory.bash soong_build_go soong_ui android/soong/cmd/soong_ui cd ${TOP} exec "$(getoutdir)/soong_ui" "$@"6.2.1 [/soong/../microfactory.bash]得到build_go的函数命令,并提供 soong_build_go的函数执行方法[/build/soong/scripts/microfactory.bash] function soong_build_go { BUILDDIR=$(getoutdir) \ SRCDIR=${TOP} \ BLUEPRINTDIR=${TOP}/build/blueprint \ EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path github.com/golang/protobuf=${TOP}/external/golang-protobuf" \ build_go $@ } source ${TOP}/build/blueprint/microfactory/microfactory.bash6.2.2 [/blueprint/../microfactory.bash]build_go主要目的就是用来构建生成 out/soong_ui这个可执行程序,用于参与最终的编译[/build/blueprint/microfactory/microfactory.bash ] function build_go { # Increment when microfactory changes enough that it cannot rebuild itself. # For example, if we use a new command line argument that doesn't work on older versions. local mf_version=3 local mf_src="${BLUEPRINTDIR}/microfactory" local mf_bin="${BUILDDIR}/microfactory_$(uname)" local mf_version_file="${BUILDDIR}/.microfactory_$(uname)_version" local built_bin="${BUILDDIR}/$1" local from_src=1 if [ -f "${mf_bin}" ] && [ -f "${mf_version_file}" ]; then if [ "${mf_version}" -eq "$(cat "${mf_version_file}")" ]; then from_src=0 fi fi local mf_cmd if [ $from_src -eq 1 ]; then # `go run` requires a single main package, so create one local gen_src_dir="${BUILDDIR}/.microfactory_$(uname)_intermediates/src" mkdir -p "${gen_src_dir}" sed "s/^package microfactory/package main/" "${mf_src}/microfactory.go" >"${gen_src_dir}/microfactory.go" mf_cmd="${GOROOT}/bin/go run ${gen_src_dir}/microfactory.go" else mf_cmd="${mf_bin}" fi # GOROOT must be absolute because `go run` changes the local directory GOROOT=$(cd $GOROOT; pwd) ${mf_cmd} -b "${mf_bin}" \ -pkg-path "github.com/google/blueprint=${BLUEPRINTDIR}" \ -trimpath "${SRCDIR}" \ ${EXTRA_ARGS} \ -o "${built_bin}" $2 if [ $? -eq 0 ] && [ $from_src -eq 1 ]; then echo "${mf_version}" >"${mf_version_file}" fi }soong_ui最终的编译命令展开为:$(cd /prebuilts/go/linux-x86/; pwd) /out/microfactory_Linux -b "/out/microfactory_Linux" \ -pkg-path "github.com/google/blueprint=/build/blueprint" \ -trimpath "./" \ -pkg-path android/soong=/build/soong -pkg-path github.com/golang/protobuf=/external/golang-protobuf} \ -o "out/soong_ui" android/soong/cmd/soong_ui从上面的流程可知,生成soong_ui经历几件事情:通过/build/blueprint/microfactory/microfactory.go 编译出/out/microfactory_Linux使用/out/microfactory_Linux来编译soong_uimicrofactory是一个增量编译go程序的工具。它类似于“go install”,但不需要GOPATH。包->路径映射可以指定为命令行选项:-pkg-path android/soong=build/soong -pkg-path github.com/google/blueprint=build/blueprint其实microfactory就是一个高级一点的go命令,它自己由go编出来,又代替了一部分go的部分功能,鸡生蛋,蛋生鸡的故事,这里得到了完美解释 ^\_^。microfactory编译示例:准备go的代码在/home/ingresge/AP/AOSP_Q中创建一个目录hello:创建hello.go---vim hello/hello.go在其中打印一个“Hello,Go!”package main import ( "log" "os" ) func main() { testlog := log.New(os.Stderr, "", log.Ltime) testlog.Println("Hello,Go!") }使用microfactory 编译hello.go/home/ingresge/AP/AOSP_Q/out/microfactory_Linux -pkg-path android/hello=/home/ingresge/AP/AOSP_Q/hello -trimpath /home/ingresge/AP/AOSP_Q/hello -o /home/ingresge/AP/AOSP_Q/out/hellogo android/hello/运行执行命令:./out/hellogo输出结果:17:18:44 Hello,Go!6.3 soong_uisoong_ui 是通过编译 build/soong/cmd/soong_ui/main.go得来,我们接下来分析一下main.go的一些流程6.3.1 main.go调用栈6.3.2 soong_ui启动编译的入口func main() { var stdio terminal.StdioInterface stdio = terminal.StdioImpl{} // dumpvar uses stdout, everything else should be in stderr if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" { stdio = terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr) } writer := terminal.NewWriter(stdio) defer writer.Finish() log := logger.New(writer) defer log.Cleanup() if len(os.Args) < 2 || !(inList("--make-mode", os.Args) || os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode") { log.Fatalln("The `soong` native UI is not yet available.") } ctx, cancel := context.WithCancel(context.Background()) defer cancel() trace := tracer.New(log) defer trace.Close() met := metrics.New() stat := &status.Status{} defer stat.Finish() stat.AddOutput(terminal.NewStatusOutput(writer, os.Getenv("NINJA_STATUS"), build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))) stat.AddOutput(trace.StatusTracer()) build.SetupSignals(log, cancel, func() { trace.Close() log.Cleanup() stat.Finish() }) buildCtx := build.Context{ContextImpl: &build.ContextImpl{ Context: ctx, Logger: log, Metrics: met, Tracer: trace, Writer: writer, Status: stat, }} var config build.Config if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" { config = build.NewConfig(buildCtx) } else { config = build.NewConfig(buildCtx, os.Args[1:]...) } build.SetupOutDir(buildCtx, config) logsDir := config.OutDir() if config.Dist() { logsDir = filepath.Join(config.DistDir(), "logs") } os.MkdirAll(logsDir, 0777) log.SetOutput(filepath.Join(logsDir, "soong.log")) trace.SetOutput(filepath.Join(logsDir, "build.trace")) stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, "verbose.log"))) stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, "error.log"))) defer met.Dump(filepath.Join(logsDir, "build_metrics")) if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok { if !strings.HasSuffix(start, "N") { if start_time, err := strconv.ParseUint(start, 10, 64); err == nil { log.Verbosef("Took %dms to start up.", time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds()) buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano())) } } if executable, err := os.Executable(); err == nil { trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace")) } } // Fix up the source tree due to a repo bug where it doesn't remove // linkfiles that have been removed fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp") fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk") f := build.NewSourceFinder(buildCtx, config) defer f.Shutdown() build.FindSources(buildCtx, config, f) if os.Args[1] == "--dumpvar-mode" { dumpVar(buildCtx, config, os.Args[2:]) } else if os.Args[1] == "--dumpvars-mode" { dumpVars(buildCtx, config, os.Args[2:]) } else { if config.IsVerbose() { writer.Print("! The argument `showcommands` is no longer supported.") writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:") writer.Print("!") writer.Print(fmt.Sprintf("! gzip -cd %s/verbose.log.gz | less -R", logsDir)) writer.Print("!") writer.Print("! Older versions are saved in verbose.log.#.gz files") writer.Print("") time.Sleep(5 * time.Second) } toBuild := build.BuildAll if config.Checkbuild() { toBuild |= build.RunBuildTests } build.Build(buildCtx, config, toBuild) } }主要执行soong/ui/build/build.go,从build.go就可以看到执行soong的大体流程。main.go中配置的toBuild为 BuildProductConfig | BuildSoong | BuildKati | BuildNinja,支持productconfig\soong\kati\ninja的构建。6.3.3 Build调用栈编译步骤如下:1) runMakeProductConfig 主要配置编译参数2) runSoong 对工具进行编译,编译出blueprint等编译工具, 把*.bp 编译成 out/soong/build.ninja/.minibootstrap/build.ninja - Run minibp to generate .bootstrap/build.ninja (Primary stage) - Run minibp to generate .minibootstrap/build.ninja.in /.bootstrap/build.ninja - Build any bootstrap_go_binary rules and dependencies -- usually the primary builder and any build or runtime dependencies. - Run the primary builder to generate build.ninja3) runKatiBuild, 加载 build/make/core/main.mk, 搜集所有的Android.mk文件生成ninja文件:out/build-aosp\_arm.ninja4) runKatiPackage, 加载build/make/packaging/main.mk, 编译生成out/build-aosp\_arm-package.ninja5) createCombinedBuildNinjaFile,将out/soong/build.ninja 、out/build-aosp_arm.ninja和out/build-aosp\_arm-package.ninja, 合成为out/combined-aosp\_arm.ninja6) runNinja,运行Ninja命令, 解析combined-aosp\_arm.ninja,执行编译过程out/combined-aosp_arm.ninja 内容展示如下:builddir = out pool local_pool depth = 42 build _kati_always_build_: phony subninja out/build-aosp_arm.ninja subninja out/build-aosp_arm-package.ninja subninja out/soong/build.ninja6.3.4 BuildBuild入口[/build/soong/ui/build/build.go] func Build(ctx Context, config Config, what int) { ctx.Verboseln("Starting build with args:", config.Arguments()) ctx.Verboseln("Environment:", config.Environment().Environ()) if config.SkipMake() { ctx.Verboseln("Skipping Make/Kati as requested") what = what & (BuildSoong | BuildNinja) } if inList("help", config.Arguments()) { help(ctx, config, what) return } else if inList("clean", config.Arguments()) || inList("clobber", config.Arguments()) { clean(ctx, config, what) return } // Make sure that no other Soong process is running with the same output directory buildLock := BecomeSingletonOrFail(ctx, config) defer buildLock.Unlock() checkProblematicFiles(ctx) SetupOutDir(ctx, config) checkCaseSensitivity(ctx, config) ensureEmptyDirectoriesExist(ctx, config.TempDir()) SetupPath(ctx, config) if config.StartGoma() { // Ensure start Goma compiler_proxy startGoma(ctx, config) } if what&BuildProductConfig != 0 { // Run make for product config runMakeProductConfig(ctx, config) } if inList("installclean", config.Arguments()) { installClean(ctx, config, what) ctx.Println("Deleted images and staging directories.") return } else if inList("dataclean", config.Arguments()) { dataClean(ctx, config, what) ctx.Println("Deleted data files.") return } if what&BuildSoong != 0 { // Run Soong runSoong(ctx, config) } if what&BuildKati != 0 { // Run ckati genKatiSuffix(ctx, config) runKatiCleanSpec(ctx, config) runKatiBuild(ctx, config) runKatiPackage(ctx, config) ioutil.WriteFile(config.LastKatiSuffixFile(), []byte(config.KatiSuffix()), 0777) } else { // Load last Kati Suffix if it exists if katiSuffix, err := ioutil.ReadFile(config.LastKatiSuffixFile()); err == nil { ctx.Verboseln("Loaded previous kati config:", string(katiSuffix)) config.SetKatiSuffix(string(katiSuffix)) } } // Write combined ninja file createCombinedBuildNinjaFile(ctx, config) if what&RunBuildTests != 0 { testForDanglingRules(ctx, config) } if what&BuildNinja != 0 { if !config.SkipMake() { installCleanIfNecessary(ctx, config) } // Run ninja runNinja(ctx, config) } }6.4 main.mk文件分析执行runKatiBuild时,有个重要的步骤,就是加载build/make/core/main.mk,main.mk文件是Android Build系统的主控文件。从main.mk开始,将通过include命令将其所有需要的.mk文件包含进来,最终在内存中形成一个包括所有编译脚本的集合,这个相当于一个巨大Makefile文件。Makefile文件看上去很庞大,其实主要由三种内容构成: 变量定义、函数定义和目标依赖规则,此外mk文件之间的包含也很重要。main.mk主要做了以下几件事情: 1. 定义编译目标product 2. 加载config.mk来初始化相关变量,检测编译环境和目标环境 3. 清除规则,清除out目录中的dex文件 4. 加载build/croe/definitions.mk,定义了很多通用函数,供编译过程调用 5. 加载平台开发工具包 build/make/core/pdk_config.mk 6. 加载系统中所有的Android.mk,最终会被存放到out/.module_paths/Android.mk.list 7. Link 类型检查,校验Link 8. 要为此产品生成的模块的基本列表由相应的产品定义文件指定,这些定义在"product_config.mk"中 9. 运行时APEX库,并进行检查校验 10. 将所有要安装的模块都保存在变量ALL_DEFAULT_INSTALLED_MODULES中,并且将build/core/Makefie文件加载进来。build/core/Makefie文件会根据要安装的模块生成system.img、super.img、boot.img和recovery.img等镜像文件的生成规则 11. 定义编译的image目标 12. 构建文件,然后将其打包成rom格式6.4.1 定义编译目标product流程1:没有KATI命令时,走run_soong_ui执行,通过soong_ui.bash --make-mode 进行编译流程2:通过kati命令编译时在Android10.0中,使用的是soong构建,因此直接走流程2确定了最终的编译目标为droidifndef KATI host_prebuilts := linux-x86 ifeq ($(shell uname),Darwin) host_prebuilts := darwin-x86 endif #流程1:没有KATI命令时,走run_soong_ui执行,通过soong_ui.bash --make-mode 进行编译 .PHONY: run_soong_ui run_soong_ui: +@prebuilts/build-tools/$(host_prebuilts)/bin/makeparallel --ninja build/soong/soong_ui.bash --make-mode $(MAKECMDGOALS) .PHONY: $(MAKECMDGOALS) $(sort $(MAKECMDGOALS)) : run_soong_ui @#empty else # KATI #流程2:通过kati命令编译时,走该流程,Android10.0中走该流程 $(info [1/1] initializing build system ...) .... #1.定义编译目标product #这是默认目标。它必须是第一个声明的目标。 .PHONY: droid DEFAULT_GOAL := droid $(DEFAULT_GOAL): droid_targets .PHONY: droid_targets droid_targets: ... #endif #KATI6.4.2 加载config.mk加载config.mk来初始化相关变量,检测编译环境和目标环境,加载clang/config.mk,配置一些编译的环境。include build/make/core/config.mk ... #加载out/soong/make_vars-aosp_arm.mk include $(SOONG_MAKEVARS_MK) #加载clang编译的一些配置 include $(BUILD_SYSTEM)/clang/config.mk ...6.4.3 清楚规则清除规则,清除out目录中的dex文件。... .PHONY: clean-dex-files clean-dex-files: $(hide) find $(OUT_DIR) -name "*.dex" | xargs rm -f $(hide) for i in `find $(OUT_DIR) -name "*.jar" -o -name "*.apk"` ; do ((unzip -l $$i 2> /dev/null | \ grep -q "\.dex$$" && rm -f $$i) || continue ) ; done @echo "All dex files and archives containing dex files have been removed." ...6.4.5 加载pdk_config.mk加载 平台开发工具包... include build/make/core/pdk_config.mk #为userdebug、eng和non-REL生成启用动态链接器警告 ifneq ($(TARGET_BUILD_VARIANT),user) ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1 else #只要user版本不是最终版本,就启用它。 ifneq ($(PLATFORM_VERSION_CODENAME),REL) ADDITIONAL_BUILD_PROPERTIES += ro.bionic.ld.warning=1 endif endif ...6.4.6 加载系统所有的Android.mk加载系统中所有的Android.mk,最终会被存放到out/.module_paths/Android.mk.list。如果环境变量ONE_SHOT_MAKEFILE的值不等于空,也就是我们执行的是mm或者mmm命令,那么就表示要编译的是特定的模块。这些指定要编译的模块的Android.mk文件路径就保存在环境变量ONE_SHOT_MAKEFILE中,因此直接将这些Android.mk文件加载进来就获得相应的编译规则。如果环境变量ONE_SHOT_MAKEFILE的值等于空,且dont_bother不为true,会通过out/soong/Android-aosp_arm.mk 来获得Android源代码工程下的所有Android.mk文件的路径列表,并存入到out/.module_paths/Android.mk.list 中。 ... ifneq ($(ONE_SHOT_MAKEFILE),) #我们可能已经被带有子目录makefile的“mm” shell函数调用了。 include $(SOONG_ANDROID_MK) $(wildcard $(ONE_SHOT_MAKEFILE)) # Change CUSTOM_MODULES to include only modules that were # defined by this makefile; this will install all of those # modules as a side-effect. Do this after including ONE_SHOT_MAKEFILE # so that the modules will be installed in the same place they # would have been with a normal make. CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS))) #帮助目标打印出安装路径 define register_module_install_path .PHONY: GET-MODULE-INSTALL-PATH-$(1) GET-MODULE-INSTALL-PATH-$(1): echo 'INSTALL-PATH: $(1) $(ALL_MODULES.$(1).INSTALLED)' endef SORTED_ALL_MODULES := $(sort $(ALL_MODULES)) UNIQUE_ALL_MODULES := $(foreach m,$(SORTED_ALL_MODULES),\ $(if $(call streq,$(m),$(lastword $(UNIQUE_ALL_MODULES))),,\ $(eval UNIQUE_ALL_MODULES += $(m)))) SORTED_ALL_MODULES := $(foreach mod,$(UNIQUE_ALL_MODULES),$(if $(ALL_MODULES.$(mod).INSTALLED),\ $(eval $(call register_module_install_path,$(mod)))\ $(foreach path,$(ALL_MODULES.$(mod).PATH),\ $(eval my_path_prefix := GET-INSTALL-PATH-IN)\ $(foreach component,$(subst /,$(space),$(path)),\ $(eval my_path_prefix := $$(my_path_prefix)-$$(component))\ $(eval .PHONY: $$(my_path_prefix))\ $(eval $$(my_path_prefix): GET-MODULE-INSTALL-PATH-$(mod)))))) UNIQUE_ALL_MODULES := else # ONE_SHOT_MAKEFILE ifneq ($(dont_bother),true) FULL_BUILD := true #包括系统中的所有makefile :out/.module_paths/Android.mk.list subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list) subdir_makefiles_total := $(words int $(subdir_makefiles) post finish) .KATI_READONLY := subdir_makefiles_total $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk))) ifneq (,$(PDK_FUSION_PLATFORM_ZIP)$(PDK_FUSION_PLATFORM_DIR)) # 加载pdk_fusion_modules.mk include $(BUILD_SYSTEM)/pdk_fusion_modules.mk endif # PDK_FUSION_PLATFORM_ZIP || PDK_FUSION_PLATFORM_DIR droid_targets : blueprint_tools endif # dont_bother endif # ONE_SHOT_MAKEFILE ...6.4.7 Link检查编译时的Link 类型检查,校验Link... #Link 类型检查 #“ALL_LINK_TYPES”包含所有链接类型前缀的列表(通常每个模块一个,但是apk可以“链接”到java和本机代码)。 #链接类型前缀由intermediates dir所需的所有信息组成: # # LINK_TYPE:TARGET:_:2ND:STATIC_LIBRARIES:libfoo # #所有未在“允许”或“警告”中列出的依赖关系链接类型都将成为错误 link_type_error := define link-type-prefix-base $(word 2,$(subst :,$(space),$(1))) endef define link-type-prefix $(if $(filter AUX-%,$(link-type-prefix-base)),$(patsubst AUX-%,AUX,$(link-type-prefix-base)),$(link-type-prefix-base)) endef define link-type-aux-variant $(if $(filter AUX-%,$(link-type-prefix-base)),$(patsubst AUX-%,%,$(link-type-prefix-base))) endef define link-type-common $(patsubst _,,$(word 3,$(subst :,$(space),$(1)))) endef define link-type-2ndarchprefix $(patsubst _,,$(word 4,$(subst :,$(space),$(1)))) endef define link-type-class $(word 5,$(subst :,$(space),$(1))) endef define link-type-name $(word 6,$(subst :,$(space),$(1))) endef define link-type-os $(strip $(eval _p := $(link-type-prefix))\ $(if $(filter HOST HOST_CROSS,$(_p)),\ $($(_p)_OS),\ $(if $(filter AUX,$(_p)),AUX,android))) endef define link-type-arch $($(link-type-prefix)_$(link-type-2ndarchprefix)ARCH) endef define link-type-name-variant $(link-type-name) ($(link-type-class) $(link-type-os)-$(link-type-arch)) endef ... # 验证$(1)是否可以链接到$(2) # $(1)和$(2)都是上面定义的链接类型前缀 define verify-link-type $(foreach t,$($(2).TYPE),\ $(if $(filter-out $($(1).ALLOWED),$(t)),\ $(if $(filter $(t),$($(1).WARN)),\ $(call link-type-warning,$(1),$(2),$(t)),\ $(call link-type-error,$(1),$(2),$(t))))) endef #验证所有分支/配置都有合理的警告/错误,并删除此重写 verify-link-type = $(eval $$(1).MISSING := true) ...6.4.8 加载product_config.mk要为此产品生成的模块的基本列表由相应的产品定义文件指定,这些定义在"product_config.mk"中... #列出特定产品安装的大多数文件,包括: # - PRODUCT_PACKAGES, and their LOCAL_REQUIRED_MODULES # - PRODUCT_COPY_FILES # 要为此产品生成的模块的基本列表由相应的产品定义文件指定,这些定义在"product_config.mk"中 define product-installed-files $(eval _mk := $(strip $(1))) \ $(eval _pif_modules := \ $(PRODUCTS.$(_mk).PRODUCT_PACKAGES) \ $(if $(filter eng,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_ENG)) \ $(if $(filter debug,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_DEBUG)) \ $(if $(filter tests,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_TESTS)) \ $(if $(filter asan,$(tags_to_install)),$(PRODUCTS.$(_mk).PRODUCT_PACKAGES_DEBUG_ASAN)) \ $(call auto-included-modules) \ ) \ $(eval ### Filter out the overridden packages and executables before doing expansion) \ $(eval _pif_overrides := $(call module-overrides,$(_pif_modules))) \ $(eval _pif_modules := $(filter-out $(_pif_overrides), $(_pif_modules))) \ $(eval ### Resolve the :32 :64 module name) \ $(eval _pif_modules_32 := $(patsubst %:32,%,$(filter %:32, $(_pif_modules)))) \ $(eval _pif_modules_64 := $(patsubst %:64,%,$(filter %:64, $(_pif_modules)))) \ $(eval _pif_modules_rest := $(filter-out %:32 %:64,$(_pif_modules))) \ $(eval ### Note for 32-bit product, 32 and 64 will be added as their original module names.) \ $(eval _pif_modules := $(call get-32-bit-modules-if-we-can, $(_pif_modules_32))) \ $(eval _pif_modules += $(_pif_modules_64)) \ $(eval ### For the rest we add both) \ $(eval _pif_modules += $(call get-32-bit-modules, $(_pif_modules_rest))) \ $(eval _pif_modules += $(_pif_modules_rest)) \ $(call expand-required-modules,_pif_modules,$(_pif_modules),$(_pif_overrides)) \ $(filter-out $(HOST_OUT_ROOT)/%,$(call module-installed-files, $(_pif_modules))) \ $(call resolve-product-relative-paths,\ $(foreach cf,$(PRODUCTS.$(_mk).PRODUCT_COPY_FILES),$(call word-colon,2,$(cf)))) endef ...6.4.9 运行APEX库运行时APEX库,并进行检查校验 ... APEX_MODULE_LIBS := \ libadbconnection.so \ libadbconnectiond.so \ libandroidicu.so \ libandroidio.so \ libart-compiler.so \ libart-dexlayout.so \ libart-disassembler.so \ libart.so \ libartbase.so \ libartbased.so \ libartd-compiler.so \ libartd-dexlayout.so \ libartd.so \ libartpalette.so \ libc.so \ libdexfile.so \ libdexfile_external.so \ libdexfiled.so \ libdexfiled_external.so \ libdl.so \ libdt_fd_forward.so \ libdt_socket.so \ libicui18n.so \ libicuuc.so \ libjavacore.so \ libjdwp.so \ libm.so \ libnativebridge.so \ libnativehelper.so \ libnativeloader.so \ libnpt.so \ libopenjdk.so \ libopenjdkjvm.so \ libopenjdkjvmd.so \ libopenjdkjvmti.so \ libopenjdkjvmtid.so \ libpac.so \ libprofile.so \ libprofiled.so \ libsigchain.so \ # Conscrypt APEX libraries APEX_MODULE_LIBS += \ libjavacrypto.so \ ... #如果下面的检查失败,那么某些库已经在system/lib或system/lib64中结束,而这些库只打算放入一些APEX包中。 #可能的原因是/system中的库或二进制文件已经增长了一个直接或间接拉入禁止的库的依赖关系。 #要解决此问题,请查找库所属的APEX包-在“APEX”构建模块中的“native_shared_lib”属性中搜索它(参见art/build/APEX/安卓.bp例如)。 #然后检查APEX包中是否有应该使用的导出库,即在其“native_shared_lib”属性中列出的库,对应的“cc_library”模块具有“stubs”子句(如art/libdexfile中的libdexfile_external)/安卓.bp). #如果您找不到适合您需要的APEX导出库,或者您认为/system中应该允许您依赖的库,那么请与包含该库的APEX包的所有者联系。 #如果在APEX中导出的库出现此错误,则APEX可能配置错误,或者生成系统中出现错误。 #请联系APEX包主和/或soong-team@,或android-building@googlegroups.com外部的。 define check-apex-libs-absence $(call maybe-print-list-and-error, \ $(filter $(foreach lib,$(APEX_MODULE_LIBS),%/$(lib)), \ $(filter-out $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE), \ $(TARGET_OUT)/$(if $(findstring %,$(dir)),$(dir),$(dir)/%)), \ $(filter $(TARGET_OUT)/lib/% $(TARGET_OUT)/lib64/%,$(1)))), \ APEX libraries found in system image (see comment for check-apex-libs-absence in \ build/make/core/main.mk for details)) endef # TODO(b/129006418): The check above catches libraries through product # dependencies visible to make, but as long as they have install rules in # /system they may still be created there through other make targets. To catch # that we also do a check on disk just before the system image is built. define check-apex-libs-absence-on-disk $(hide) ( \ cd $(TARGET_OUT) && \ findres=$$(find lib* \ $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE),-path "$(subst %,*,$(dir))" -prune -o) \ -type f \( -false $(foreach lib,$(APEX_MODULE_LIBS),-o -name $(lib)) \) \ -print) && \ if [ -n "$$findres" ]; then \ echo "APEX libraries found in system image (see comment for check-apex-libs-absence" 1>&2; \ echo "in build/make/core/main.mk for details):" 1>&2; \ echo "$$findres" | sort 1>&2; \ false; \ fi; \ ) endef endif ...6.4.10 保存所有模块将所有要安装的模块都保存在变量ALL_DEFAULT_INSTALLED_MODULES中,并且将build/core/Makefie文件加载进来。... #build/core/Makefie文件会根据要安装的模块生成system.img、super.img、boot.img和recovery.img等镜像文件的生成规则 # TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES # and get rid of it from this list. modules_to_install := $(sort \ $(ALL_DEFAULT_INSTALLED_MODULES) \ $(product_target_FILES) \ $(product_host_FILES) \ $(call get-tagged-modules,$(tags_to_install)) \ $(CUSTOM_MODULES) \ ) ... #build/make/core/Makefile包含了我们不想污染这个顶级Makefile的额外内容。 #它希望“ALL_DEFAULT_INSTALLED_MODULES”包含当前make期间构建的所有模块,但它还进一步扩展了“ALL_DEFAULT_INSTALLED_MODULES”。 ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install) include $(BUILD_SYSTEM)/Makefile modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES)) ALL_DEFAULT_INSTALLED_MODULES := ... #这是用来获得正确的秩序,你也可以使用这些,但他们被认为是没有文件,所以不要抱怨,如果他们的行为改变。 #依赖于所有复制头的内部目标(请参见复制_标题.make). 需要首先复制头的其他目标可以依赖于此目标。 .PHONY: all_copied_headers all_copied_headers: ; $(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers # All the droid stuff, in directories .PHONY: files files: $(modules_to_install) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) ...6.4.11 定义编译的image目标定义了我们编译过程中的所有image目标... .PHONY: ramdisk ramdisk: $(INSTALLED_RAMDISK_TARGET) .PHONY: ramdisk_debug ramdisk_debug: $(INSTALLED_DEBUG_RAMDISK_TARGET) .PHONY: systemtarball systemtarball: $(INSTALLED_SYSTEMTARBALL_TARGET) .PHONY: boottarball boottarball: $(INSTALLED_BOOTTARBALL_TARGET) .PHONY: userdataimage userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET) ifneq (,$(filter userdataimage, $(MAKECMDGOALS))) $(call dist-for-goals, userdataimage, $(BUILT_USERDATAIMAGE_TARGET)) endif .PHONY: userdatatarball userdatatarball: $(INSTALLED_USERDATATARBALL_TARGET) .PHONY: cacheimage cacheimage: $(INSTALLED_CACHEIMAGE_TARGET) .PHONY: bptimage bptimage: $(INSTALLED_BPTIMAGE_TARGET) .PHONY: vendorimage vendorimage: $(INSTALLED_VENDORIMAGE_TARGET) .PHONY: productimage productimage: $(INSTALLED_PRODUCTIMAGE_TARGET) .PHONY: productservicesimage productservicesimage: $(INSTALLED_PRODUCT_SERVICESIMAGE_TARGET) .PHONY: odmimage odmimage: $(INSTALLED_ODMIMAGE_TARGET) .PHONY: systemotherimage systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) .PHONY: superimage_empty superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) .PHONY: bootimage bootimage: $(INSTALLED_BOOTIMAGE_TARGET) .PHONY: bootimage_debug bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) .PHONY: vbmetaimage vbmetaimage: $(INSTALLED_VBMETAIMAGE_TARGET) .PHONY: auxiliary auxiliary: $(INSTALLED_AUX_TARGETS) ...6.4.12 构建系统,打包rom构建文件,然后将其打包成rom格式... .PHONY: droidcore droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \ $(INSTALLED_SYSTEMIMAGE_TARGET) \ $(INSTALLED_RAMDISK_TARGET) \ $(INSTALLED_BOOTIMAGE_TARGET) \ $(INSTALLED_DEBUG_RAMDISK_TARGET) \ $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \ $(INSTALLED_RECOVERYIMAGE_TARGET) \ $(INSTALLED_VBMETAIMAGE_TARGET) \ $(INSTALLED_USERDATAIMAGE_TARGET) \ $(INSTALLED_CACHEIMAGE_TARGET) \ $(INSTALLED_BPTIMAGE_TARGET) \ $(INSTALLED_VENDORIMAGE_TARGET) \ $(INSTALLED_ODMIMAGE_TARGET) \ $(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \ $(INSTALLED_PRODUCTIMAGE_TARGET) \ $(INSTALLED_SYSTEMOTHERIMAGE_TARGET) \ $(INSTALLED_FILES_FILE) \ $(INSTALLED_FILES_JSON) \ $(INSTALLED_FILES_FILE_VENDOR) \ $(INSTALLED_FILES_JSON_VENDOR) \ $(INSTALLED_FILES_FILE_ODM) \ $(INSTALLED_FILES_JSON_ODM) \ $(INSTALLED_FILES_FILE_PRODUCT) \ $(INSTALLED_FILES_JSON_PRODUCT) \ $(INSTALLED_FILES_FILE_PRODUCT_SERVICES) \ $(INSTALLED_FILES_JSON_PRODUCT_SERVICES) \ $(INSTALLED_FILES_FILE_SYSTEMOTHER) \ $(INSTALLED_FILES_JSON_SYSTEMOTHER) \ $(INSTALLED_FILES_FILE_RAMDISK) \ $(INSTALLED_FILES_JSON_RAMDISK) \ $(INSTALLED_FILES_FILE_DEBUG_RAMDISK) \ $(INSTALLED_FILES_JSON_DEBUG_RAMDISK) \ $(INSTALLED_FILES_FILE_ROOT) \ $(INSTALLED_FILES_JSON_ROOT) \ $(INSTALLED_FILES_FILE_RECOVERY) \ $(INSTALLED_FILES_JSON_RECOVERY) \ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \ auxiliary \ soong_docs ... #构建一个完整的系统——默认情况下是构建droidcore droid_targets: droidcore dist_files ...7. 总结至此,make的流程我们基本理清了,后面还有ninja是如何把build.ninja编译出来的,image如何打包的,我们后面继续分析。
2021年03月01日
1,705 阅读
0 评论
0 点赞
2021-03-01
[转载]编译环境初始化-Android10.0编译系统(二)
版权声明:本文为CSDN博主「IngresGe」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/yiranfeng/article/details/1090837541. 概述上一节针对Android编译系统做了一个笼统的说明,这一节针对编译环境初始化做一下详细的展示。2. 编译环境初始化初始化命令:source build/envsetup.shenvsetup.sh 主要做了下面几个事情:envsetup.sh 构建代码: ... validate_current_shell source_vendorsetup addcompletions2.1 hmm查看支持接口输入hmm可以看到envsetup支持的一些接口命令说明lunchlunch <product_name>-<build_variant>选择<product_name>作为要构建的产品,<build_variant>作为要构建的变体,并将这些选择存储在环境中,以便后续调用“m”等读取。tapas交互方式:tapas [<App1> <App2> ...] [arm\x86\mips\arm64\x86_64\mips64] [eng\userdebug\user]croot将目录更改到树的顶部或其子目录。m编译整个源码,可以不用切换到根目录mm编译当前目录下的源码,包含他们的依赖模块(11包含)mmm编译指定目录下的所有模块,包含他们的依赖模块(11包含) 例如:mmm dir/:target1,target2.mma编译当前目录下的源码,包含他们的依赖模块mmma编译指定目录下的所模块,包含他们的依赖模块provision具有所有必需分区的闪存设备。选项将传递给fastboot。cgrep对系统本地所有的C/C++ 文件执行grep命令ggrep对系统本地所有的Gradle文件执行grep命令jgrep对系统本地所有的Java文件执行grep命令resgrep对系统本地所有的res目录下的xml文件执行grep命令mangrep对系统本地所有的AndroidManifest.xml文件执行grep命令mgrep对系统本地所有的Makefiles文件执行grep命令sepgrep对系统本地所有的sepolicy文件执行grep命令sgrep对系统本地所有的source文件执行grep命令godir根据godir后的参数文件名在整个目录下查找,并且切换目录allmod列出所有模块gomod转到包含模块的目录pathmod获取包含模块的目录refreshmod刷新allmod/gomod的模块列表2.2 validate_current_shell确定当前的shell环境,建立shell命令function validate_current_shell() { local current_sh="$(ps -o command -p $$)" case "$current_sh" in *bash*) function check_type() { type -t "$1"; } ;; *zsh*) function check_type() { type "$1"; } enable_zsh_completion ;; *) echo -e "WARNING: Only bash and zsh are supported.\nUse of other shell would lead to erroneous results." ;; esac }2.3 source_vendorsetup从device\vendor\product等目录遍历搜索vendorsetup.sh,并source进来function source_vendorsetup() { allowed= for f in $(find -L device vendor product -maxdepth 4 -name 'allowed-vendorsetup_sh-files' 2>/dev/null | sort); do if [ -n "$allowed" ]; then echo "More than one 'allowed_vendorsetup_sh-files' file found, not including any vendorsetup.sh files:" echo " $allowed" echo " $f" return fi allowed="$f" done allowed_files= [ -n "$allowed" ] && allowed_files=$(cat "$allowed") for dir in device vendor product; do for f in $(test -d $dir && \ find -L $dir -maxdepth 4 -name 'vendorsetup.sh' 2>/dev/null | sort); do if [[ -z "$allowed" || "$allowed_files" =~ $f ]]; then echo "including $f"; . "$f" else echo "ignoring $f, not in $allowed" fi done done }例:1.建立一个目录:/vendor/ingres/build2.创建一个vendorsetup.sh写一个log: echo "vendor build test."3.执行source build/envsetup.shsource后打印:including vendor/ingres/build/vendorsetup.sh vendor build test.2.4 addcompletionsfunction addcompletions() { local T dir f # Keep us from trying to run in something that's neither bash nor zsh. # 检测shell版本字符串BASH_VERSION 或ZSH_VERSION长度为0时,返回 if [ -z "$BASH_VERSION" -a -z "$ZSH_VERSION" ]; then return fi # Keep us from trying to run in bash that's too old. # 检测bash主版本低于3时返回 if [ -n "$BASH_VERSION" -a ${BASH_VERSINFO[0]} -lt 3 ]; then return fi # 指定bash文件目录并检查是否存在 local completion_files=( system/core/adb/adb.bash system/core/fastboot/fastboot.bash tools/asuite/asuite.sh ) # Completion can be disabled selectively to allow users to use non-standard completion. # e.g. # ENVSETUP_NO_COMPLETION=adb # -> disable adb completion # ENVSETUP_NO_COMPLETION=adb:bit # -> disable adb and bit completion #*.bash文件列表,并将这些*.bash文件包含进来 for f in ${completion_files[*]}; do if [ -f "$f" ] && should_add_completion "$f"; then # 对*.bash文件执行'.'操作 . $f fi done if should_add_completion bit ; then complete -C "bit --tab" bit fi if [ -z "$ZSH_VERSION" ]; then # Doesn't work in zsh. complete -o nospace -F _croot croot fi complete -F _lunch lunch # _lunch命令提供lunch命令的补全操作 complete -F _complete_android_module_names gomod complete -F _complete_android_module_names m }3. lunch aosp_arm_eng3.1 lunch说明环境变量初始化完成后,我们需要选择一个编译目标。lunch 主要作用是根据用户输入或者选择的产品名来设置与具体产品相关的环境变量。执行命令:lunch 1, 可以看到配置的一些环境变量lunch结果说明PLATFORM_VERSION_CODENAME=REL表示平台版本的名称PLATFORM_VERSION=10Android平台的版本号TARGET_PRODUCT=aosp_arm所编译的产品名称TARGET_BUILD_VARIANT=userdebug所编译产品的类型TARGET_BUILD_TYPE=release编译的类型,debug和releaseTARGET_ARCH=arm表示编译目标的CPU架构TARGET_ARCH_VARIANT=armv7-a-neon表示编译目标的CPU架构版本TARGET_CPU_VARIANT=generic表示编译目标的CPU代号HOST_ARCH=x86_64表示编译平台的架构HOST_2ND_ARCH=x86表示编译平台的第二CPU架构HOST_OS=linux表示编译平台的操作系统HOST_OS_EXTRA=Linux-4.15.0-112-generic-x86_64-Ubuntu-16.04.6-LTS编译系统之外的额外信息HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release编译类型BUILD_ID=QQ1D.200205.002BUILD_ID会出现在版本信息中,可以利用OUT_DIR=out编译结果输出的路径lunch aosp_arm-eng 结束后,后创建一个out文件夹,生成一些中间文件如下图所示:3.2 lunch()lunch命令用来设置 TARGET_PRODUCT、TARGET_BUILD_VARIANT、TARGET_PLATFORM_VERSION、TARGET_BUILD_TYPE、TARGET_BUILD_APPS等环境变量lunch操作流程如下:1.获取lunch操作的参数,如果参数不为空,参数则为指定要编译的设备型号和编译类型;如果参数为空,会调用print_lunch_menu来显示Lunch菜单项,读取用户的输入,存入answer2.如果answer为空,即之前在lunch菜单用,用户只敲了一个回车。会将默认选项改为aosp_arm-eng,结果存入selection3.如果lunch操作得到的输入是数字,则将数字转换为LUNCH_MENU_CHOICES中的字符串,结果存入selection4.解析selection的值,得到product = aosp_arm 和variant = eng, 把他们分别保存到TARGET_PRODUCT 和 TARGET_BUILD_VARIANT 中5.根据前面的设置,调用build_build_var_cache 来更新编译环境相关变量6.export 编译选项TARGET_PRODUCT, TARGET_BUILD_VARIANT和TARGET_BUILD_TYPE三元组7.调用set_stuff_for_environment 来设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等8.调用printconfig 来输出当前的设置选项function lunch() { local answer # 获取lunch操作的参数 if [ "$1" ] ; then answer=$1 else # lunch操作不带参数,则先显示lunch menu,然后读取用户输入 print_lunch_menu echo -n "Which would you like? [aosp_arm-eng] " read answer fi local selection= # lunch操作得到的结果为空(例如用户直接在lunch要求输入时回车的情况) # 则将选项默认为"aosp_arm-eng" if [ -z "$answer" ] then selection=aosp_arm-eng # lunch操作得到的输入是数字,则将数字转换为LUNCH_MENU_CHOICES中的字符串 elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$") then local choices=($(TARGET_BUILD_APPS= get_build_var COMMON_LUNCH_CHOICES)) if [ $answer -le ${#choices[@]} ] then # array in zsh starts from 1 instead of 0. if [ -n "$ZSH_VERSION" ] then selection=${choices[$(($answer))]} else selection=${choices[$(($answer-1))]} fi fi else selection=$answer fi export TARGET_BUILD_APPS= local product variant_and_version variant version product=${selection%%-*} # Trim everything after first dash variant_and_version=${selection#*-} # Trim everything up to first dash if [ "$variant_and_version" != "$selection" ]; then variant=${variant_and_version%%-*} if [ "$variant" != "$variant_and_version" ]; then version=${variant_and_version#*-} fi fi if [ -z "$product" ] then echo echo "Invalid lunch combo: $selection" return 1 fi # 设置TARGET_PRODUCT和TARGET_BUILD_VARIANT TARGET_PRODUCT=$product \ TARGET_BUILD_VARIANT=$variant \ TARGET_PLATFORM_VERSION=$version \ # 根据前面的设置,更新编译环境相关变量 build_build_var_cache #参考[3.1.1] if [ $? -ne 0 ] then return 1 fi # export 编译选项TARGET_PRODUCT, TARGET_BUILD_VARIANT和TARGET_BUILD_TYPE三元组 export TARGET_PRODUCT=$(get_build_var TARGET_PRODUCT) export TARGET_BUILD_VARIANT=$(get_build_var TARGET_BUILD_VARIANT) if [ -n "$version" ]; then export TARGET_PLATFORM_VERSION=$(get_build_var TARGET_PLATFORM_VERSION) else unset TARGET_PLATFORM_VERSION fi export TARGET_BUILD_TYPE=release echo set_stuff_for_environment # 设置其他环境变量,如PROMPT_COMMAND,编译toolchain和tools相关的路径等 printconfig # 输出当前的设置选项 destroy_build_var_cache }3.1.1 build_build_var_cache()根据前面的设置,更新编译环境相关变量主要通过执行 "build/soong/soong_ui.bash --dumpvars-mode" 完成最终执行的是 "./out/soog_ui --dumpvars-mode"function build_build_var_cache() { local T=$(gettop) # Grep out the variable names from the script. cached_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/get_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`) cached_abs_vars=(`cat $T/build/envsetup.sh | tr '()' ' ' | awk '{for(i=1;i<=NF;i++) if($i~/get_abs_build_var/) print $(i+1)}' | sort -u | tr '\n' ' '`) # Call the build system to dump the "<val>=<value>" pairs as a shell script. build_dicts_script=`\builtin cd $T; build/soong/soong_ui.bash --dumpvars-mode \ --vars="${cached_vars[*]}" \ --abs-vars="${cached_abs_vars[*]}" \ --var-prefix=var_cache_ \ --abs-var-prefix=abs_var_cache_` local ret=$? if [ $ret -ne 0 ] then unset build_dicts_script return $ret fi # Execute the script to store the "<val>=<value>" pairs as shell variables. eval "$build_dicts_script" ret=$? unset build_dicts_script if [ $ret -ne 0 ] then return $ret fi BUILD_VAR_CACHE_READY="true" }soong_ui 由build/soong/cmd/soong_ui/main.go编译生成[build/soong/cmd/soong_ui/main.go] func main() { ... if os.Args[1] == "--dumpvar-mode" { dumpVar(buildCtx, config, os.Args[2:]) } else if os.Args[1] == "--dumpvars-mode" { dumpVars(buildCtx, config, os.Args[2:]) } else { ... } ... } [build/soong/cmd/soong_ui/main.go] func dumpVars(ctx build.Context, config build.Config, args []string) { varData, err := build.DumpMakeVars(ctx, config, nil, allVars) }最后调用到了ckati执行-f build/make/core/config.mk[/build/soong/ui/build/dumpvars.go] func dumpMakeVars(ctx Context, config Config, goals, vars []string, write_soong_vars bool) (map[string]string, error) { ctx.BeginTrace(metrics.RunKati, "dumpvars") defer ctx.EndTrace() cmd := Command(ctx, config, "dumpvars", config.PrebuiltBuildTool("ckati"), "-f", "build/make/core/config.mk", "--color_warnings", "--kati_stats", "dump-many-vars", "MAKECMDGOALS="+strings.Join(goals, " ")) cmd.Environment.Set("CALLED_FROM_SETUP", "true") if write_soong_vars { cmd.Environment.Set("WRITE_SOONG_VARIABLES", "true") } cmd.Environment.Set("DUMP_MANY_VARS", strings.Join(vars, " ")) cmd.Sandbox = dumpvarsSandbox output := bytes.Buffer{} cmd.Stdout = &output pipe, err := cmd.StderrPipe() if err != nil { ctx.Fatalln("Error getting output pipe for ckati:", err) } cmd.StartOrFatal() // TODO: error out when Stderr contains any content status.KatiReader(ctx.Status.StartTool(), pipe) cmd.WaitOrFatal() ret := make(map[string]string, len(vars)) ... return ret, nil }下面我们单独研究一下config.mk。4. config.mk 说明:config.mk首先加载了build/make/common 中的core.mk、math.mk、strings.mk、json.mk 用来配置一些shell环境、math函数、string和json的一些支持函数。最主要的操作还是加载build/make/core中的envsetup.mk和dumpvar.mk ... #配置两个目录的变量,供之后的mk使用 BUILD_SYSTEM :=$= build/make/core BUILD_SYSTEM_COMMON :=$= build/make/common #加载core.mk, 只使用ANDROID_BUILD_SHELL来包装bash。 include $(BUILD_SYSTEM_COMMON)/core.mk #设置make中使用的有效数学函数。 include $(BUILD_SYSTEM_COMMON)/math.mk include $(BUILD_SYSTEM_COMMON)/strings.mk include $(BUILD_SYSTEM_COMMON)/json.mk # 避免硬件解码路径被覆盖的调用pathmap.mk建立硬解映射 include $(BUILD_SYSTEM)/pathmap.mk # 允许项目定义自己的全局可用变量 include $(BUILD_SYSTEM)/project_definitions.mk # ############################################################### # Build system internal files # ############################################################### # 构建系统内部文件(写Android.mk时会调用include头文件,也就是这些makefile文件) BUILD_COMBOS:= $(BUILD_SYSTEM)/combo CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk ... BUILD_NOTICE_FILE := $(BUILD_SYSTEM)/notice_files.mk BUILD_HOST_DALVIK_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_java_library.mk BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY := $(BUILD_SYSTEM)/host_dalvik_static_java_library.mk BUILD_HOST_TEST_CONFIG := $(BUILD_SYSTEM)/host_test_config.mk BUILD_TARGET_TEST_CONFIG := $(BUILD_SYSTEM)/target_test_config.mk #定义大多数全局变量。这些是特定于用户的构建配置的。 include $(BUILD_SYSTEM)/envsetup.mk #构建系统为在哪里找到内核公开了几个变量 #(1)TARGET_DEVICE_KERNEL_HEADERS是为当前正在构建的设备自动创建的。 #它被设置为$(TARGET_DEVICE_DIR)/kernel headers, #例如DEVICE/samsung/tuna/kernel headers。此目录不是由任何人显式设置的,生成系统总是添加此子目录。 TARGET_DEVICE_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_DEVICE_DIR)/kernel-headers)) #(2)TARGET_BOARD_KERNEL_HEADERS由BoardConfig.mk允许包含其他目录的文件。 #如果有一些常见的地方为一组设备保留了一些报头,那么这很有用。 #例如,device/<vendor>/common/kernel头可以包含一些<vendor>设备的头。 TARGET_BOARD_KERNEL_HEADERS := $(strip $(wildcard $(TARGET_BOARD_KERNEL_HEADERS))) TARGET_BOARD_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_BOARD_KERNEL_HEADERS)) $(call validate-kernel-headers,$(TARGET_BOARD_KERNEL_HEADERS)) #(3)TARGET_PRODUCT_KERNEL_头由产品继承图生成。 #这允许体系结构产品为使用该体系结构的设备提供报头。 TARGET_PRODUCT_KERNEL_HEADERS := $(strip $(wildcard $(PRODUCT_VENDOR_KERNEL_HEADERS))) TARGET_PRODUCT_KERNEL_HEADERS := $(patsubst %/,%,$(TARGET_PRODUCT_KERNEL_HEADERS)) $(call validate-kernel-headers,$(TARGET_PRODUCT_KERNEL_HEADERS)) # 选择一个Java编译器 include $(BUILD_SYSTEM)/combo/javac.mk # A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports. #框架支持的SEPolicy版本列表,除了PLATFORM_SEPOLICY_VERSION PLATFORM_SEPOLICY_COMPAT_VERSIONS := \ 26.0 \ 27.0 \ 28.0 \ ifeq ($(CALLED_FROM_SETUP),true) include $(BUILD_SYSTEM)/ninja_config.mk include $(BUILD_SYSTEM)/soong_config.mk endif #加载dumpvar.mk,用来生成make目标 include $(BUILD_SYSTEM)/dumpvar.mk4.1 build/make/core/envsetup.mkenvsetup.mk 主要加载了product_config.mk和board_config.mk,用来得到TARGET_DEVICE和其他变量。... #设置host和target编译链相关的变量 include $(BUILD_SYSTEM)/combo/select.mk #(1)阅读产品规格,这样我们就可以得到TARGET_DEVICE和其他变量,我们需要找到输出文件 include $(BUILD_SYSTEM)/product_config.mk include $(BUILD_SYSTEM)/board_config.mk ...4.2 build/make/core/product_config.mk阅读产品规格,这样我们就可以得到TARGET_DEVICE和其他变量,我们需要找到输出文件。... # --------------------------------------------------------------- # Include the product definitions. # We need to do this to translate TARGET_PRODUCT into its # underlying TARGET_DEVICE before we start defining any rules. # include $(BUILD_SYSTEM)/node_fns.mk include $(BUILD_SYSTEM)/product.mk include $(BUILD_SYSTEM)/device.mk ... ############################################################################# # Sanity check and assign default values TARGET_DEVICE := $(PRODUCT_DEVICE) ...4.3 build/make/core/board_config.mk板级可以在$(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)下定义,也可以在vendor/\*/$(TARGET_DEVICE)下定义。在这两个地方搜索,但要确保只存在一个。真正的板级应始终与OEM vendor相关联。... # Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE) # or under vendor/*/$(TARGET_DEVICE). Search in both places, but # make sure only one exists. # Real boards should always be associated with an OEM vendor. ifdef TARGET_DEVICE_DIR ifneq ($(origin TARGET_DEVICE_DIR),command line) $(error TARGET_DEVICE_DIR may not be set manually) endif board_config_mk := $(TARGET_DEVICE_DIR)/BoardConfig.mk else board_config_mk := \ $(strip $(sort $(wildcard \ $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \ $(shell test -d device && find -L device -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \ $(shell test -d vendor && find -L vendor -maxdepth 4 -path '*/$(TARGET_DEVICE)/BoardConfig.mk') \ ))) ifeq ($(board_config_mk),) $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE)) endif ifneq ($(words $(board_config_mk)),1) $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk)) endif TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk))) .KATI_READONLY := TARGET_DEVICE_DIR endif include $(board_config_mk) ...5. 总结至此,envsetup.sh 和lunch()的初始化流程基本上理清了,主要就是加载了环境变量,并选择了编译目标,后面只要执行一下make就能够进行启动编译,下一节让我们一起看看敲下make后到底发生了什么。
2021年03月01日
1,243 阅读
0 评论
0 点赞
2021-03-01
[转载]编译系统入门篇-Android10.0编译系统(一)
转载自编译系统入门篇-Android10.0编译系统(一)版权声明:本文为CSDN博主「IngresGe」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:https://blog.csdn.net/yiranfeng/article/details/1090824891. 概述在 Android 7.0 之前,Android 编译系统使用 GNU Make 描述和shell来构建编译规则,模块定义都使用Android.mk进行定义,Android.mk的本质就是Makefile,但是随着Android的工程越来越大,模块越来越多,Makefile组织的项目编译时间越来越长。这样下去Google工程师觉得不行,得要优化。因此,在Android7.0开始,Google采用ninja来代取代之前使用的make,由于之前的Android.mk数据实在巨大,因此Google加入了一个kati工具,用于将Android.mk转换成ninja的构建规则文件buildxxx.ninja,再使用ninja来进行构建工作。ninja的网址:https://ninja-build.org编译速度快了一些,但是既然要干, 那就干个大的,最终目标要把make都取代,于是从Android8.0开始,Google为了进一步淘汰Makefile,因此引入了Android.bp文件来替换之前的Android.mk。Android.bp只是一个纯粹的配置文件,不包括分支、循环语句等控制流程,本质上就是一个json配置文件。Android.bp 通过Blueprint+soong转换成ninja的构建规则文件build.ninja,再使用ninja来进行构建工作。Android10.0上,mk和bp编译的列表可以从 \out.module_paths中的Android.bp.list、Android.mk.list中看到,Android10.0还有400多个mk文件没有被替换完,Google任重道远。Android编译演进过程:Android7.0之前 使用GNU MakeAndroid7.0 引入ninja、kati、Android.bp和soong构建系统Android8.0 默认打开Android.bpAndroid9.0 强制使用Android.bpGoogle在 Android 7.0之后,引入了Soong构建系统,旨在取代make,它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。Make 构建系统得到了广泛的支持和使用,但在 Android 层面变得缓慢、容易出错、无法扩展且难以测试。Soong 构建系统正好提供了 Android build 所需的灵活性。Android系统的编译历程:2. 编译流程2.1 编译构成Android的编译目录在/build 中,看一下Android 10源码中的build目录,现在是这个样子: 这个目录中可以看到core文件夹被link到了make/core,envsetup.sh被link到make/envsetup.sh,这主要是为了对使用者屏蔽切换编译系统的差异。这里重点看四个文件夹:blueprint、kati、make、soongblueprint:用于处理Android.bp,编译生成*.ninja文件,用于做ninja的处理kati:用于处理Android.mk,编译生成*.ninja文件,用于做ninja的处理make:文件夹还是原始的make那一套流程,比如envsetup.shsoong:构建系统,核心编译为soong_ui.bash Soong编译系统家族成员及各自关系如下图所示:在编译过程中,Android.bp会被收集到out/soong/build.ninja.d,blueprint以此为基础,生成out/soong/build.ninjaAndroid.mk会由kati/ckati生成为out/build-aosp_arm.ninja两个ninja文件会被整合进入out/combined-aosp_arm.ninjaout/combined-aosp_arm.ninja内容如下所示:builddir = out pool local_pool depth = 42 build _kati_always_build_: phony subninja out/build-aosp_arm.ninja subninja out/build-aosp_arm-package.ninja subninja out/soong/build.ninja2.2 编译步骤source build/envsetup.shlunch aosp_arm-eng // 或者 m PRODUCT-aosp_x86_64-eng ,Android10.0不一定需要lunch命令make -j8 //编译模块也可以直接用 m libartAndroid10.0编译步骤如下图所示:3. 编译环境初始化3.1 envsetup说明 source build/envsetup.sh这里的envsetup.sh被link到了 build/make/envsetup.shenvsetup.sh 主要做了下面几个事情:在source build/envsetup.sh后,输入hmm可以看到envsetup支持的一些接口:命令说明lunchlunch <product_name>-<build_variant>选择<product_name>作为要构建的产品,<build_variant>作为要构建的变体,并将这些选择存储在环境中,以便后续调用“m”等读取。tapas交互方式:tapas [<App1> <App2> ...] [arm\x86\mips\arm64\x86_64\mips64] [eng\userdebug\user]croot将目录更改到树的顶部或其子目录。m编译整个源码,可以不用切换到根目录mm编译当前目录下的源码,不包含他们的依赖模块mmm编译指定目录下的所有模块,不包含他们的依赖模块 例如:mmm dir/:target1,target2.mma编译当前目录下的源码,包含他们的依赖模块mmma编译指定目录下的所模块,包含他们的依赖模块provision具有所有必需分区的闪存设备。选项将传递给fastboot。cgrep对系统本地所有的C/C++ 文件执行grep命令ggrep对系统本地所有的Gradle文件执行grep命令jgrep对系统本地所有的Java文件执行grep命令resgrep对系统本地所有的res目录下的xml文件执行grep命令mangrep对系统本地所有的AndroidManifest.xml文件执行grep命令mgrep对系统本地所有的Makefiles文件执行grep命令sepgrep对系统本地所有的sepolicy文件执行grep命令sgrep对系统本地所有的source文件执行grep命令godir根据godir后的参数文件名在整个目录下查找,并且切换目录allmod列出所有模块gomod转到包含模块的目录pathmod获取包含模块的目录refreshmod刷新allmod/gomod的模块列表3.2 Lunch说明环境变量初始化完成后,我们需要选择一个编译目标。lunch 主要作用是根据用户输入或者选择的产品名来设置与具体产品相关的环境变量。如果你不知道想要编译的目标是什么,直接执行一个lunch命令,会列出所有的目标,直接回车,会默认使用aosp\_arm-eng这个目标。执行命令:lunch 1, 可以看到配置的一些环境变量这些环境变量的含义如下:lunch结果说明PLATFORM_VERSION_CODENAME=REL表示平台版本的名称PLATFORM_VERSION=10Android平台的版本号TARGET_PRODUCT=aosp_arm所编译的产品名称TARGET_BUILD_VARIANT=userdebug所编译产品的类型TARGET_BUILD_TYPE=release编译的类型,debug和releaseTARGET_ARCH=arm表示编译目标的CPU架构TARGET_ARCH_VARIANT=armv7-a-neon表示编译目标的CPU架构版本TARGET_CPU_VARIANT=generic表示编译目标的CPU代号HOST_ARCH=x86_64表示编译平台的架构HOST_2ND_ARCH=x86表示编译平台的第二CPU架构HOST_OS=linux表示编译平台的操作系统HOST_OS_EXTRA=Linux-4.15.0-112-generic-x86_64-Ubuntu-16.04.6-LTS编译系统之外的额外信息HOST_CROSS_OS=windows HOST_CROSS_ARCH=x86 HOST_CROSS_2ND_ARCH=x86_64 HOST_BUILD_TYPE=release编译类型BUILD_ID=QQ1D.200205.002BUILD_ID会出现在版本信息中,可以利用OUT_DIR=out编译结果输出的路径4. Make说明执行完lunch命令后,就可以使用make命令来执行编译Build。Android10.0上是通过soong执行编译构建,这里执行make命令时,main.mk文件把一些环境变量和目标都配置好后,会执行envsetup.sh中的make()进行编译。如果找到“build/soong/soong_ui.bash”,就使用soong_ui.bash 来进行编译,否则使用原始的make命令进行编译。function make() { _wrap_build $(get_make_command "$@") "$@" } function get_make_command() { # If we're in the top of an Android tree, use soong_ui.bash instead of make if [ -f build/soong/soong_ui.bash ]; then # Always use the real make if -C is passed in for arg in "$@"; do if [[ $arg == -C* ]]; then echo command make return fi done echo build/soong/soong_ui.bash --make-mode else echo command make fi }配置一些资源环境,得到一些函数命令,例如:soong_build_go,最终回退到根目录,执行out/soong_ui --make-mode进行真正的构建soong_build_go soong_ui android/soong/cmd/soong_ui 是通过编译android/soong/cmd/soong_ui/main.go来编译生成soong_ui。[build/soong/soong_ui.bash] # Save the current PWD for use in soong_ui export ORIGINAL_PWD=${PWD} export TOP=$(gettop) source ${TOP}/build/soong/scripts/microfactory.bash soong_build_go soong_ui android/soong/cmd/soong_ui cd ${TOP} exec "$(getoutdir)/soong_ui" "$@" “echo build/soong/soong_ui.bash --make-mode ”最终会执行 exec out/soong_ui --make-mode 进行编译soong的编译过程如下图所示:执行runKatiBuild时,有个重要的步骤,就是加载build/make/core/main.mk,main.mk文件是Android Build系统的主控文件。从main.mk开始,将通过include命令将其所有需要的.mk文件包含进来,最终在内存中形成一个包括所有编译脚本的集合,这个相当于一个巨大Makefile文件。Makefile文件看上去很庞大,其实主要由三种内容构成: 变量定义、函数定义和目标依赖规则,此外mk文件之间的包含也很重要。main.mk的包含关系如下图所示:一些关键的mk文件说明:文件说明build/make/core/main.mkBuild的主控文件,主要作用是包含其他mk,以及定义几个最重要的编译目标,同时检查编译工具的版本,例如如gcc、clang、java等build/make/core/config.mkBuild的配置文件,主要是区分各个产品的配置,并将这些编译器参数引入产品配置 BoardConfig.mk,同时也配置了一些编译器的路径等build/make/core/clang/config.mkclang编译的配置文件build/make/core/definitions.mk最重要的 Make 文件之一,在其中定义了大量的函数。这些函数都是 Build 系统的其他文件将用到的。例如:my-dir,all-subdir-makefiles,find-subdir-files,sign-package 等,关于这些函数的说明请参见每个函数的代码注释。build/make/core/dex_preopt.mk定义了dex优化相关的路径和参数build/make/core/pdk_config.mk编译pdk的配置文件build/make/core/Makefile系统最终编译完成所需要的各种目标和规则build/make/core/envsetup.mk包含进product_config.mk文件并且根据其内容设置编译产品所需要的环境变量,并检查合法性,指定输出路径等build/make/core/combo/select.mk根据当前编译器的平台选择平台相关的 Make 文件build/make/core/ninja_config.mk解析makefile的的列表,传给kati,配置传给ninja和kati的目标build/make/core/soong_config.mk配置soong的环境变量,建立go变量和mk变量的json映射关系,让go变量可以获取到mk中定义的变量值5. 编译工具链说明Android10.0的编译系统中,涉及以下一些工具链,由这些工具链相辅相成,才最终编译出了我们所需要的镜像版本。Android10.0编译工具链:soong\kati\blueprint\ninja 5.1 Soong说明Soong 构建系统是在 Android 7.0 (Nougat) 中引入的,旨在取代 Make。它利用 Kati GNU Make 克隆工具和 Ninja 构建系统组件来加速 Android 的构建。Soong是由Go语言写的一个项目,从Android 7.0开始,在prebuilts/go/目录下新增了Go语言所需的运行环境,Soong在编译时使用,解析Android.bp,将之转化为Ninja文件,完成Android的选择编译,解析配置工作等。故Soong相当于Makefile编译系统的核心,即build/make/core下面的内容。另外Soong还会编译产生一个androidmk命令,可以用来手动将Android.mk转换成Android.bp文件。不过这只对无选择、循环等复杂流程控制的Android.mk生效。soong脚本和代码目录:/build/soong5.2 kati说明kati是一个基于Makefile来生成ninja.build的小项目。主要用于把Makefiel转成成ninja file,自身没有编译能力,转换后使用Ninja编译。在编译过程中,kati负责把既有的Makefile、Android.mk文件,转换成Ninja文件。在Android 8.0以后,它与Soong一起,成为Ninja文件的两大来源。Kati更像是Google过渡使用的一个工具,等所有Android.mk都被替换成Android.bp之后,Kati有可能退出Android编译过程.在单独使用时,它对普通的小项目还能勉强生效。面对复杂的、多嵌套的Makefile时,它往往无法支持,会出现各种各样的问题。当然,也可以理解为,它只为Android而设计。kati脚本和代码目录:/build/kati5.3 blueprint说明Blueprint由Go语言编写,是生成、解析Android.bp的工具,是Soong的一部分。Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义。在Android编译最开始的准备阶段,会执行build/soong/soong_ui.bash进行环境准备。 对blueprint项目编译完成之后会在out/soong/host/linux-x86/bin目录下生成soong编译需要的5个执行文件(bpfix,bpfmt,bpmodify,microfatory,bpmodify)。Soong是与Android强关联的一个项目,而Blueprint则相对比较独立,可以单独编译、使用。blueprint代码目录:/build/blueprint5.4 ninja说明最开始,Ninja 是用于Chromium 浏览器中,Android 在SDK 7.0 中也引入了Ninja。Ninja是一个致力于速度的小型编译系统(类似于Make),如果把其他编译系统比做高级语言的话,Ninja就是汇编语言。通常使用Kati或soong把makefile转换成Ninja files,然后用Ninja编译。主要两个特点:1)可以通过其他高级的编译系统生成其输入文件;2)它的设计就是为了更快的编译;ninja核心是由C/C++编写的,同时有一部分辅助功能由python和shell实现。由于其开源性,所以可以利用ninja的开源代码进行各种个性化的编译定制。从Android 7开始,编译时默认使用Ninja。但是,Android项目里是没有.ninja文件的。遵循Ninja的设计哲学,编译时,会先把Makefile通过kati转换成.ninja文件,然后使用ninja命令进行编译。这些.ninja文件,都产生在out/目录下,共有三类:第一类是build-*.ninja文件,通常非常大,几十到几百MB。对make全编译,命名是build-<product_name>.ninja。如果Makefile发生修改,需要重新产生Ninja文件。mm、mma的Ninja文件,命名是build-<product_name>-<path_to_Android.mk>.ninja。而mmm、mmma的Ninja文件,命名是build-<product_name>-_<path_to_Android.mk>.ninja。第二类是combined-*.ninja文件。在使用了Soong后,除了build-*.ninja之外,还会产生对应的combined-*.ninja,二者的*内容相同。这类是组合文件,是把build-*.ninja和out/soong/build.ninja组合起来。所以,使用Soong后,combined-*.ninja是编译执行的真正入口。第三类是out/soong/build.ninja文件,它是从所有的Android.bp转换过来的。build-*.ninja是从所有的Makefile,用Kati转换过来的,包括build/core/*.mk和所有的Android.mk。所以,在不使用Soong时,它是唯一入口。在使用了Soong以后,会新增源于Android.bp的out/soong/build.ninja,所以需要combined-*.ninja来组合一下。6. 工具链Android.mk文件、Android.bp、kati、Soong、Blueprint、Ninja之间的关系如下:Android.bp --> Blueprint --> Soong --> Ninja Makefile or Android.mk --> kati --> Ninja (Android.mk --> Soong --> Blueprint --> Android.bp)Blueprint是生成、解析Android.bp的工具,是Soong的一部分。Soong则是专为Android编译而设计的工具,Blueprint只是解析文件的形式,而Soong则解释内容的含义。 Android.mk可以通过Soong提供的androidmk转换成Android.bp,但仅限简单配置。目前Oreo的编译流程中,仍然是使用kati来做的转换。 现存的Android.mk文件、既有的Android.bp,都会分别被转换成Ninja。从Android.mk与其它Makefile,会生成out/build-<product_name>.ninja文件。而从Android.bp,则会生成out/soong/build.ninja。此外,还会生成一个较小的out/combined-<product_name>.ninja文件,负责把二者组合起来,作为执行入口。最终,Ninja文件才是真正直接控制源码编译的工具。7. 总结Android10.0中,mk文件通过kati\ckati编译生成 build-aosp_arm.ninja, bp文件通过blueprint-soong解析编译生成为build.ninja ,这些ninja文件会合并成combined-aosp_arm.ninja,最终通过ninja工具进行最终的编译。随着Google的不停演进,make的编译会最终退出历史舞台,kati\ckati也会退出,最终全部切到 blueprint-soong的编译。
2021年03月01日
2,685 阅读
0 评论
1 点赞
2021-02-24
[转载]浅谈UML中常用的几种图
转载自https://blog.csdn.net/qq_35495763/article/details/807649141. UML简介统一建模语言(Unified Modeling Language,UML)又称标准建模语言,是始于1997年的一个OMG标准,它是一个支持模型化和软件系统开发的图形化语言,为软件开发的所有阶段提供模型化和可视化支持,包括由需求分析到规格,到构造和配置。‘UML感兴趣的可以阅读UML 1规 范,包含了UML 的所有知识内容。注:OMG, Object Management Group 对象管理组织2. UML常见图分类UML从考虑系统的不同角度出发,定义了用况图、类图、对象图、包图、状态图、活动图、序列图、通信图、构件图、部署图等10种图。分类:面向对象动态建模,用于建立行为的实体间行为交互的四种图:状态图(Stage Diagram),序列图(Sequence Diagram),协作图(Communication Diagram),活动图(Activity Diagram) 。“序列图”与“协作图”表述的是相似的消息,“活动图”是“状态图”的一种。静态结构图Static Structure Diagram类图Class Diagram对象图Object Diagram用况图Use Case Diagram交互图Interaction Diagram顺序图Sequence Diagram协作图Collaboration Diagram状态图State chart Diagrams活动图Activity Diagrams实现图Implementation Diagrams构件图Component Diagram部署图Deployment Diagram3. (用况图)用例用例图,展现了一组用例、参与者(actor)以及它们之间的关系。用例图从用户角度描述系统的静态使用情况,用于建立需求模型。(用于需求分析)参与者(Actor)在系统外部与系统直接交互的人或事物。需要注意以下两点:1)参与者是角色而不是具体的人(可以是外部系统),它代表了参与者在与系统打交道的过程中所扮演的角色。所以在系统的实际运作中,一个实际用户可能对应系统的多个参与者。不同的用户也可以只对应于一个参与者,从而代表同一参与者的不同实例。2)参与者作为外部用户(而不是内部)与系统发生交互作用,是它的主要特征。在UML中,参与者使用如图所示的一个小人表示:在UML中,参与者使用如图所示的一个小人表示:用例(Use Case)系统外部可见的一个系统功能单元。系统的功能由系统单元所提供,并通过一系列系统单元与一个或多个参与者之间交换的消息所表达。用椭圆表示,椭圆中的文字简述系统的功能:子系统(Subsystem)用来展示系统的一部分功能,这部分功能联系紧密。关系(Relationship)常见关系类型有关联、泛化、包含和扩展。以上各关系在UML图中的表示方式,如下表所示:关联(Association)表示参与者与用例之间的通信,任何一方都可发送或接受消息。【箭头指向】:指向消息接收方泛化(Inheritance)就是通常理解的继承关系,子用例和父用例相似,但表现出更特别的行为;子用例将继承父用例的所有结构、行为和关系。子用例可以使用父用例的一段行为,也可以重载它。父用例通常是抽象的。【箭头指向】:指向父用例包含(Include)包含关系用来把一个较复杂用例所表示的功能分解成较小的步骤。【箭头指向】:指向分解出来的功能用例扩展(Extend)扩展关系是指用例功能的延伸,相当于为基础用例提供一个附加功能。【箭头指向】:指向基础用例包含(include)、扩展(extend)、泛化(Inheritance) 的区别:条件性:泛化中的子用例和include中的被包含的用例会无条件发生,而extend中的延伸用例的发生是有条件的;直接性:泛化中的子用例和extend中的延伸用例为参与者提供直接服务,而include中被包含的用例为参与者提供间接服务。对extend(扩展)而言,延伸用例并不包含基础用例的内容,基础用例也不包含延伸用例的内容。对Inheritance(泛化)而言,子用例包含基础用例的所有内容及其和其他用例或参与者之间的关系;4. 类图在UML类图中,常见的有以下几种关系: 泛化(Generalization), 实现(Realization),关联(Association),聚合(Aggregation),组合(Composition),依赖(Dependency)泛化(Generalization)【泛化关系】:是一种继承关系,表示一般与特殊的关系,它指定了子类如何特化父类的所有特征和行为。例如:老虎是动物的一种,即有老虎的特性也有动物的共性。【箭头指向】:带三角箭头的实线,箭头指向父类 实现类的继承关系实现(Realization)【实现关系】:是一种类与接口的关系,表示类是接口所有特征和行为的实现.【箭头指向】:带三角箭头的虚线,箭头指向接口注意可以理解类的继承的关系的另外一种表现形式。关联(Association)【关联关系】:是一种拥有的关系,它使一个类知道另一个类的属性和方法;如:老师与学生,丈夫与妻子关联可以是双向的,也可以是单向的。双向的关联可以有两个箭头或者没有箭头,单向的关联有一个箭头。【代码体现】:成员变量【箭头及指向】:带普通箭头的实心线,指向被拥有者上图中,老师与学生是双向关联,老师有多名学生,学生也可能有多名老师。但学生与某课程间的关系为单向关联,一名学生可能要上多门课程,课程是个抽象的东西他不拥有学生。下图为自身关联:聚合(Aggregation)【聚合关系】:是整体与部分的关系,且部分可以离开整体而单独存在。如车和轮胎是整体和部分的关系,轮胎离开车仍然可以存在。聚合关系是关联关系的一种,是强的关联关系;关联和聚合在语法上无法区分,必须考察具体的逻辑关系。【代码体现】:成员变量【箭头及指向】:带空心菱形的实心线,菱形指向整体组合(Composition)【组合关系】:是整体与部分的关系,但部分不能离开整体而单独存在。如公司和部门是整体和部分的关系,没有公司就不存在部门。组合关系是关联关系的一种,是比聚合关系还要强的关系,它要求普通的聚合关系中代表整体的对象负责代表部分的对象的生命周期。【代码体现】:成员变量【箭头及指向】:带实心菱形的实线,菱形指向整体依赖(Dependency)【依赖关系】:是一种使用的关系,即一个类的实现需要另一个类的协助,所以要尽量不使用双向的互相依赖.【代码表现】:局部变量、方法的参数或者对静态方法的调用【箭头及指向】:带箭头的虚线,指向被使用者各种关系的强弱顺序:泛化 = 实现 > 组合 > 聚合 > 关联 > 依赖下面这张UML图,比较形象地展示了各种类图关系:图片转自:http://blog.csdn.net/tianhai110/article/details/6339565注意这里的雁群和大雁的关系画反了。简单类图使用 举例根据下面的陈述画出类图1)学生包括本科生、研究生两种。2)研究生的一部分利用课余时间担任助教。3)教师包括讲师和教授两种。4)一名助教可以为一位讲师或一位教授助课,一位讲师只能有一名助教,一位教授可以有5名助教。按如下描述画出一个自治机器人的类图。这张图的焦点是聚集在那些让机器人在路上行走的机制所对应的类上。你可以发现一个虚类Motor和两个从它派生出来的类: SteeringMotor和MainMotor。这两个类都从它的父亲Motor继承了五个方法:move()、stop()、resetCounter()、statues()、distance()。这两个类又是另一个类Driver的一部分。类PathAgent 和Driver有一个1对1的关系,和CollisionSensor有1对n的关系。【问题:】综上所述请你用UML来绘制分析类图。参考答案:5. 其他辅助用途时序图时序图别称为顺序图、序列图,在我的大致印象中时序图是以时间为主线,有生命线的动态视图,主要描述的对象是对象。是交互图的一种。主要的作用是表达设计者心中对于未来程序在运作时的对象协作建模,验证软件领域模型的正确性,为程序员提供编码的蓝图。(一)概念定义:时序图是以时间为序的表示方法,主要用来描述对象之间的时间顺序。将交互关系表示为二维图的。纵轴为时间,横轴为在协作中各个独立的对象。对象存在时用一条虚线表示,当对象的过程处于激活状态是,生命线是一个双道线。消息用从一个对象的生命线到另一个对象生命线的箭头表示。箭头以时间顺序在图中从上到下排列。如下图:四个元素(对象,生命线,消息,激活)(1)对象对象的概念就不多说了,就是类的实例化,在系统中随便都可以找到一个对象,因为我们使用的是基于OO编程的VB。(2)生命线表示对象的存在存在多久的时间(3)消息对象之间的单路通信,人有人的语言,机器有机器的语言,人和机器都是靠消息传递信息和指令的。消息的类型大致有:同步与异步,返回、阻止和超时。(4)激活表示这个时间,对象实现操作。时序图是将交互关系表示为二维图的。纵轴为时间,横轴为在协作中各个独立的对象。对象存在时用一条虚线表示,当对象的过程处于激活状态,生命线是一个双道线。消息用从一个对象的生命线到另一个对象生命线的箭头表示。箭头以时间顺序在图中从上到下排列。(二)建模主要是按时间顺序对控制流建模(三)实例首先要分析用例的正常流,学生刷卡上机,系统记录学生上机时间,同时显示学生的基本信息,此时的学生信息时系统更新,系统根据学生上机时长和类别计算上机费用,系统保存学上上机信息。,学生下机,系统记录时间,跟新数据库,数据库更新,反馈学生信息,系统根据学生上机和下机时间、类别计算上机费用,系统保存学生上机费用然后开始画图:)创建时序图)添加对象并设置对象属性,持续性)添加消息(消息的编号,显示或取消激活显示)如图:(图片有错,不提供参考)版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/tsj11514oo/article/details/38179121协作图(Collaboration Diagram/communication Diagram)/通信图协作图(Collaboration Diagram /Communication Diagram,也叫合作图)是一种交互图(interaction diagram),强调的是发送和接收消息的对象之间的组织结构。一个协作图显示了一系列的对象和在这些对象之间的联系以及对象间发送和接收的消息。对象通常是命名或匿名的类的实例,也可以代表其他事物的实例,例如协作、组件和节点。使用协作图来说明系统的动态情况。下面这张图介绍协作图的基本内容:下面一张图是一个协作图的实例,创建课程的协作图:由于协作图和时序图在语意上是相通的,所以可以互相转换,下面是利用ROSE把上面的协作图转换成的时序图的实例,可以直接使用功能进行转换:由于协作图和时序图在作用上可以替代的,在使用中,一般有了时序图就没有必要使用协作图。状态图1.概述:状态图(Statechart Diagram)主要用于描述一个特定的对象的所有可能状态以及由于各种事件的发生而引起的状态之间的转换。2.构成要素:状态图由状态、转移和事件组成。联合使用状态和转移可以更好地建模它们,有时需要包含决策点和同步条来显示更高层次的细节信息。2.1状态(States):在对象的生命周期中满足某些条件、执行某些活动或等待某些事件的一个条件或状况。所有的对象都有状态,状态是对象执行了一系列活动的结果,当某个事件发生后,对象的状态将发生变化。状态图中可以包含多个开始状态,也可以包含多个结束状态。模型不必同时具有开始和结束状态,因为模型可以总是运行,从不停止。2.2转移(Transitions):两个状态之间的一种关系,表示对象将在第一个状态中执行一定的动作并在某个特定事件发生或某个特定条件满足时进入第二个状态。2.3事件:使状态发生变化的某时刻发生的动作或活动,用来指示是什么触发了转移从而导致状态发生了改变。事件通常在从一个状态到另一个状态的转移路径上直接指定。2.4判断:判断点通过对事件判断分组转移到各自方向,提高了状态图的可视性。2.4同步:使用同步和活动图一样是为了说明并发工作流的分叉与联合。3.机房实例(注册部分):总结:状态图重点在与描述对象的状态及其状态之间的转移,与活动图区别在于状态图注重的是行为的结果,活动图更注重是行为的动作。活动图(Activity Diagram)“活动图”可通过一系列操作将业务流程或软件进程以工作流的形式显示出来。这些操作可以由人、软件组件或计算机来执行。使用活动图可以描述多种类型的流程,如下:1、用户和您的系统之间的业务流程或工作流。2、某一用例中执行的步骤。3、软件协议,即允许在组件间进行的交互序列。4、软件算法。下面用一张图来介绍活动图的基本内容下面是用rose 和 onenote结合画的一张活动图的实例,基本囊括了活动图的重要元素。组件图(ComponentDiagram)、配置图(Deployment Diagram)组件(构件图)和配置图(部署图)是面向对象系统的物理建模时使用的两种图。组件图:描述软件组件以及组件之间的关系。组件图元素:组件、接口、依赖关系组件:是定义良好接口的物理实现单元,遵从同一组接口,提供实现物理的可替换的部分。接口:一个类提供另一个类的一组操作。依赖关系:一种使用关系(这里不做详解)构件与结构之间关系:构件与构件之间关系:依赖构件与其对应接口之间关系:实现导出接口:组件实现的接口,由提供操作的组件提供导入接口:访问服务的组件使用导入接口机房收费系统构件图:机房收费系统引用控件与报表生成器,导出EXcel,打印报表,通过代码与数据库连接实时更新数据。配置图:描述系统硬件的物理结构以及如何将软件部署在硬件上。用于对系统的实现视图建模,主要是为了描述系统各个物理组成部分的分布、提交、安装过程。组成元素:节点、关系机房收费系统部署图:补充:UML是不断修订的 在UML 中,主要是使用了9 张图,到了UML 2 中有新增了几张图;概览
2021年02月24日
1,486 阅读
0 评论
0 点赞
2021-02-24
画图技能之--时序图
转载自程序员必备画图技能之——时序图[TOC]什么是时序图时序图(Sequence Diagram),又名序列图、循序图,是一种 UML 交互图。它通过描述对象之间发送消息的时间顺序显示多个对象之间的动态协作。使用场景时序图的使用场景非常广泛,几乎各行各业都可以使用。当然,作为一个软件工作者,我这边主要列举和软件开发有关的场景。1. 梳理业务流程一般的软件开发都是为了支撑某个具体的业务。有时候业务的流程会比较复杂,涉及到多种角色,这时就可以使用时序图来梳理这个业务逻辑。这样会使业务看起来非常清晰,代码写起来也是水到渠成的事情了。一般的软件开发都是为了支撑某个具体的业务。有时候业务的流程会比较复杂,涉及到多种角色,这时就可以使用时序图来梳理这个业务逻辑。这样会使业务看起来非常清晰,代码写起来也是水到渠成的事情了。2. 梳理开源软件作为一个合格的程序员,阅读源代码的能力一定要过关。一般成熟框架的源代码调用深度都比较深,类之间的调用关系也比较复杂。我喜欢用时序图来梳理框架中这些对象之间的关系。比如再看 Tomcat 启动流程的过程中,我就时序图梳理了各个组件之间的关系,看起来层次非常清楚,也便于记忆。3. 时序图的角色我们在画时序图时会涉及下面7种元素:角色(Actor)对象(Object)生命线(LifeLine)控制焦点(Activation)消息(Message)自关联消息组合片段。其中前6种是比较常用和重要的元素,最后的组合片段元素不是很常用,但是比较复杂。我们先介绍前6种元素,再单独介绍组合片段元素。1. 角色(Actor)系统角色,可以是人或者其他系统和子系统。以一个小人图标表示。2. 对象(Object)对象位于时序图的顶部,以一个矩形表示。对象的命名方式一般有三种:对象名和类名。例如:华为手机:手机、loginServiceObject:LoginService;只显示类名,不显示对象,即为一个匿名类。例如::手机、:LoginSservice。只显示对象名,不显示类名。例如:华为手机:、loginServiceObject:。3. 生命线(LifeLine)时序图中每个对象和底部中心都有一条垂直的虚线,这就是对象的生命线(对象的时间线)。以一条垂直的虚线表示。4. 控制焦点(Activation)控制焦点代表时序图中在对象时间线上某段时期执行的操作。以一个很窄的矩形表示。5. 消息(Message)表示对象之间发送的信息。消息分为三种类型同步消息(Synchronous Message)消息的发送者把控制传递给消息的接收者,然后停止活动,等待消息的接收者放弃或者返回控制。用来表示同步的意义。以一条实线和实心箭头表示。异步消息(Asynchronous Message)消息发送者通过消息把信号传递给消息的接收者,然后继续自己的活动,不等待接受者返回消息或者控制。异步消息的接收者和发送者是并发工作的。以一条实线和大于号表示。返回消息(Return Message)返回消息表示从过程调用返回。以小于号和虚线表示。6. 自关联消息表示方法的自身调用或者一个对象内的一个方法调用另外一个方法。以一个半闭合的长方形+下方实心剪头表示。下面举例一个时序图的列子,看下上面几种元素具体的使用方式。ps:这个图上的返回消息画的不太对,应该是空心箭头7. 组合片段组合片段用来解决交互执行的条件和方式,它允许在序列图中直接表示逻辑组件,用于通过指定条件或子进程的应用区域,为任何生命线的任何部分定义特殊条件和子进程。组合片段共有13种,名称及含义如下:组合名称组合含义ref引用其他地方定义的组合片段alt在一组行为中根据特定的条件选择某个交互opt表示一个可选的行为break提供了和编程语言中的break类拟的机制par支持交互片段的并发执行seq强迫交互按照特定的顺序执行strict明确定义了一组交互片段的执行顺序neg用来标志不应该发生的交互region标志在组合片段中先于其他交互片断发生的交互ignore明确定义了交互片段不应该响应的消息consider明确标志了应该被处理的消息assert标志了在交互片段中作为事件唯一的合法继续者的操作数loop说明交互片段会被重复执行组合片段的功能平时用的不是很多,具体使用时可以参考本文最后关于组合片段的文章,这边不做深入介绍了。参考:https://blog.csdn.net/fly_zxy/article/details/80911942http://baijiahao.baidu.com/s?id=1561926824533534&wfr=spider&for=pchttps://www.cnblogs.com/whylaughing/p/5794693.html
2021年02月24日
942 阅读
0 评论
0 点赞
2021-02-19
vim入门--(3)小改动
暂无简介
2021年02月19日
851 阅读
0 评论
1 点赞
2021-02-07
vim入门--(2)移动
暂无简介
2021年02月07日
777 阅读
0 评论
0 点赞
2021-02-04
Android init.rc文件
最好直接去看Android的官方文档,那里更完整准确,system/core/init/README.md1. 文件简介Android init.rc文件由系统第一个启动的init程序解析,此文件由语句组成,主要包含了四种类型的语句:Action,Commands,Services,Options.在init.rc文件中一条语句通常是占据一行.单词之间是通过空格符来相隔的.如果需要在单词内使用空格,那么得使用转义字符”\”,如果在一行的末尾有一个反斜杠,那么是换行折叠符号,应该和下一行合并成一起来处理,这样做主要是为了避免一行的字符太长,与C语言中的含义是一致的。注释是以#号开头。 Action和services显式声明了一个语句块,而commands和options属于最近声明的语句块。在第一个语句块之前 的commands和options会被忽略.init.rc:Android在启动过程中读取的启动脚本文件,主要完成一些初级的初始化,在/system/core/init/init.cpp中解析。rc 经常被用作程序之启动脚本的文件名。它是“run commands”(运行命令)的缩写。init.xx.rc:与具体CPU相关的启动脚本,比如对于MTK的CPU,名字为init.usb.rc,init.trace.rc。在init.rc之后得到解析。厂商的rc文件一般位于device目录2.init.rc和init.xx.rc文件的修改我们以 init.rc 来入手,学习 rc 的用法。2.1 文件结构init.rc 基本单位是 section(语句块)。一个Section以Service或On开头的语句块.以Service开头的Section叫做服务,而以On开头的叫做动作(Action).services: 服务.Action: 动作commands:命令.options:选项.trigger:触发器,或者叫做触发条件.class: 类属,即可以为多个service指定一个相同的类属,方便操作同时启动或停止.section 有三种类型:onserviceimporton 类型作表示了一组命令(commands)组成.动作包含一个触发器,决定了何时执行这个动作。当触发器的条件满足时,这个动作会被加入到已被执行的队列尾。如果此动作在队列中已经存在,那么它将不会执行. 一个动作所包含的命令将被依次执行。动作的语法如下所示:on <trigger> <command> <command> ...on 类型 表示一系列的命令组合,eg:on init # See storage config details at http://source.android.com/tech/storage/ #mkdir /storage/sdcard 0555 root root #export EXTERNAL_STORAGE /storage/sdcard # Support legacy paths #symlink /storage/sdcard /sdcard #symlink /storage/sdcard /mnt/sdcard # mkdir /tmp 01775 root root # mount tmpfs tmpfs /tmp size=25m mkdir /dev/cpuctl/bg_non_interactive chown system system /dev/cpuctl/bg_non_interactive/tasks chmod 0666 /dev/cpuctl/bg_non_interactive/tasks # KSM config write /sys/kernel/mm/ksm/pages_to_scan 100 write /sys/kernel/mm/ksm/sleep_millisecs 500 write /sys/kernel/mm/ksm/run 1 write /sys/block/zram0/comp_algorithm lz4 write /sys/block/zram0/max_comp_streams 2 # Swap in only 1 page at a time write /proc/sys/vm/page-cluster 0 这样一个 section 里面包含了多个命令。命令的执行是以 section 为单位的。上面这些命令都会一起顺序执行,不会单独执行。service 类型服务是指那些需要在系统初始化时就启动或退出时自动重启的程序.它的语法结构如下所示:service <name> <pathname> [<argument>]* <option> <option> ...service 类型 的section 表示一个可执行的程序,下面是个人工作内容中片段代码:service submcu_srv /vendor/bin/submcu_srv class core user root group root oneshotsubmcu_srv作为一个名字标识了这个 service,这个可执行程序的位置在 /vendor/bin/submcu_srv下面的oneshot 被称为 options,options 是用来描述的 service 的特点,不同的 service 有不同的 options。service 的执行总存在于某个 on 类型的section 中作为一个服务启动的动作(Action)。例如,这个submcu_srv可以在on fs中启动on fs export OMX_BELLAGIO_REGISTRY /vendor/etc/omxregister start submcu_srv例二:service yo_service1 /system/bin/yo_service1 class core user system disabled group system radio shell oneshot on yo_fs class_start core 其中 yo_service1 这个 service 的类型是 core。在 yo_fs 被调用的时候则将会 class_start 而执行所有类型为 core 的 service。options(选项)选项是用来修饰服务的。它们影响如何及何时运行这个服务.trigger(触发器)触发器用来描述一个触发条件,当这个触发条件满足时可以执行动作。commands(命令)propertiesinit程序在运行时会更新属性系统的一些属性,提供程序内部正在执行的信息.import类型import 类型表示包含了另外一些 section,在解析完 init.rc 后会继续调用 init_parse_config_file 来解析引入的 .rc 文件。(最新的android11不一定是这个函数了)eg:比如我们在 init.sc8830.rc 的开始可以看到import /init.usb.rc import /init.trace.rc表示在运行完本 rc 后还将继续运行 init.usb.rc 和 init.trace.rc。2.2 调试注意事项在默认情况下,通过init程序启动的程序的标准输出stdout和标准错误输出stderr会重定向到/dev/null.如:service akmd /system/bin/logwrapper /sbin/akmd为了更方便调试你的程序,你可以使用Android的log系统,标准输出和标准错误输出会重定义到Android的log系统中来.参考文献:init.rc介绍
2021年02月04日
1,262 阅读
0 评论
0 点赞
2021-02-04
Android.bp简介
转载自Android.bp 语法浅析-Android10.0编译系统1.概述在 Android 7.0 之前,Android 编译系统使用 GNU Make 描述和shell来构建编译规则,模块定义都使用Android.mk进行定义,Android.mk的本质就是Makefile,但是随着Android的工程越来越大,模块越来越多,Makefile组织的项目编译时间越来越长。这样下去Google工程师觉得不行,得要优化。因此,在Android7.0开始,Google采用ninja来代取代之前使用的make,由于之前的Android.mk数据实在巨大,因此Google加入了一个kati工具,用于将Android.mk转换成ninja的构建规则文件buildxxx.ninja,再使用ninja来进行构建工作。ninja的网址:https://ninja-build.org编译速度快了一些,但是既然要干, 那就干个大的,最终目标要把make都取代,于是从Android8.0开始,Google为了进一步淘汰Makefile,因此引入了Android.bp文件来替换之前的Android.mk。 Android系统的编译历程: 2.Android.bp文件格式根据设计,Android.bp 文件很简单。它们不包含任何条件语句,也不包含控制流语句;所有复杂问题都由用 Go 编写的构建逻辑处理。Android.bp 文件的语法和语义都尽可能与 Bazel BUILD 文件类似。2.1 模块Android.bp 文件中的模块以模块类型开头,后跟一组 name: "value", 格式的属性:cc_binary { name: "gzip", srcs: ["src/test/minigzip.c"], shared_libs: ["libz"], stl: "none", }每个模块都必须具有 name 属性,并且相应值在所有 name 文件中必须是唯一的,仅有两个例外情况是命名空间和预构建模块中的 Android.bp 属性值,这两个值可能会重复。srcs 属性以字符串列表的形式指定用于构建模块的源文件。您可以使用模块引用语法 ":<module-name>" 来引用生成源文件的其他模块的输出,如 genrule 或 filegroup。如需有效模块类型及其属性的列表,请参阅 Soong 模块参考:https://www.cnblogs.com/linhaostudy/p/12361659.html常用模块类型:cc_binarycc_librarycc_library_staticandroid_appjava_libraryhidl_interfaceaidl_interface2.2 类型变量和属性是强类型,变量根据第一项赋值动态变化,属性由模块类型静态设置。支持的类型为:布尔值Bool(true 或 false)整数Integers (int)字符串Strings ("string")字符串列表List of string (["string1", "string2"])映射Maps ({key1: "value1", key2: ["value2"]})映射可以包含任何类型的值,包括嵌套映射。列表和映射可能在最后一个值后面有终止逗号。2.3 Glob接受文件列表的属性(例如 srcs)也可以采用 glob 模式。glob 模式可以包含普通的 UNIX 通配符 *,例如 *.java。glob 模式还可以包含单个 ** 通配符作为路径元素,与零个或多个路径元素匹配。例如,java/**/*.java 同时匹配 java/Main.java 和 java/com/android/Main.java 模式。2.4 变量Android.bp 文件可能包含顶级变量赋值:gzip_srcs = ["src/test/minigzip.c"] cc_binary { srcs: gzip_srcs, shared_libs: ["libz"], stl: "none", }变量的作用域限定在声明它们的文件的其余部分,以及所有子 Android.bp文件,可以使用 “=” 号赋值, 但是不能使用 “:=” 赋值。变量是不可变的,但有一个例外情况:可以使用 += 赋值将变量附加到别处,但只能在引用它们之前附加。例:gzip_srcs = ["src/test/minigzip.c"] gzip_srcs += [ "src/test/test.cpp", ] cc_binary { srcs: gzip_srcs, shared_libs: ["libz"], stl: "none", }2.5 注释Android.bp 文件可以包含 C 样式的多行 / / 注释以及 C++ 样式的单行 // 注释。2.6 运算符可以使用 + 运算符附加字符串、字符串列表和映射。可以使用 + 运算符对整数求和。附加映射会生成两个映射中键的并集,并附加在两个映射中都存在的所有键的值。2.7 条件语句Soong 不支持 Android.bp 文件中的条件语句。但是,编译规则中需要条件语句的复杂问题将在 Go(在这种语言中,您可以使用高级语言功能,并且可以跟踪条件语句引入的隐式依赖项)中处理。大多数条件语句都会转换为映射属性,其中选择了映射中的某个值并将其附加到顶级属性。例如,要支持特定于架构的文件,请使用以下命令:cc_library { ... srcs: ["generic.cpp"], arch: { arm: { srcs: ["arm.cpp"], }, x86: { srcs: ["x86.cpp"], }, }, }2.8 格式设置语句Soong 包含一个针对 Blueprint 文件的规范格式设置工具,类似于 gofmt。如需以递归方式重新设置当前目录中所有 Android.bp 文件的格式,请运行以下命令:bpfmt -w .规范格式包括缩进四个空格、多元素列表的每个元素后面有换行符,以及列表和映射末尾有英文逗号。2.9 默认模块默认模块可用于在多个模块中重复使用相同的属性。例如:cc_defaults { name: "gzip_defaults", shared_libs: ["libz"], stl: "none", } cc_binary { name: "gzip", defaults: ["gzip_defaults"], srcs: ["src/test/minigzip.c"], }2.10 预编译的模块某些预构建的模块类型允许模块与其基于源代码的对应模块具有相同的名称。例如,如果已有同名的 cc_binary,也可以将 cc_prebuilt_binary 命名为 foo。这让开发者可以灵活地选择要纳入其最终产品中的版本。如果编译配置包含两个版本,则预编译模块定义中的 prefer 标记值会指示哪个版本具有优先级。请注意,某些预编译模块的名称不能以 prebuilt开头,例如 android_app_import。2.11 命名空间模块在 Android 完全从 Make 转换为 Soong 之前,Make 产品配置必须指定 PRODUCT_SOONG_NAMESPACES 值。它的值应该是一个以空格分隔的列表,其中包含 Soong 导出到 Make 以使用 m 命令进行编译的命名空间。在 Android 完成到 Soong 的转换之后,启用命名空间的详细信息可能会发生变化。Soong 可以让不同目录中的模块指定相同的名称,只要每个模块都在单独的命名空间中声明即可。可以按如下方式声明命名空间:soong_namespace { imports: ["path/to/otherNamespace1", "path/to/otherNamespace2"], }请注意,命名空间没有 name 属性;其路径会自动指定为其名称。系统会根据每个 Soong 模块在树中的位置为其分配命名空间。每个 Soong 模块都会被视为处于 Android.bp(位于当前目录或最近的父级目录中的 soong_namespace 文件内)定义的命名空间中。如果未找到此类 soong_namespace 模块,则认为该模块位于隐式根命名空间中。下面是一个示例:Soong尝试解析由模块 M 在名称空间 N(导入命名空间 I1、I2、I3…)中声明的依赖项D。如果 D 是 //namespace:module 格式的完全限定名称,系统将仅在指定的命名空间中搜索指定的模块名称。否则,Soong 将首先查找在命名空间 N 中声明的名为 D 的模块。如果该模块不存在,Soong 会在命名空间 I1、I2、I3…中查找名为 D 的模块。最后,Soong 在根命名空间中查找。2.12 Android.mk文件 自动转Android.bp方法Android源码里边提供了快捷直接Android.mk转换成Android.bp的工具:androidmk。androidmk源码位置: build/soong/androidmk/cmd/androidmk/androidmk.go编译出来后androidmk可执行文件位置:aosp/out/soong/host/linux-x86/bin/androidmk转换方法:aosp/out/soong/host/linux-x86/bin/androidmk [Android.mk PATH] > [Android.bp PATH]该工具可以转换变量、模块、注释和一些条件,但任何自定义生成文件规则、复杂条件或额外的包含都必须手动转换。
2021年02月04日
1,792 阅读
0 评论
0 点赞
2021-02-04
Android.mk简介
Android.mk本页介绍了 ndk-build 所使用的 Android.mk 构建文件的语法。概览Android.mk 文件位于项目 jni/ 目录的子目录中,用于向构建系统描述源文件和共享库。它实际上是一个微小的 GNU makefile 片段,构建系统会将其解析一次或多次。Android.mk 文件用于定义 Application.mk、构建系统和环境变量所未定义的项目级设置。它还可替换特定模块的项目级设置。Android.mk 的语法支持将源文件分组为“模块”。模块是静态库、共享库或独立的可执行文件。您可在每个 Android.mk 文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。构建系统只将共享库放入您的应用软件包。此外,静态库可生成共享库。除了封装库之外,构建系统还可为您处理各种其他事项。例如,您无需在 Android.mk 文件中列出头文件或生成的文件之间的显式依赖关系。NDK 构建系统会自动计算这些关系。因此,您应该能够享受到未来 NDK 版本中支持的新工具链/平台功能带来的益处,而无需处理 Android.mk 文件。此文件的语法与随整个 Android 开源项目分发的 Android.mk 文件中使用的语法非常接近。虽然使用这些语法的构建系统实现并不相同,但通过有意将语法设计得相似,可使应用开发者更轻松地将源代码重复用于外部库。基础知识在详细了解语法之前,最好先了解 Android.mk 文件所含内容的基本信息。为此,本部分使用 Hello-JNI 示例中的 Android.mk 文件解释文件中每一行的作用。Android.mk 文件必须先定义 LOCAL_PATH 变量:LOCAL_PATH := $(call my-dir)此变量表示源文件在开发树中的位置。在上述命令中,构建系统提供的宏函数 my-dir 将返回当前目录(Android.mk 文件本身所在的目录)的路径。下一行声明 CLEAR_VARS 变量,其值由构建系统提供。include $(CLEAR_VARS)CLEAR_VARS 变量指向一个特殊的 GNU Makefile,后者会为您清除许多 LOCAL_XXX 变量,例如 LOCAL_MODULE、LOCAL_SRC_FILES 和 LOCAL_STATIC_LIBRARIES。请注意,GNU Makefile 不会清除 LOCAL_PATH。此变量必须保留其值,因为系统在单一 GNU Make 执行上下文(其中的所有变量都是全局变量)中解析所有构建控制文件。在描述每个模块之前,您必须声明(重新声明)此变量。接下来,LOCAL_MODULE 变量存储您要构建的模块的名称。请在应用的每个模块中使用一次此变量。LOCAL_MODULE := hello-jni每个模块名称必须唯一,且不含任何空格。构建系统在生成最终共享库文件时,会对您分配给 LOCAL_MODULE 的名称自动添加正确的前缀和后缀。例如,上述示例会生成名为 libhello-jni.so 的库。注意:如果模块名称的开头已经是 lib,构建系统不会添加额外的 lib 前缀;而是按原样采用模块名称,并添加 .so 扩展名。因此,比如原来名为 libfoo.c 的源文件仍会生成名为 libfoo.so 的共享对象文件。此行为是为了支持 Android 平台源文件根据 Android.mk 文件生成的库;所有这些库的名称都以 lib 开头。下一行会列举源文件,以空格分隔多个文件:LOCAL_SRC_FILES := hello-jni.cLOCAL_SRC_FILES 变量必须包含要构建到模块中的 C 和/或 C++ 源文件列表。最后一行帮助系统将一切连接到一起:include $(BUILD_SHARED_LIBRARY)BUILD_SHARED_LIBRARY 变量指向一个 GNU Makefile 脚本,该脚本会收集您自最近 include 以来在 LOCAL_XXX 变量中定义的所有信息。此脚本确定要构建的内容以及构建方式。在示例目录中有更为复杂的示例,包括带有注释的 Android.mk 文件供您查看。此外,示例:native-activity 详细介绍了该示例的 Android.mk 文件。最后,变量和宏提供了关于本部分中变量的更多信息。变量和宏构建系统提供许多可在 Android.mk 文件中使用的变量。其中的许多变量已预先赋值。另一些变量由您赋值。除了这些变量之外,您还可以自己定义任意变量。在定义变量时请注意,NDK 构建系统保留了下列变量名称:以 LOCAL\_ 开头的名称,例如 LOCAL_MODULE。以 PRIVATE\_、NDK\_ 或 APP 开头的名称。构建系统在内部使用这些变量名。小写名称,例如 my-dir。构建系统也是在内部使用这些变量名。如果您需要在 Android.mk 文件中定义您自己的便利变量,建议在名称前附加 MY_。NDK定义的include变量本部分探讨了构建系统在解析 Android.mk 文件前定义的 GNU Make 变量。在某些情况下,NDK 可能会多次解析 Android.mk 文件,每次使用其中某些变量的不同定义。CLEAR_VARS此变量指向的构建脚本用于取消定义下文“开发者定义的变量”部分中列出的几乎所有 LOCAL_XXX 变量。在描述新模块之前,请使用此变量来包含此脚本。使用它的语法为:include $(CLEAR_VARS)BUILD_EXECUTABLE此变量指向的构建脚本会收集您在 LOCAL_XXX 变量中提供的模块的所有相关信息,以及确定如何根据您列出的源文件构建目标可执行文件。请注意,使用此脚本要求您至少已经为 LOCAL_MODULE 和 LOCAL_SRC_FILES 赋值;如需详细了解这些变量,请参阅模块描述变量。使用此变量的语法为:include $(BUILD_EXECUTABLE)注意:大多数 Android 应用不包含可执行文件,但它们对于创建单元测试和其他调试工具很有用。BUILD_SHARED_LIBRARY此变量指向的构建脚本会收集您在 LOCAL_XXX 变量中提供的模块的所有相关信息,以及确定如何根据您列出的源文件构建目标共享库。请注意,使用此脚本要求您至少已经为 LOCAL_MODULE 和 LOCAL_SRC_FILES 赋值;如需详细了解这些变量,请参阅模块描述变量。使用此变量的语法为:include $(BUILD_SHARED_LIBRARY)共享库变量会导致构建系统生成扩展名为 .so 的库文件。BUILD_STATIC_LIBRARY用于构建静态库的 BUILD_SHARED_LIBRARY 的变体。构建系统不会将静态库复制到您的项目/软件包中,但可以使用静态库构建共享库(请参阅下文的 LOCAL_STATIC_LIBRARIES 和 LOCAL_WHOLE_STATIC_LIBRARIES)。使用此变量的语法为:include $(BUILD_STATIC_LIBRARY)静态库变量会导致构建系统生成扩展名为 .a 的库。PREBUILT_SHARED_LIBRARY指向用于指定预构建共享库的构建脚本。与 BUILD_SHARED_LIBRARY 和 BUILD_STATIC_LIBRARY 的情况不同,这里的 LOCAL_SRC_FILES 值不能是源文件,而必须是指向预构建共享库的单一路径,例如 foo/libfoo.so。使用此变量的语法为:include $(PREBUILT_SHARED_LIBRARY)您也可以使用 LOCAL_PREBUILTS 变量引用另一个模块中的预构建库。如需详细了解如何使用预构建库,请参阅使用预构建库。PREBUILT_STATIC_LIBRARY与 PREBUILT_SHARED_LIBRARY 相同,但用于预构建静态库。如需详细了解如何使用预构建库,请参阅使用预构建库。目标信息变量构建系统会根据 APP_ABI 变量所指定的每个 ABI 分别解析 Android.mk 一次,该变量通常在 Application.mk 文件中定义。如果 APP_ABI 为 all,构建系统会根据 NDK 支持的每个 ABI 分别解析 Android.mk 一次。本部分介绍构建系统每次解析 Android.mk 时定义的变量。TARGET_ARCH构建系统解析此 Android.mk 文件时指向的 CPU 系列。此变量将是下列其中一项:arm、arm64、x86 或 x86_64。TARGET_PLATFORM构建系统解析此 Android.mk 文件时指向的 Android API 级别号。例如,Android 5.1 系统映像对应于 Android API 级别 22:android-22。如需查看平台名称和相应 Android 系统映像的完整列表,请参阅原生 API。以下示例展示了使用此变量的语法:ifeq ($(TARGET_PLATFORM),android-22) # ... do something ... endifTARGET_ARCH_ABI构建系统解析此 Android.mk 文件时指向的 ABI。表 1 显示了用于每个受支持 CPU 和架构的 ABI 设置。表 1. 不同 CPU 和架构的 ABI 设置。CPU和架构设置ARMv7armeabi-v7aARMv8 AArch64arm64-v8ai686x86x86-64x86_64以下示例演示了如何检查 ARMv8 AArch64 是否为目标 CPU 与 ABI 的组合:ifeq ($(TARGET_ARCH_ABI),arm64-v8a) # ... do something ... endif如需详细了解架构 ABI 和相关兼容性问题,请参阅 Android ABI。未来的新目标 ABI 将使用不同的值。TARGET_ABI目标 Android API 级别与 ABI 的串联,特别适用于要针对实际设备测试特定目标系统映像的情况。例如,要检查在 Android API 级别 22 上运行的 64 位 ARM 设备:ifeq ($(TARGET_ABI),android-22-arm64-v8a) # ... do something ... endif模块描述变量本部分中的变量会向构建系统描述您的模块。每个模块描述都应遵守以下基本流程:使用 CLEAR_VARS 变量初始化或取消定义与模块相关的变量。为用于描述模块的变量赋值。使用 BUILD_XXX 变量设置 NDK 构建系统,使其将适当的构建脚本用于该模块。LOCAL_PATH此变量用于指定当前文件的路径。必须在 Android.mk 文件开头定义此变量。以下示例演示了如何定义此变量:LOCAL_PATH := $(call my-dir)CLEAR_VARS 所指向的脚本不会清除此变量。因此,即使 Android.mk 文件描述了多个模块,您也只需定义此变量一次。LOCAL_MODULE此变量用于存储模块名称。指定的名称在所有模块名称中必须唯一,并且不得包含任何空格。您必须先定义该名称,然后才能添加任何脚本(CLEAR_VARS 的脚本除外)。无需添加 lib 前缀或 .so 或 .a 文件扩展名;构建系统会自动执行这些修改。在整个 Android.mk 和 Application.mk 文件中,请用未经修改的名称引用模块。例如,以下行会导致生成名为 libfoo.so 的共享库模块:LOCAL_MODULE := "foo"如果您希望生成的模块使用除“lib + LOCAL_MODULE 的值”以外的名称,可以使用 LOCAL_MODULE_FILENAME 变量为生成的模块指定自己选择的名称。LOCAL_MODULE_FILENAME此可选变量使您能够替换构建系统为其生成的文件默认使用的名称。例如,如果 LOCAL_MODULE 的名称为 foo,您可以强制系统将其生成的文件命名为 libnewfoo。以下示例演示了如何完成此操作:LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo对于共享库模块,此示例将生成一个名为 libnewfoo.so 的文件。注意:您无法替换文件路径或文件扩展名。LOCAL_SRC_FILES此变量包含构建系统生成模块时所用的源文件列表。只列出构建系统实际传递到编译器的文件,因为构建系统会自动计算所有相关的依赖项。请注意,您可以使用相对(相对于 LOCAL_PATH)和绝对文件路径。建议您避免使用绝对文件路径;相对路径可以提高 Android.mk 文件的移植性。注意:务必在构建文件中使用 Unix 样式的正斜杠 (/)。构建系统无法正确处理 Windows 样式的反斜杠 ()。LOCAL_CPP_EXTENSION可以使用此可选变量为 C++ 源文件指定 .cpp 以外的文件扩展名。例如,以下行将扩展名更改为 .cxx(设置必须包含点)。LOCAL_CPP_EXTENSION := .cxx您可以使用此变量指定多个扩展名。例如:LOCAL_CPP_EXTENSION := .cxx .cpp .ccLOCAL_CPP_FEATURES您可使用此可选变量指明您的代码依赖于特定 C++ 功能。它会在构建过程中启用正确的编译器标记和链接器标记。对于预构建的二进制文件,此变量还会声明二进制文件依赖于哪些功能,从而确保最终链接正常运行。我们建议您使用此变量,而不要直接在 LOCAL_CPPFLAGS 定义中启用 -frtti 和 -fexceptions。使用此变量可让构建系统对每个模块使用适当的标记。使用 LOCAL_CPPFLAGS 会导致编译器将所有指定的标记用于所有模块,而不管实际需求如何。例如,如需指明您的代码使用 RTTI(运行时类型信息),请写入:LOCAL_CPP_FEATURES := rtti如需指明您的代码使用 C++ 异常,请输入:LOCAL_CPP_FEATURES := exceptions您还可以为此变量指定多个值。例如:LOCAL_CPP_FEATURES := rtti features描述值的顺序无关紧要。LOCAL_C_INCLUDES您可使用此可选变量指定相对于 NDK root 目录的路径列表,以便在编译所有源文件(C、C++ 和 Assembly)时添加到 include 搜索路径中。例如:LOCAL_C_INCLUDES := sources/foo或者甚至:LOCAL_C_INCLUDES := $(LOCAL_PATH)/<subdirectory>/foo请在通过 LOCAL_CFLAGS 或 LOCAL_CPPFLAGS 设置任何对应的包含标记前定义此变量。在使用 ndk-gdb 启动原生调试时,构建系统也会自动使用 LOCAL_C_INCLUDES 路径。LOCAL_CFLAGS此可选变量用于设置在构建 C 和 C++ 源文件时构建系统要传递的编译器标记。这样,您就可以指定额外的宏定义或编译选项。可以使用 LOCAL_CPPFLAGS 仅为 C++ 指定标记。请勿尝试在 Android.mk 文件中更改优化/调试级别。构建系统可以使用 Application.mk 文件中的相关信息自动处理此设置。这样,构建系统就可以生成供调试期间使用的有用数据文件。您可通过输入以下代码指定额外的 include 路径:LOCAL_CFLAGS += -I<path>,但是,最好使用 LOCAL_C_INCLUDES,因为这样也可以使用可用于 ndk-gdb 原生调试的路径。LOCAL_CPPFLAGS只构建 C++ 源文件时将传递的一组可选编译器标记。它们将出现在编译器命令行中的 LOCAL_CFLAGS 后面。使用 LOCAL_CFLAGS 为 C 和 C++ 指定标记。LOCAL_STATIC_LIBRARIES此变量用于存储当前模块依赖的静态库模块列表。如果当前模块是共享库或可执行文件,此变量将强制这些库链接到生成的二进制文件。如果当前模块是静态库,此变量只是指出依赖于当前模块的其他模块也会依赖于列出的库。LOCAL_SHARED_LIBRARIES此变量会列出此模块在运行时依赖的共享库模块。此信息是链接时必需的信息,用于将相应的信息嵌入到生成的文件中。LOCAL_WHOLE_STATIC_LIBRARIES此变量是 LOCAL_STATIC_LIBRARIES 的变体,表示链接器应将相关的库模块视为完整归档。如需详细了解完整归档,请参阅有关 --whole-archive 标记的 GNU Id 文档。多个静态库之间存在循环依赖关系时,此变量十分有用。使用此变量构建共享库时,它将强制构建系统将静态库中的所有对象文件添加到最终二进制文件。但是,生成可执行文件时不会发生这种情况。LOCAL_LDLIBS此变量列出了在构建共享库或可执行文件时使用的额外链接器标记。利用此变量,您可使用 -l 前缀传递特定系统库的名称。例如,以下示例指示链接器生成在加载时链接到 /system/lib/libz.so 的模块:LOCAL_LDLIBS := -lz如需查看此 NDK 版本中可以链接的公开系统库列表,请参阅原生 API。注意:如果您为静态库定义此变量,构建系统会忽略此变量,并且 ndk-build 显示一则警告。LOCAL_LDFLAGS此变量列出了构建系统在构建共享库或可执行文件时使用的其他链接器标记。例如,若要在 ARM/X86 上使用 ld.bfd 链接器:LOCAL_LDFLAGS += -fuse-ld=bfd注意:如果您为静态库定义此变量,构建系统会忽略此变量,并且 ndk-build 会显示一则警告。LOCAL_ALLOW_UNDEFINED_SYMBOLS默认情况下,如果构建系统在尝试构建共享库时遇到未定义的引用,将会抛出“未定义的符号”错误。此错误可帮助您捕获源代码中的错误。如需停用此检查,请将此变量设置为 true。请注意,此设置可能会导致共享库在运行时加载。注意:如果您为静态库定义此变量,构建系统会忽略此变量,并且 ndk-build 会显示一则警告。LOCAL_ARM_MODE默认情况下,构建系统会以 thumb 模式生成 ARM 目标二进制文件,其中每条指令都是 16 位宽,并与 thumb/ 目录中的 STL 库链接。将此变量定义为 arm 会强制构建系统以 32 位 arm 模式生成模块的对象文件。以下示例演示了如何执行此操作:LOCAL_ARM_MODE := arm您也可以对源文件名附加 .arm 后缀,指示构建系统仅以 arm 模式构建特定的源文件。例如,以下示例指示构建系统始终以 ARM 模式编译 bar.c,但根据 LOCAL_ARM_MODE 的值构建 foo.c。LOCAL_SRC_FILES := foo.c bar.c.arm注意:您也可以在 Application.mk 文件中将 APP_OPTIM 设置为 debug,强制构建系统生成 ARM 二进制文件。指定 debug 会强制构建 ARM,因为工具链调试程序无法正确处理 Thumb 代码。LOCAL_ARM_NEON此变量仅在以 armeabi-v7a ABI 为目标时才有意义。它允许在 C 和 C++ 源文件中使用 ARM Advanced SIMD (NEON) 编译器内建函数,以及在 Assembly 文件中使用 NEON 指令。请注意,并非所有基于 ARMv7 的 CPU 都支持 NEON 扩展指令集。因此,必须执行运行时检测,以便在运行时安全地使用此代码。如需了解详情,请参阅 Neon 支持和 CPU 功能。此外,您也可以使用 .neon 后缀,指定构建系统仅以 NEON 支持来编译特定源文件。在以下示例中,构建系统以 Thumb 和 NEON 支持编译 foo.c,以 Thumb 支持编译 bar.c,并以 ARM 和 NEON 支持编译 zoo.c:LOCAL_SRC_FILES = foo.c.neon bar.c zoo.c.arm.neon如果同时使用这两个后缀,.arm 必须在 .neon 前面。LOCAL_DISABLE_FORMAT_STRING_CHECKS默认情况下,构建系统会在编译代码时保护格式字符串。这样的话,如果 printf 样式的函数中使用了非常量格式的字符串,就会强制引发编译器错误。此保护默认启用,但您也可通过将此变量的值设置为 true 将其停用。如果没有必要的原因,我们不建议停用。LOCAL_EXPORT_CFLAGS此变量用于记录一组 C/C++ 编译器标记,这些标记将添加到通过 LOCAL_STATIC_LIBRARIES 或 LOCAL_SHARED_LIBRARIES 变量使用此模块的任何其他模块的 LOCAL_CFLAGS 定义中。例如,假设有以下模块对:foo 和 bar,它们依赖于 foo:include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_CFLAGS := -DFOO=1 include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_CFLAGS := -DBAR=2 LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)在这里,构建系统在构建 bar.c 时会向编译器传递 -DFOO=1 和 -DBAR=2 标记。它还会在模块的 LOCAL_CFLAGS 前面加上导出的标记,以便您轻松进行替换。此外,模块之间的关系也具有传递性:如果 zoo 依赖于 bar,而后者依赖于 foo,那么 zoo 也会继承从 foo 导出的所有标记。最后,构建系统在执行局部构建时(即,构建要导出标记的模块时),不使用导出的标记。因此,在以上示例中,构建系统在构建 foo/foo.c 时不会将 -DFOO=1 传递到编译器。如需执行局部构建,请改用 LOCAL_CFLAGS。LOCAL_EXPORT_CPPFLAGS此变量与 LOCAL_EXPORT_CFLAGS 相同,但仅适用于 C++ 标记。LOCAL_EXPORT_C_INCLUDES此变量与 LOCAL_EXPORT_CFLAGS 相同,但适用于 C include 路径。例如,当 bar.c 需要包括模块 foo 的头文件时,此变量很有用。LOCAL_EXPORT_LDFLAGS此变量与 LOCAL_EXPORT_CFLAGS 相同,但适用于链接器标记。LOCAL_EXPORT_LDLIBS此变量与 LOCAL_EXPORT_CFLAGS 相同,用于指示构建系统将特定系统库的名称传递到编译器。请在您指定的每个库名称前附加 -l。请注意,构建系统会将导入的链接器标记附加到模块的 LOCAL_LDLIBS 变量值上。其原因在于 Unix 链接器的工作方式。当模块 foo 是静态库并且具有依赖于系统库的代码时,此变量通常很有用。然后,您可以使用 LOCAL_EXPORT_LDLIBS 导出依赖项。例如:include $(CLEAR_VARS) LOCAL_MODULE := foo LOCAL_SRC_FILES := foo/foo.c LOCAL_EXPORT_LDLIBS := -llog include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := bar LOCAL_SRC_FILES := bar.c LOCAL_STATIC_LIBRARIES := foo include $(BUILD_SHARED_LIBRARY)在此示例中,构建系统在构建 libbar.so 时,将在链接器命令的末尾指定 -llog。这样就会告知链接器,由于 libbar.so 依赖于 foo,因此它也依赖于系统日志记录库。LOCAL_SHORT_COMMANDS当您的模块有很多源文件和/或依赖的静态或共享库时,请将此变量设置为 true。这样会强制构建系统将 @ 语法用于包含中间对象文件或链接库的归档。此功能在 Windows 上可能很有用,在 Windows 上,命令行最多只接受 8191 个字符,这对于复杂的项目来说可能太少。它还会影响个别源文件的编译,而且将几乎所有编译器标记都放在列表文件内。请注意,true 以外的任何值都将恢复为默认行为。您也可以在 Application.mk 文件中定义 APP_SHORT_COMMANDS,对项目中的所有模块强制实施此行为。我们不建议默认启用此功能,因为它会减慢构建速度。LOCAL_THIN_ARCHIVE构建静态库时,请将此变量设置为 true。这样会生成一个瘦归档,即一个库文件,其中不含对象文件,而只包含它通常包含的实际对象的文件路径。这对于减小构建输出的大小非常有用。但缺点是,这样的库无法移至其他位置(其中的所有路径都是相对路径)。有效值为 true、false 或空白。您可在 Application.mk 文件中通过 APP_THIN_ARCHIVE 变量来设置默认值。注意:在非静态库模块或预构建的静态库模块中,将会忽略此变量。LOCAL_FILTER_ASM请将此变量定义为一个 shell 命令,供构建系统用于过滤根据您为 LOCAL_SRC_FILES 指定的文件提取或生成的汇编文件。定义此变量会导致发生以下情况:构建系统从任何 C 或 C++ 源文件生成临时汇编文件,而不是将它们编译到对象文件中。构建系统在任何临时汇编文件以及 LOCAL_SRC_FILES 中所列任何汇编文件的 LOCAL_FILTER_ASM 中执行 shell 命令,因此会生成另一个临时汇编文件。构建系统将这些过滤的汇编文件编译到对象文件中。例如:LOCAL_SRC_FILES := foo.c bar.S LOCAL_FILTER_ASM := foo.c --1--> $OBJS_DIR/foo.S.original --2--> $OBJS_DIR/foo.S --3--> $OBJS_DIR/foo.o bar.S --2--> $OBJS_DIR/bar.S --3--> $OBJS_DIR/bar.o“1”对应于编译器,“2”对应于过滤器,“3”对应于汇编程序。过滤器必须是一个独立的 shell 命令,它接受输入文件名作为第一个参数,接受输出文件名作为第二个参数。例如:myasmfilter $OBJS_DIR/foo.S.original $OBJS_DIR/foo.S myasmfilter bar.S $OBJS_DIR/bar.SNDK 提供的函数宏本部分介绍了 NDK 提供的 GNU Make 函数宏。使用 $(call <function>) 可以对其进行求值;其返回文本信息。my-dir这个宏返回最后包括的 makefile 的路径,通常是当前 Android.mk 的目录。my-dir 可用于在 Android.mk 文件开头定义 LOCAL_PATH。例如:LOCAL_PATH := $(call my-dir)由于 GNU Make 的工作方式,这个宏实际返回的是构建系统解析构建脚本时包含的最后一个 makefile 的路径。因此,包括其他文件后就不应调用 my-dir。例如:LOCAL_PATH := $(call my-dir) # ... declare one module include $(LOCAL_PATH)/foo/`Android.mk` LOCAL_PATH := $(call my-dir) # ... declare another module这里的问题在于,对 my-dir 的第二次调用将 LOCAL_PATH 定义为 $PATH/foo,而不是 $PATH,因为这是其最近的 include 所指向的位置。在 Android.mk 文件中的任何其他内容后指定额外的 include 可避免此问题。例如:LOCAL_PATH := $(call my-dir) # ... declare one module LOCAL_PATH := $(call my-dir) # ... declare another module # extra includes at the end of the Android.mk file include $(LOCAL_PATH)/foo/Android.mk如果以这种方式构造文件不可行,请将第一个 my-dir 调用的值保存到另一个变量中。例如:MY_LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(MY_LOCAL_PATH) # ... declare one module include $(LOCAL_PATH)/foo/`Android.mk` LOCAL_PATH := $(MY_LOCAL_PATH) # ... declare another moduleall-subdir-makefiles返回位于当前 my-dir 路径所有子目录中的 Android.mk 文件列表。利用此函数,您可以为构建系统提供深度嵌套的源目录层次结构。默认情况下,NDK 只在 Android.mk 文件所在的目录中查找文件。this-makefile返回当前 makefile(构建系统从中调用函数)的路径。parent-makefile返回包含树中父 makefile 的路径(包含当前 makefile 的 makefile 的路径)。grand-parent-makefile返回包含树中祖父 makefile 的路径(包含当前父 makefile 的 makefile 的路径)。import-module此函数用于按模块名称来查找和包含模块的 Android.mk 文件。典型的示例如下所示:$(call import-module,<name>)在此示例中,构建系统在 NDK_MODULE_PATH 环境变量所引用的目录列表中查找具有 标记的模块,并且自动包括其 Android.mk 文件。google Android.mk
2021年02月04日
753 阅读
0 评论
0 点赞
1
...
32
33
34
...
39