目錄
1 -> 定制HAP多目標構建產物
1.1 -> 定義產物的HAP包名
1.2 -> 定義產物的deviceType
1.3 -> 定義產物的distributionFilter
1.4 -> 定義產物preloads的分包
1.5 -> 定義產物的source源碼集-pages
1.6 -> 定義產物的source源碼集-sourceRoots
1.7 -> 定義產物的資源
1.8 -> 定義產物的icon、label、launchType
1.9 -> 定義C++工程依賴的.so文件
2 -> 構建定義的目標產物
3 -> 調試和運行指定的Target
1 -> 定制HAP多目標構建產物
每一個Entry/Feature模塊均支持定制不同的target,通過在模塊中的build-profile.json5文件中實現差異化定制,當前支持HAP包名、設備類型(deviceType)、源碼集(source)、資源(resource)、buildOption配置項(如C++依賴的.so、混淆配置、abi類型、cppFlags等)、分發規則(distributionFilter)的定制。
定義目標產物target
每一個target對應一個定制的HAP,因此,在定制HAP多目標構建產物前,應提前規劃好需要定制的target名稱。例如,以ArkTS Stage模型為例,定義一個免費版和付費版,模塊級build-profile.json5文件示例如下:
{ "apiType": 'stageMode', "buildOption": { }, "targets": [ //定義不同的target { "name": "default", //默認target名稱default }, { "name": "free", //免費版target名稱 }, { "name": "pay", //付費版target名稱 } ]
}
按照上述target的定義,在編譯構建時,會同時打包生成default、free和pay三個不同的HAP。
1.1 -> 定義產物的HAP包名
每一個target均可以指定產物命名。
{ "apiType": "stageMode", "buildOption": { }, "targets": [ { "name": "default", "output": { "artifactName": "customizedTargetOutputName-1.0.0" //產物名稱為customizedTargetOutputName-1.0.0} }, { "name": "free", "output": { "artifactName": "customizedTargetOutputName1-1.0.0" //產物名稱為customizedTargetOutputName1-1.0.0} }, { "name": "pay", "output": { "artifactName": "customizedTargetOutputName2-1.0.0" //產物名稱為customizedTargetOutputName2-1.0.0} } ]
}
如果已配置簽名,target產物對應的HAP包名為開發者定制的名稱;如果未配置簽名,target產物對應的HAP包名為開發者定制的名稱+unsigned。
1.2 -> 定義產物的deviceType
每一個target均可以指定支持的設備類型deviceType,也可以不定義。如果不定義,則該target默認支持config.json或module.json5中定義的設備類型。
同時,在定義每個target的deviceType時,支持的設備類型必須在config.json或module.json5中已經定義。例如,在上述定義的3個target中,分別定義default默認支持所有設備類型,free和pay版本只支持phone設備。
{ "apiType": 'stageMode', "buildOption": { }, "targets": [ { "name": "default", //未定義deviceType,默認支持config.json或module.json5中定義的設備類型 }, { "name": "free", "config": { "deviceType": [ //定義free支持的設備類型為phone "phone" ] } }, { "name": "pay", "config": { "deviceType": [ //定義pay支持的設備類型為phone "phone" ] } } ]
}
1.3 -> 定義產物的distributionFilter
在未定義target的分發規則distributionFilter時,以module配置distroFilter/distributionFilter分發規則為準。
針對多target存在相同設備類型deviceType的場景,相同設備類型的target需要指定分發規則distributionFilter。
如果是FA工程,請將distributionFilter字段替換為distroFilter。
{ "apiType": "stageMode", "buildOption": { }, "targets": [ { "name": "default", }, { "name": "free", "config": { "distributionFilter": { // 具體請參考distributionFilter標簽"screenShape": { // 屏幕形狀枚舉 "policy": "include", "value": ["circle"] } } } }, { "name": "pay", "config": { "distributionFilter": { "screenShape": { "policy": "include", "value": ["rect"] } } } } ]
}
1.4 -> 定義產物preloads的分包
對于元服務,每一個target均可以指定preloads的分包,也可以不定義。如果不定義,則以module.json5中的配置為準。
{ "apiType": 'stageMode', "showInServiceCenter": true, "buildOption": { }, "targets": [ { "name": "default", },{ "name": "free", },{ "name": "pay", "config": { "atomicService": { "preloads": [ //指定preloads的分包 { "moduleName": "preloadSharedLibrary"} ] } } } ]
}
1.5 -> 定義產物的source源碼集-pages
對于source源碼集的定制,由于Stage模型和FA模型的差異,Stage模型支持對pages源碼目錄的page頁面進行定制,FA模型則支持對Ability源碼目錄下的page頁面進行定制。
- 例如,Stage模型中的工程,在模塊的pages目錄下分別定義了Index.ets、Page1.ets和Page2.ets三個頁面。其中default使用了Index.ets頁面;free使用了Index.ets和Page1.ets頁面;pay使用了Index.ets和Page2.ets頁面,則示例代碼如下所示:
{ "apiType": 'stageMode', "buildOption": { }, "targets": [ { "name": "default", "source": { //定義Stage模型中默認版target的pages源碼文件"pages": [ "pages/Index" ] } }, { "name": "free", "config": { "deviceType": [ "phone" ] }, "source": { //定義Stage模型中免費版target的pages源碼文件"pages": [ "pages/Index", "pages/Page1" ] } }, { "name": "pay", "config": { "deviceType": [ "phone" ] }, "source": { //定義Stage模型中付費版target的pages源碼文件"pages": [ "pages/Index", "pages/Page2" ] } } ] }
- 例如,FA模型中的工程,在模塊的MainAbility中定義了index.ets、page1.ets和page2.ets,其中:default使用了index.ets 頁面;free使用了index.ets和page1.ets頁面;pay使用了index.ets和page2.ets頁面。
{ "apiType": 'faMode', "buildOption": { }, "targets": [ { "name": "default", "source": { //定義FA模型中默認版target的pages源碼文件"abilities": [ { "name": ".MainAbility", "pages": [ "pages/index" ] } ], } }, { "name": "free", "config": { "deviceType": [ "phone" ] }, "source": { //定義FA模型中免費版target的pages源碼文件"abilities": [ { "name": ".MainAbility", "pages": [ "pages/index", "pages/page1" ] } ], } }, { "name": "pay", "config": { "deviceType": [ "phone" ] }, "source": { //定義FA模型中付費版target的pages源碼文件"abilities": [ { "name": ".MainAbility", "pages": [ "pages/index", "pages/page2" ] } ], } } ] }
1.6 -> 定義產物的source源碼集-sourceRoots
在模塊的主代碼空間(src/main)下,承載著開發者編寫的公共代碼。如果開發者需要實現不同target之間的差異化邏輯,可以使用差異化代碼空間(sourceRoots)。配合差異化代碼空間的能力,可以在主代碼空間中代碼不變的情況下,針對不同的target,編譯對應的代碼到最終產物中。
概念說明
- packageName:當前模塊的oh-package.json5中的name字段對應的值。
- sourceRoot:<defaultSourceRoot> | <targetSourceRoot> ,其中<defaultSourceRoot>是 src/main,<targetSourceRoot>可自定義,尋址優先級為 <targetSourceRoot> > <defaultSourceRoot>。
- sourcePath:在sourceRoot中的代碼結構目錄。
- sourceFileName:代碼目錄下的ets文件名。
例如以下工程目錄:
entry
|--src
|----main
|------ets
|--------code
|----------test.ets
|----target
|------util
|--------util.ets
|--index.ets
- packageName為entry。
- sourceRoot為src/main、src/target。
- sourcePath為ets/code、util。
- sourceFileName為test.ets、util.ets。
規格限制
1. import xxx from '<packageName>/sourcePath/sourceFileName' :通過packageName的方式,省略sourceRoot,可以實現不同target下的差異化構建。
2. 支持hap、hsp、har(請注意:開啟文件/文件夾名稱混淆的har模塊需要使用-keep-file-name指定sourceRoot,sourcePath,sourceFileName對應的文件/文件夾名稱不被混淆)。
3. 不支持跨模塊引用。
4. 不支持動態import。
編譯時模塊target的選擇優先級說明
在模塊編譯的過程中,該模塊使用的sourceRoots由當前模塊編譯時的target來決定。當前模塊編譯時選擇target的優先級則為:命令行顯式指定 > 直接引用方target > default。
如以下示例:
hap -> hsp -> har(->表示依賴)
其中hap和hsp存在三個target:default、custom、static,而har存在兩個target:default、static。
- 執行編譯命令:hvigorw -p module=hap@custom assembleHap,hap指定target為custom進行編譯,那么三個模塊編譯時的target分別為:
hap: custom,命令行顯式指定;
hsp: custom,命令行沒有顯式指定,則基于直接引用方查找,hsp的直接引用方為hap,hap的target為custom,hsp存在該target,則hsp的target為custom;
har: default,命令行沒有顯式指定,則基于直接引用方查找,har的直接引用方為hsp,hsp的target為custom,har不存在該target,則har的target為default;
- 執行編譯命令:hvigorw -p module=hap@custom,hsp@static assembleHap assembleHsp,hap指定target為custom,hsp則指定target為static進行編譯,那么三個模塊編譯時的target分別為:
hap: custom,命令行顯式指定;
hsp: static,命令行顯式指定;
har: static,命令行沒有顯式指定,則基于直接引用方查找,har的直接引用方為hsp,hsp的target為static,har存在該target,則har的target為static。
- 在當前依賴關系的基礎上,添加依賴:hap -> har。執行編譯命令:hvigorw -p module=hap@custom,hsp@static assembleHap assembleHsp。由于har沒有顯示指定target,且存在兩個target不同的直接引用方(hap和hsp,對應的target分別為custom和static),所以編譯過程中har的target只能二選一。基于這種場景,建議開發者顯示指定模塊的target進行編譯:hvigorw -p module=hap@custom,hsp@static,har@static assembleHap assembleHsp assembleHar。
示例
1. 在entry模塊的build-profile.json5中添加sourceRoots:
{"apiType": "stageMode","buildOption": {},"targets": [ { "name": "default", "source": { "sourceRoots": ["./src/default"] // 配置target為default的差異化代碼空間} }, { "name": "custom", "source": { "sourceRoots": ["./src/custom"] // 配置target為custom的差異化代碼空間} } ]
}
2. 在src目錄下新增default/Test.ets和custom/Test.ets,新增后的模塊目錄結構:
entry
? |--src
? ? |--main
? ? ? |--ets
? ? ? ? |--pages
? ? ? ? ? |--Index.ets
? ? |--default
? ? ? |--Test.ets ?// 新增
? ? |--custom
? ? ? |--Test.est ?// 新增 ?
3. 在default/Test.ets中寫入代碼:
export const getName = () => "default"
4. 在custom/Test.ets中寫入代碼:
export const getName = () => "custom"
5. 修改src/main/ets/pages/Index.ets的代碼:
import { getName } from 'entry/Test'; // 其中entry為模塊級的oh-package.json5中的name字段的值
@Entry
@Component
struct Index { @State message: string = getName(); build() { RelativeContainer() { Text(this.message) } .height('100%') .width('100%') }
}
6. 在工程級的build-profile.json5中配置targets:
{"app": {"signingConfigs": [],"products": [{"name": "default","signingConfig": "default","compatibleSdkVersion": "5.0.2(14)","runtimeOS": "HarmonyOS",},{"name": "custom","signingConfig": "default","compatibleSdkVersion": "5.0.2(14)","runtimeOS": "HarmonyOS",}],"buildModeSet": [{"name": "debug",},{"name": "release"}]},"modules": [{"name": "entry","srcPath": "./entry","targets": [{"name": "default","applyToProducts": ["default"]},{"name": "custom","applyToProducts": ["default"]}]}]
}
7. Sync完成后,選擇entry的target為default,點擊Run,界面展示default;選擇entry的target為custom,點擊Run,則界面展示custom。
1.7 -> 定義產物的資源
每個target使用的資源文件可能存在差異,在開發過程中,開發者可以將每個target所使用的資源存放在不同的資源目錄下。其中,ArkTS工程支持對main目錄下的資源文件目錄(resource)進行定制;JS工程支持對main目錄下的資源文件目錄(resource)及 Ability下的資源文件目錄(res)進行定制。如下為ArkTS工程的資源文件目錄定制示例:
{ "apiType": 'stageMode', "buildOption": { }, "targets": [ { "name": "default", "source": { "pages": [ "pages/Index" ] }, "resource": { //定義默認版target使用的資源文件目錄 "directories": [ "./src/main/resources_default" ] } }, { "name": "free", "config": { "deviceType": [ "phone" ] }, "source": { "pages": [ "pages/Index", "pages/Page1" ] }, "resource": { //定義免費版target使用的資源文件目錄 "directories": [ "./src/main/resources_default", "./src/main/resources_free" ] } }, { "name": "pay", "config": { "deviceType": [ "phone" ] }, "source": { "pages": [ "pages/Index", "pages/Page2" ] }, "resource": { //定義付費版target使用的資源文件目錄,該功能在API 9及以上版本的工程中生效 "directories": [ "./src/main/resources_default", "./src/main/resources_pay" ] } } ]
}
請注意,如果target引用的多個資源文件目錄下,存在同名的資源,則在構建打包過程中,將按照配置的資源文件目錄順序進行選擇。例如,上述付費版target引用的資源中,resource_default和resource_pay中存在同名的資源文件,則resource_default中的資源會被打包到HAP中。
1.8 -> 定義產物的icon、label、launchType
針對每一個的target的ability,均可以定制不同的icon、label和launchType。如果不定義,則該target采用module.json5中module.abilities配置的icon、label,launchType默認為"singleton"。示例如下所示:
{ "apiType": 'stageMode', "buildOption": { }, "targets": [ { "name": "default", "source": {"abilities": [{"name": "EntryAbility","icon":"$media:layered_image","label":"$string:EntryAbility_label","launchType": "singleton"}]}}, { "name": "free", "source": {"abilities": [{"name": "EntryAbility","icon":"$media:layered_image","label":"$string:EntryAbility_label","launchType": "multiton"}]}}] }
1.9 -> 定義C++工程依賴的.so文件
在 C++ 工程中,可以對每個target依賴的.so文件進行定制。例如某模塊依賴了function1.so、function2.so和function3.so三個文件,其中target為default的產物依賴了function1.so和function2.so;其中target為vip的產物依賴了function1.so和 function3.so,則示例代碼如下所示:
{"apiType": 'stageMode',"buildOption": {"externalNativeOptions": {"path": "./src/main/cpp/CMakeLists.txt","arguments": [],"abiFilters": ["arm64-v8a","x86_64"],"cppFlags": "",}},"targets": [ //定義不同的target {"name": "default","config": {"buildOption": {"nativeLib": {"filter": {//按照.so文件的優先級順序,打包最高優先級的function1.so文件 "pickFirsts": ["**/function1.so"],//排除不打包的function3.so文件 "excludes": ["**/function3.so"],//允許當.so中資源重名沖突時,使用高優先級的.so文件覆蓋低優先級的.so文件 "enableOverride": true}}}}},{"name": "vip","config": {"buildOption": {"nativeLib": {"filter": {//按照.so文件的優先級順序,打包最高優先級的function1.so文件 "pickFirsts": ["**/function1.so"],//排除不打包的function2.so文件 "excludes": ["**/function2.so"],//允許當.so中資源重名沖突時,使用高優先級的.so文件覆蓋低優先級的.so文件 "enableOverride": true}}}}}]
}
2 -> 構建定義的目標產物
每個target對應一個HAP,每個product對應一個APP包,在編譯構建時,如果存在多product或多target時,您可以指定編譯具體的包。
單擊右上角的
圖標,指定需要打包的Product及Target,然后單擊Apply保存。例如選擇"ProductA"中,entry模塊對應的"free" Target。
- Product:選擇需要構建的 APP 包。
- Build Mode:選擇編譯模式。
- Product Info:該APP包的BundleName和SigningConfig信息。
- Target Select:選擇各個模塊的Target,該Target需要包含在定義的Product中才能選擇,如果未包含則顯示"No Target to apply"。
然后執行編譯構建APP/HAP的任務:
- 單擊菜單欄的Build > Build Hap(s)/APP(s) > Build APP(s)?,構建指定的Product對應的APP。例如,按照上述設置,此時DevEco Studio將構建生成 ProductA 的 APP 包。default和ProductB的APP均不會生成。
- 單擊菜單欄的Build > Build Hap(s)/APP(s) > Build Hap(s),構建指定Product下的所有Target對應發的HAP。例如,按照上述配置,此時DevEco Studio將構建生成entry模塊下default和free的HAP。
如果您想將某個模塊下的指定target打包生成HAP,可以在工程目錄中,單擊模塊名,然后再單擊Build > Make Module?‘模塊名?’,此時DevEco Studio將構建生成模塊下指定target對應的包。例如,按照上述配置,此時DevEco Studio將構建生成entry模塊下free的HAP,不會生成default的HAP。
3 -> 調試和運行指定的Target
使用DevEco Studio調試或運行應用/元服務時,每個模塊只能選擇其中的一個target運行,可以通過單擊右上角的
圖標,指定需要調試或運行的Product下對應的Module Target,然后單擊Apply保存。
說明
在選擇需要調試或運行的target時,需要注意選擇該target所屬的Product,否則將找不到可調試和運行的target。
感謝各位大佬支持!!!
互三啦!!!