花式吊打源碼中 Android.mk 集錦

一、區分版本編譯 app

最近在修改 Android P 中的 Camera 應用,看到了 mk 的強大之處,特記錄一下。

ifeq ($(MTK_CAMERA_APP_VERSION), 3)
LOCAL_ROOT_PATH:= $(call my-dir)

include $(LOCAL_ROOT_PATH)/host/Android.mk
include $(LOCAL_ROOT_PATH)/portability/Android.mk
include $(LOCAL_ROOT_PATH)/tests/Android.mk
include $(LOCAL_ROOT_PATH)/feature/setting/matrixdisplay/matrixdisplay_ext/Android.mk
include $(LOCAL_ROOT_PATH)/testscat/Android.mk
#include $(LOCAL_ROOT_PATH)/feature/pluginroot/Android.mk
#include $(LOCAL_ROOT_PATH)/feature/mode/panorama/Android.mk
#include $(LOCAL_ROOT_PATH)/feature/mode/pip/Android.mk
#include $(LOCAL_ROOT_PATH)/feature/mode/vsdof/Android.mk
#include $(LOCAL_ROOT_PATH)/feature/mode/slowmotion/Android.mk
endif

這是 Camera2 app 下的 mk 文件,一開始判斷了宏 MTK_CAMERA_APP_VERSION,可以理解爲要編譯的 Camera 版本,經過搜索發現定義在 ProjectConfig.mk

MS5Pp9.png

編譯不同版本時會選擇不同的 Camera 進行編譯打包,Camera1 中 mk 的 MTK_CAMERA_APP_VERSION 對應值爲 2,如果你單獨 mmm Camera1,則會出現如下錯誤

ninja: error: unknown target ‘MODULES-IN-vendor-mediatek-proprietary-packages-apps-Camera1’

MS5Inx.png

拓展延伸

經過測試,預裝 app 編譯需要在 core.mk 中增加定義,這裏定義的是 app 的名稱,而不是 app 的父文件夾名稱,只不過我們一般都寫成一樣。

應用場景,

在 core.mk 中增加 AndroidDemo 定義

在 package/app/AndroidO/AndroidDemo.apk 路徑下放置 app ($(DEMO_APP_VERSION), 8)

在 package/app/AndroidP/AndroidDemo.apk 路徑下放置 app ($(DEMO_APP_VERSION), 9)

ifeq ($(DEMO_APP_VERSION), 8)
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)

# Module name should match apk name to be installed
LOCAL_MODULE := AndroidDemo
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
include $(BUILD_PREBUILT)
endif

修改 ProjectConfig.mk 中的 DEMO_APP_VERSION 值編譯,out 目錄下 AndroidDemo 文件中生成的 apk 隨之改變

吊打方式二,快來看呀, AndroidDemo 文件下的 Android.mk 原來還可這樣寫
在這裏插入圖片描述
在這裏插入圖片描述

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

# Module name should match apk name to be installed
LOCAL_MODULE := AndroidDemo
LOCAL_MODULE_TAGS := optional

ifeq ($(findstring YM,$(MTK_BUILD_VERNO)), YM)
LOCAL_SRC_FILES := ./AndroidO/AndroidDemo.apk
else
LOCAL_SRC_FILES := ./AndroidP/AndroidDemo.apk
endif
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)


LOCAL_CERTIFICATE := PRESIGNED
ifeq ($(findstring YM,$(MTK_BUILD_VERNO)), YM)
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
else
LOCAL_PRIVILEGED_MODULE := true
endif
include $(BUILD_PREBUILT)

寫了這麼大堆,讓我們來康康都是什麼意思,((findstringYM,(findstring YM,(MTK_BUILD_VERNO)), YM) 是 mk 中的字符串查找函數,意思就是

在 MTK_BUILD_VERNO 中查找 YM 字符串,如果找到則返回該字符串,找不到則返回空字符串,前面的查找結果再和後面的 YM 判斷是否相等

MTK_BUILD_VERNO 在 ProjectConfig.mk 中定義,對應系統版本號。翻譯一下就是,如果版本號中帶 YM 字符串,就編譯 AndroidO 文件下

的 apk,反之則編譯 AndroidP 文件下。再看編譯後app的存放路徑,如果是 YM 版本路徑爲 data/app,反之路徑爲 system/priv-app,

如果 LOCAL_MODULE_PATH 和 LOCAL_PRIVILEGED_MODULE 都不指定,則默認路徑爲 system/app

二、預裝可卸載等相關配置

放置到 system/app/應用名稱 路徑下,在 build/target/product/core.mk 中增加 WeChat \

預裝不可卸載

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed
LOCAL_MODULE := WeChat
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
include $(BUILD_PREBUILT)

預裝可卸載,恢復出廠不恢復

mtk 主要通過 userdata.img 來保存預裝可卸載的 app

6.0 寫法

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed
LOCAL_MODULE := WeChat
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
include $(BUILD_PREBUILT)

8.1 以上寫法

mk 和 6.0 的一樣,都是通過 userdata.img 來實現,首先需要確保你的文件系統類型爲 ext4,

然後再回退 PackageManagerService.java 中 Google 的更新, 註釋 else 語句塊拋出的異常即可

frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java

--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -11394,6 +11394,10 @@
                                     + " but expected at " + known.codePathString
                                     + "; ignoring.");
                         }
+                    } else {
+                        throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
+                                "Application package " + pkg.packageName
+                                + " not found; ignoring.");
                     }
                 }
             }

具體可參考爲什麼 Android8.1 使用f2fs文件系統的預置app到data/app不行?

預裝可卸載,恢復出廠可恢復

6.0 寫法

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed
LOCAL_MODULE := WeChat
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
LOCAL_MODULE_PATH := $(TARGET_OUT)/vendor/operator/app
include $(BUILD_PREBUILT)

8.1 以上寫法

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# Module name should match apk name to be installed
LOCAL_MODULE := WeChat
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_MODULE_CLASS := APPS
LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
LOCAL_CERTIFICATE := PRESIGNED
include $(BUILD_PREBUILT)

還需要在

vendor/mediatek/proprietary/frameworks/base/data/etc/pms_sysapp_removable_system_list.txt

增加 WeChat 的包名,這樣就可以卸載了

三、mk 中常用參數簡介

LOCAL_PATH:= $(call my-dir)

宏函數’my-dir’,由編譯系統提供,用於返回當前路徑,在開發樹中查找源文件

—-----------------------------------------------------------------------------

include $(CLEAR_VARS) 開始編譯模塊

include $(BUILD_XXX) 結束編譯模塊

—-----------------------------------------------------------------------------

一個 mk 中可以編譯多個模塊,可定義多組模塊,但注意開始和結束需要一一對應

CLEAR_VARS 指的是 clear_vars.mk,由編譯系統提供,它會讓GNU MAKEFILE 爲你清除除 LOCAL_PATH 以外的所有 LOCAL_XXX 變量,

如 LOCAL_MODULE,LOCAL_SRC_FILES,LOCAL_SHARED_LIBRARIES,LOCAL_STATIC_LIBRARIES 等。

LOCAL_MODULE_TAGS := eng/debug/tests/optional

該模塊在 eng/debug/tests/所有版本下編譯 大多 mk 中都是定義爲 optional,eng 在 mtkLogger 中有定義

LOCAL_STATIC_JAVA_LIBRARIES :=
android-support-v4 當前模塊依賴的Java靜態庫,所謂靜態庫,即編譯完會存在於你的module裏面,成爲其一部分

LOCAL_SRC_FILES :=
$(call all-java-files-under, src) 當前模塊需要編譯的 java 文件,指定 src 目錄下

LOCAL_RESOURCE_DIR :=
$(LOCAL_PATH)/res
prebuilts/sdk/current/support/v7/recyclerview/res \ 當前模塊需要編譯的資源文件

LOCAL_PACKAGE_NAME := MtkLauncher3

編譯出對應的 app 名稱

LOCAL_OVERRIDES_PACKAGES := Home Launcher2 Launcher3

當前編譯模塊覆蓋對應的模塊,Home Launcher2 Launcher3 將不再進行編譯,跳過

LOCAL_CERTIFICATE := PRESIGNED/platforms

簽署當前應用的證書名稱,保留應用原來的/籤系統簽名

LOCAL_SDK_VERSION := current

標記 SDK 的 version 狀態,可選值爲 current system_current test_current core_current

LOCAL_PRIVATE_PLATFORM_APIS := true

模塊編譯時能引用 hide 的 api

LOCAL_MANIFEST_FILE := go/AndroidManifest.xml

指定當前編譯選用的 AndroidManifest.xml 文件,LOCAL_MANIFEST_FILE 省略時默認採用根目錄下

吊打方式三,假設你需要對 Launcher3 編譯帶桌面屬性的版本和不帶桌面屬性的版本,有時候客戶自己做桌面應用,就不需要系統的 Launcher 在這裏插入圖片描述

一般都是將 AndroidManifest.xml 中的屬性配置註釋

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <!-- <category android:name="android.intent.category.HOME" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.MONKEY"/>
        <category android:name="android.intent.category.LAUNCHER_APP" /> -->
</intent-filter>

現在通過 LOCAL_MANIFEST_FILE 我們依舊可以根據版本號加載不同文件下的 xml

ifeq ($(findstring YM,$(MTK_BUILD_VERNO)), YM)
LOCAL_MANIFEST_FILE := noLauncher/AndroidManifest.xml
else
LOCAL_MANIFEST_FILE := Launcher/AndroidManifest.xml
endif

四、mk 和 shell 腳本同時編譯

先來介紹一些 mk 中常用的路徑,便於作爲參數傳遞至 shell 腳本中

${LOCAL_PATH} 當前 mk 所處的路徑

${OUT} 當前整編結束後 out 目錄的全路徑 /home/xxx/android8.1/6737_oreo/alps/out/target/product/k37tv1_64_bsp

${TARGET_OUT} 當前整編結束後 system 目錄的根路徑 out/target/product/k37tv1_64_bsp/system

${TARGET_OUT_ROOT} =out/target

${PRODUCT_OUT}=out/target/product/k37tv1_64_bsp

${TARGET_COPY_OUT_VENDOR}=vendor

${TARGET_PRODUCT_OUT_ROOT}=out/target/product

${TARGET_DEVICE}=k37tv1_64_bsp

上面的宏定義大多在 build\core\envsetup.mk 中可查到,通過如下 mk 進行打印查看結果

LOCAL_PATH := $(call my-dir)

$(info LOCAL_PATH=${LOCAL_PATH})
$(info OUT=${OUT})

$(info TARGET_OUT=${TARGET_OUT})
$(info TARGET_OUT_ROOT=${TARGET_OUT_ROOT})
$(info PRODUCT_OUT=${PRODUCT_OUT})

$(info TARGET_COPY_OUT_VENDOR=${TARGET_COPY_OUT_VENDOR})
$(info TARGET_PRODUCT_OUT_ROOT=${TARGET_PRODUCT_OUT_ROOT})
$(info TARGET_DEVICE=${TARGET_DEVICE})

COPY_FILES = $(shell ${LOCAL_PATH}/test.sh ${OUT} ${TARGET_DEVICE})
$(info ${COPY_FILES})

$(shell ${LOCAL_PATH}/test.sh ${OUT} ${TARGET_DEVICE}) 就是執行 sh 的命令,

傳遞 out 和 target_device,在 test.sh 中通過 ${1} ${2} 獲取

#!/bin/bash

echo -e "out_path=${1}"

echo -e "target_device_name=${2}"

rm  ${OUT}/lk.img 

rm  ${OUT}/userdata.img 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章