摘自:https://blog.csdn.net/weixin_30317869/article/details/111981053?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-4&spm=1001.2101.3001.4242
前言
隨著Android版本的迭代,越來越多的移動終端都用上了香噴噴的Android 9和10系統。相對于原來的Android版本,Android 9和10越來越多的使用Android.bp替換以前的Android.mk編譯腳本。我們知道 Android.mk采用Makefile的語言,所以為了控制條件編譯和多版本適配,我們可以在不同產品直接在Android.mk中采用Makefile語言控制編譯。雖然我們也可以通過Android的預置工具androidmk將Android.mk轉換為Android.bp腳本,但是對于有宏控制的條件編譯,androidmk是無能為力的。這是因為Android.bp是類似JSON的純文本形式,對于Android.mk里面流控制部分,在Android.bp里要借助使用go語言檔案去進行控制。好了說了這么多了,我想讀者應該知道這個篇章要寫什么了,那就是手把手帶領大伙手擼一把Android.bp添加宏控制編譯。
一.實戰開始
我們知道在Android原來的編譯腳本Android.mk中添加宏控制有兩種場景,分別是:
無條件控制的宏添加,形如:
#無條件控制宏編譯
LOCAL_CFLAGS += -Wno-error=implicit-function-declaration -DPRINT
有條件控制的宏添加,即滿足某個條件才添加對應的宏,這個場景比較,形如:
#有條件的添加宏控制
ifeq ($(ANDROIDBP_FUN), "YES")
LOCAL_CFLAGS += -DXXX
endif
下面的篇章我們將分別從上述兩種場景出發,來說明在Android.bp中如何添加宏控制。
1.1 無條件控制的宏添加
這里我會以實際案例來說明,怎么一步步添加無條件控制的宏。
1.1.1 撰寫源檔案main.c
#include
int main()
{
#ifdef PRINT//宏控制
printf("Hello world\n");
#endif
#ifdef XXX//宏控制
printf("XXX\n");
#endif
printf("This is AndroidBp Test\n");
return 0;
}
代碼非常簡單,就是一個簡單的列印。在我們正式在Android.bp中添加宏前,我們先看看Android.mk應該怎么撰寫。
1.1.2 撰寫Android.mk編譯腳本
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := AndroidBp
LOCAL_SRC_FILES := main.c
ANDROIDBP_FUN=YES
#有條件的添加宏控制
ifeq ($(ANDROIDBP_FUN), "YES")
LOCAL_CFLAGS += -DXXX
endif
#無條件宏控制
LOCAL_CFLAGS += -Wno-error=implicit-function-declaration -DPRINT
LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
include $(BUILD_EXECUTABLE)
這個也非常簡單,就是編譯一個可執行檔案,并有條件和無條件的添加宏控制。
1.1.3 將Android.mk轉換為Android.bp
在前面的博客中,我們知道Android內置的androidmk工具能將Android.mk轉換為對應的Android.bp,但是當Android.mk中有宏條件控制時就無能為力了。下面讓我們轉換一下看看是否如此:
[SPRD] xxx@pd:~/ssd/xxx/ap/idh.code/xxxxroid/external/AndroidBp$ androidmk Android.mk > Android.bp
[SPRD] xxx@pd:~/ssd/xxx/ap/idh.code/xxxxroid/external/AndroidBp$
查看轉換后生成的Android.bp檔案如下,可以看到androidmk的轉換失效了,
ANDROIDBP_FUN = ["YES"]
cc_binary {
name: "AndroidBp",
srcs: ["main.c"],
// ANDROIDMK TRANSLATION ERROR: unsupported conditional
// ifeq ($(ANDROIDBP_FUN), "YES")
cflags: ["-DXXX"] + [ // ANDROIDMK TRANSLATION ERROR: endif from unsupported contitional
// endif
"-Wno-error=implicit-function-declaration",
"-DPRINT",
],
shared_libs: [
"libcutils",
"liblog",
"libutils",
],
}
1.1.4 Android.bp直接添加宏
通過前面的章節我們可以直接,Android.bp直接添加宏非常簡單,只需要在cflags后面添加對應的宏就OK了。
cflags: ["-DXXX"] + [ // ANDROIDMK TRANSLATION ERROR: endif from unsupported contitional
// endif
"-Wno-error=implicit-function-declaration",
"-DPRINT",
],
1.2 有條件控制的宏添加
根據前面的章節我們知道,在Android.bp中需要有條件的添加宏是不能直接做到的,必須借助go腳本實作動態控制編譯項,那么讓我們看看怎么動態添加如下的宏:
#有條件的添加宏控制
ifeq ($(ANDROIDBP_FUN), "YES")
LOCAL_CFLAGS += -DXXX
endif
1.2.1 添加go腳本
我們在Android.bp同級目錄添加go腳本檔案xxxparser.go,其內容如下:
package xxxparser
import (
"android/soong/android"
"android/soong/cc"
)
func init() {
// resister a module "xxxparser_defaults"
android.RegisterModuleType("xxxparser_defaults", xxxdroidDefaultsFactory)
}
func xxxdroidDefaultsFactory() (android.Module) {
module := cc.DefaultsFactory()
android.AddLoadHook(module, xxxdroidDefaults)
return module
}
func xxxdroidDefaults(ctx android.LoadHookContext) {
type props struct {
Cflags []string
}
p := &props{}
p.Cflags = globalDefaults(ctx)
ctx.AppendProperties(p)
}
func globalDefaults(ctx android.BaseContext) ([]string) {
var cppflags []string
if ctx.AConfig().Getenv("ANDROIDBP_FUN") == "YES" {
cppflags = append(cppflags,"-DXXX")
}
return cppflags
}
1.2.2 Android.bp使用go腳本
在Android.bp開頭位置引入go腳本檔案xxxparser.go,如下:
//引入go腳本
bootstrap_go_package {
name: "soong-xxxparser",
pkgPath: "android/soong/xxxparser",
deps: [
"blueprint",
"blueprint-pathtools",
"soong",
"soong-android",
"soong-cc",
"soong-genrule",
],
srcs: [
"xxxparser.go",
],
pluginFor: ["soong_build"],
}
xxxparser_defaults {
name: "xxxparser_defaults",
}
ANDROIDBP_FUN = ["YES"]
cc_binary {
defaults: ["xxxparser_defaults"],
name: "AndroidBp",
srcs: ["main.c"],
cflags: ["-Wno-error=implicit-function-declaration"],
shared_libs: [
"libcutils",
"liblog",
"libutils",
],
}
以上陳述句可以保證運行Android.bp時,先編譯對應的xxxparser.go運行go腳本時,會首先運行init函式,將 xxxdroidDefaultsFactory函式注冊到module中,之后呼叫xxxdroidDefaultsFactory函式時,會將回呼函式 xxxdroidDefaults注冊進去之后呼叫 privateParserDefaults 時,我們可以從 ctx.AConfig() 中獲取好多屬性
(參考 build/soong/android/config.go 中對 build/soong/android/module.go中的 androidBaseContext interface的各種函式實作),其中有一項是獲取宏值的,之后回呼xxxdroidDefaults添加宏資訊。
1.2.3 演示運行
在正式進行編譯之前,讓我們對編譯環境設定環境變數,我這里使用的是ubuntu編譯,所以這個不用多說怎么配置環境變數了,如下:
[SPRD] xxx@pd:~/ssd/xxx/ap/idh.code$ export ANDROIDBP_FUN=YES
[SPRD] xxx@pd:~/ssd/xxx/ap/idh.code$ echo $ANDROIDBP_FUN
YES
[SPRD] xxx@pd:~/ssd/xxx/ap/idh.code$
執行可執行檔案,查看輸出結果,完美。
1|xxx:/system/bin # ./AndroidBp
XXX
This is AndroidBp Test
xxx:/system/bin #
二.淺析go腳本檔案
由于對go語法不是非常了解,這里只對xxxparser.go中用到的語法進行簡單的決議,以便大家了解,如果想深入的話得自行研究內功了。這里臣妾做不到。
2.1 決議 init 函式
我們知道bin檔案執行先從main開始,而運行go語言時,先運行init函式,我們這里的此函式為:
func init() {
// resister a module "xxxparser_defaults"
// 注冊xxxparser_defaults, 指定要呼叫的方法入口xxxdroidDefaultsFactory
android.RegisterModuleType("xxxparser_defaults", xxxdroidDefaultsFactory)
}
2.1.1 決議RegisterModuleType函式
腳本開頭import了兩個包 “android/soong/android” 和 “android/soong/cc”從 build/soong/ 目錄下搜索 “func RegisterModuleType” 來獲取RegisterModuleType函式的定義位置RegisterModuleType函式位于build/soong/android/register.go中,如下:
func RegisterModuleType(name string, factory ModuleFactory) {
moduleTypes = append(moduleTypes, moduleType{name, ModuleFactoryAdaptor(factory)})
}
可以看出將 name 和 ModuleFactoryAdaptor(factory) append到 var moduleTypes []moduleType 中
moduleType型別如下:
type moduleType struct {
name string
factory blueprint.ModuleFactory
}
2.1.2 查找ModuleFactory型別
xxxparser.go 開頭 import “github.com/google/blueprint”我們從github上下載 github.com/google/blueprint/ 倉后,搜索 “type ModuleFactory” 來獲取 ModuleFactory 定義位置,位于 github.com/buleprint/context.go中,如下:
// A ModuleFactory function creates a new Module object. See the
// Context.RegisterModuleType method for details about how a registered
// ModuleFactory is used by a Context.
type ModuleFactory func() (m Module, propertyStructs []interface{})
可以看出ModuleFactory為一函式指標,形參為null,回傳型別為 Module, []interface{}。
注: 此處我們詳細的講解了如何搜索變數名和函式名的位置,即關鍵字 “type xxx"和"func xxx”,之后不再給出詳細步驟。
2.1.3 決議ModuleFactoryAdaptor函式
通過搜索如下所示:
// ModuleFactoryAdapter Wraps a ModuleFactory into a blueprint.ModuleFactory by converting an Module
// into a blueprint.Module and a list of property structs
func ModuleFactoryAdaptor(factory ModuleFactory) blueprint.ModuleFactory {
return func() (blueprint.Module, []interface{}) {
module := factory()
return module, module.GetProperties()
}
}
此處將xxxparser.go中的 type ModuleFactory func() Module 函式指標,轉換成 blueprint.ModuleFactory 型別的函式指標。
2.2 決議回呼函式 xxxdroidDefaultsFactory
從勺ò干以得知,xxxdroidDefaultsFactory型別為 type ModuleFactory func() Module
我們實作了這個函式為:
func xxxdroidDefaultsFactory() (android.Module) {
module := cc.DefaultsFactory()
android.AddLoadHook(module, xxxdroidDefaults)
return module
}
2.2.1 決議 DefaultsFactory()函式
函式實作見 build/soong/cc/cc.go,如下:
func DefaultsFactory(props ...interface{}) android.Module {
module := &Defaults{}
module.AddProperties(props...)
module.AddProperties(
&BaseProperties{},
&VendorProperties{},
&BaseCompilerProperties{},
&BaseLinkerProperties{},
&LibraryProperties{},
&FlagExporterProperties{},
&BinaryLinkerProperties{},
&TestProperties{},
&TestBinaryProperties{},
&UnusedProperties{},
&StlProperties{},
&SanitizeProperties{},
&StripProperties{},
&InstallerProperties{},
&TidyProperties{},
&CoverageProperties{},
&SAbiProperties{},
&VndkProperties{},
<OProperties{},
&PgoProperties{},
&android.ProtoProperties{},
)
android.InitDefaultsModule(module)
return module
}
好了,就到這里了。不是本人賴,是因為這個我也是參考別人的。但是各位放心,后續我會繼續將本章接力下去繼續分析精進的。
寫在最后
Android.bp正確姿勢添加宏控制編譯指南中介紹的兩種添加宏的場景就到這里了,我想通過這個檔案大伙一定能構建自己的Android.bp腳本了。好了最后如果文章對你有幫助麻煩點個贊,如果覺得很low也可以拍個磚一起探討。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/284409.html
標籤:Google技術社區
