Android.bp⽂件详解
本⽂⽂档在如下位置,如有需要可以下载:
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/⽬录下,我们可以通过查阅相关代码来学习!
Android.bp⽂件配置规则
【模块和属性】
Android.bp描述的编译对象都是以模块为组织单位的,定义⼀个模块从模块的类型开始,模块有不同
的类型,模块包含⼀些属性,下⾯举⼀个例⼦来具体说明:
cc_binary {
name: ”avbctl”,
defaults: [“avb_defaults”],
static_libs: [
“libavb_user”,
“libfs_mgr”,
],
shared_libs: [“libbase”],
srcs: [“tools/”],
}
上⾯例⼦中的cc_binary就是模块类型,表⽰该模块⽬标为⼆进制可执⾏⽂件。如果要编译⼀个APP,那么就使⽤android_app模块,要编译⼀个动态库,那么就使⽤cc_library_shared.soong⼯具⽀持的模块类型
在android/build/soong/androidmk/cmd/中可以查到,有以下:
●模块类型后⾯⽤⼤括号“{}”将模块的所有属性包裹起来。
●每个属性的名字和值⽤中间⽤冒号连接起来,属性值要⽤双引号””包裹起来(如果属性值是变量,变量不需要加双引号):name:
”avbctl”表⽰模块name属性的值为avbctl,就是类⽐Android.mk中的LOCAL_MODULE := avbctl。模块的name属性是必须的,内容必须是独⼀⽆⼆的。如果属性被定义为数组,需要⽤中括号“[]”将数组的各元素包裹起来,每个元素中间⽤逗号“,”连接,⼀般常⽤的属性有name,srcs,cflags, cppflags, shared_libs,static_libs。
●查看全部⽀持的模块和各个模块⽀持的属性定义,请查看这个⽹址:。
●cc_defaults模块⽐较特殊,它表⽰该模块的属性可以被其他模块重复引⽤,类似于我们的头⽂件被其他cpp⽂件引⽤,举例:
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"],
}
●属性可以使⽤列表数组的形式,也可以使⽤unix通配符,例如:”*.java”
●每⼀条完整的属性定义语句加上逗号“,”表⽰结束
●注释包括单⾏注释//和多⾏注释/**/
●最重要的⼀点:⽬前android编译系统同时⽀持mk和bp两种,但是这两种是彼此单独运⾏的,所以bp中依赖的⽬标,例如动态库静态库⽬标,如果库是源代码的形式存在的,那么库的编译脚本必须也是通过bp⽂件编译才能被到,否则⽤mk⽂件编译库,bp会提⽰不到依赖的库⽬标。(paxdroid代码中因为要将framework相关内容编译到android的framework中,⽽android已经全部转还成bp,所以这⼀部分paxdroid也是使⽤的bp来编写的,不然会提⽰不到)
更多的说明可以在android/build/soong/README.md⽂件中查。如果不到想要的,可以在官⽹:
【变量】
变量可以直接定义,使⽤“=”号赋值,例如:
avbctl_srcs = [“tools/”],
cc_binary {
name: ”avbctl”,
defaults: [“avb_defaults”],
static_libs: [
“libavb_user”,
“libfs_mgr”,
],
shared_libs: [“libbase”],
srcs: avbctl_srcs,
}
【条件编译】
例如我们的mk⽂件中包括条件判断:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := fs_mgr
ifeq ($(ENABLE_USER2ENG),true)
LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
LOCAL_CFLAGS += -DENABLE_USER2ENG=1
endif
LOCAL_CFLAGS += -Wno-error=implicit-function-declaration
ifeq ($(shell if [ -d $(TOPDIR)paxdroid/external/libethtool ]; then echo "exist"; else echo "notexist"; fi;), exist)
LOCAL_SHARED_LIBRARIES +=libethtool
endif
include $(BUILD_EXECUTABLE)
我们先分析下上⾯两个条件的意思,如果变量ENABLE_USER2ENG的值为true,那么追加这两个编译参数,否则不追加。第⼆个条件是说如果存在paxdroid/external/libethtool这个⽬录,那么就添加libethtool这个动态库,否则不添加。
要转换为bp写法,就需要我们通过go语⾔写⼀个新⽂件,新建⼀个⾃定义类型的模块,来判断这些条件,⽐如我的修改是
在system/core/fs_mgr/Android.bp,那么要在添加 system/core/fs_mgr/,
//这是申明当前的包名,就是你在哪个⽂件夹下,就写啥
package fs_mgr
//导⼊android的soong⼯具⽬录
import (
"android/soong/android"
"android/soong/cc"
"fmt"
//⼀般情况下不需要引⽤os包,这⾥要判断⽂件夹是否存在需要引⽤
//⼀般情况下不需要引⽤os包,这⾥要判断⽂件夹是否存在需要引⽤
"os"
)
//初始化⼊⼝函数
func init() {
// for DEBUG
fmt.Println("init start")
//注册模块名:fs_mgr_condition,模块名的⽅法⼊⼝是:fs_mgrDefaultsFactory⽅法
android.RegisterModuleType("fs_mgr_condition", fs_mgrDefaultsFactory)
}
//实现fs_mgrDefaultsFactory⽅法:
func fs_mgrDefaultsFactory() (android.Module) {
//如果我们编译的⽬标是bin⽂件,这⾥就是调⽤cc.DefaultsFactory()
//如果编译⽬标是so库,那么调⽤的是cc.LibrarySharedFactory()。这⾥我们举例⽬标是bin⽂件:
module := cc.DefaultsFactory()
//添加装载时的钩⼦函数fs_mgrDefaults
android.AddLoadHook(module, fs_mgrDefaults)
return module
}
//实现钩⼦函数
func fs_mgrDefaults(ctx android.LoadHookContext) {
//这⾥定义我们所有需要受到条件控制的变量,⽐如我们这⾥需要根据条件来控制Cflags和依赖
//的动态库两个变量,所以我们只需要定义这两个即可,按照实际需求定义:
type props struct {
Cflags []string
Shared_libs []string
}
p := &props{}
p.Cflags = getCflags(ctx)
p.Shared_libs = getShared_libs(ctx)
ctx.AppendProperties(p)
}
//实现getCflags(ctx)⽅法
func getCflags(ctx android.BaseContext) ([]string) {
var cppflags []string
fmt.Println("ENABLE_USER2ENG:",
ctx.AConfig().IsEnvTrue("ENABLE_USER2ENG"))
if ctx.AConfig().IsEnvTrue("ENABLE_USER2ENG")
{
cppflags = append(cppflags, "-DALLOW_ADBD_DISABLE_VERITY=1", "-DENABLE_USER2ENG=1") }
return cppflags
}
//实现getShared_libs(ctx)⽅法
func getShared_libs(ctx android.BaseContext) ([]string) {
var shared_libs []string
//判断⽂件是否存在,该⽅法返回两个参数,⼀个isExists,⼀个error
isExists,error := PathExists ("paxdroid/external/libethtool")
if(isExists && error == nil){
//路径存在
shared_libs = append(shared_libs, “libethtool”)
}
return shared_libs
}
/
/实现判断⽂件夹是否存在的⼯具⽅法:
func PathExists(path string) (bool, error) {
_, err := os.Stat(path)gzip是什么文件夹
if err == nil {
return true, nil
return true, nil
}
if os.IsNotExist(err) {
return false, nil
}
return false, err
}
go脚本写完了,相应的再Android.bp⽂件引⽤,如下:
// 这些个必须添加,编译刚刚写的那个go脚本需要的⼀些依赖
bootstrap_go_package {
// 名字和包路径和刚刚写的go⽂件⼀致
name: "soong-fs_mgr",
pkgPath: "android/soong/fs_mgr",
deps: [
"blueprint",
"blueprint-pathtools",
"soong",
"soong-android",
"soong-cc",
"soong-genrule",
],
//这⾥的srcs就写我们刚刚写的go脚本
srcs: [
"",
],
pluginFor: ["soong_build"],
}
// fs_mgr_condition 是我们在go语⾔中⾃定义的模块类型,模块的类型是fs_mgr_condition,// 这个模块名字叫做:fs_mgr_defaults
fs_mgr_condition {
name: "fs_mgr_defaults",
}
//-----------------------------------------------------------------------
//以上所有代码是标准的添加⾃定义模块来实现条件控制的代码,我们可以记下来作为参考,//下⾯的代码才是我们编译具体模块的:
cc_binary {
name: "fs_mgr",
//这⾥引⽤上⾯定义的模块
defaults: ["fs_mgr_defaults"],
cppflags: ["-Wno-error=implicit-function-declaration "],
}
【操作符】
String类型、字符串列表类型和Map类型⽀持操作符“+”,例如:
binder_src_files = ["lib/libsystool_client/binder.c"],
cc_library_static {
srcs: ["lib/libsystool_client/systool_client_static.c",
"ipc/pipe_client.c",
] + binder_src_files,
}
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论