読者です 読者をやめる 読者になる 読者になる

cocos2d-x-3.0beta2のプロジェクトをgradle化してみる(x-plugin検証<インターフェース編)

自メモ)
記事分割
 分割元:

x-plugin周りがまたビルド怪しくなってきたので再検証のため記述分離
一応インターフェース部分の組み込みは解決


>>>x-pluginの追加によるビルド)

x-pluginを使うには、まず

cocos2d/plugin/tools/publish.sh

を実行してx-pluginの構成を生成し

  • cocos2d/plugin/protocols/proj.android

も更にライブラリプロジェクトとして追加してやる必要があります*1
ただこのpublish.shは

  • COCOS2DXルート本体側で実行済みでも構成はコピーされない為毎回遣る必要があり?
  • 実行時にcocos2d/plugin/tools/toolsForPublish/environment.sh がないと

毎回初回に ANDROID_SDK,NDK,ANTのPATHを聞いてくるのでウザい
のでenvironment.shのコピーを忘れずに。


irof
├── Classes
├── cocos2d
├──cocos/2d/platform/android/java/build.gradle //☆☆☆(cocosのjniブリッジ)
├──plugin/protocols/proj.android/build.gradle //☆☆☆☆(x-pluginのインターフェース)
├── gradle //☆
├── gradlew //☆
├── gradlew.bat //☆
├── proj.android
├── build.gradle //☆☆☆
├── proj.ios_mac
├── proj.linux
├── proj.win32
├── Resources
├── settings.gradle //☆☆
└── build.gradle //☆☆

  • settings.gradle(修正)
include ':proj.android',':cocos2d/cocos/2d/platform/android/java',':cocos2d/plugin/protocols/proj.android'

実際は、複数行に

include ':proj.android'
include ':cocos2d/cocos/2d/platform/android/java'
include ":cocos2d/plugin/protocols/proj.android"

みたいな形とか仮想パスで短く表現等でも書けるが、

 ASのIDEA Pluginのsettings.gradle解釈処理が横着してて

  • 一行目のカンマスプリッタのものしか認識&パースしない

という糞仕様なのでAS使用前提なら注意をすること*2
ダブルクオートで囲むのも駄目かもしれない。
ASだとsettings.gradleはIDEで自動生成するような認識だろうし。

 でもこれってIDEのデバック実行をすること前提だと
x-pluginのデバックはlibrary-projectを全部突っ込んでいくことになるので
ある意味長さがナンセンスになっていくかな。。。(汗

 コマンドラインベースだけで考えるなら settings.gradleって
proj.android/project.properties の

android.library.reference.1=../cocos2d/cocos/2d/platform/android/java
android.library.reference.2=../cocos2d/plugin/protocols/proj.android

なADTの設定と自動連動させるべきなんじゃないかなとか思ったりも。。

  • proj.android/build.gradle(修正)
dependencies {
    // libs/ にある *.jar を一括で追加する
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(':cocos2d/cocos/2d/platform/android/java')
    compile project(":cocos2d/plugin/protocols/proj.android") //☆
}

☆を追加

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := cocos2dcpp_shared
LOCAL_MODULE_FILENAME := libcocos2dcpp

#LOCAL_SRC_FILES := hellocpp/main.cpp \
#                  ../../Classes/AppDelegate.cpp \
#                   ../../Classes/HelloWorldScene.cpp

# ソースの自動検索
CPP_FILES := $(shell find $(LOCAL_PATH)/../../Classes -name *.cpp)                   
LOCAL_SRC_FILES := hellocpp/main.cpp
LOCAL_SRC_FILES += $(CPP_FILES:$(LOCAL_PATH)/%=%)

LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../Classes

LOCAL_WHOLE_STATIC_LIBRARIES := cocos2dx_static
LOCAL_WHOLE_STATIC_LIBRARIES += cocosdenshion_static
LOCAL_WHOLE_STATIC_LIBRARIES += box2d_static

# add STATIC_LIBRARIES(☆☆☆☆)
LOCAL_WHOLE_STATIC_LIBRARIES += cocos2dxandroid_static
LOCAL_WHOLE_STATIC_LIBRARIES += PluginProtocolStatic

include $(BUILD_SHARED_LIBRARY)

# add seatch path(☆)
COCOS2DX_ROOT := $(LOCAL_PATH)/../../cocos2d
$(call import-add-path,$(COCOS2DX_ROOT))
$(call import-add-path,$(COCOS2DX_ROOT)/cocos)
$(call import-add-path,$(COCOS2DX_ROOT)/external)

$(call import-module,2d)
$(call import-module,audio/android)
$(call import-module,Box2D)

# add seatch path(☆☆☆☆)
#$(call import-add-path,$(COCOS2DX_ROOT)/cocos/2d/platform/android)
$(call import-add-path,$(COCOS2DX_ROOT)/plugin/publish)

$(call import-module,2d/platform/android)
$(call import-module,protocols/android)
  • proj.android/jni/hellocpp/main.cpp
#include "AppDelegate.h"
#include "cocos2d.h"
#include "CCEventType.h"
#include <jni.h>
#include <android/log.h>

//以下追記 start
#include "PluginJniHelper.h"
#include "PluginUtils.h"
#include <android_native_app_glue.h>
//以下追記 end

#define  LOG_TAG    "main"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)

using namespace cocos2d;

void cocos_android_app_init(struct android_app* app) {
    LOGD("cocos_android_app_init");
    AppDelegate *pAppDelegate = new AppDelegate();

    //以下追記
    PluginJniHelper::setJavaVM(app->activity->vm);
    PluginJniHelper::setClassLoaderFrom(app->activity->clazz);
    cocos2d::plugin::PluginUtils::initPluginWrapper(app);
}
apply plugin: 'android-library'

dependencies {
    // libs/ にある *.jar を一括で追加する
    compile fileTree(dir: 'libs', include: '*.jar')
    //compile "com.android.support:support-v4:${supportPackageVersion}"
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"

//ADT LIKE Source Location add start
	sourceSets {
    	main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
    	}
    }
//ADT LIKE Source Location add end

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 19
    }

    compileOptions {
        encoding = "UTF-8"
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}
    • cocos2d/plugin/protocols/proj.android/build.gradle
apply plugin: 'android-library'

dependencies {
    // libs/ にある *.jar を一括で追加する
    compile fileTree(dir: 'libs', include: '*.jar')
    //compile "com.android.support:support-v4:${supportPackageVersion}"

    //library-projectから更に依存参照しているプロジェクトも記述
    compile project(':cocos2d/cocos/2d/platform/android/java')
}

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"

//ADT LIKE Source Location add start
	sourceSets {
    	main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
    	}
    }
//ADT LIKE Source Location add end

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 19
    }

    compileOptions {
        encoding = "UTF-8"
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
}



◎再検証>

どうもコンパイルエラーが出るみたいなので
通常の

python build_native.py

の環境を構築してみる

  • proj.android/build_native.py (添付されてるものを改定していく)
#!/usr/bin/python
# build_native.py
# Build native codes

from __future__ import with_statement
import sys
import os, os.path
import shutil
from optparse import OptionParser

# ==== append start ===

def load_properties_variables():
    d = {}

    with open('local.properties') as f:
        for line in f:
            tokens = line.split('=')
            if len(tokens) <2:
                continue
            d[tokens[0]] = '='.join(tokens[1:]).rstrip('\n')
            print ('%s, [%s]' % (tokens[0],d[tokens[0]]))

    try:
        NDK_ROOT = d['ndk.dir']
    except Exception:
        NDK_ROOT = None
    return NDK_ROOT

def load_properties_variables_sdk():
    d = {}

    with open('local.properties') as f:
        for line in f:
            tokens = line.split('=')
            if len(tokens) <2:
                continue
            d[tokens[0]] = '='.join(tokens[1:]).rstrip('\n')
            print ('%s, [%s]' % (tokens[0],d[tokens[0]]))

    try:
        SDK_ROOT = d['sdk.dir']
    except Exception:
        SDK_ROOT = None
    return SDK_ROOT

# === append end ===

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

def check_environment_variables_sdk():
    ''' Checking the environment ANDROID_SDK_ROOT, which will be used for building
    '''
    SDK_ROOT = None
    SDK_ROOT = load_properties_variables()
    print ('[PROP]SDK_ROOT=[%s]' % SDK_ROOT)
    if SDK_ROOT != None:
        return SDK_ROOT

    try:
        SDK_ROOT = os.environ['ANDROID_SDK_ROOT']
    except Exception:
        print ("ANDROID_SDK_ROOT not defined. Please define ANDROID_SDK_ROOT in your environment")
        sys.exit(1)

    return SDK_ROOT

def check_environment_variables():
    ''' Checking the environment NDK_ROOT, which will be used for building
    '''
    NDK_ROOT = None
    NDK_ROOT = load_properties_variables()
    print ('[PROP]NDK_ROOT=[%s]' % NDK_ROOT)
    if NDK_ROOT != None:
        return NDK_ROOT

    try:
        NDK_ROOT = os.environ['NDK_ROOT']
    except Exception:
        print ("NDK_ROOT not defined. Please define NDK_ROOT in your environment")
        sys.exit(1)

    return NDK_ROOT

def select_toolchain_version():
    '''Because ndk-r8e uses gcc4.6 as default. gcc4.6 doesn't support c++11. So we should select gcc4.7 when
    using ndk-r8e. But gcc4.7 is removed in ndk-r9, so we should determine whether gcc4.7 exist.
    Conclution:
    ndk-r8e  -> use gcc4.7
    ndk-r9   -> use gcc4.8
    '''

    ndk_root = check_environment_variables()
    if os.path.isdir(os.path.join(ndk_root,"toolchains/arm-linux-androideabi-4.8")):
        os.environ['NDK_TOOLCHAIN_VERSION'] = '4.8'
        print ("The Selected NDK toolchain version was 4.8 !")
    elif os.path.isdir(os.path.join(ndk_root,"toolchains/arm-linux-androideabi-4.7")):
        os.environ['NDK_TOOLCHAIN_VERSION'] = '4.7'
        print ("The Selected NDK toolchain version was 4.7 !")
    else:
        print ("Couldn't find the gcc toolchain.")
        exit(1)

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)
        ndk_module_path = 'NDK_MODULE_PATH=%s;%s/external;%s/cocos;%s/2d/platform/android;%s/plugin/publish' % (cocos_root, cocos_root, cocos_root, cocos_root, cocos_root)
    else:
        #ndk_module_path = 'NDK_MODULE_PATH=%s:%s/external:%s/cocos' % (cocos_root, cocos_root, cocos_root)
        ndk_module_path = 'NDK_MODULE_PATH=%s:%s/external:%s/cocos:%s/2d/platform/android:%s/plugin/publish' % (cocos_root, cocos_root, 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, ''.join(str(e) for e in ndk_build_param), ndk_module_path)
        command = '%s -j%d -C %s %s %s' % (ndk_path, num_of_cpu, app_android_root, ' '.join(str(e) for e in ndk_build_param), ndk_module_path)
	
    print ('[do_build]command=[%s]' % 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/2d/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)

def copy_files(src, dst):

    for item in os.listdir(src):
        path = os.path.join(src, item)
        # Android can not package the file that ends with ".gz"
        if not item.startswith('.') and not item.endswith('.gz') and os.path.isfile(path):
            shutil.copy(path, dst)
        if os.path.isdir(path):
            new_dst = os.path.join(dst, item)
            os.mkdir(new_dst)
            copy_files(path, new_dst)

def copy_resources(app_android_root):

    # remove app_android_root/assets if it exists
    assets_dir = os.path.join(app_android_root, "assets")
    if os.path.isdir(assets_dir):
        shutil.rmtree(assets_dir)

    # copy resources
    os.mkdir(assets_dir)
    resources_dir = os.path.join(app_android_root, "../Resources")
    if os.path.isdir(resources_dir):
        copy_files(resources_dir, assets_dir)

def build(ndk_build_param,android_platform,build_mode):

    ndk_root = check_environment_variables()
    sdk_root = None
    select_toolchain_version()

    current_dir = os.path.dirname(os.path.realpath(__file__))
    cocos_root = os.path.join(current_dir, "../cocos2d")

    app_android_root = current_dir
    copy_resources(app_android_root)
    
    if android_platform is not None:
				sdk_root = check_environment_variables_sdk()
				if android_platform.isdigit():
						android_platform = 'android-'+android_platform
				else:
						print ('please use vaild android platform')
						exit(1)
		
    if build_mode is None:
    	  build_mode = 'debug'
    elif build_mode != 'release':
        build_mode = 'debug'
    
    do_build(cocos_root, ndk_root, app_android_root,ndk_build_param,sdk_root,android_platform,build_mode)

# -------------- main --------------
if __name__ == '__main__':

    parser = OptionParser()
    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 project. 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()
    
    build(opts.ndk_build_param,opts.android_platform,opts.build_mode)


で出力見てみると


[armeabi] SharedLibrary : libcocos2dcpp.so
/Users/XXX/android-ndk-r9c/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.8/../../../../arm-linux-androideabi/bin/ld: error: ./obj/local/armeabi/libcocos2d.a: member at 38262786 is not an ELF object
/Users/XXX/android-ndk-r9c/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/../lib/gcc/arm-linux-androideabi/4.8/../../../../arm-linux-androideabi/bin/ld: error: ./obj/local/armeabi/libcocos2d.a: member at 42063518 is not an ELF object
collect2: error: ld returned 1 exit status
make: *** [obj/local/armeabi/libcocos2dcpp.so] Error 1
make: Leaving directory `/Users/XXX/cocos2d/cocos2d-x-3.0beta2/projects/irof/proj.android'
Traceback (most recent call last):
File "build_native.py", line 212, in
build(opts.ndk_build_param,opts.android_platform,opts.build_mode)
File "build_native.py", line 199, in build
do_build(cocos_root, ndk_root, app_android_root,ndk_build_param,sdk_root,android_platform,build_mode)
File "build_native.py", line 135, in do_build
raise Exception("Build dynamic library for project [ " + app_android_root + " ] fails!")
Exception: Build dynamic library for project [ /Users/XXX/cocos2d/cocos2d-x-3.0beta2/projects/irof/proj.android ] fails!
iMac-No5:proj.android kimura$
ググると NDKのversion依存じゃね? みたいなのでてくるんだけどなんだこれ。。。(汗
alpha1 => beta2 でNDK verupしていないのにな。。。><


collect2: ld returned 1 exit status
っての所が怪しいかな。。


最終的には

obj/local/armeabi/app_process

とかがどうも掴まれて消すの失敗してるっぽい

  • build_native.py/build_native.sh で差分があるNDK_MODULE_PATHを両方通す*4
        #ndk_module_path = 'NDK_MODULE_PATH=%s:%s/external:%s/cocos' % (cocos_root, cocos_root, cocos_root)
        ndk_module_path = 'NDK_MODULE_PATH=%s:%s/external:%s/cocos:%s/2d/platform/android:%s/plugin/publish' % (cocos_root, cocos_root, cocos_root, cocos_root, cocos_root)

でコレで動作確認後に

./gradlew clean assembleDebug

を再実行=>成功

 まあOS寄りの処理みんな書いてないから問題視されてないんだろうな。。(汗
フレームワークの制限だけでかけばそんなに難しくはないし‥‥*5

*1:genymotionに転送する場合は cocos2d/plugin/protocols/proj.android/jni/Application.mk でx86を追加してからコンパイルしておくことを忘れずに!

*2:コマンドラインでbuild通す観点だけならgradle文法的に間違ってないので問題ない

*3:python build_native.py clean が正常に動いてないみたい

*4:ここらへんは中国のフォーラムでも質問されてたけど、「動くならいいじゃん」って遣り取りされてたw。なんかすごくいい加減な感じかな。。。

*5:広告とかアプリ内課金とか遣らなければ。。