`

Android ROM研究---Android build system增加模块

阅读更多

Android build system就是编译系统的意思

在我们需要向自己编译的源代码中增加模块的时候,需要一些规则,当然这个规则都是类似的。

 

Android.mk文件解析

 

让我们来看一个 Android.mk 文件的样子

 

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=Hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_SHARED_LIBRARY)

 

 

     LOCAL_PATH :=$(call my-dir)

固定写法, LOCAL_PATH 表示此时位于工程目录的根目录中, (call my-dir) 的功能由编译器提供,被用来返回当前目录的地址(包含 Android.mk 本身)

     include $(CLEAR_VARS)

固定写法, CLEAR_VARS 这个变量由编译系统提供,并且要执行一个 GNU makefile 文件,这个功能会清理掉所有以 LOCAL_ 开头的内容(比如 LOCAL_MODULE LOCAL_SRC_FILES 等),除了 LOCAL_PATH 。这句话也是必须的,因为如果所有变量都是全局变量的话,所有的可控的编译文件都需要在一个单独的 GNU 中被解析并执行

     LOCAL_MODULE :=Hello

LOCAL_MODLE 变量必须被定义,用来区分 Android.mk 中的每一个模块。文件名必须是唯一的,不能有空格。注意,编译器会为你自动加上一些前缀和后缀,来保证文件是一致的。比如:这里表明一个动态链接库模块被命名为“ Hello ”,但是最后会生成“ libHello.so ”文件。但是在 java 中装载这个库的时候还要使用“ Hello ”名称。

     LOCAL_SRC_FILES :=hello.c

LOCAL_SRC_FILES 变量必须包含一个 C C++ 源文件的列表,这些会被编译并聚合到一个模块中

注意:这里并不需要列头文件和被包含的文件,因为编译系统会自动为你计算相关的属性,源代码的列表会直接传递给编译器

     include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY 这个变量由系统提供,并且指定给 GNU makefile 的脚本,它可以收集所有你定义的 ”include $(CLEAR_VARS)” 中以 LOCAL_ 开头的变量,并且决定哪些要编译,哪些应该做的更加准确。我们同样可以使用 BUILD_STATIC_LIBRARY 来生成一个静态库,如果使用 BUILD_STATIC_LIBRARY 编译系统便会生成一个以“ lib$(LOCAL_MODULE).a ”为文件名的文件来提供动态库的调用

     TARGET_ARCH

TARGET_ARCH 是指架构中 CPU 的名字已经被 Android 开源代码明确指出了,这里的 arm 包含了任何 ARM- 独立结构的架构,以及每个独立的 CPU 版本

    TARGET_PLATFORM

Android 平台的名字在 Android.mk 文件中被解析,比如 ”android-2.3”

     TARGET_ROOT_OUT :表示根文件系统

用法: CAL_MODULE_PATH:=$(TARGET_ROOT_OUT)

     TARGET_OUT: 表示 system 文件系统

    TARGET_OUT_DATA: 表示 data 文件系统

         TARGET_ABI

TARGET_ABI 平台目标板和 abi 的链接,这里要定义 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI) ,它们都非常有用,特变是当你想测试一个具体的系统镜像在几个真实设备的时候

下面是 GNU 编译出来的宏,并且它们都必须使用“ $(call <function>) ”才能返回文字化的信息。

all-subdir-makefiles :返回一个 Android.mk 文件所在位置的列表,以及当前 my-dir 的路径。

比如: include $(call all-subdir-makefiles)

this-makefile :返回当前 makefile 的路径(就是哪个功能被调用了)

parent-makefile :返回 makefile 的包含树,也就是包含 makefile 的当前文件

 

Application.mk

 

要讲 C\C++ 编译为 so 文件,光有 Android.mk 文件是不行的,还需要 Application.mk 文件。

Application.mk 文件存放的位置是 NDK 工程的根目录, Application.mk 文件就是用来描述应用程序中所需要的原生的模块(也就是静态库和动态库),每一个 Application.mk 文件都必须放在应用目录下的子目录,例如 $NDK/apps/HelloNdk/Application.mk ,作为 GNU makefile 的一部分, Application.mk 文件必须要定义以下部分

1、  APP_MODULES

APP_MODULES 变量是强制性的,并且会列出所有你所需要的模块(通过 Android.mk

2、  APP_PROJECT_PATH

APP_PROJECT_PATH 变量也是强制性的,并且会给出应用程序工程的根目录一个绝对路径。这是用来复制或者安装一个没有任何版本限制的 JNI 库,从而给 APK 生成工具一个详细的路径。

例如: \HelloNDK\Application.mk

APP_PROJECT_PATH := $(call my-dir)/project

APP_MODULES := HelloNdk

这里定义了工程路径为 $(call my-dir)/project ,而要编译的模块则是 HelloNdk ,这样编译系统才会找到我们要编译的库和原文件

3、  APP_CFLAGS 则是当要编译模块中有任何 C 文件的时候, C 编译器的信号就会被发出

4、  APP_OPTIM

这个变量是可选的,可以定义为发布版或者测试版

 

------------------------------------------------------------------------------------------------------------------------

在Android.mk中:

include $(BUILD_EXECUTABLE)表示生成二进制可执行文件

include $(BUILD_STATIC_LIBRARY)表示生成静态库.a文件,名字叫做lib<工程名>.a

include $(BUILD_SHARED_LIBRARY)表示生成动态库.so文件,名字叫做lib<工程名>.so

 

另外需要注意的是,生成的文件需要放在手机的data/local目录下,才可以有执行的权限(未root),如果root了,则会有些目录是可以执行,但是某些目录依然不能执行,当然可以用umount命令解决。SD卡是没有执行权限的,所以当你生成的比如二进制可执行文件放到sdcard中时,是无法运行的。

 

可以这样测试一下哪些是有执行权限,哪些是没有的:

*将手机插入电脑,并打开USB调试

*在终端输入adb shell进入

*su(root了的手机)

*mount:可以看到一大堆的列表,如果对应的目录的信息中有noexec,说明这个目录就没有执行权限,剩下的都可以执行二进制等文件。

 

Android.mk文件的具体语法参见我的博客:http://hualang.iteye.com/blog/1140414

 

向Android源代码中增加模块主要有如下几种:

 

1、增加可执行文件

增加可执行文件,这些可执行文件一般都是由C/C++生成,下面简单的介绍一些如何向源代码中生成可执行文件

假设我的源代码在~/mydroid目录下

在external/loulijun/test目录下,创建两个文件,一个C,一个Android.mk

hello.c

#include <stdio.h>
int main(void)
{
	printf("Hello world!\n");
	return 0;
}

 Android.mk

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

LOCAL_SRC_FILES :=hello.c
LOCAL_MODULE_TAGS :=optional
LOCAL_MODULE :=test

include $(BUILD_EXECUTABLE)
 

首先退出到mydroid目录下,执行

. build/envsetup.sh
或者
source build/envsetup.sh

 进行环境变量的配置

然后进入到test目录下,执行“mm”(mm表示编译当前项目),如果想重新执行,可以"mm -B"

 

这样,会在out/target/product/generic/obj/EXECUTABLES/test_intermediates/LINKED/目录下生成可执行文件test

 

然后将test文件用adb push test /data/local 到data/local目录下。

 

下面开始执行,你可以在手机中用terminal emulator来执行,也可以以adb shell后,执行

./test
显示:Hello world!
 

2、增加静态库(.a)

Android.mk文件

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

LOCAL_SRC_FILES := \
hello.c
LOCAL_MODULE :=test

include $(BUILD_STATIC_LIBRARY)

编译:

mydroid#. build/envsetup.sh

test#mm

生成的结果在out/target/product/generic/obj/STATIC_LIBRARY

目标文件夹{XXX}_static_intermediates下,XXX为你定义的模块名称test

 

假如这个静态库是由hello.c生成的,但是生成的静态库是不能直接使用的,而是由动态库调用它

 

3、增加动态库(.so)

编译动态库其实可以用NDK的,那样生成非常方便,但是有时候还是需要掌握其他的方法的

Android.mk

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

LOCAL_SRC_FILES := \
hello.c
LOCAL_MODULE :=test

include $(BUILD_SHARED_LIBRARY)

 编译的放法都差不多,只不过Android.mk不同的是最后一句,如果比较一下就会发现那句话决定了生成的是什么

 

不过要想生成动态库,绝非是这么简单的,有时候只需要Android.mk和源文件即可,但是有时候还需要Application.mk文件。Application.mk文件的具体语法很快会在博客中更新

 

 

 

下面是使用 NDK 来生成 .so 文件的,环境是在 ubuntu11.04 下面

1、 下载 Android-NDK-r6 ,将其解压到 ~/android/android-ndk-r6 目录下

2、  配置 .bash_profile 文件,加入

NDK=~/android/android-ndk-r6

export NDK

3、  cd $NDK 后,进入 ndk 的目录,我以它自带的项目为例,进入 samples/hello-jni

在终端输入 $NDK/ndk-build

系统会自动编译这个文件,并将生成的 libhello-jni.so 文件存放在当前目录的 libs/armeabi 目录下

4、  我们可以将这个生成的 libhello-jni.so 放在 Android 源代码的适当的位置即可使用了

下面是相应的文件

hello-jni.c

 

#include <string.h>
#include <jni.h>
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}
 

 

Android.mk 文件

 

LOCAL_PATH := $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c
 
include $(BUILD_SHARED_LIBRARY)
 

 

由于这里只是使用了一个简单的 C 文件,所以没用用到 Application.mk 文件

然后将 hello-jni 导入到 eclipse 中,运行模拟器,即可显示


下面再看一个例子 native-activity

1、  进入目录后执行 $NDK/ndk-build

2、  生成 libnative_activity.so 并存于当前目录的 lib/armeabi 目录下,另外由源代码生成的还有静态库,存放于 obj/local/armeabi/ libandroid_native_app_glue.a

这里,由于 main.c 比较大,就不贴上了

Android.mk

 

LOCAL_PATH := $(call my-dir)
 
include $(CLEAR_VARS)
 
LOCAL_MODULE    := native-activity
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM
LOCAL_STATIC_LIBRARIES := android_native_app_glue
 
include $(BUILD_SHARED_LIBRARY)
 
$(call import-module,android/native_app_glue)

 

Application.mk

 

APP_PLATFORM := android-10 

 

 

运行结果:颜色会逐渐变浅,然后再次从这个颜色变浅



 

 

4、增加apk文件(有源代码)

 

如果将 android 程序的源代码加入到 build system 中呢

(1)       eclipse 开发环境中创建你的 android 工程,比如叫做 Success

(2)       将工程拷贝到源代码的 package/apps 目录下

(3)       进入 Success 目录下,创建一个 Android.mk 文件,内容如下

 

LOCAL_PATH :=$(call my-dir)
include $(CLEAR_VARS)
 
LOCAL_MODULE_TAGS :=optional
LOCAL_SRC_FILES :=$(call all-java-files-under, src)
 
LOCAL_PACKAGE_NAME :=(工程名字)
LOCAL_CERTIFICATE :=platform
include $(BUILD_PACKAGE)

 

 

(4)       退回到 android 源代码目录下,执行

#. build/envsetup.sh

#mmm packages/apps/Success

编译成功之后,会在 out/target/product/generic/system/app/Success.apk

(5)       如果要在真机上测试, system 的目录是在 out/target/product/crespo 目录下,编译的时候需要设置一些参数。为了测试,将 crespo 中的 system 记其他内核等文件放入到一个叫做 samsung 的文件夹中,再将 Success.apk 放到 system/app

(6)       #zip –r update.zip . 命令将其打包为 zip ,也可以用 zip 直接压缩

(7)      #java –jar testsign.jar Samsung/update.zip update.zip zip 包签名

(8)       打开手机的 usb 调试,连接到电脑上,在终端输入 #adb push update.zip /sdcard/update.zip ,将 zip 包上传到设备的 sdcard 目录下

(9)       输入 #adb reboot bootloader 进入 bootloader 界面

(10)   输入 #fastboot flash recovery recovery.img recovery, 我刷的是 Recovery 3.0

(11)   进入 Recovery 选项,刷机,重启后就可以见到 Success.apk 程序了

注意:修改 AndroidManifest.xml ,在 manifest 标签中加入 android:sharedUserId=”media” ,当然这个 media 只是个 id ,它的名字随便一般类似包名。我们知道,在不同的 apk 包中默认是不能相互访问数据的,但是如果在 AndroidManifest.xml 中设置 sharedUserId 并且相同的话,那么这两个包就可以相互访问数据。由于我写的只是个测试程序,所以没有加入这条

 

 

5、增加apk文件(无源代码)

 

(1)      这种方式最简单,就是将 ***.apk 文件拷贝到编译 android 源代码时候生成的 out/target/product/crespo/system/app 中,执行 make snod 后就可以把 apk 添加到 system.img 中了,然后将 system 目录及其他的几个文件打包成 zip 并签名后即可,刷机后可以看到这个内置的系统程序。

(2)       上一种方式在 make clean 之后,再次 make 以后才能执行上述的操作,比较麻烦

     新建一个目录,在 packages/apps 下面,专门用来存放 apk 文件

#mkdir packages/apps/apks

#cd packages/apps/apks

在这个目录中新建一个 Android.mk 文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_POST_PROCESS_COMMAND := (shell cp –r $(LOCAL_PATH)/*.apk $(TARGET_OUT)/app/)

保存退出

     把需要编译的 apk 拷贝到 apks 目录中,执行 make ,在 apks 中的 apk 就会被拷贝到 out/target/product/generic/system/app

    执行 make snod 即可

这样,在执行 make clean 之后,再次 make ,只需要 make snod 即可了

 

  • 大小: 6 KB
  • 大小: 5.7 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics