写在前面:
前段时间下载了cocos2d-x 3.1,按照官网的教程,配置环境,编译打包,走了一遍,感觉不错,顺便发现其中用了很多python的脚本文件,比如今天要说的android-build.py.这个文件把编译,打包的功能全部整合到了一起.也就是传说中的一键打包.正好最近在看python,就顺手拿这个脚本学习一下.
小贴士:
在正式看这个脚本文件前,推荐先到cocos2d-x的官网按教程,配置环境,编译,打包,走一遍,先有个大概的了解.教程地址.http://www.cocos2d-x.org/wiki/Getting_Started_with_Cocos2d-x.
我是做coco2d-x游戏开发的,用的是windows环境.运行cocos2d-x 3.1需要安装和配置下面的软件.JDK,android-sdk,ant,python,android-ndk-r9d.系统就必须win7以上了.想进一步学习的话,也可以去看看python,我也是最近在看,大家可以相互学习进步.
正文:
android-build.py脚本文件在引擎目录下的build文件夹里.里面的代码还是有点多,这里就不全部粘贴出来了,可以在原文件上直接看源代码.
下面的一些内容会涉及到python的语法内容,如果对python比较熟悉的话可以直接跳到看重点关注.写了很多python相关的,主要是自己也在学python. :-)
import sys import os, os.path import shutil from optparse import OptionParser CPP_SAMPLES = ['cpp-empty-test', 'cpp-tests'] LUA_SAMPLES = ['lua-empty-test', 'lua-tests'] ALL_SAMPLES = CPP_SAMPLES + LUA_SAMPLES这是开始的东东,前面几行的import,就和java的import一样,导入一些需要的模块.
CPP_SAMPLES = ['cpp-empty-test', 'cpp-tests']
这个是python里的一种数据类型,叫做列表,和java,c++里的数组类似.里面也可以放整型,字符型啦这些数据.这里放的是字符串,python中的字符串可以用单引号,也可以用双引号括起.两个列表可以直接用 "+",合成一个新的列表.还有就是python里的数据类型都没有类型标识.所以你看python代码时变量前面一般不会看到,long,int这些啦.这三行存的是代码里要编译的demo的名字,后面要用到.
def get_num_of_cpu(): ''' The build process can be accelerated by running multiple concurrent job processes using the -j-option. ''' try: platform = sys.platform if platform == 'win32': if 'NUMBER_OF_PROCESSORS' in os.environ: return int(os.environ['NUMBER_OF_PROCESSORS']) else: return 1 else: from numpy.distutils import cpuinfo return cpuinfo.cpu._getNCPUs() except Exception: print "Can't know cpuinfo, use default 1 cpu" return 1
platform = sys.platform获得当前的操作系统平台
os.environ['NUMBER_OF_PROCESSORS']获得系统变量的值.在python中有种数据类型叫做字典,类似java,c++中的map,通过键找到对应的值.
def check_environment_variables():
检查是否设置了ndk的环境变量'NDK_ROOT',就是在环境变量里新建一个'NDK_ROOT',再把你ndk的根目录路径设置到其中.
def check_environment_variables_sdk():检查android sdk的环境变量设置,如果没有,则需要把sdk的根目录路径设置到新建的环境变量'ANDROID_SDK_ROOT'中.
def select_toolchain_version():选择需要的toolchain.代码注释说的很明白.因为 cocos2d-x 3.1用的是c++11,所以toolchain的版本需要用gcc4.7以上的版本.
def do_build(cocos_root, ndk_root, app_android_root, ndk_build_param,sdk_root,android_platform,build_mode): ndk_path = os.path.join(ndk_root, "ndk-build") # windows should use ";" to seperate module paths platform = sys.platform if platform == 'win32': ndk_module_path = 'NDK_MODULE_PATH=%s;%s/external;%s/cocos' % (cocos_root, cocos_root, cocos_root) else: ndk_module_path = 'NDK_MODULE_PATH=%s:%s/external:%s/cocos' % (cocos_root, cocos_root, cocos_root) num_of_cpu = get_num_of_cpu() if ndk_build_param == None: command = '%s -j%d -C %s %s' % (ndk_path, num_of_cpu, app_android_root, ndk_module_path) else: command = '%s -j%d -C %s %s %s' % (ndk_path, num_of_cpu, app_android_root, ndk_build_param, ndk_module_path) print command if os.system(command) != 0: raise Exception("Build dynamic library for project [ " + app_android_root + " ] fails!") elif android_platform is not None: sdk_tool_path = os.path.join(sdk_root, "tools/android") cocoslib_path = os.path.join(cocos_root, "cocos/platform/android/java") command = '%s update lib-project -t %s -p %s' % (sdk_tool_path,android_platform,cocoslib_path) if os.system(command) != 0: raise Exception("update cocos lib-project [ " + cocoslib_path + " ] fails!") command = '%s update project -t %s -p %s -s' % (sdk_tool_path,android_platform,app_android_root) if os.system(command) != 0: raise Exception("update project [ " + app_android_root + " ] fails!") buildfile_path = os.path.join(app_android_root, "build.xml") command = 'ant clean %s -f %s -Dsdk.dir=%s' % (build_mode,buildfile_path,sdk_root) os.system(command)
ndk_path = os.path.join(ndk_root, "ndk-build")os.path.join的功能是把,ndk_root和"ndk-build"链接到一起,ndk_root你ndk的根目录路径,上面已经做了设置."ndk-build"是ndk下的编译命令,用过ndk的会知道,这个命令是用来编译c/c++代码的.具体的可以参考你ndk目录中的docs文件夹,其中包括NDK-BUILD.xml这个文档就是来介绍ndk-build命令的.因为执行"ndk-build"命令,需要在ndk目录中执行.(也可以如上面的操作,设置到环境变量里,在执行此命令时就无需在根目录执行了).上面的输出,在我的机子上是"e:\\android-ndk-r9d\\ndk-build".
num_of_cpu = get_num_of_cpu() if ndk_build_param == None: command = '%s -j%d -C %s %s' % (ndk_path, num_of_cpu, app_android_root, ndk_module_path) else: command = '%s -j%d -C %s %s %s' % (ndk_path, num_of_cpu, app_android_root, ndk_build_param, ndk_module_path)获得编译命令。代码功能类似c++中的sprintf,将%后的参数依次置换为ndk_path等等,最后command获得置换后的值。
这个command就是要执行的编译命令,其中包括一些参数。目前做ndk编译的,一般都是在cygwin中执行,针对coco2d-x 2.x的编译用的是一个.sh文件。可以参考2.x版本的sh脚本。不过从ndkr8开始,其中已经内置了cygwin,所以使用ndk编译时就无需再在cygwin命令下执行编译命令了,可以直接在ndk中使用ndk-build命令。有个好处就是可以不需要安装cygwin了。 最后一个参数
ndk_module_path 用以指示查找android.mk中使用$(call import-module, XXX)函数引入外部库文件的路径。
ndk_build_param 这个参数没有搞懂是干嘛用的,脚本示例用的是-p,在ndk参考文档上没有找到。
if os.system(command) != 0: raise Exception("Build dynamic library for project [ " + app_android_root + " ] fails!")os.system(XX)可以执行上面的编译命令,如果不成功会抛个异常。这个方法很好用,如果你把XX换成你电脑上的某个程序,如我电脑上os.system("F:\\Program Files\\Tencent\\Bin\\QQ.exe"),就可以打开QQ了。
command中的第一个参数是你ndk根目录加"ndk-build",-j后面跟的是处理器核心数(在ndk官方文档上没找到这个参数,不过加上这个参数后,编译速度确实有所提高:)),-C后面跟的是编译生成的.so放置的路径。
ndk_module_path 指示引入外部参考库的文件路径,在android.mk中会用到。
ndk_build_param 这个参数脚本示例用的是 -p,在ndk文档里没找到这个参数,不知道其作用。
sdk_tool_path = os.path.join(sdk_root, "tools/android") 获取sdk目录下android命令的执行路径,用来使用"android",执行update操作. cocoslib_path = os.path.join(cocos_root, "cocos/platform/android/java") 获取引擎里的java库工程路径
command = '%s update lib-project -t %s -p %s' % (sdk_tool_path,android_platform,cocoslib_path) command = '%s update project -t %s -p %s -s' % (sdk_tool_path,android_platform,app_android_root)上面两条命令,第一条是执行android update lib-project的命令来更新库工程。第二条是执行android update project的命令来更新应用程序工程。
更新过的android工程会生成build.xml和ant属性文件,这样就可以使用ant进行打包程序了。
因为cocos2d-x引擎是分为两个部分的(针对for android开发来说),一个是c++端的,一个是java端的。c++端是重头,引擎的大功能都是c++实现的。但是针对android来说,最基本的还是java,首先你程序的启动必须是从java这边的activity启动的,然后再调用c++端的openGL_ES进行渲染,其中一些获取硬件功能的方法,如获取重力感应,也是需要java这边android sdk提供的接口来操作的,所以cocos2d-x for android,java这边的代码是必须存在的。2.x版本前,引擎java这边的代码是放到一个包里的,你需要把这个包引入到你的android工程中就可以使用java这端的引擎,进而调用c++那端的代码了,两者交互用的是jni,不懂的童鞋可以上网学习,这里就不做介绍了。3.x版本后,将java这端的引擎代码做成了一个库工程,到时你的android工程直接引用这个库工程就可以了,相对2.x的代码集成,这样管理代码更加方便和安全。
buildfile_path = os.path.join(app_android_root, "build.xml") command = 'ant clean %s -f %s -Dsdk.dir=%s' % (build_mode,buildfile_path,sdk_root)这里就是最后的android打包命令了,执行完后会在bin下生成apk.简单介绍下,clean就是清空工程目录下的gen,bin这些东东,-f 强制覆盖 -Dsd.dir 设置android sdk的路径
如果对ant不熟悉也没关系,你只需要知道两点:1.在eclipse上的创建,打包android程序就是执行的ant命令,eclipse只是封装成可视化操作而已。2.要学会使用ant创建和打包android工程(这里不做演示了,如果要搞定build.xml,要讲的也不少,可以去百度 android ant,里面有很多介绍)。
其实说完了上面的重点关注后,脚本剩下的就和上面的一些检查方法类似了。
def copy_resources(target, app_android_root): 用来拷贝资源的,包括src下的代码,resource,asset下的资源文件。在ant打包时会把拷贝的资源打到apk里,基本上都是python文件操作的内容。还包括shutil高级文件操作模块。感兴趣的童鞋就去学习python吧。:)
def build_samples(target,ndk_build_param,android_platform,build_mode):当前脚本文件编译的samples。我们这个脚本就是运行这个方法来执行拷资源,编译,打包的。
可以从代码中看出,执行的顺序是先把各种环境变量检查,赋值,然后执行拷贝资源,最后编译打包。
# -------------- main -------------- if __name__ == '__main__': #parse the params usage = """ This script is mainy used for building tests built-in with cocos2d-x. Usage: %prog [options] [cpp-empty-test|cpp-tests|lua-empty-test|lua-tests|cpp|lua|all] If you are new to cocos2d-x, I recommend you start with cpp-empty-test, lua-empty-test. You can combine these targets like this: python android-build.py -p 10 cpp-empty-test lua-empty-test Note: You should install ant to generate apk while building the andriod tests. But it is optional. You can generate apk with eclipse. """ parser = OptionParser(usage=usage) parser.add_option("-n", "--ndk", dest="ndk_build_param", help='Parameter for ndk-build') parser.add_option("-p", "--platform", dest="android_platform", help='Parameter for android-update. Without the parameter,the script just build dynamic library for the projects. Valid android-platform are:[10|11|12|13|14|15|16|17|18|19]') parser.add_option("-b", "--build", dest="build_mode", help='The build mode for java project,debug[default] or release. Get more information,please refer to http://developer.android.com/tools/building/building-cmdline.html') (opts, args) = parser.parse_args() if len(args) == 0: parser.print_help() sys.exit(1) else: try: build_samples(args, opts.ndk_build_param,opts.android_platform,opts.build_mode) except Exception as e: print e sys.exit(1)注释写的很详细,说明了这个脚本的执行命令参数和其作用。
parser = OptionParser(usage=usage)上面这个也是python相关的。可以参考 点击打开链接这篇博客。
写在最后:
非常感谢您看完了这篇博客,看得很窝火,很想打扁博主? 想打就来吧! :)
这算是工作以来的第一篇技术博客,而且之前已经很久没有写文章了,写的过程也是断断续续的,写完后发现博客也挺难写的,对那些写了很多技术博客的人表示崇拜和感谢。
写之前觉得自己基本都懂了,但是写的过程中发现,其实还不是真懂,于是期间自己也想了很久,就像给人讲授东西一样,自己又重新学了一遍。其中会有说不好的,或者错误的,等着拍砖!!!也等着以后自己来给自己拍砖!!!!!!!