2021年9月

转自: https://blog.csdn.net/qq_28193019/article/details/91050915

生成TAG

logt+Tab键:

private static final String TAG = "Extract";

生成Log.d()

logd+Tab键:

Log.d(TAG, "onCreate: ");

生成Log.e()

loge+Tab键:

Log.e(TAG, "onCreate: ",new Throwable());

生成Log.i/w类似

Log.i(TAG, "onCreate: ");
Log.w(TAG, "onCreate: ", );

logm+Tab:打印参数

Log.d(TAG, "onCreate() called with: savedInstanceState = [" + savedInstanceState + "]");

logr+Tab:打印返回值

Log.d(TAG, "onCreate() returned: " + );

查看日志输出

在Android Studio Logcat选项卡,中可以选择过滤的级别,来过滤日志输出,比如Verbose、Debug、Error等,上面的选项(如Debug)会包含下面选项(如Error)的日志

当系统中缺少某个特定版本的 libc 的时候,可以在编译的时候,添加 -nostdlib 选项,并在后面附上指定的库即可。类似于

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/lib/libc.so

当c文件编译的时候,找不到 log 方面的头文件和库的时候,可以手动添加,类似下面这样

arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so -I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/lib/libc.so -I /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/include /work/android-5.0.2/prebuilts/ndk/9/platform/android-19/arch-arm/usr/lib/liblog.so

硬件服务中,xxx_service.cpp, hal_xxx.c 的关系

com_android_server_LedService.cpp 这样的文件是 jni 源码,主要做的是向系统注册的事情。hal_led.c 这样的是实际的对硬件进行操作的源码,比如说 read, write, ioctrl 等等。 之所以分开来主要考虑两个原因。

  • com_android_server_LedService.cpp 这样的文件如果有变更,那么需要系统重新编译。hal_led.c这样的正常时编译为 so 库,所以可以不用系统重新编译。
  • 有些厂商为了保密的需要,不提供 hal_led.c 这样的源码,只是提供一个 so 的库文件。
  • onload.cpp 调用 com_android_server_LedService.cpp 中实现的函数
  • SystemServer.java 需要 new LedService 然后再 addService
  • LedService.java 里面调用 native 方法
  • ILedService.java 给 app 来使用。

ILedService.java

  1. 仿照其他的 aidl 文件,比如说 vibr 的 aidl 文件,复制为ILedService.aidl,然后修改一下接口。
  2. 查看 vibr aidl 文件所在的目录,放到类似的目录
  3. 从当前目录开始查看有没有 Android.mk,如果没有,就向上一层继续查找,知道找到为止。
  4. Android.mk 中仿照 vibr aidl,把ILedService.aidl加入进去。
  5. 使用 mmm . 进行编译。
  6. 到 out 目录下找找有没有 ILedService.java 生成
  7. 可以估计 app 中对 led 的用法是
    ILedService iLedService;
    iLedService = ILedService.stub.asInterface(ServiceManager.getService("led"));
    iLedService.ledCtrl(0, 1);

参考 VibratorService.java 来写 LedService.java.

  1. LedService 继承了 ILedService.Stub, 到 ILedService.javaStub 申明实现的接口 android.os.ILedService , android.os.ILedService 这个里面自动生成的需要实现的只有 ledCtrl 这个函数。
  2. LedService.java 中实现 ledCtrl,这个函数中直接调用 native 方法即可。别忘了申明 native 方法
  3. LedService.java 中的构造函数中也需要调用 open 的 native 方法。别忘了申明 native 方法
  4. SystemServer.java 中,找到 vibr 使用 ServiceManager.addService 的地方,仿照写个 led 的添加服务的调用。

com_android_server_LedService.cpp

参考 com_android_server_VibratorService.cpp 来写。

Onload.cpp

找到 register_android_server_VibratorService(env) 这个注册的地方,复制一份,改为 register_android_server_LedService(env). 别忘了添加这个函数相应的申明。

保存 mmm 编译时的指令

mmm frameworks/base show commands >log.txt 2>&1

在 app 中直接 import android.os.ILedService 会找不到

  1. 可以参考: https://stackoverflow.com/questions/7888191/how-do-i-build-the-android-sdk-with-hidden-and-internal-apis-available ,把 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar 这个复制出来,并改个名字。
  2. 可以参考 https://stackoverflow.com/questions/16608135/android-studio-add-jar-as-library 中的在 android studio 中添加 jar 文件。

led_hal.c

参考 hardware/libhardware/modules/vibrator/vibrator.chardware.h 来写 led_hal.c

编译

上面这些修改好了之后,可以使用命令进行编译。

mmm frameworks/base/services
mmm hardware/libhardware/modules/led
make snod

logcat

logcat 在命令行中可以使用类似 logcat LedHal:I *:S 这样的方法来进行过滤。

转自: https://www.cnblogs.com/wangshuo1/p/5697746.html

常常在网上看到有人询问:如何把 java 程序编译成 .exe 文件。通常回答只有两种,一种是制作一个可执行的 JAR 文件包,然后就可以像.chm 文档一样双击运行了;而另一种是使用 JET 来进行 编译。但是 JET 是要用钱买的,而且据说 JET 也不是能把所有的 Java 程序都编译成执行文件,性能也要打些折扣。所以,使用制作可执行 JAR 文件包的方法就是最佳选择了,何况它还能保持Java 的跨平台特性。

下面就来看看什么是 JAR 文件包吧:

1. JAR 文件包

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件--准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。举个例子,如果我们具有如下目录结构的一些文件:

==

`-- test

  `-- Test.class

把它压缩成 ZIP 文件 test.zip,则这个 ZIP 文件的内部目录结构为:

test.zip

`-- test

  `-- Test.class

如果我们使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar,则这个 JAR 文件的内部目录结构为:

test.jar

|-- META-INF

|  `-- MANIFEST.MF

`-- test

`--Test.class

2. 创建可执行的 JAR 文件包

制作一个可执行的 JAR 文件包来发布你的程序是 JAR 文件包最典型的用法。

Java 程序是由若干个 .class 文件组成的。这些 .class 文件必须根据它们所属的包不同而分级分目录存放;运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命令的 -cp 参数;运行时还要到控制台下去使用 java 命令来运行,如果需要直接双击运行必须写 Windows 的批处理文件 (.bat) 或者 Linux 的 Shell 程序。因此,许多人说,Java 是一种方便开发者苦了用户的程序设计语言。

其实不然,如果开发者能够制作一个可执行的 JAR 文件包交给用户,那么用户使用起来就方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候,安装文件会将 .jar 文件映射给 javaw.exe 打开。那么,对于一个可执行的 JAR 文件包,用户只需要双击它就可以运行程序了,和阅读 .chm 文档一样方便 (.chm 文档默认是由 hh.exe 打开的)。那么,现在的关键,就是如何来创建这个可执行的 JAR 文件包。

创建可执行的 JAR 文件包,需要使用带 cvfm 参数的 jar 命令,同样以上述 test 目录为例,命令如下:

jar cvfm test.jar manifest.mf test

这里 test.jar 和 manifest.mf 两个文件,分别是对应的参数 f 和 m,其重头戏在 manifest.mf。因为要创建可执行的 JAR 文件包,光靠指定一个 manifest.mf 文件是不够的,因为 MANIFEST 是 JAR 文件包的特征,可执行的 JAR 文件包和不可执行的 JAR 文件包都包含 MANIFEST。关键在于可执行 JAR 文件包的 MANIFEST,其内容包含了 Main-Class 一项。这在 MANIFEST 中书写格式如下:

Main-Class: 可执行主类全名(包含包名)

例如,假设上例中的 Test.class 是属于 test 包的,而且是可执行的类 (定义了 public static void main(String[]) 方法),那么这个 manifest.mf 可以编辑如下:

Main-Class: test.Test <回车>

这个 manifest.mf 可以放在任何位置,也可以是其它的文件名,只需要有 Main-Class: test.Test 一行,且该行以一个回车符结束即可。创建了 manifest.mf 文件之后,我们的目录结构变为:

==

|-- test

|  `-- Test.class

`-- manifest.mf

这时候,需要到 test 目录的上级目录中去使用 jar 命令来创建 JAR 文件包。也就是在目录树中使用“==”表示的那个目录中,使用如下命令:

jar cvfm test.jar manifest.mf test

之后在“==”目录中创建了 test.jar,这个 test.jar 就是执行的 JAR 文件包。运行时只需要使用 java -jar test.jar 命令即可。

需要注意的是,创建的 JAR 文件包中需要包含完整的、与 Java 程序的包结构对应的目录结构,就像上例一样。而 Main-Class 指定的类,也必须是完整的、包含包路径的类名,如上例的 test.Test;而且在没有打成 JAR 文件包之前可以使用 java <类名> 来运行这个类,即在上例中 java test.Test 是可以正确运行的 (当然要在 CLASSPATH 正确的情况下)。

3. jar 命令详解

jar 是随 JDK 安装的,在 JDK 安装目录下的 bin 目录中,Windows 下文件名为 jar.exe,Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做,因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。

使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下:

jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ...

其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示:

 -c 创建新的 JAR 文件包

 -t 列出 JAR 文件包的内容列表

 -x 展开 JAR 文件包的指定文件或者所有文件

 -u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中)

 [vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数

 -v 生成详细报告并打印到标准输出

 -f 指定 JAR 文件名,通常这个参数是必须的

 -m 指定需要包含的 MANIFEST 清单文件

 -0 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快

 -M 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数

 [jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数

 [manifest-文件] 即 MANIFEST 清单文件,它是 -m 参数的附属参数

 [-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。

文件名 ... 指定一个文件/目录列表,这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录,那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。

下面举一些例子来说明 jar 命令的用法:

  1. jar cf test.jar test

该命令没有执行过程的显示,执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar,那么该文件将被覆盖。

  1. jar cvf test.jar test

该命令与上例中的结果相同,但是由于 v 参数的作用,显示出了打包过程,如下:

标明清单(manifest)

增加:test/(读入= 0) (写出= 0)(存储了 0%)

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%)

  1. jar cvfM test.jar test

该命令与 2) 结果类似,但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件,打包过程的信息也略有差别:

增加:test/(读入= 0) (写出= 0)(存储了 0%)

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%)

  1. jar cvfm test.jar manifest.mf test

运行结果与 2) 相似,显示信息也相同,只是生成 JAR 包中的 META-INF/MANIFEST 内容不同,是包含了 manifest.mf 的内容

  1. jar tf test.jar

在 test.jar 已经存在的情况下,可以查看 test.jar 中的内容,如对于 2) 和 3) 生成的 test.jar 分别应该此命令,结果如下;

对于 2)

META-INF/

META-INF/MANIFEST.MF

test/

test/Test.class

对于 3)

test/

test/Test.class
  1. jar tvf test.jar

除显示 5) 中显示的内容外,还包括包内文件的详细信息,如:

0 Wed Jun 19 15:39:06 GMT 2002 META-INF/

86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF

0 Wed Jun 19 15:33:04 GMT 2002 test/

7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class
  1. jar xf test.jar

解开 test.jar 到当前目录,不显示任何信息,对于 2) 生成的 test.jar,解开后的目录结构如下:

==

|-- META-INF

|  `-- MANIFEST

`-- test

`--Test.class

jar xvf test.jar

运行结果与 7) 相同,对于解压过程有详细信息显示,如:

创建:META-INF/

展开:META-INF/MANIFEST.MF

创建:test/

展开:test/Test.class

  1. jar uf test.jar manifest.mf

在 test.jar 中添加了文件 manifest.mf,此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下,如果使用 -m 参数并指定 manifest.mf 文件,那么 manifest.mf 是作为清单文件 MANIFEST 来使用的,它的内容会被添加到 MANIFEST 中;但是,如果作为一般文件添加到 JAR 文件包中,它跟一般文件无异。

  1. jar uvf test.jar manifest.mf

与 9) 结果相同,同时有详细信息显示,如:

增加:manifest.mf(读入= 17) (写出= 19)(压缩了 -11%)

4. 关于 JAR 文件包的一些技巧

  1. 使用 unzip 来解压 JAR 文件

在介绍 JAR 文件的时候就已经说过了,JAR 文件实际上就是 ZIP 文件,所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件,如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观,方便。而使用 unzip,则是因为它解压时可以使用 -d 参数指定目标目录。

在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的,因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下,再进行解压,比较麻烦。如果使用 unzip,就不需要这么麻烦了,只需要指定一个 -d 参数即可。如:

unzip test.jar -d dest/
  1. 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件

上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件,所以,只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包,再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况,只需要将这个 MANIFEST 按需要修改即可。

  1. 使用 jar 命令创建 ZIP 文件

有些 Linux 下提供了 unzip 命令,但没有 zip 命令,所以需要可以对 ZIP 文件进行解压,即不能创建 ZIP 文件。如要创建一个 ZIP 文件,使用带 -M 参数的 jar 命令即可,因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单,那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名,创建的就是一个不折不扣的 ZIP 文件了,如将上一节的第 3) 个例子略作改动:

jar cvfM test.zip test

  使用jar命令解决解压缩后,文件乱码问题。

使用JAR解压,命令行下执行 jar -xvf 压缩包.zip

转自: http://gityuan.com/2016/03/19/android-build/

工欲善其事,必先利其器,对于想要深入学习Android源码,必须先掌握Android编译命令.

一、引言

关于Android Build系统,这个话题很早就打算整理下,迟迟没有下笔,决定跟大家分享下。先看下面几条指令,相信编译过Android源码的人都再熟悉不过的。

source setenv.sh
lunch
make -j12

记得最初刚接触Android时,同事告诉我用上面的指令就可以编译Android源码,指令虽短但过几天就记不全或者忘记顺序,每次编译时还需要看看自己的云笔记,冰冷的指令总是难以让我记忆。后来我决定认真研究下这个指令的含义。知其然还需知其所以然,这样能更深层次的理解并记忆,才能与自身的知识体系建立强连接,或许还有意外收获,果然如此,接下来跟大家分享一下在研究上述几条指令含义的过程中,深入了解到的Android Build(编译)系统。

二、编译命令

准备好编译环境后,编译Android源码的第一步是 source build/envsetup.sh,其中source命令就是用于运行shell脚本命令,功能等价于”.”,因此该命令也等价于. build/envsetup.sh。在文件envsetup.sh声明了当前会话终端可用的命令,这里需要注意的是当前会话终端,也就意味着每次新打开一个终端都必须再一次执行这些指令。起初并不理解为什么新开的终端不能直接执行make指令,到这里总算明白了。

接下来,解释一下本文开头的引用的命令:

source setenv.sh  //初始化编译环境,包括后面的lunch和make指令
lunch  //指定此次编译的目标设备以及编译类型
make  -j12 //开始编译,默认为编译整个系统,其中-j12代表的是编译的job数量为12。

所有的编译命令都在envsetup.sh文件能找到相对应的function,比如上述的命令lunch,make,在文件一定能找到

function lunch(){
    ...
}

function make(){
    ...
}

source envsetup.sh,需要cd到setenv.sh文件所在路径执行,路径可能在build/envsetup.sh,或者integrate/envsetup.sh,再或者不排除有些厂商会封装自己的.sh脚本,但核心思路是一致的。

具体实现这里就不展开说明,下面精炼地总结了一下各个指令用法和功效。

2.1 代码编译

编译指令 解释
m 在源码树的根目录执行编译
mm 编译当前路径下所有模块,但不包含依赖
mmm [module_path] 编译指定路径下所有模块,但不包含依赖
mma 编译当前路径下所有模块,且包含依赖
mmma [module_path] 编译指定路径下所有模块,且包含依赖
make [module_name] 无参数,则表示编译整个Android代码

下面列举部分模块的编译指令:

模块 make命令 mmm命令
init make init mmm system/core/init
zygote make app_process mmm frameworks/base/cmds/app_process
system_server make services mmm frameworks/base/services
java framework make framework mmm frameworks/base
framework资源 make framework-res mmm frameworks/base/core/res
jni framework make libandroid_runtime mmm frameworks/base/core/jni
binder make libbinder mmm frameworks/native/libs/binder

上述mmm命令同样适用于mm/mma/mmma,编译系统采用的是增量编译,只会编译发生变化的目标文件。当需要重新编译所有的相关模块,则需要编译命令后增加参数-B,比如make -B [module_name],或者 mm -B [module_path]

Tips:

  • 对于m、mm、mmm、mma、mmma这些命令的实现都是通过make方式来完成的。
  • mmm/mm编译的效率很高,而make/mma/mmma编译较缓慢;
  • make/mma/mmma编译时会把所有的依赖模块一同编译,但mmm/mm不会;
  • 建议:首次编译时采用make/mma/mmma编译;当依赖模块已经编译过的情况,则使用mmm/mm编译。

2.2 代码搜索

搜索指令 解释
cgrep 所有C/C++文件执行搜索操作
jgrep 所有Java文件执行搜索操作
ggrep 所有Gradle文件执行搜索操作
mangrep [keyword] 所有AndroidManifest.xml文件执行搜索操作
mgrep [keyword] 所有Android.mk文件执行搜索操作
sepgrep [keyword] 所有sepolicy文件执行搜索操作
resgrep [keyword] 所有本地res/*.xml文件执行搜索操作
sgrep [keyword] 所有资源文件执行搜索操作

上述指令用法最终实现方式都是基于grep指令,各个指令用法格式:

xgrep [keyword]  //x代表的是上表的搜索指令

例如,搜索所有AndroidManifest.xml文件中的launcher关键字所在文件的具体位置,指令

mangrep launcher

再如,搜索所有Java代码中包含zygote所在文件

jgrep zygote

又如,搜索所有system_app的selinux权限信息

sepgrep system_app

Tips: Android源码非常庞大,直接采用grep来搜索代码,不仅方法笨拙、浪费时间,而且搜索出很多无意义的混淆结果。根据具体需求,来选择合适的代码搜索指令,能节省代码搜索时间,提高搜索结果的精准度,方便定位目标代码。

2.3 导航指令

导航指令 解释
croot 切换至Android根目录
cproj 切换至工程的根目录
godir [filename] 跳转到包含某个文件的目录

Tips: 当每次修改完某个文件后需要编译时,执行cproj后会跳转到当前模块的根目录,也就是Android.mk文件所在目录,然后再执行mm指令,即可编译目标模块;当进入源码层级很深后,需要返回到根目录,使用croot一条指令完成;另外cd - 指令可用于快速切换至上次目录。

2.4 信息查询

查询指令 解释
hmm 查询所有的指令help信息
findmakefile 查询当前目录所在工程的Android.mk文件路径
print_lunch_menu 查询lunch可选的product
printconfig 查询各项编译变量值
gettop 查询Android源码的根目录
gettargetarch 获取TARGET_ARCH值

2.5 其他指令

上述只是列举比较常用的指令,还有其他指令,而且不同的build编译系统,支持的指令可能会存在一些差异,当忘记这些编译指令,可以通过执行hmm,查询指令的帮助信息。

最后再列举两个比较常用的指令:

  • make clean:执行清理操作,等价于 rm -rf out/
  • make update-api:更新API,在framework API改动后需执行该指令,Api记录在目录frameworks/base/api

三、编译系统

Android 编译系统是Android源码的一部分,用于编译Android系统,Android SDK以及相关文档。该编译系统是由Make文件、Shell以及Python脚本共同组成,其中最为重要的便是Make文件。关于编译系统可参考 理解 Android Build 系统。 www.ibm.com/developerworks/cn/opensource/os-cn-android-build/

3.1 Makefile分类

整个Build系统的Make文件分为三大类:

  • 系统核心的Make文件:定义了Build系统的框架,文件全部位于路径/build/core,其他Make文件都是基于该框架编写的;
  • 针对产品的Make文件:定义了具体某个型号手机的Make文件,文件路径位于/device,该目录下往往又以公司名和产品名划分两个子级目录,比如/device/qcom/msm8916
  • 针对模块的Make文件:整个系统分为各个独立的模块,每个模块都一个专门的Make文件,名称统一为”Android.mk”,该文件定义了当前模块的编译方式。Build系统会扫描整个源码树中名为”Android.mk”的问题,并执行相应模块的编译工作。

3.2 编译产物

经过make编译后的产物,都位于/out目录,该目录下主要关注下面几个目录:

  • /out/host:Android开发工具的产物,包含SDK各种工具,比如adb,dex2oat,aapt等。
  • /out/target/common:通用的一些编译产物,包含Java应用代码和Java库;
  • /out/target/product/[product_name]:针对特定设备的编译产物以及平台相关C/C++代码和二进制文件;

在/out/target/product/[product_name]目录下,有几个重量级的镜像文件:

  • system.img:挂载为根分区,主要包含Android OS的系统文件;
  • ramdisk.img:主要包含init.rc文件和配置文件等;
  • userdata.img:被挂载在/data,主要包含用户以及应用程序相关的数据;

当然还有boot.img,reocovery.img等镜像文件,这里就不介绍了。

3.3 Android.mk解析

在源码树中每一个模块的所有文件通常都相应有一个自己的文件夹,在该模块的根目录下有一个名称为“Android.mk” 的文件。编译系统正是以模块为单位进行编译,每个模块都有唯一的模块名,一个模块可以有依赖多个其他模块,模块间的依赖关系就是通过模块名来引用的。也就是说当模块需要依赖一个jar包或者apk时,必须先将jar包或apk定义为一个模块,然后再依赖相应的模块。

对于Android.mk文件,通常都是以下面两行

LOCAL_PATH := $(call my-dir)  //设置当编译路径为当前文件夹所在路径
include $(CLEAR_VARS)  //清空编译环境的变量(由其他模块设置过的变量)

为方便模块编译,编译系统设置了很多的编译环境变量,如下:

  • LOCAL_SRC_FILES:当前模块包含的所有源码文件;
  • LOCAL_MODULE:当前模块的名称(具有唯一性);
  • LOCAL_PACKAGE_NAME:当前APK应用的名称(具有唯一性);
  • LOCAL_C_INCLUDES:C/C++所需的头文件路径;
  • LOCAL_STATIC_LIBRARIES:当前模块在静态链接时需要的库名;
  • LOCAL_SHARED_LIBRARIES:当前模块在运行时依赖的动态库名;
  • LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的Java静态库;
  • LOCAL_JAVA_LIBRARIES:当前模块依赖的Java共享库;
  • LOCAL_CERTIFICATE:签署当前应用的证书名称,比如platform。
  • LOCAL_MODULE_TAGS:当前模块所包含的标签,可以包含多标签,可能值为debgu,eng,user,development或optional(默认值)

针对这些环境变量,编译系统还定义了一些便捷函数,如下:

  • $(call my-dir):获取当前文件夹路径;
  • $(call all-java-files-under, ):获取指定目录下的所有Java文件;
  • $(call all-c-files-under, ):获取指定目录下的所有C文件;
  • $(call all-Iaidl-files-under, ) :获取指定目录下的所有AIDL文件;
  • $(call all-makefiles-under, ):获取指定目录下的所有Make文件;

示例:

  LOCAL_PATH := $(call my-dir)
  include $(CLEAR_VARS)

  # 获取所有子目录中的Java文件
  LOCAL_SRC_FILES := $(call all-subdir-java-files)

  # 当前模块依赖的动态Java库名称
  LOCAL_JAVA_LIBRARIES := com.gityuan.lib

  # 当前模块的名称
  LOCAL_MODULE := demo

  # 将当前模块编译成一个静态的Java库
  include $(BUILD_STATIC_JAVA_LIBRARY)

转自: https://segmentfault.com/a/1190000021742841

基于 Android 9.0。
许多命令时间一长就记不清了,记录一下。

Android 环境引入的命令

在shell中执行"source build/envsetup.sh"后,Android将增加一些命令到当前环境中。

  • lunch:选择编译的产品类型,并存储在当前环境中。
        lunch <product_name>-<build_variant>
  • tapas:与 'lunch' 类似,可以指定编译的应用。
        tapas [<App1> <App2> ...] [arm|x86|mips|arm64|x86_64|mips64] [eng|userdebug|user]
  • printconfig:打印当前配置。
  • croot:切换到顶层目录
  • m:从顶层目录开始编译。
  • mm:编译当前目录中的所有模块,但不包含依赖的模块。

  • mmm:编译指定目录的所有模块,但不包含依赖的模块。可以使用下述方式限制编译的模块。
        mmm dir/:target1,target2.
  • mma:编译当前目录中的所有模块,包含依赖的模块。
  • mmma:编译指定目录的所有模块,包含依赖的模块
  • provision:对设备进行固件升级,选项将被传递到 fastboot 中。
  • cgrep:在当前目录的所有 C/C++ 文件中查找。
  • ggrep:在当前目录的所有 Gradle 文件中查找。
  • jgrep:在当前目录的所有 Java 文件中查找。
  • resgrep:在当前目录的所有 res/*.xml 文件中查找。
  • mangrep:在当前目录的所有 AndroidManifest.xml 文件中查找。
  • mgrep:在当前目录的所有 Makefiles 文件中查找。
  • sepgrep:在当前目录的所有 sepolicy 文件中查找。
  • sgrep:在当前目录的所有文件中查找。
  • godir:切换到包含指定文件的目录。

make相关命令

make 或 m 后面可以跟随不同的参数来指定编译目标。

  • droid:默认的 make 编译目标。
  • all:编译所有内容,包括不含droid标记的内容。编译服务器会运行此命令,以确保项目中包含Android.mk文件的所有元素都会编译。
  • checkbuild:编译所有的模块。
  • showcommands:显示编译步骤中实际的编译指令。
  • PRODUCT-xxx-yyy:编译指定的产品。
  • dump-products:显示产品的编译信息。

  • LOCAL_MODULE:编译指定的模块。例如,
        make runtime
  • nothing:不编译任何事情,只是解析和验证编译框架。
  • java:编译项目中的所有java代码。
  • native:编译项目中的所有native代码。
  • host:编译host端代码。
  • target:编译target端代码。
  • (java|native)-(host|target):按组合编译限定代码。
  • (host|target)-(java|native):按组合编译限定代码。
  • snod:快速重新编译system镜像。
  • vnod:快速重新编译vendor镜像。
  • pnod:快速重新编译product镜像。
  • update-api:更新SDK API。
  • otapackage:生成OTA升级包。
  • updatepackage:生成zip升级包。
  • docs:编译文档。
  • ndk-docs:生成ndk文档。
  • sdk:编译生成SDK。
  • ndk:编译生成NDK。
  • ramdisk:编译生成ramdisk。
  • systemtarball:生成system的tar包。
  • boottarball:生成boot的tar包。
  • userdatatarball:生成data的tar包。
  • systemimage:生成system.img镜像。
  • bootimage:生成boot.img镜像。
  • vbmetaimage:生成vbmeta.img镜像。
  • userdateimage:生成userdata.img镜像。
  • cacheimage:生成cache.img镜像。
  • bptimage:生成partition-table.img镜像。
  • vendorimage:生成vendor.img镜像。
  • productimage:生成product.img镜像。
  • apps_only:编译不包含 user,userdebug,eng 标签的应用程序。
  • dist:拷贝输出到dist目录。
  • dist_files:仅仅拷贝库文件到dist目录。
  • libandroid_runtime:编译JNI相关内容。
  • framework:编译java framework相关内容。
  • services:编译系统服务相关内容。
  • cts:编译CTS测试套件。
  • vts:编译VTS测试套件。

make clean 相关命令

  • clean:清除当前配置编译的所有文件,等同于rm -rf out//。
  • **clean-$(LOCAL_MODULE):清理指定模块的编译结果。
  • clean-$(LOCAL_PACKAGE_NAME):清理指定包的编译结果。
  • clobber:清除所有输出,等同于rm -rf out/。
  • dataclean:删除当前配置上data目录的所有文件。
  • clean-sdk:清理SDK编译输出。
  • installclean:清除与编译类型相关的文件。在切换编译目标是使用该命令可以加快编译。

new product

new directory

device/[company]/[device] [device] is your PCBA name. product is your software.

mkdir -p device/qiushao/pure

lunch
--> COMMON_LUNCH_CHOICES := pure-eng(AndroidProducts.mk)
--> PRODUCT_MAKEFILES := $(LOCAL_DIR)/pure.mk(AndroidProducts.mk)
--> PRODUCT_DEVICE := pure(pure.mk)
--> device/[company]/pure/BoardConfig.mk
==> out directory: PRODUCT_NAME := pure(pure.mk)

vim AndroidProducts.mk

PRODUCT_MAKEFILES := \
    $(LOCAL_DIR)/pure.mk

COMMON_LUNCH_CHOICES := \
    pure-eng
vim pure.mk

$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_x86_64.mk)

PRODUCT_NAME   := pure
PRODUCT_DEVICE := pure
vim BoardConfig.mk

include $(SRC_TARGET_DIR)/board/generic_x86_64/BoardConfig.mk
tree 

.
└── pure
    ├── AndroidProducts.mk
    ├── BoardConfig.mk
    └── pure.mk

pre

sudo apt-get install -y openjdk-8-jdk
sudo apt-get install -y git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev libgl1-mesa-dev libxml2-utils xsltproc unzip

download

git

sudo apt install git
git config --global user.name "xyz"
git config --global user.email "xyz@gmail.com"

ethstatus

sudo apt install -y ethstatus net-tools

use ifconfig to get your net name, may be it looks like wlp4s0, then use ethstatus -i wlp4s0 to get net status.

repo

mkdir ~/bin
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo -o ~/bin/repo
chmod +x ~/bin/repo
mkdir -p ~/source/android-10
vim ~/repo_sync.sh

#!/bin/bash

export PATH=$PATH:~/bin 
export REPO_URL="https://mirrors.tuna.tsinghua.edu.cn/git/git-repo/"

cd ~/source/android-10
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-10.0.0_r8
repo sync

compile

vim build_aosp.sh
#!/bin/bash

cd source/android-10/
source build/envsetup.sh
lunch aosp_x86_64-eng

#make system-api-stubs-docs-update-current-api

if error with 1 or 137, the ram may be less than needed, you should increase swap size or ram size.

emulator

vim emulator.sh

#!/bin/bash

cd source/android-10/

source build/envsetup.sh
lunch aosp_x86_64-eng

#sudo emulator
emulator

if error is "Please ensure KVM is properly installed and usable." you need:

  1. sudo apt install -y qemu-kvm cpu-checker
  2. use kvm-ok to check the kvm has already?
  3. use usermod -a -G kvm xyz to add user: xyz in kvm group.
  4. user echo 'KERNEL=="kvm",GROUP="kvm",MODE="0660"' >> /etc/udev/rules.d/androidUseKVM.rules to add kvm rules.
  5. reboot or relogin

if still has the error like "grant current user access to /dev/kvm", "/dev/kvm devices: permission denies", then sudo chown xyz -R /dev/kvm

reference:

Ubuntu 18.04 emulator运行Android源码编译的 X86 虚拟机image
https://blog.csdn.net/csusunxgg/article/details/81060890
Ubentu编译Android源码(AOSP)
https://www.cnblogs.com/caoxinyu/p/10568480.html

转自: https://blog.csdn.net/u011913612/article/details/52503318

Android编译系统分析系列文章: android编译系统分析(一)-source build/envsetup.sh与lunch Android编译系统(二)-mm编译单个模块 android编译系统分析(三)-make android编译系统(四)-实战:新增一个产品 Android编译系统分析(五)-system.img的生成过程

我们在完整编译android系统的时候,最终会生成几个重要的镜像文件,其中有system.img,userdata.img,ramdisk.img等。这篇文章的目的是分析system.img的生成过程。 回想下我们完整编译android系统时的动作,我们会在android源码顶级目录执行make命令,这样就会完整的编译android系统,我们没有传入任何参数(-jx等加快编译的除外),因为我们没有明确指定make的目标,所以android编译系统会执行默认的编译目标,也就是droid。因此,我们还是从droid着手,看看system.img怎么生成。

- 阅读剩余部分 -

转自: https://blog.csdn.net/u011913612/article/details/52434445

Android编译系统分析系列文章:

android编译系统分析一<source build/envsetup.sh与lunch> Android编译系统<二>-mm编译单个模块 android编译系统分析(三)-make android编译系统(四)-实战:新增一个产品 Android编译系统分析(五)-system.img的生成过程

通过上一节“android编译系统(三)-make”的分析,初步理清楚了编译初期加载产品相关信息的流程,整个过程主要涉及三个文件:1.AndroidProducts.mk,2.具体产品的配置文件,3.BoardConfig.mk,加上lunch需要的vendorsetup.sh文件,总共只需要四个文件就可以了,所以我们不妨成热打铁,赶紧尝试下如何新增一个产品。因此,android编译系统镜像文件的创建过程方到下一节。

- 阅读剩余部分 -

转自: https://blog.csdn.net/u011913612/article/details/52434411

Android编译系统分析系列文章:

android编译系统分析一<source build/envsetup.sh与lunch> Android编译系统<二>-mm编译单个模块 android编译系统分析(三)-make android编译系统(四)-实战:新增一个产品 Android编译系统分析(五)-system.img的生成过程

这篇博客的目标是摸清楚默认编译整个android系统时代码的流程。

当我们执行make的时候,会查找当前的Makefie文件或者makefile文件并且执行,在android顶级源码目录下面,确实有个Makefile,它之后一行内容:

### DO NOT EDIT THIS FILE ###
include build/core/main.mk
### DO NOT EDIT THIS FILE ###

因此,正真执行的是build/core/main.mk

- 阅读剩余部分 -