转载声明:本文为转载文章,版权归原作者所有,本人(XShawn)仅进行排版优化和文字校对。


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:> https://blog.csdn.net/fanx9339/article/details/106896653>


本文文档下载:
https://download.csdn.net/download/fanx9339/12542402

1、Android.bp文件是什么?

Android.bp文件首先是Android系统的一种编译配置文件,是用来代替原来的Android.mk文件的。在Android7.0以前,Android都是使用make来组织各模块的编译,对应的编译配置文件就是Android.mk。在Android7.0开始,Google引入了ninja和kati来编译,为啥引入ninja?因为随着Android越来越庞大,module越来越多,编译时间也越来越久,而使用ninja在编译的并发处理上较make有很大的提升。Ninja的配置文件就是Android.bp,Android系统使用Blueprint和Soong工具来解析Android.bp转换生成ninja文件。为了兼容老的mk配置文件,Android当初也开发了Kati 工具来转换mk文件生成ninja,目前Android Q里边,还是支持Android.mk方式的。相信在将来的版本中,会彻底让mk文件废弃,同时Kati也就淘汰了,只保留bp配置方式,所以我们要提前学习bp。Blueprint和Soong工具的源码在Android/build/目录下,我们可以通过查阅相关代码来学习!
[转][rom] Android.bp文件详解 - 图1

2、Android.bp文件配置规则

2.1、模块和属性

Android.bp描述的编译对象都是以模块为组织单位的,定义一个模块从模块的类型开始,模块有不同的类型,模块包含一些属性,下面举一个例子来具体说明:

  1. cc_binary {
  2. name: "avbctl",
  3. defaults: ["avb_defaults"],
  4. static_libs: [
  5. "libavb_user",
  6. "libfs_mgr",
  7. ],
  8. shared_libs: ["libbase"],
  9. srcs: ["tools/avbctl/avbctl.cc"],
  10. }

上面例子中的cc_binary就是模块类型,表示该模块目标为二进制可执行文件。如果要编译一个APP,那么就使用android_app模块,要编译一个动态库,那么就使用cc_library_shared.soong工具支持的模块类型在build/soong/androidmk/cmd/androidmk/android.go中可以查到,有以下:

  1. var moduleTypes = map[string]string{
  2. "BUILD_SHARED_LIBRARY": "cc_library_shared",
  3. "BUILD_STATIC_LIBRARY": "cc_library_static",
  4. "BUILD_HOST_SHARED_LIBRARY": "cc_library_host_shared",
  5. "BUILD_HOST_STATIC_LIBRARY": "cc_library_host_static",
  6. "BUILD_HEADER_LIBRARY": "cc_library_headers",
  7. "BUILD_EXECUTABLE": "cc_binary",
  8. "BUILD_HOST_EXECUTABLE": "cc_binary_host",
  9. "BUILD_NATIVE_TEST": "cc_test",
  10. "BUILD_HOST_NATIVE_TEST": "cc_test_host",
  11. "BUILD_NATIVE_BENCHMARK": "cc_benchmark",
  12. "BUILD_HOST_NATIVE_BENCHMARK": "cc_benchmark_host",
  13. "BUILD_JAVA_LIBRARY": "java_library_installable", // will be rewritten to java_library by bpfix
  14. "BUILD_STATIC_JAVA_LIBRARY": "java_library",
  15. "BUILD_HOST_JAVA_LIBRARY": "java_library_host",
  16. "BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik",
  17. "BUILD_PACKAGE": "android_app",
  18. "BUILD_CTS_EXECUTABLE": "cc_binary", // will be further massaged by bpfix depending on the output path
  19. "BUILD_CTS_SUPPORT_PACKAGE": "cts_support_package", // will be rewritten to android_test by bpfix
  20. "BUILD_CTS_PACKAGE": "cts_package", // will be rewritten to android_test by bpfix
  21. "BUILD_CTS_TARGET_JAVA_LIBRARY": "cts_target_java_library", // will be rewritten to java_library by bpfix
  22. "BUILD_CTS_HOST_JAVA_LIBRARY": "cts_host_java_library", // will be rewritten to java_library_host by bpfix
  23. }
  • 模块类型后面用大括号“{}”将模块的所有属性包裹起来。
  • 每个属性的名字和值用中间用冒号连接起来,属性值要用双引号“”””包裹起来(如果属性值是变量,变量不需要加双引号):name: “avbctl”表示模块name属性的值为avbctl,就是类比Android.mk中的LOCAL_MODULE := avbctl。模块的name属性是必须的,内容必须是独一无二的。如果属性被定义为数组,需要用中括号“[]”将数组的各元素包裹起来,每个元素中间用逗号“,”连接,一般常用的属性有name,srcs,cflags, cppflags, shared_libs,static_libs。
  • 查看全部支持的模块和各个模块支持的属性定义,请查看这个网址:https://ci.android.com/builds/submitted/6504066/linux/latest/view/soong_build.html
  • cc_defaults模块比较特殊,它表示该模块的属性可以被其他模块重复引用,类似于我们的头文件被其他cpp文件引用,举例: ```groovy cc_defaults { name: “gzip_defaults”, shared_libs: [“libz”], stl: “none”, }

cc_binary { name: “gzip”, defaults: [“gzip_defaults”], /这里等价于 shared_libs: [“libz”], stl: “none”,/ srcs: [“src/test/minigzip.c”], }

  1. - 属性可以使用列表数组的形式,也可以使用unix通配符,例如:”*.java
  2. - 每一条完整的属性定义语句加上逗号“,”表示结束
  3. - 注释包括单行注释//和多行注释/**/
  4. - 最重要的一点:目前android编译系统同时支持mkbp两种,但是这两种是彼此单独运行的,所以bp中依赖的目标,例如动态库静态库目标,如果库是源代码的形式存在的,那么库的编译脚本必须也是通过bp文件编译才能被找到,否则用mk文件编译库,bp会提示找不到依赖的库目标。(paxdroid代码中因为要将framework相关内容编译到androidframework中,而android已经全部转还成bp,所以这一部分paxdroid也是使用的bp来编写的,不然会提示找不到)
  5. > 更多的说明可以在android/build/soong/README.md文件中查找。如果找不到想要的,可以在官网找:[https://ci.android.com/builds/submitted/6504066/linux/latest/view/soong_build.html](https://ci.android.com/builds/submitted/6504066/linux/latest/view/soong_build.html)
  6. <a name="3WChy"></a>
  7. ## 2.2、变量
  8. 变量可以直接定义,使用“=”号赋值,例如:
  9. ```groovy
  10. avbctl_srcs = ["tools/avbctl/avbctl.cc"],
  11. cc_binary {
  12. name: "avbctl",
  13. defaults: ["avb_defaults"],
  14. static_libs: [
  15. "libavb_user",
  16. "libfs_mgr",
  17. ],
  18. shared_libs: ["libbase"],
  19. srcs: avbctl_srcs,
  20. }

2.3、条件编译

例如我们的mk文件中包括条件判断:

  1. LOCAL_PATH := $(call my-dir)
  2. include $(CLEAR_VARS)
  3. LOCAL_MODULE := fs_mgr
  4. ifeq ($(ENABLE_USER2ENG),true)
  5. LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
  6. LOCAL_CFLAGS += -DENABLE_USER2ENG=1
  7. endif
  8. LOCAL_CFLAGS += -Wno-error=implicit-function-declaration
  9. ifeq ($(shell if [ -d $(TOPDIR)paxdroid/external/libethtool ]; then echo "exist"; else echo "notexist"; fi;), exist)
  10. LOCAL_SHARED_LIBRARIES +=libethtool
  11. endif
  12. include $(BUILD_EXECUTABLE)

我们先分析下上面两个条件的意思,如果变量ENABLE_USER2ENG的值为true,那么追加这两个编译参数,否则不追加。第二个条件是说如果存在paxdroid/external/libethtool这个目录,那么就添加libethtool这个动态库,否则不添加。

要转换为bp写法,就需要我们通过go语言写一个新文件,新建一个自定义类型的模块,来判断这些条件,比如我的修改是在system/core/fs_mgr/Android.bp,那么要在添加 system/core/fs_mgr/fs_mgr.go,

  1. //这是申明当前的包名,就是你在哪个文件夹下,就写啥
  2. package fs_mgr
  3. //导入android的soong工具目录
  4. import (
  5. "android/soong/android"
  6. "android/soong/cc"
  7. "fmt"
  8. //一般情况下不需要引用os包,这里要判断文件夹是否存在需要引用
  9. "os"
  10. )
  11. //初始化入口函数
  12. func init() {
  13. // for DEBUG
  14. fmt.Println("init start")
  15. //注册模块名:fs_mgr_condition,模块名的方法入口是:fs_mgrDefaultsFactory方法
  16. android.RegisterModuleType("fs_mgr_condition", fs_mgrDefaultsFactory)
  17. }
  18. //实现fs_mgrDefaultsFactory方法:
  19. func fs_mgrDefaultsFactory() (android.Module) {
  20. //如果我们编译的目标是bin文件,这里就是调用cc.DefaultsFactory()
  21. //如果编译目标是so库,那么调用的是cc.LibrarySharedFactory()。这里我们举例目标是bin文件:
  22. module := cc.DefaultsFactory()
  23. //添加装载时的钩子函数fs_mgrDefaults
  24. android.AddLoadHook(module, fs_mgrDefaults)
  25. return module
  26. }
  27. //实现钩子函数
  28. func fs_mgrDefaults(ctx android.LoadHookContext) {
  29. //这里定义我们所有需要受到条件控制的变量,比如我们这里需要根据条件来控制Cflags和依赖
  30. //的动态库两个变量,所以我们只需要定义这两个即可,按照实际需求定义:
  31. type props struct {
  32. Cflags []string
  33. Shared_libs []string
  34. }
  35. p := &props{}
  36. p.Cflags = getCflags(ctx)
  37. p.Shared_libs = getShared_libs(ctx)
  38. ctx.AppendProperties(p)
  39. }
  40. //实现getCflags(ctx)方法
  41. func getCflags(ctx android.BaseContext) ([]string) {
  42. var cppflags []string
  43. fmt.Println("ENABLE_USER2ENG:",
  44. ctx.AConfig().IsEnvTrue("ENABLE_USER2ENG"))
  45. if ctx.AConfig().IsEnvTrue("ENABLE_USER2ENG") {
  46. cppflags = append(cppflags, "-DALLOW_ADBD_DISABLE_VERITY=1", "-DENABLE_USER2ENG=1")
  47. }
  48. return cppflags
  49. }
  50. //实现getShared_libs(ctx)方法
  51. func getShared_libs(ctx android.BaseContext) ([]string) {
  52. var shared_libs []string
  53. //判断文件是否存在,该方法返回两个参数,一个isExists,一个error
  54. isExists,error := PathExists ("paxdroid/external/libethtool")
  55. if(isExists && error == nil){
  56. //路径存在
  57. shared_libs = append(shared_libs, libethtool”)
  58. }
  59. return shared_libs
  60. }
  61. //实现判断文件夹是否存在的工具方法:
  62. func PathExists(path string) (bool, error) {
  63. _, err := os.Stat(path)
  64. if err == nil {
  65. return true, nil
  66. }
  67. if os.IsNotExist(err) {
  68. return false, nil
  69. }
  70. return false, err
  71. }

go脚本写完了,相应的再Android.bp文件引用,如下:

  1. // 这些个必须添加,编译刚刚写的那个go脚本需要的一些依赖
  2. bootstrap_go_package {
  3. // 名字和包路径和刚刚写的go文件一致
  4. name: "soong-fs_mgr",
  5. pkgPath: "android/soong/fs_mgr",
  6. deps: [
  7. "blueprint",
  8. "blueprint-pathtools",
  9. "soong",
  10. "soong-android",
  11. "soong-cc",
  12. "soong-genrule",
  13. ],
  14. //这里的srcs就写我们刚刚写的go脚本
  15. srcs: [
  16. "fs_mgr.go",
  17. ],
  18. pluginFor: ["soong_build"],
  19. }
  20. // fs_mgr_condition 是我们在go语言中自定义的模块类型,模块的类型是fs_mgr_condition,
  21. // 这个模块名字叫做:fs_mgr_defaults
  22. fs_mgr_condition {
  23. name: "fs_mgr_defaults",
  24. }
  25. //-----------------------------------------------------------------------
  26. //以上所有代码是标准的添加自定义模块来实现条件控制的代码,我们可以记下来作为参考,
  27. //下面的代码才是我们编译具体模块的:
  28. cc_binary {
  29. name: "fs_mgr",
  30. //这里引用上面定义的模块
  31. defaults: ["fs_mgr_defaults"],
  32. cppflags: ["-Wno-error=implicit-function-declaration "],
  33. }

2.4、操作符

String类型、字符串列表类型和Map类型支持操作符“+”,例如:

  1. binder_src_files = ["lib/libsystool_client/binder.c"],
  2. cc_library_static {
  3. srcs: ["lib/libsystool_client/systool_client_static.c",
  4. "ipc/pipe_client.c",
  5. ] + binder_src_files,
  6. }

3、Androidmk自动转换工具

另外对于现成的Android.mk文件,也可以尝试使用androidmk工具自动将mk文件转换成bp文件,进入android/out/soong/host/linux-x86/bin/androidmk目录下,输入命令: androidmk Android.mk > Android.bp即可将当前目录下的Android.mk文件自动转换成Android.bp(部分复杂的mk文件可能会提示错误,需要手动书写)。

4、自己动手再写一个

编译一个可执行文件,目录结构如下:

  1. .
  2. ├── Android.bp
  3. ├── testbin.go
  4. └── test.c

Android.bp文件内容如下:

  1. bootstrap_go_package {
  2. name:"soong-testbin",
  3. pkgPath:"android/soong/testbin",
  4. deps:[
  5. "blueprint",
  6. "soong",
  7. "soong-android",
  8. "soong-cc",
  9. "soong-genrule",
  10. ],
  11. srcs: [
  12. "testbin.go",
  13. ],
  14. pluginFor: ["soong_build"],
  15. }
  16. testbin_condition{
  17. name:"testbin_defaults",
  18. }
  19. cc_binary{
  20. name: "testbin",
  21. defaults: [
  22. "testbin_defaults",
  23. ],
  24. srcs:[
  25. "test.c",
  26. ],
  27. }

testbin.go文件内容:

  1. package testbin
  2. import (
  3. "android/soong/android"
  4. "android/soong/cc"
  5. "fmt"
  6. )
  7. func init(){
  8. fmt.Println("init start test")
  9. android.RegisterModuleType("testbin_condition", testbinDefaultFactory)
  10. }
  11. func testbinDefaultFactory()(android.Module){
  12. module := cc.DefaultsFactory()
  13. android.AddLoadHook(module, testbinDefault)
  14. return module
  15. }
  16. func testbinDefault(ctx android.LoadHookContext){
  17. type props struct {
  18. Proprietary *bool
  19. }
  20. p := &props{}
  21. var proprietary bool
  22. proprietary = getProprietary(ctx)
  23. p.Proprietary = &proprietary
  24. ctx.AppendProperties(p)
  25. }
  26. func getProprietary(ctx android.LoadHookContext)(bool){
  27. if(ctx.AConfig().Getenv("BUILD_VENDOR") == "yes"){
  28. fmt.Println("BUILD_VENDOR ============= yes")
  29. return true
  30. }else{
  31. fmt.Println("BUILD_VENDOR ============= no")
  32. return false
  33. }
  34. }

上面代码的功能是根据编译的环境变量【BUILD_VENDOR】的值,确定testbin二进制目标编译生成的位置在system/bin还是在vendor/bin目录下。以上例程需要注意的两点是:

  1. proprietary这个属性定义的时候本来是小写的,但是在go语言中,首字母大写意味着可以被别的文件引用。所以红色部分,不管啥属性本来定义的名称是什么,都应该大写字母开头。
  2. 通常情况下我们的属性是字符串数组类型[]string,如果碰到了是bool型的属性来根据条件编写的话,应该定义为指针【*bool】切记!!!