版权信息:本文为本人原创,欢迎转载,但请著明出处,并保留本版权信息。
Android NDK编译脚本编写起来还是是比较简单条理的,然而它的语法和传统的linux GNU Make编译脚本的编写似乎有很大的不同,这让习惯了GNU Makefile的用户很不习惯。有许多人会用它,但对其工作原理却不理解,笔者甚至一度误认为那是一套全新脚本语言。我将在后文中对这一编译管理系统做一些分析,试着分析一下它和传统GNU Makefile的关系。
本文假设您已经拥有 Linux命令和GNU Makefile的基础知识,当然您也可以边阅读,边查找参考相关的知识。参考资料2是徐海兵先生整理的 GNU Makefile 中文手册,推荐英文不太好的朋友拿来作为参考。
这篇文章是对自己前段时间所学知识的一个记录整理,以期与大家互相交流,希望朋友们不要吝于来信,指出本文的错漏之处或者你们不同理解(绝非客气,真的期望能收到您的信件,我的邮箱 techres#163.com,请把 # 换成 @)。
====================================================================
Android NDK 的编译脚本系统,实际上并不是什么新东西,就是对 GNU MakeFile 系统的封装。Android NDK 自带了很多 .mk 文件(保存在ndk的 NDK_ROOT/build 目录中),文件中预定义了很多 Make 脚本函数和变量,以供用户编写自己的编译脚本时使用。另外,NDK为所有应用统一提供了一个固定的编译入口--即大家熟悉的ndk-build。
我们首先从每个应用编译时都需要用到的编译入口开始分析。
一、ndk-build分析
(一)、脚本内容分析
该文件存在于ndk安装根目录中,实际上是一个 shell 脚本。其有效内容如下:
PROGDIR=`dirname $0`
PROGDIR=`cd $PROGDIR && pwd`
# If GNUMAKE is defined, check that it points to a valid file
if [ -n "$GNUMAKE" ] ; then
ABS_GNUMAKE=`which $GNUMAKE 2> /dev/null`
if [ $? != 0 ] ; then
echo "ERROR: Your GNUMAKE variable is defined to an invalid name: $GNUMAKE"
echo "Please fix it to point to a valid make executable (e.g. /usr/bin/make)"
exit 1
fi
GNUMAKE="$ABS_GNUMAKE"
else
# Otherwise, use 'make' and check that it is available
GNUMAKE=`which make 2> /dev/null`
if [ $? != 0 ] ; then
echo "ERROR: Cannot find 'make' program. Please install Cygwin make package"
echo "or define the GNUMAKE variable to point to it."
exit 1
fi
fi
# On Windows, when running under cygwin, check that we are
# invoking a cygwin-compatible GNU Make binary. It is unfortunately
# common for app developers to have another non-cygwin compatible
#
if [ "$OSTYPE" = "cygwin" ] ; then
GNUMAKE=`cygpath -u $GNUMAKE`
PROGDIR_MIXED=`cygpath -m $PROGDIR`
CYGWIN_GNUMAKE=`$GNUMAKE -f $PROGDIR_MIXED/build/core/check-cygwin-make.mk 2>&1`
if [ $? != 0 ] ; then
echo "ERROR: You are using a non-Cygwin compatible Make program."
echo "Currently using: `cygpath -m $GNUMAKE`"
echo ""
echo "To solve the issue, follow these steps:"
echo ""
echo "1. Ensure that the Cygwin 'make' package is installed."
echo " NOTE: You will need GNU Make 3.81 or later!"
echo ""
echo "2. Define the GNUMAKE environment variable to point to it, as in:"
echo ""
echo " export GNUMAKE=/usr/bin/make"
echo ""
echo "3. Call 'ndk-build' again."
echo ""
exit 1
fi
fi
$GNUMAKE -f $PROGDIR/build/core/build-local.mk "$@"
下面我们对这些有效代码逐一进行分析:
PROGDIR=`dirname $0`
PROGDIR=`cd $PROGDIR && pwd`
在GNU makefile中,两个反引号相当于 shell 函数,反引号括起来的内容相当于 shell 函数的参数。而shell函数的功能就是调用操作系统的命令。
PROGDIR=`dirname $0`
linux 命令 dirname, 功能是获取后面参数的路径。例如:
#dirname ../../../ndk-build
执行的结果是:"../../.."。顺便提一下,这里不能用pwd命令,pwd 命令,是获得当前路径,获取的是当前路径的绝对路径。所以,上面两条语句的结果就是在 PROGDIR 变量中保存 ndk-build 所在的路径。有了这两条语句之后,就可以在任何位置执行 ndk-build ,而不必非要在 ndk 根目录中执行。
再之后的两段语句,是用来确定不同平台下正确的 make 工具。
# If GNUMAKE is defined, check that it points to a valid file
if [ -n "$GNUMAKE" ] ; then
ABS_GNUMAKE=`which $GNUMAKE 2> /dev/null`
if [ $? != 0 ] ; then
echo "ERROR: Your GNUMAKE variable is defined to an invalid name: $GNUMAKE"
echo "Please fix it to point to a valid make executable (e.g. /usr/bin/make)"
exit 1
fi
GNUMAKE="$ABS_GNUMAKE"
else
# Otherwise, use 'make' and check that it is available
GNUMAKE=`which make 2> /dev/null`
if [ $? != 0 ] ; then
echo "ERROR: Cannot find 'make' program. Please install Cygwin make package"
echo "or define the GNUMAKE variable to point to it."
exit 1
fi
fi
# On Windows, when running under cygwin, check that we are
# invoking a cygwin-compatible GNU Make binary. It is unfortunately
# common for app developers to have another non-cygwin compatible
#
if [ "$OSTYPE" = "cygwin" ] ; then
GNUMAKE=`cygpath -u $GNUMAKE`
PROGDIR_MIXED=`cygpath -m $PROGDIR`
CYGWIN_GNUMAKE=`$GNUMAKE -f $PROGDIR_MIXED/build/core/check-cygwin-make.mk 2>&1`
if [ $? != 0 ] ; then
echo "ERROR: You are using a non-Cygwin compatible Make program."
echo "Currently using: `cygpath -m $GNUMAKE`"
echo ""
echo "To solve the issue, follow these steps:"
echo ""
echo "1. Ensure that the Cygwin 'make' package is installed."
echo " NOTE: You will need GNU Make 3.81 or later!"
echo ""
echo "2. Define the GNUMAKE environment variable to point to it, as in:"
echo ""
echo " export GNUMAKE=/usr/bin/make"
echo ""
echo "3. Call 'ndk-build' again."
echo ""
exit 1
fi
fi
最后一条语句,是调用:
$GNUMAKE -f $PROGDIR/build/core/build-local.mk "$@"
这是真正开始执行 make ,指定 Makefile 为 build-local.mk 文件。同时,将 ndk-build 的所有参数($@)原封不动地都传递进去。
总之ndk-build 是对 make 指令的封装,并不对各个参数作处理,具体参数原封不动地传给 make 和Makefile。由此可见,ndk-build,只是对make命令的一个封装,为编译提供一个统一的且适应性更强的编译入口。
(二)、技巧
二、 build-local.mk文件分析
=======================================================================================================================
一、Android.mk文件内容分析
典型的 Android.mk 文件一般会包含如下内容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
下面逐一对之作分析
1、LOCAL_PATH := $(call my-dir)
宏 my-dir 定义在 difinitions.mk 文件中,其功能是获取当前文件所在的路径。
2、include $(CLEAR_VARS)
在文件 build-all.mk 文件中有如下定义:
CLEAR_VARS := $(BUILD_SYSTEM)/clear-vars.mk
build/core/clear-vars.mk 内容如下:
# this file is included repeatedly from Android.mk files in order to clean
# the module-specific variables from the environment,
NDK_LOCAL_VARS := \
LOCAL_MODULE \
LOCAL_SRC_FILES \
LOCAL_C_INCLUDES \
LOCAL_CFLAGS \
LOCAL_CXXFLAGS \
LOCAL_CPPFLAGS \
LOCAL_LDFLAGS \
LOCAL_LDLIBS \
LOCAL_ARFLAGS \
LOCAL_CPP_EXTENSION \
LOCAL_STATIC_LIBRARIES \
LOCAL_STATIC_WHOLE_LIBRARIES \
LOCAL_SHARED_LIBRARIES \
LOCAL_MAKEFILE \
LOCAL_ALLOW_UNDEFINED_SYMBOLS \
LOCAL_ARM_MODE \
LOCAL_ARM_NEON \
LOCAL_DISABLE_NO_EXECUTE \
$(call clear-src-tags)
$(call clear-vars, $(NDK_LOCAL_VARS))
注释中说得很清楚,该文件的功能就是将一些模块内部的变量的内容清除。最后两条语句是实际的清除操作:
$(call clear-src-tags)
宏 clear-src-tags 没有搜到其定义,不知道其确切功能是什么。不过,从名字来看,应该是对文件名的tags进行清理的。例如编译是为文件名加上的 .neon .arm 等等tags。
最后那条语句是真正清除上面罗列出的这些变量的值。从上面的列表中可以看出,都是一些 LOCAL 变量。【参考资料】
1、GNU Makefile 英文手册:http://www.gnu.org/software/make/manual/