Android MakeFile + 预置apk



一、编译    
1、**五个阶段**:词法分析、语法分析、语义分析与中间代码产生、优化、目标代码生成

2、makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。


3、makefile文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。

**android.mk与MakeFile的区别**
android.mk是Android操作系统编译的时候使用的编译规则文件。Makefile是Linux操作系统编译的时候使用的编译规则文件。作用是一样的,只是名称不一样。

二、Android MakeFile
1、 **Android.mk基本组成**

    1、LOCAL_PATH 定义了当前模块的相对路径,必须出现在所有的编译模块之前 。Android.mk和需要编译的源文件在同一目录下,所以定义成如下形式:LOCAL_PATH:=$(call my-dir)上面的语句的意思是将LOCAL_PATH变量定义为本文件所在目录路径。

    2、每个编译模块由include$(CLEAR_VARS) 开始,由include $(BUILD_XXX) 结束 ,include $(CLEAR_VARS)是一个编译模块的开始,它会清空除LOCAL_PATH之外的所有LOCA_XXX变量 
    3、include $(BUILD_XXX)描述了编译目标   
    4、LOCAL_SRC_FILES定义了本模块编译使用的源文件,采用的是基于LOCAL_PATH的相对路径  
    5、 LOCAL_MODULE 定义了本模块的模块名
    6、include $(BUILD_SHARED_LIBRARY)表示编译成动态库。
    7、 include $(BUILD_EXECUTABLE)表示编译成可执行程序

  
2、**编译目标**
上面用到include $(CLEAR_VARS)和include $(BUILD_HOST_EXECUTABLE),那么他们是在哪里定义的呢?除了BUILD_HOST_EXECUTABLE还有哪些BUILD_XXX目标呢?
它们的定义位于**build/core/config.mk**文件,当然config.mk文件定义的编译目标也很多,下面列举几个常用的目标:
编译目标  说明
BUILD_HOST_STATIC_LIBRARY  主机上的静态库
BUILD_HOST_SHARED_LIBRARY  主机上的动态库
BUILD_HOST_EXECUTABLE  主机上的可执行文件
BUILD_STATIC_LIBRARY  目标设备上的静态库
BUILD_SHARED_LIBRARY  目标设备上的动态库
BUILD_EXECUTABLE  目标设备上的可执行文件
BUILD_JAVA_LIBRARY  JAVA库
BUILD_STATIC_JAVA_LIBRARY  静态JAVA库
BUILD_HOST_JAVA_LIBRARY  主机上的JAVA库
BUILD_PACKAGE  APK程序


**3、指定相关的变量**

>
LOCAL_ASSET_FILES :在Android.mk文件中编译应用程序(BUILD_PACKAGE)时设置此变量,表示资源文件,通常会定义成LOCAL_ASSET_FILES += $(call find-subdir-assets)
LOCAL_CC :指定C编译器
LOCAL_CERTIFICATE  :签名认证
LOCAL_JAVA_LIBRARIES:编译java应用程序和库的时候指定包含的java类库,目前有core和framework两种,多数情况下定义成:LOCAL_JAVA_LIBRARIES := core framework。注意LOCAL_JAVA_LIBRARIES不是必须的,而且编译APK时不允许定义(系统会自动添加)

LOCAL_LDLIBS :为可执行程序或者库的编译指定额外的库,指定库以"-lxxx"格式,举例: LOCAL_LDLIBS += -lcurses -lpthread
           LOCAL_LDLIBS += -Wl,-z,origin
          
LOCAL_MODULE :生成的模块的名称(注意应用程序名称用LOCAL_PACKAGE_NAME而不是LOCAL_MODULE)
LOCAL_MODULE_PATH :生成模块的路径
LOCAL_MODULE_TAGS :生成模块的标记
LOCAL_PRELINK_MODULE:是否需要预连接处理(默认需要,用来做动态库优化)
LOCAL_REQUIRED_MODULES:指定模块运行所依赖的模块(模块安装时将会同步安装它所依赖的模块)
LOCAL_SHARED_LIBRARIES: 可链接动态库
LOCAL_SRC_FILES :编译源文件
LOCAL_STATIC_LIBRARIES: 可链接静态库
LOCAL_WHOLE_STATIC_LIBRARIES :指定模块所需要载入的完整静态库
LOCAL_STATIC_LIBRARIES 表示编译本模块时需要链接的静态库
    LOCAL_C_INCLUDES 表示了本模块需要引用的include文件
    LOCAL_ACP_UNAVAILABLE 表示是否支持acp,如果支持acp,则使用acp进行拷贝,否则使用linux cp拷贝,本模块编译acp,当然是不支持acp了。
   
**4、android最顶层的目录结构如下:**

|-- Makefile        (全局的Makefile)   
|-- bionic          (Bionic含义为仿生,这里面是一些基础的库的源代码)
|-- bootloader      (引导加载器)   
|-- build           (build目录中的内容不是目标所用的代码,而是编译和配置所需要的脚本和工具)   
|-- dalvik          (JAVA虚拟机)   
|-- development     (程序开发所需要的模板和工具)   
|-- external        (目标机器使用的一些库)   
|-- frameworks      (应用程序的框架层)   
|-- hardware        (与硬件相关的库)   
|-- kernel          (Linux的源代码)   
|-- packages        (Android的各种应用程序)   
|-- prebuilt        (Android在各种平台下编译的预置脚本)   
|-- recovery        (与目标的恢复功能相关)   
|-- system          (Android的底层的一些库)

**5、如何定义多个makefile文件**
有的时候,需要编译的模块比较多,可能会将对应的模块放置在相应的目录中,这样,可以在每个目录中定义对应的Android.mk文件,最后,在根目录放置一个Android.mk文件,内容如下:
  include $(call all-subdir-makefiles)
  它的作用就是包含所有子目录中的Android.mk文件

 另一种 方法就是可以在一个Android.mk文件里包含多个模块。
  很直观的想法就是将第一个Android.mk文件的内容复制一份,然后修改,但是会出现问题,在第二个模块中的源码找不到,因为:
  LOCAL_PATH := $(call my-dir)
  大意是:在这个Android.mk里面只需要调用一次$(call my-dir)就够了,否则 会出错,如果所有的源文件都在一个目录中,可以在第一次调用call my-dir的时候,将值保存下来,比如:
  MY_LOCAL_PATH := $(call my-dir)
  LOCAL_PATH := $(MY_LOCAL_PATH)
  然后,在另外一个模块中,继续如下定义:
  LOCAL_PATH := $(MY_LOCAL_PATH)

6、makefile常用语法
 “ifdef”:是条件关键字。语法是ifdef <variable-name>;<text-if-true>; else <text-if-false>; endif
ifdef:只检验一个变量是否被赋值,它并不会去推导这个变量,并不会把变量扩展到当前位置。
“ifeq”:语法是ifeq (<arg1>;,<arg2>;),功能是比较参数“arg1”和“arg2”的值是否相同。
函数origin:并不操作变量的值,只是告诉你你的这个变量是哪里来的。 语法是: $(origin <variable>;)origin函数的返回值有:“undefined”从来没有定义过、“default”是一个默认的定义、“environment”是一个环境变量、
 “file”这个变量被定义在Makefile中、“commandline”这个变量是被命令行定义的、 “override”是被override指示符重新定义的、“automatic”是一个命令运行中的自动化变量
 
应用变量的语法是:$(变量名)。如KBUILD_VERBOSE = $(V)中的$(V)。
KBUILD_VERBOSE的值根据在命令行中是否定义了变量V,
 当没有定义时,默认为V=O,输出为short version;可以用make V=1 来输出全部的命令。
ifndef与ifdef语法类似,但功能恰好相反。ifndef是判断变量是不是没有被赋值


**举例:**
(1)在3710项目,如/home/android/3710/build/tools/acp下的Android.mk文件,内容如下:
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_SRC_FILES := \ acp.c
LOCAL_STATIC_LIBRARIES := libhost
LOCAL_MODULE := acp
LOCAL_ACP_UNAVAILABLE := true
LOCAL_CXX_STL := none

include $(BUILD_HOST_EXECUTABLE)

说明:使用当前路径下的acp.c源码,引用的include和链接的library都是host模块,最终编译生成一个可在当前主机运行的可执行文件,名字为acp(linux环境)。

(2)3710项目下,/home/android/3710/vendor/tinno/v3710/art_uz下的Android.mk(在此目录下还有一个配置的mk文件config.mk)

    LOCAL_PATH:= $(call my-dir)
   
    PRJ_PATH:= vendor/tinno/$(MTK_TARGET_PROJECT)/$(PROJECT_NAME)
    # $ (error $(LOCAL_PATH) ---- $(PRJ_PATH))
    $(warning --$(LOCAL_PATH) --$(PRJ_PATH))
    ifeq ($(strip $(PRJ_PATH)),$(strip $(LOCAL_PATH)))
    include $(call all-subdir-makefiles)
    endif
    说明:包含所有子目录中的Android.mk文件,即art_uz下面的mk文件
   “ifeq”语法是ifeq(<arg1>;,<arg2>;),功能是比较参数“arg1”和“arg2”的值是否相同。

(3)在3710项目中用到的最多的就是通过mk文件在编译时将文件复制到out目录下,一般是通过一下方式,如在/home/android/3710/vendor/tinno/v3710/art_uz/sounds下的Android.mk文件中:

    LOCAL_PATH := vendor/tinno/$(MTK_TARGET_PROJECT)/$(PROJECT_NAME)/sounds
    PRODUCT_COPY_FILES += \
        $(LOCAL_PATH)/notifications/Arrive.ogg:system/media/audio/notifications/Arrive.ogg \

即将Arrive.ogg复制到system/media/audio/notifications/下,名字依然为Arrive.ogg
(4)在3710/home/android/3710/vendor/tinno/v3710/art_uz/apps/apks下的Android.mk文件:

    LOCAL_PATH:= $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := MTBFTool_M01_M02_R06
    LOCAL_MODULE_TAGS := optional
    LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
    LOCAL_MODULE_CLASS := APPS
    LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
    LOCAL_CERTIFICATE := platform
    include $(BUILD_PREBUILT)
   
    说明:
    LOCAL_MODULE 表示了一个预置的apk在编译中的唯一标识,同时编译后该apk会以此命名;LOCAL_SRC_FILES表示了当前要预置的apk的文件名。
    BUILD_PREBUILT是prebuilt机制对应的宏,它将静态库复制到out目录下的obj中去,然后在连接的时候就会在对于目录下去找这个静态库,动态库或者其他可执行文件,甚至是配置文件都可以使用这个机制来进行copy。
   
   
**拓展:关于预置apk到MTK目录**
      5.0之后项目预置方式通用步骤为:
        建立apk文件夹;
        置目标apk到该文件夹下;
        解压缩apk查看是否包含lib/文件夹(apk项目是否包含lib库文件);
        在该文件夹下编写Android.mk脚本 ;

理论上apk文件夹可以建立在项目内任意目录,编译系统会自动搜索并根据其内Android.mk (编译脚本)来进行编译。编译系统采用的是递归搜索,在搜索到父文件目录的Android.mk脚本后递归便被终止。因此一般可以将需要预置的apk文件夹放到一个总文件夹内,并在该文件夹根目录另外写一个Android.mk (管理脚本) ,以便对所有预置apk进行管理。对于 管理脚本 的编写将在文末解释。预置目录如下例:

Apps/
-/Android.mk 管理脚本
-/Test1
—-/Android.mk 编译脚本
—-/Test1.apk
—-/lib/*
-/Test2
—-/Android.mk 编译脚本
—-/Test2.apk
—-/lib/*

编译脚本 如下例:

    LOCAL_PATH := $(call my-dir)
    include $(CLEAR_VARS)
    LOCAL_MODULE := Test
    LOCAL_MODULE_CLASS := APPS
    LOCAL_MODULE_TAGS := optional
    LOCAL_BUILT_MODULE_STEM := package.apk
    LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
    LOCAL_CERTIFICATE := PRESIGNED
    LOCAL_SRC_FILES := Test_*.apk
    include $(BUILD_PREBUILT)

其中,LOCAL_MODULE := Test表示了一个预置的apk在编译中的唯一标识,同时编译后该apk会以此命名;LOCAL_SRC_FILES := Test_*.apk 表示了当前要预置的apk的文件名,”Test_*.apk”匹配任意以”Test_”开头的apk文件。
对于apk预置路径,在Android.mk中可以通过以下方式指名:

1、 默认预置apk到system/app/目录(普通系统apk,不可卸载),如前文Android.mk脚本编写之后即可
2、预置apk到system/priv-app/目录(系统核心apk,不可卸载),在前文Android.mk脚本中添加并配置变量:
 LOCAL_PRIVILEGED_MODULE := true
3、预置apk到data/app/目录并且卸载后不需要再会恢复,在前文Android.mk脚本中配置变量:LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
4、预置apk到data/app/目录并且卸载后恢复出厂可以恢复,在前文Android.mk简本中配置变量:LOCAL_MODULE_PATH := $(TARGET_OUT)/vendor/operator/app


注:关于更多预置apk到MTK的相关知识可以参考博客:http://blog.csdn.net/a462533587/article/details/46380795

猜你喜欢

转载自blog.csdn.net/Toc_SunWinner/article/details/79396449