AOSP系统开发

AOSP下的系统开发修改aosp三个的方面系统app开发framework层定制native层定制AOSP开发的覆盖面是非常广的:第一个方向:以前是搞android应用开发,现在负责系统application开发,这样就会上手非常快,只需要对osp有基本了解就可以了。但有时候在解决bug的时候,需要对下层有所了解。就会逐渐的走向framework层的开发。在framework层中做实现,修改,定制的过程中,就会逐渐的接触到native层,这是由上走向下。第二个方向:以前是搞linux内核以及驱动的,由于linux内核和驱动都是以C语言为核心的,这样接触native层也是如鱼得水的。在native层定制接口的时候,可能需要和上层(framework层)的人进行配合,在配合中,要了解framework层是如何做的,如何使用接口的,怎么去调用的。从而会去学习java的语言。这样干脆就做application去了,这是由下走向上。1. Android的启动流程简述现在首先了解android的启动流程,以此加深android系统的了解

android启动流程.jpg2. 系统app开发在packages/apps找到需要修改的相应app。在该app目录中是属于java的部分,与android应用的开发语言相同。但是与平常的app在androidstudio编写不同,在源码环境中,需要利用编辑器来进行编辑。并且在源码中的编译就需要利用make编译系统进行编译。源码的编译是以模块进行。利用mm命令进行编译。编译完成之后,会生成odex以及apk。与普通的apk的结构不同,系统编译生成的application分成了两部分:apk包括了签名,资源,AndroidMannifest清单文件,资源的二进制文件。而原本的classes.dex则被放在了odex这一部分中。为什么apk分成了两个文件,这就是Dalvik与art虚拟机的区别:Dalvik:JIT(just in time)实时编译,运行的时候将字节码翻译成机器码,所运行的目标文件与硬件平台无关,app运行效率低ART:AOT(Ahead of time)预先编译,运行前将字节码翻译成机器码,所运行的目标文件(oat)与硬件平台相关.app运行效率高。但会占用空间。APK安装所需时间增加2.1 odex是干什么的Dalvik时代:apk运行的时候,会把apk的classes.dex解压出来并通过dexopt优化为.odex文件,缓存在/data/dalvik-cache目录下,提高后续执行的效率。这也是android被诟病的比较卡。ART时代:apk安装的时候,会把apk的classes.dex解压后,通过dex2oat工具转化为.odex文件(ELF格式,与Dalvik时代的.odex文件完全不同,只是为了和以前的Dalvik时代的命名相同,内容完全不同),存储在apk所在目录的oat目录下。用空间换时间2.2 为什么在源码环境编译生成了odex文件ROM:apk,jar,bin,so等组成优点:降低系统更新后启动的时间:未做odex的Rom,首次开机的过程会执行odex操作。编译时做,开机时候就不用做了减少在设备上进行odex操作所造成的空间浪费:编译时进行dexopt/dex2oat,会直接将apk资源与代码拆开。如果在设备上安装时进行dexopt/dex2oat,apk的大小不会减少,但又会多一个odex文件占据磁盘空间缺点:增加开发的编译时间不能直接执行apk的install操作,需要将apk和odex都sync到设备上2.2.1 如何在开发阶段关闭dex2oat在当前module的Android.mk里增加:LOCAL_DEX_PREOAT = false在build/core/main.mk中关闭所有module的dex优化:## eng ##在lunch命令时选择eng版本ifeq($(TARGET_BUILD_VARIANT),eng)tags_to_install := debug eng#关闭odex优化WITH_DEXPREOPT := falseodex优化只在linux环境下生效3. Framework层定制Framework的定制,一般是为了满足app层或者整个系统的某一种需求。确定要修改的Framework层服务在哪(一般在源码的frameworks目录下)修改要定制的代码用mm命令进行module的编译用编译生成的文件(如framework.jar,services.jar等)替换设备中的文件(一般在system/framework目录下)重启系统:#只重启安卓系统:stop;start验证修改(通过日志等方式)4. native层代码native层的定制,一般是为了满足framework层代码的调用需求。Native层分为两部分JNI(AndroidRuntime)和natie。JNI层是native层C/C++与framework层java交互的桥梁。4.1 修改jni层:从framework层往下层寻找jni的native实现。jni的规范:例如framework层中的android.util.Log.java这个类中的jni:该类的包名加类名为:android.util.Log将"."改为"_",则为android_util_Log则该类native的实现就在android_util_Log这个文件中,在源码中找到该文件进行修改修改之后在jni实现的目录下,执行mm命令进行编译将编译生成的文件(如libandroid_runtime.so等)替换设备中的文件(一般在system/lib/目录下)重启系统#只重启安卓系统:stop;start验证修改(通过日志等方式)4.2 修改native层还没了解到,未完待续....AOSP下常用工作目录和ABS编译流程1. 常见AOSP目录目录名介绍build/core/AOSP整个编译过程中核心的编译规则makefilebuild/envsetup.sh编译初始化脚本build/targetAOSP自带的Target(模拟器)的一些makefilebuild/tools/编译中使用的shell及python写的工具脚本packagess系统app以及一些provider,输入法等桌面看不到的appframework/av/多媒体相关的native层源码目录framework/webview/浏览器内核chromium的源码目录framework/native/power、surface、input、binder等服务的native层实现源码目录framework/base/core/framework.jar、framework-res.apk、libandroid_runtime.so等的源文件framework/base/native/libandroid.so的源代码目录.java中的api的native实现.比如looper,assertManagerframework/base/media/多媒体相关的JavaApi和jni层的源文件framework/base/packages/SettingProviders,SystemUI等不在桌面启动的APP源码framework/base/service/service.jar、libandroid_service.so的源文件framework/base/service/wifi服务相关的JavaApi,WifiManager,WifiService等device/(vendor_name)/(product_name)跟某些厂商的某个硬件平台相关的内核,硬件配置等vendor/(vendor_name)/(product_name)厂商对AOSP进行的修改或者定制,放在vendor目录。包括但不限于framework层新增API,新增APP等业务需求,但是现在Google更推荐放在devices目录下/out/host该目录下包含了针对当前操作系统所编译出的Android开发工具产物,例如adb,aapt,fastboot等命令/out/target/common/该目录包含了针对Android设备的通用的编译产物,主要是java应用代码和java库。Framework.jar,services.jar,android.policy.jar等等/out/target/(product)/(product_name)包含了针对特定设备的编译结果以及平台相关的C/C++库和二进制文件。其中,product_name是具体目标设备的名称2. AndroidBuildSystemandorid build系统用来编译android系统,android sdk以及相关文档.该系统主要由make文件,shell脚本以及python脚本组成,在编译时能够支持面向不同的硬件设备,不同的编译类型,且提供面向各个厂商的定制扩展。2.1 什么是makefilemakefile是一些有特定语法的,可供make命令读取并执行的脚本性质的配置文件。作用就是可以告知编译系统,对哪些源文件进行编译,怎么编译,怎么处理依赖关系。makefile可以定义变量,函数,调用系统命令,shell、python脚本,管理module之间的依赖。整个Android Build系统中的 Make 文件可以分为三类:Build系统核心 Makefile这类makefile定义了整个Build系统的框架,而其他所有Make文件都是在这个框架的基础上编写出来的。位于/build/core目录下。针对某个产品的Makefile这类makefile是针对某个产品Make文件,这些文件通常位于device/<vendor>/<product>目录下。针对某个模块的Makefile Android.mk第三类是针对某个模块的makefile文件.AOSP中,不会针对某一个文件进行编译,每一个编译单位都是一个模块,每一个模块由一个名为"Android.mk"的makefile来声明。该文件中定义了如何编译当前模块。2.2 make文件中的内容LOCAL_PATH := $(call my-dir)表示源代码的目录在哪include $(CLEAR_VARS)表示变量全部清空,其中CLEAR_VARS也是一个makefile文件LOCAL_MODULE_TAGS := optional表示是哪个TAG,可为eng,user,userdebug,optional等,在哪个版本下执行编译等...3. ABS思维导图ABS思维导图ps:原谅我不会在markdown上画思维导图

android编译.png略丑的流程图ps:图不是重点,重点是掌握build系统的流程

ABS流程.jpg附上文字描述:入口为Makefile.mk文件build/core/main.mk:检查当前的编译环境inclue build/core/下的核心makefile从而完成编译环境的配置检索所有的BoardConfig.mk和AndroidProduct.mk载入到编译系统main.mk里会使用python脚本遍历AOSP下的所有模块(AndroidProduct.mk)并include所有模块下的AndroidProduct.mk:告诉编译系统,当前module的一些配置信息(源文件,依赖库等)和编译输出类型(可执行文件,共享库,静态库,java库,app等)。根据不同的输出类型,会include不同的makefile(package.mk java_library.mk等),这些makefile会调用相关的编译工具,达到编译目的Android编译系统(Android.mk文件详解-仅供参考)注:原作内容为2012年所发布,与最新版本的aosp有些区别主要是没有一个完整的Android Build System 中文版,所以写了一个也可以以后作为参考。Makefile & Android build system在进行讲述Android编译系统之前,应该先了解一下编译时所使用的Makefile,或者说复习下这方面的知识,这样才能更好的了解Android build system的原理。MakefileMakefile的规则首先介绍Makefile的规则:target ... : prerequisites ... command...target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label)。prerequisites就是要生成那个target所需要(依赖)的文件或是目标。command也就是make需要执行的命令(任意的Shell命令)。这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容,Android编译系统符合GNU make的标准,当然这也是Android编译系统最核心的内容。编译helloworld下面用一个最简单的例子来说明这个规则,当然就是我们最喜欢写的helloworld,创建一个简单的helloworld.c,下面就是编译helloworld的makefile:helloworld : helloworld.o cc -o helloworld helloworld .ohelloworld.o : helloworld.c cc -c main.cclean: rm helloworld helloworl.o执行make就可以编译helloworld.c了,执行make clean就可以清除编译结果了(其实就是删除helloworld helloworl.o)。Android build ystemMakefile文件用来告诉make命令需要怎么样的去编译和链接程序。在编译时,需要根据编译环境和编译目标选择编译工具,编译参数,以及选择编译安装哪些模块。同时Makefile指定了构建目标所需的依赖性以及生成规则。在Android中,主要的Makefile文件存在于build/core/目录下,它的表现形式为多个后缀为*.mk的文件组成,也称为build system。Android build system主要有两大部分构成:配置部分和目标构建部分。Build system的主流程文件为build/core/main.mk文件,有它以及所需要的其它*.mk文件共同完成一次Build的任务。下面以表格的形式概要描述几个重要的*.mk的文件的作用:文件名作用Android.mkmodule/package的设置文件,每个module/package的目录下都会有一个Android.mkAndroidProducts.mk即为Android build system提供给厂商的接口文件,通过此文件即可定义所需编译和安装的packages,默认为genericbase_rules.mk对一些Makefile的变量规则化BoardConfig.mk是为product主板做设定,例如driver选择设定,选择CPU架构等等Binary.mk控制如何生成目标文件buildspec.mk位于根目录下,可在此选择要产生的product 、平台、额外的module/package等。build/buildspec.mk.default是样板Clear_vars.mk清除编译系统中用到的临时变量Copy_headers.mk将头文件拷贝到指定目录config.mk定义了编译目标程序所需的工具链及编译参数definations.mk定义了很多编译系统中用到的宏,相当于函数库envsetup.mk初始化编译环境,定义一些实用的shell函数,方便编译main.mk实际的主控Makefile,例如找到TOP目录下所有Android.mk文件Makefile辅助main.mk主要控制生成 system.img,ramdisk.img,userdata.img等build/envsetup.sh提供了几个有用的命令,如:执行. build/envsetup.shCombo/linux-arm.mk控制如何生成linux-arm二进制文件,包括ARM相关的编译器,编译参数等的设置Android.mkAndroid.mk是Android编译系统中最重要的一个文件,下面将详细介绍:概述Android.mk在编译中起着至关重要的作用,这其实就是Android编译环境中的makefile。Android.mk文件是为了向生成系统描述你的源代码。更明确的说:这个文件实际上是GNU Make文件的一小片段,它会被生成系统解析一次或多次。因此,你应该在Android.mk里尽量少地声明变量。下面是在NDK中Android.mk网页中引用的一段:An Android.mk file is written to describe your sources to thebuild system. More specifically:The file is really a tiny GNU Makefile fragment that will be parsed one or more times by the build system. As such, you should try to minimize the variables you declare there and do not assume that anything is not defined during parsing.The file syntax is designed to allow you to group your sources into 'modules'. A module is one of the following:a static librarya shared libraryOnly shared libraries will be installed/copied to your application package. Static libraries can be used to generate shared libraries though.You can define one or more modules in each Android.mk file, and you can use the same source file in several modules.The build system handles many details for you. For example, you don't need to list header files or explicit dependencies between generated files in your Android.mk. The NDK build system will compute these automatically for you. This also means that, when updating to newer releases of the NDK, you should be able to benefit from new toolchain/platform support without having to touch your Android.mk files.详细说明首先,对这些变量的命名做一说明:LOCAL_XXX变量:在每个module中都要设置以LOCAL_开头的变量。它们会被include $(CLEAR_VARS)命令来清除,你会在你的很多module中使用这些LOCAL_开头的变量。PRIVATE_XXX变量:这些变量是make-target-specific(编译具体目标)的变量。INTERNAL_XXX变量:这些变量是编译系统所使用的变量,所以你最好不要将你的变量用此来命名,而且最好也不要在你的makefile中见到这些变量。HOST_XXX变量和TARGET_XXX变量:这些变量包含或者定义了一些特定的host或者target编译。所以不要在你的makefile中设置以开头HOST_和TARGET_的变量。BUILD_XXX变量和CLEAR_VARS:这些变量都包含在那些清晰的makefile模板中了,比如CLEAR_VARS和BUILD_HOST_PACKAGE。当然,你还可以在你的Android.mk文件中使用其它你所需要的命名变量。但是,要记住的是Android是一个非递归的编译系统,所以很有可能,你的变量可能会被其它的Android.mk改变,当你的module执行命令是,这些变量可能已经不同了。说明:为保证可参考性,也将英语原文记录如下。But first, a note on variable naming:LOCAL_ These variables are set per-module. They are cleared by the include $(CLEAR_VARS) line, so you can rely on them being empty after including that file. Most of the variables you'll use in most modules are LOCAL_ variables.PRIVATE_ These variables are make-target-specific variables. That means they're only usable within the commands for that module. It also means that they're unlikely to change behind your back from modules that are included after yours. This link to the make documentation describes more about target-specific variables. Please note that there are a couple of these laying around the tree that aren't prefixed with PRIVATE_. It is safe, and they will be fixed as they are discovered. Sorry for the confusion.INTERNAL_ These variables are critical to functioning of the build system, so you shouldn't create variables named like this, and you probably shouldn't be messing with these variables in your makefiles.HOST_ and TARGET_ These contain the directories and definitions that are specific to either the host or the target builds. Do not set variables that start with HOST_ or TARGET_ in your makefiles.BUILD_ and CLEAR_VARS These contain the names of well-defined template makefiles to include. Some examples are CLEAR_VARS and BUILD_HOST_PACKAGE.Any other name is fair-game for you to use in your Android.mk. However, remember that this is a non-recursive build system, so it is possible that your variable will be changed by another Android.mk included later, and be different when the commands for your rule / module are executed下面对Android.mk文件中涉及到的变量做详细说明:LOCAL_XXX变量LOCAL_XXX变量用于向编译系统描述你的模块中所使用的变量,你应该在include $(CLEAR_VARS)和include $(BUILD_XXXXX)语句之间来定义你想要使用的变量。下面来详细说明以LOCAL_开头的变量:LOCAL_ASSET_FILESLOCAL_ASSET_FILES在Android.mk文件中编译应用程序(BUILD_PACKAGE)时设置此变量,表示引用资源文件,通常会定义成:LOCAL_ASSET_FILES += $(call find-subdir-assets)说明:为保证可参考性,也将英语原文记录如下(下同,不再说明)。In Android.mk files that include $(BUILD_PACKAGE) set this to the set of files you want built into your app. Usually:LOCAL_ASSET_FILES += $(call find-subdir-assets)This will probably change when we switch to ant for the apps' build system.LOCAL_CC如果你想在你的module中使用不同的C编译器,可以设置这个变量。如果LOCAL_CC是空的,它就使用默认的编译器。If you want to use a different C compiler for this module, set LOCAL_CC to the path to the compiler. If LOCAL_CC is blank, the appropriate default compiler is used.LOCAL_CXX如果你想在你的module中使用不同的C++编译器,可以设置这个变量。如果LOCAL_CXX是空的,它就使用默认的编译器。If you want to use a different C++ compiler for this module, set LOCAL_CXX to the path to the compiler. If LOCAL_CXX is blank, the appropriate default compiler is used.LOCAL_CFLAGSLOCAL_CFLAGS变量为C/C++编译器定义额外的标志,当编译C/C++源文件时传递一个可选的编译器标志,这对于指定额外的宏定义或编译选项很有用。例如:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1If you have additional flags to pass into the C or C++ compiler, add them here. For example:LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1重要提示:尽量不要改变Android.mk中的优化/调试级别,这个可以通过在Application.mk中设置相应的信息来自动为你处理,并且会会让NDK生成在调试过程中使用的有用的数据文件。注意:在Android-ndk-1.5_r1中,只使用于C源文件,而不适用于C++源文件。在匹配所有Android build system的行为已经得到了纠正。(现在你可以为C++源文件使用LOCAL_CPPFLAGS来指定标志)它可以用LOCAL_CFLAGS += -I<path>来指定额外的包含路径,然而,如果使用LOCAL_C_INCLUDES会更好,因为用ndk-gdk进行本地调试的时候,那些路径依然是需要使用的。LOCAL_CPPFLAGSLOCAL_CPPFLAGS变量和LOCAL_CFLAGS变量类似,如果你只想要增加一些标记(flag)在你的C++编译器中,使用LOCAL_CPPFLAGS变量来增加它们,例如:LOCAL_CPPFLAGS += -ffriend-injectionLOCAL_CPPFLAGS必须在LOCAL_CFLAGS变量命令行的后面使用,所以你可以用它来重写你在LOCAL_CFLAGS定义的标记。If you have additional flags to pass into only the C++ compiler, add them here. For example:LOCAL_CPPFLAGS += -ffriend-injectionLOCAL_CPPFLAGS is guaranteed to be after LOCAL_CFLAGS on the compile line, so you can use it to override flags listed in LOCAL_CFLAGS.注意:在Android NDK-1.5_r1版本中,相应的标志可以应用于C或C++源文件上。在配合完整的Android build system的时候,这已经得到了纠正(你可以使用LOCAL_CFLAGS去指定C或C++源文件)。LOCAL_CPP_EXTENSION如果你的C++文件不是以cpp为文件后缀,通过LOCAL_CPP_EXTENSION指定C++文件后缀名例如:LOCAL_CPP_EXTENSION := .cc需要注意的是在module中给出的所有的C++文件必须具有相同的扩展名,它是不允许混合使用不同扩展名的。If your C++ files end in something other than ".cpp", you can specify the custom extension here. For example:LOCAL_CPP_EXTENSION := .ccNote that all C++ files for a given module must have the same extension; it is not currently possible to mix different extensions.注意:统一模块中C++文件后缀必须保持一致。LOCAL_C_INCLUDESLOCAL_C_INCLUDES变量可以指定额外的目录来指引C/C++编译器来寻找头文件。这些路径必须是最顶端的目录路径,使用LOCAL_PATH来包含你的子目录路径下的文件,例如:LOCAL_C_INCLUDES += extlibs/zlib-1.2.3LOCAL_C_INCLUDES += $(LOCAL_PATH)/src注意:如果你在代码(main.c)中引用了一些头文件,而在编译时如果找不到这些头文件,就会报如下图的错误:

所以要确保你所包含的路径目录下,有你所需要的头文件,以避免编译时出现错误。Additional directories to instruct the C/C++ compilers to look for header files in. These paths are rooted at the top of the tree. Use LOCAL_PATH if you have subdirectories of your own that you want in the include paths. For example:LOCAL_C_INCLUDES += extlibs/zlib-1.2.3LOCAL_C_INCLUDES += $(LOCAL_PATH)/srcYou should not add subdirectories of include to LOCAL_C_INCLUDES, instead you should reference those files in the #include statement with their subdirectories. For example:#include <utils/KeyedVector.h>not #include <KeyedVector.h>There are some components that are doing this wrong, and should be cleaned up.补充1:如果在你的头文件目录include下有以下*.h文件:/include/ utils/ KeyedVector.h log.h你不应该在LOCAL_C_INCLUDES变量中包含子目录,相反,你应该在使用#include来声明这些文件的引用,以及它们的子目录路径。例如:#include <utils/KeyedVector.h>而不是:#include <KeyedVector.h>如果你想引用log.h文件,那么你应该这么写:#include <log.h>补充2:如果你在编译JNI时,在你的JNI代码中要引用jni.h时,既当你的代码(helloneon.c)中写道:#include <jni.h>如果你没有在该目录下的Android.mk中定义:# Also need the JNI headersLOCAL_C_INCLUDES += $(JNI_H_INCLUDE)那么就会在编译时报如下图所示的错误:

如果你能预先定义该变量的值,那么就不会出现上述的错误。LOCAL_MODULE_TAGS可以用空白的空格来分开一些标签,可以设置LOCAL_MODULE_TAGS,如果这个标签列表是空的或者包含有droid,这个module就会当作droid来编译,否则,它只能用make <your-module>来编译和安装你的module,或者使用make allSet LOCAL_MODULE_TAGS to any number of whitespace-separated tags. If the tag list is empty or contains droid, the module will get installed as part of a make droid. Otherwise, it will only get installed by running make <your-module>or with the make allpseudotarget.补充:LOCAL_MODULE_TAGS:模块标记,一般的取值范围为debug、eng、test、optional,如果不定义则默认为optional。对这几个模式的解释为:user:指该模块只在user版本下才编译;eng:指该模块只在eng版本下才编译;tests:指该模块只在tests版本下才编译;optional:指该模块在所有版本下都编译。LOCAL_REQUIRED_MODULES可以用空格来分开不同的module的名字,以用来设置LOCAL_REQUIRED_MODULES,像libblah或者Email。如果安装了这个module,那么同时也会安装这个module所必需的模块。确保所需要的共享库和必须已经安装好,当所给的程序安装时。Set LOCAL_REQUIRED_MODULES to any number of whitespace-separated module names, like "libblah" or "Email". If this module is installed, all of the modules that it requires will be installed as well. This can be used to, e.g., ensure that necessary shared libraries or providers are installed when a given app is installed.LOCAL_FORCE_STATIC_EXECUTABLE如果编译的可执行程序要进行静态链接(执行时不依赖于任何动态库),则设置:LOCAL_FORCE_STATIC_EXECUTABLE:=true目前只有libc有静态库形式,这个只有文件系统中/sbin目录下的应用程序会用到,这个目录下的应用程序在运行时通常文件系统的其它部分还没有加载,所以必须进行静态链接。If your executable should be linked statically, setLOCAL_FORCE_STATIC_EXECUTABLE:=true.There is a very short list of libraries that we have in static form (currently only libc). This is really only used for executables in /sbin on the root filesystem.LOCAL_JAVA_LIBRARIESLOCAL_JAVA_LIBRARIES编译java应用程序和库的时候指定包含的java类库,目前有core和framework两种情况下定义成:注意:LOCAL_JAVA_LIBRARIES不是必须的,而且编译APK时不允许定义(系统会自动添加)When linking Java apps and libraries, LOCAL_JAVA_LIBRARIES specifies which sets of java classes to include. Currently there are two of these: core and framework. In most cases, it will look like this:LOCAL_JAVA_LIBRARIES := core frameworkNote that setting LOCAL_JAVA_LIBRARIES is not necessary (and is not allowed) when building an APK with include $(BUILD_PACKAGE). The appropriate libraries will be included automatically.LOCAL_LDFLAGS你可以通过设置LOCAL_LDFLAGS来增加额外的标记传递给连接器。不过要记住,这些参数对ld来说是非常重要的,所以你最好在所有的平台都测试下。You can pass additional flags to the linker by setting LOCAL_LDFLAGS. Keep in mind that the order of parameters is very important to ld, so test whatever you do on all platforms.LOCAL_LDLIBSLOCAL_LDLIBS允许你在你编译你的可执行程序或者库的时候,添加一些指定的额外的库。用lxxx的格式来指定你要引用的库,它们会被连接命令行直接解析。然而,要知道它不会为这些库生成附属。这在使用模拟器编译而又想使用库预编译在主机上时是非常有用的。例如:LOCAL_LDLIBS += -lcurses -lpthreadLOCAL_LDLIBS += -Wl,-z,originLOCAL_LDLIBS allows you to specify additional libraries that are not part of the build for your executable or library. Specify the libraries you want in -lxxx format; they're passed directly to the link line. However, keep in mind that there will be no dependency generated for these libraries. It's most useful in simulator builds where you want to use a library preinstalled on the host. The linker (ld) is a particularly fussy beast, so it's sometimes necessary to pass other flags here if you're doing something sneaky. Some examples:LOCAL_LDLIBS += -lcurses -lpthreadLOCAL_LDLIBS += -Wl,-z,origin补充1:LOCAL_LDLIBS:生成你的模块时用到的额外的连接器标记(linkerflags)的名单,在传递有“-l”前缀的特殊系统库的名称时很有用。例如:LOCAL_LDLIBS := -labc上面的语句会告诉连接器在load time时生成连接到/system/lib/目录下名字叫做libabc.so的动态库。补充2:如果在你的Android.mk代码中定义了LOCAL_LDLIBS变量,例如:LOCAL_LDLIBS := -lGLESv1_CM -ldl -llog但是,如果在编译的过程中找不到这个库,或者说这个库并没有存在与你编译环境下,那么在编译的时候就会出现如下图所示的错误:所以,在对LOCAL_LDLIBS变量赋值时,要确保其正确性以及存在性,避免出现上图的编译错误。LOCAL_NO_MANIFEST如果你的Package没有Manifest(AndroidManifest.xml),你可以设置LOCAL_NO_MANIFEST:=true.If your package doesn't have a manifest (AndroidManifest.xml), then setLOCAL_NO_MANIFEST:=true.The common resources package does this.LOCAL_PACKAGE_NAMELOCAL_PACKAGE_NAME变量是一个App的名字,例如:Dialer、Contacts等等。它可能在我们使用ant编译系统编译App时会发生改变。LOCAL_PACKAGE_NAME is the name of an app. For example, Dialer, Contacts, etc. This will probably change or go away when we switch to an ant-based build system for the apps.LOCAL_PATHLOCAL_PATH := $(call my-dir):每个Android.mk文件都必须以定义LOCAL_PATH变量开始,其目的是为了定位源文件的位置。例如:LOCAL_PATH := $(my-dir)my-dir宏函数使用的是MAKEFILE_LIST变量,你必须在include其它任何makefile之前来调用它。另外,考虑到当你include任何子目录时都要重新设置LOCAL_PATH,你必须在include它们之前设置它。The directory your Android.mk file is in. You can set it by putting the following as the first line in your Android.mk:LOCAL_PATH := $(my-dir)The my-dir macro uses the MAKEFILE_LIST variable, so you must call it before you include any other makefiles. Also, consider that any subdirectories you inlcude might reset LOCAL_PATH, so do your own stuff before you include them. This also means that if you try to write several include lines that reference LOCAL_PATH, it won't work, because those included makefiles might reset LOCAL_PATH.LOCAL_PREBUILT_EXECUTABLESLOCAL_PREBUILT_EXECUTABLES预编译including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)时所用,指定需要复制的可执行文件。When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to executables that you want copied. They're located automatically into the right bin directory.LOCAL_PREBUILT_LIBSLOCAL_PREBUILT_LIBS变量是在预编译including $(BUILD_PREBUILT)或者$(BUILD_HOST_PREBUILT)时所用, 指定需要复制的库.When including $(BUILD_PREBUILT) or $(BUILD_HOST_PREBUILT), set these to libraries that you want copied. They're located automatically into the right lib directory.LOCAL_SHARED_LIBRARIESLOCAL_SHARED_LIBRARIES变量用来列出模块所需的共享库的列表,不需要加上.so后缀。例如:LOCAL_SHARED_LIBRARIES := / libutils / libui / libaudio / libexpat / libsglThese are the libraries you directly link against. You don't need to pass transitively included libraries. Specify the name without the suffix:LOCAL_SHARED_LIBRARIES := libutils libui libaudio libexpat libsglLOCAL_SRC_FILESLOCAL_SRC_FILES变量必须包含一系列将被构建和组合成模块的C/C++源文件。注意:不需要列出头文件或include文件,因为生成系统会为你自动计算出源文件的依赖关系。默认的C++源文件的扩展名是.cpp,但你可以通过定义LOCAL_DEFAULT_EXTENSION来指定一个扩展名。The build system looks at LOCAL_SRC_FILES to know what source files to compile -- .cpp .c .y .l .java. For lex and yacc files, it knows how to correctly do the intermediate .h and .c/.cpp files automatically. If the files are in a subdirectory of the one containing the Android.mk, prefix them with the directory name:LOCAL_SRC_FILES := file1.cpp dir/file2.cppLOCAL_STATIC_LIBRARIESLOCAL_STATIC_LIBRARIES变量和LOCAL_SHARED_LIBRARIES类似,用来列出你的模块中所需的静态库的列表,你可以在你的module中包含一些想使用的静态库,通常我们使用共享库,但是有些地方,像在sbin下的可执行程序和主机上的可执行程序我们要使用静态库。例如:LOCAL_STATIC_LIBRARIES := / libutils / libtinyxmlThese are the static libraries that you want to include in your module. Mostly, we use shared libraries, but there are a couple of places, like executables in sbin and host executables where we use static libraries instead.LOCAL_STATIC_LIBRARIES := libutils libtinyxmlLOCAL_MODULELOCAL_MODULE变量必须定义,用来标识在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。如果有其它moudle中已经定义了该名称,那么你在编译时就会报类似这样的错误:libgl2jni already defined by frameworks/base/opengl/tests/gl2_jni/jni. Stop.下面就是该错误的截图:

接下来就是修改你的module的名字了,或者找到跟你重名的module把它干掉,但不建议你那么做,因为有可能会带来未知的错误(你修改了别人的module的名字,而别人不一定知道,当他再编译或者做其它时,就会出错)。LOCAL_MODULE is the name of what's supposed to be generated from your Android.mk. For exmample, for libkjs, the LOCAL_MODULE is "libkjs" (the build system adds the appropriate suffix -- .so .dylib .dll).注意:编译系统会自动产生合适的前缀和后缀,例如:LOCAL_MODULE := screenshot一个被命名为“screenshot”的共享库模块,将会生成“libscreenshot.so”文件。补充1:变量命名的规范性如果LOCAL_MODULE变量定义的值可能会被其它module调用时,就要考虑为其变量命名的规范性了。特别是在使用JNI时,既在LOCAL_JNI_SHARED_LIBRARIES变量中定义的值,最好要和LOCAL_MODULE变量定义的值保存一致(具体请参考LOCAL_JNI_SHARED_LIBRARIES变量的使用说明)。这时的LOCAL_MODULE变量的命名最好以lib开头,既libxxx,例如:LOCAL_MODULE := libscreenshotLOCAL_MODULE_PATH通知编译系统将module放到其它地方而不是它通常的类型。如果你重写这个变量,确保你还要再设置LOCAL_UNSTRIPPED_PATH变量的值。如果你忘了设置LOCAL_UNSTRIPPED_PATH变量的值的话,就会报错。Instructs the build system to put the module somewhere other than what's normal for its type. If you override this, make sure you also set LOCAL_UNSTRIPPED_PATH if it's an executable or a shared library so the unstripped binary has somewhere to go. An error will occur if you forget to.LOCAL_WHOLE_STATIC_LIBRARIESLOCAL_WHOLE_STATIC_LIBRARIES 指定模块所需要载入的完整静态库(这些静态库在链接是不允许链接器删除其中无用的代码)。通常这在你想往共享库中增加一个静态库时是非常有用的,共享库就会接受到静态库暴露出的content,例如:LOCAL_WHOLE_STATIC_LIBRARIES := / libsqlite3_androidThese are the static libraries that you want to include in your module without allowing the linker to remove dead code from them. This is mostly useful if you want to add a static library to a shared library and have the static library's content exposed from the shared library.LOCAL_WHOLE_STATIC_LIBRARIES := libsqlite3_androidLOCAL_REQUIRED_MODULESLOCAL_REQUIRED_MODULES 指定模块运行所依赖的模块(模块安装时将会同步安装它所依赖的模块)Set LOCAL_REQUIRED_MODULES to any number of whitespace-separated module names, like "libblah" or "Email". If this module is installed, all of the modules that it requires will be installed as well. This can be used to, e.g., ensure that necessary shared libraries or providers are installed when a given app is installed.LOCAL_PRELINK_MODULELOCAL_PRELINK_MODULE变量用来规定是否需要预连接处理(默认需要,用来做动态库优化)。LOCAL_PRELINK_MODULE只有在编译.so的时候才会有的选项,主要是通过预链接的方式来加快程序启动和执行的速度,如果在你的代码(/jni/Android.mk)中有下面一条语句:LOCAL_PRELINK_MODULE := true那么你要在build/core/prelink-linux-arm.map中定义你的库所需要使用的空间,如果不定义或者空间不够的话,在编译的时候就会报错。如下图所示:

当在build/core/prelink-linux-arm.map中定义了我们这里使用的libhello-jni.so库的空间之后,既在该文件中加入一条语句:libhello-jni.so 0x99E00000注意:在prelink-linux-arm.map文件的开头部分有明确的规定,指定的内存取值范围分配给不同的部分使用,而我们的App的库也给指定了一个范围:0x90000000 - 0x9FFFFFFF Prelinked App Libraries重新编译,就不会再报错了,下面的截图中很清晰地看到已经将libhello-jni.so库预编译成功了:

注意:在给我们的应用库分配地址空间时,最好以1M为边界,地址空间大小按照由大到小的降序进行排序。下面是对于Prelink的说明:Prelink利用事先链接代替运行时链接的方法来加速共享库的加载,它不仅可以加快起动速度,还可以减少部分内存开销。程序运行时的动态链接尤其是重定位(relocation)的开销对于大型系统来说是很大的。动态链接和加载的过程开销很大,并且在大多数的系统上,函数库并不会常常被更动,每次程序被执行时所进行的链接动作都是完全相同的,对于嵌入式系统来说尤其如此。因此,这一过程可以改在运行时之前就可以预先处理好,即花一些时间利用Prelink工具对动态共享库和可执行文件进行处理,修改这些二进制文件并加入相应的重定位等信息,节约了本来在程序启动时的比较耗时的查询函数地址等工作,这样可以减少程序启动的时间,同时也减少了内存的耗用。Prelink的这种做法当然也有代价的,每次更新动态共享库时,相关的可执行文件都需要重新执行一遍Prelink才能保证有效,因为新的共享库中的符号信息、地址等很可能与原来的已经不同了,这就是为什么android framework代码一改动,这时候就会导致相关的应用程序重新被编译。LOCAL_EXPORT_CFLAGSLOCAL_JNI_SHARED_LIBRARIES变量主要是用在JNI的编译中,如果你要在你的Java代码中引用JNI中的共享库*.so,此变量就是共享库的名字。那么你要注意的一点是:在你的Project根目录下的Android.mk中要定义此变量用来引用你要使用的JNI中的共享库*.so。例如:$(Project)/Android.mkLOCAL_JNI_SHARED_LIBRARIES := libsanangeles而在你的jni目录下的Android.mk中则要定义LOCAL_MODULE变量的值,一定要让这两个变量的值相同。假如你没有这么做,而是像这样:$(Project)/jni/Android.mkLOCAL_MODULE := sanangeles那么,在编译的时候就会出现下图的错误:

这说明在编译libsanangeles.so找不到其规则,因为在上面的代码中定义的是sanangeles。重新修改LOCAL_MODULE变量的值:$(Project)/jni/Android.mkLOCAL_MODULE := libsanangeles即可正常编译。LOCAL_EXPORT_CPPFLAGS定义这个变量用来记录C/C++编译器标志集合,并且会被添加到其他任何以LOCAL_STATIC_LIBRARIES和LOCAL_SHARED_LIBRARIES的模块的LOCAL_CFLAGS定义中。例如:这样定义"foo"模块:# foo/Android.mkinclude $(CLEAR_VARS)LOCAL_MODULE :=fooLOCAL_SRC_FILES :=foo/foo.cLOCAL_EXPORT_CFLAGS :=-DFOO=1include $(BUILD_STATIC_LIBRARY)另一个模块,叫做"bar",并且依赖于上面的模块:# bar/Android.mkinclude $(CLEAR_VARS)LOCAL_MODULE :=barLOCAL_SRC_FILES :=bar.cLOCAL_CFLAGS:=-DBAR=2LOCAL_STATIC_LIBRARIES:=fooinclude $(BUILD_SHARED_LIBRARY)然后,当编译bar.c的时候,标志"-DFOO=1 -DBAR=2"将被传递到编译器。输出的标志被添加到模块的LOCAL_CFLAGS上,所以你可以很容易重写它们。它们也有传递性:如果"zoo"依赖"bar",“bar”依赖"foo",那么"zoo"也将继承"foo"输出的所有标志。最后,当编译模块输出标志的时候,这些标志并不会被使用。在上面的例子中,当编译foo/foo.c时,-DFOO=1将不会被传递给编译器。LOCAL_EXPORT_C_INCLUDES类似LOCAL_EXPORT_CFLAGS,但适用于C++标志。具体请参考LOCAL_EXPORT_CFLAGS条目。LOCAL_EXPORT_LDLIBS类似于LOCAL_EXPORT_CFLAGS,但是只用于链接标志。注意,引入的链接标志将会被追加到模块的LOCAL_LDLIBS,这是由UNIX连接器的工作方式决定的。当模块foo是一个静态库的时候并且代码依赖于系统库时会很有用的。LOCAL_EXPORT_LDLIBS可以用于输出依赖,例如:# Frist build the static library libfoo.ainclude $(CLEAR_VARS)LOCAL_MODULE := fooLOCAL_SRC_FILES := foo/foo.cLOCAL_EXPORT_LDLIBS := -lloginclude $(BUILD_STATIC_LIBRARY)# Then build the shared library libbar.soinclude $(CLEAR_VARS)LOCAL_MODULE := barLOCAL_SRC_FILES := bar.cLOCAL_STATIC_LIBRARIES := fooinclude $(BUILD_SHARED_LIBRARY)这里,在连接器命令最后,libbar.so将以”-llog”参数进行编译来表明它依赖于系统日志库,因为它依赖于foo。LOCAL_ALLOW_UNDEFINED_SYMBOLS默认情况下,当试图编译一个共享库的时候遇到任何未定义的引用都可能导致"未定义符号"(undefined symbol)的错误。这在你的源代码中捕获bug会很有用。然而,但是由于某些原因,你需要禁用此检查的话,设置变量为"true"即可。需要注意的是,相应的共享库在运行时可能加载失败。LOCAL_ARM_MODELOCAL_ARM_MODE变量主要是应用与嵌入式产品的编译系统中,可以指定为arm模式。例如:LOCAL_ARM_MODE := arm注意:你需要执行编译系统为在ARM模式下通过文件的名字增加后缀的方式编译指定的源文件。例如:LOCAL_SRC_FILES :=foo.c bar.c.arm这会告诉编译系统一直以ARM模式编译"bar.c",并且通过LOCAL_ARM_MODE的值编译foo.c。BUILD_XXX变量BUILD_SHARED_LIBRARYBUILD_SHARED_LIBRARY:指明要编译生成动态共享库。指向一个生成脚本,这个脚本通过LOCAL_XXX变量收集关于组件的信息,并决定如何根据你列出来的源文件生成目标共享库。注意:在include这个脚本文件之前你必须至少已经定义了LOCAL_MODULE和LOCAL_SRC_FILES。例如:include $(BUILD_SHARED_LIBRARY)注意:这会生成一个名为lib$(LOCAL_MODULE).so的动态库。BUILD_STATIC_LIBRARYBUILD_STATIC_LIBRARY与BUILD_SHARED_LIBRARY类似,但用来生成目标静态库。静态库不会被拷贝至你的project/packages文件夹下,但可用来生成共享库。例如:include $(BUILD_STATIC_LIBRARY)注意:这会生成一个静态库,名叫lib$(LOCAL_MODULE).a的静态库。BUILD_PACKAGEBUILD_PACKAGE变量用于在最好编译时生成*.apk,例如:include $(BUILD_STATIC_LIBRARY)注意:这会生成一个apk安装包,名字就叫$(LOCAL_MODULE).apk的安装包。其它变量CLEAR_VARSCLEAR_VARS变量是生成系统提供的,它指向一个特殊的GNU Makefile,它将会为你自动清除许多名为LOCAL_XXX的变量(比如:LOCAL_MODULE、LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES等),但LOCAL_PATH是例外,它不会被清除。注意:这些变量的清除是必须的,因为所有的控制文件是在单一的Makefile,执行环境中解析的,在这里所有的变量都是全局的。TARGET_PLATFORMTARGET_PLATFORM:当解析该Android.mk文件时用它来指定Andoid目标平台的名称。例如:android-3与Android 1.5相对应。NDK提供的宏函数下面是GNU Make的宏函数,必须通过这样的形式调用:$(call <function>)my-dirmy-dir:返回放置当前Android.mk的文件夹相对于NDK生成系统根目录的路径。可用来在Android.mk的开始处定义LOCAL_PATH的值:LOCAL_PATH := $(call my-dir)all-subdir-makefilesall-subdir-makefiles:返回my-dir子目录下的所有Android.mk。例如:代码的结构如下:sources/foo/Android.mksources/foo/lib1/Android.mksources/foo/lib2/Android.mk如果sources/foo/Android.mk里有这样一行:include $(call all-subdir-makefiles)那么,它将会自动地包含sources/foo/lib1/Android.mk和sources/foo/lib2/Android.mk。这个函数能将深层嵌套的代码文件夹提供给生成系统。注意:默认情况下,NDK仅在source/*/Android.mk里寻找文件。this-makefilethis-makefile:返回当前Makefile所在目录的路径。parent-makefileparent-makefile:返回父Makefile所在目录makefile的路径。import-module一个允许你通过名字找到并包含另一个模块的的Android.mk的功能,例如:$(call import-module,<name>)这将会找到通过NDK_MODULE_PATH环境变量引用的模块<name>的目录列表,并且将其自动包含到Android.mk中。Application.mk作用Application.mk目的是描述在你的应用程序中所需要的模块(即静态库或动态库)。Application.mk文件通常被放置在$PROJECT/jni/Application.mk下,$PROJECT指的是您的项目。另一种方法是将其放在顶层的子目录下,既$NDK/apps目录下,例如:$NDK/apps/<myapp>/Application.mk<myapp>是一个简称,用于描述你的NDK编译系统的应用程序(这个名字不会生成共享库或者最终的包),这个方法是Android NDK r4以前的,现在仍然兼容。但是我们强烈建议你使用第一种方法,因为它更简单并且不用修改NDK安装树的目录。详细说明下面是Application.mk中定义的几个变量:APP_MODULESAPP_MODULES 变量是强制性的,并且会列出所有你所需要的模块。它不允许用一个空格来分隔其模块列表,这个模块名字被定义在Android.mk文件中的LOCAL_MODULE中。APP_PROJECT_PATHAPP_PROJECT_PATH变量也是强制性的,并且会给出应用程序工程的根目录一个绝对路径。这是用来复制或者安装一个没有任何版本限制的JNI库,从而给 APK 生成工具一个详细的路径。例如:# \HelloNDK\Application.mkAPP_PROJECT_PATH := $(call my-dir)/projectAPP_MODULES := HelloNdk这里定义了工程路径为$(call my-dir)/project,而要编译的模块则是HelloNdk,这样编译系统才会找到我们要编译的库和源文件。APP_CFLAGSAPP_CFLAGS则是当要编译模块中有任何C文件或者C++文件的时候,C编译器的信号就会被发出。这里可以在你的应用中需要这些模块时,进行编译的调整,这样就不许要直接更改Android.mk为文件本身了。APP_OPTIM这个变量是可选的,可以定义成两个值release或者debug,用于修改编译程序模块时的优化层级。release模式是默认的,会产生高优化的文件,debug模式会生成不优化的文件,使得调试更容易进行。注意:调试release和debug文件都是可能的,但是release版在调试节提高的信息很少,一些变量被优化输出,无法检查,代码被重排序,使得跟踪代码很困难,堆栈追踪也不可靠,等等。APP_CPPFLAGS当编译的只有C++源文件的时候,可以通过这个C++编译器来设置。注意:在Android NDK-1.5_r1中,这个标志可以应用于C和C++源文件中。并且得到了纠正,以建立完整的与系统相匹配的Android编译系统。你先可也可以使用APP_CFLAGS来应用于C或者C++源文件中。建议使用APP_CFLAGS。补充两种不同级别的应用apk目前我所理解是在Android开发中我们会遇到两种不同级别的应用apk:系统级应用apk和普通级应用apk。下面分别描述两种apk:编译系统级应用apk将应用程序的代码放到武当源代码目录路径下,然后进行编译。将编译生成的*.apk通过adb或者其它方式放到/system/app目录下即可。编译普通级应用apk应用程序的代码并没有放到平台的源代码目录下,然后通过编译生成的*.apk通过adb install的方式放到/data/app目录下,就是普通级的apk。AOSP Mac平台编译前言编译Android源码是一个相对容易的事件,有存储空间和合适的编译环境即可。最近想查看一些应用的布局(Layout), 需要修改Android系统的debuggable属性,这个操作需要设备具有root权限,想着自己有一台pixel XL,何不编译个userdebug版本的rom,除了精简的的应用外,debuggable和root属性也是直接含有的,后续有其他地方调整的话,可以直接修改源码编译。准备鉴于Android设备为pixel XL,查找到最新的rom版本为android-10.0.0_r17,开始初始化代码环境:下载repo工具mkdir ~/binPATH=~/bin:$PATHcurl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repochmod a+x ~/bin/repo这里有一点需要注意,如果需要外置硬盘存放源码,请不要使用exfat格式,因为exfat不支持软连接(文章地址),尽量使用hfs和apfs,同时选择区分大小写的磁盘格式。更新的初始化包使用初始化包更加方便,减少同步中因为网络问题引起的中断wget -c https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/aosp-latest.tar # 下载初始化包tar xf aosp-latest.tar # 解压缩下载初始化包过程大概持续了大约3h,网速基本上维持在10M/s左右。同步对应分支tag代码cd AOSP # 解压得到的 AOSP 工程目录~/bin/repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-10.0.0_r17# 这时 ls 的话什么也看不到,因为只有一个隐藏的 .repo 目录~/bin/repo sync # 正常同步一遍即可得到完整目录设备驱动下载设备驱动,执行对应文件后,将生成的目录到根目录( AOSP)即可。https://developers.google.com/android/drivers#marlinqp1a.191005.007.a3源码调整为了能够在mac平台上编译,需要调整部分配置:系统版本添加当前系统支持,笔者电脑升级到了Mac OS Catalina 10.15,添加了10.15版本。修改文件: ./build/soong/cc/config/x86_darwin_host.go, diffdarwinSupportedSdkVersions = []string{ "10.10", "10.11", "10.12", "10.13", "10.14", "10.15", // 增加该行 }Segmentation fault修改文件: ./system/sepolicy/tests/Android.bp, diffstl: "libc++_static", // 去除该行编译source ./build/envsetup.sh # 初始化环境变量lunch aosp_marlin-userdebug # 编译pixel XL对应rom, pixel XL对应设备内部名为marlinmake -j8 # 开始编译在2015款mbp上大概持续了6个小时烧入#!/bin/bashadb reboot bootloaderfastboot flash boot boot.imgfastboot flash system system.imgfastboot flash userdata userdata.imgfastboot flash ramdisk ramdisk.imgfastboot flash vendor vendor.imgfastboot erase cachefastboot reboot小结之前编译aosp基本上都在ubuntu系统上,最新的代码对mac已经做了更好的适配,可以方便的编译出系统镜像。

(0)

相关推荐