axmol/tools/cocos2d-console/plugins/plugin_compile/project_compile.py

1019 lines
41 KiB
Python

#!/usr/bin/python
# ----------------------------------------------------------------------------
# cocos2d "compile" plugin
#
# Copyright 2013 (C) Luis Parravicini
#
# License: MIT
# ----------------------------------------------------------------------------
'''
"compile" plugin for cocos command line tool
'''
__docformat__ = 'restructuredtext'
import multiprocessing
import cocos
from MultiLanguage import MultiLanguage
import cocos_project
import os
import re
import sys
import shutil
import json
from . import build_web
import utils
class CCPluginCompile(cocos.CCPlugin):
"""
compiles a project
"""
BUILD_CONFIG_FILE = "build-cfg.json"
CFG_KEY_WIN32_COPY_FILES = "copy_files"
CFG_KEY_WIN32_MUST_COPY_FILES = "must_copy_files"
CFG_KEY_COPY_RESOURCES = "copy_resources"
CFG_KEY_MUST_COPY_RESOURCES = "must_copy_resources"
OUTPUT_DIR_NATIVE = "bin"
OUTPUT_DIR_SCRIPT_DEBUG = "simulator"
OUTPUT_DIR_SCRIPT_RELEASE = "publish"
WEB_PLATFORM_FOLDER_NAME = "html5"
PROJ_CFG_KEY_IOS_SIGN_ID = "ios_sign_id"
PROJ_CFG_KEY_ENGINE_DIR = "engine_dir"
BACKUP_SUFFIX = "-backup"
ENGINE_JS_DIRS = [
"frameworks/js-bindings/bindings/script",
"cocos/scripting/js-bindings/script"
]
CMAKE_VS_GENERATOR_MAP = {
"12" : "Visual Studio 12 2013",
"14" : "Visual Studio 14 2015",
"15" : "Visual Studio 15 2017",
"16" : "Visual Studio 16 2019",
}
@staticmethod
def plugin_name():
return "compile"
@staticmethod
def brief_description():
return MultiLanguage.get_string('COMPILE_BRIEF')
def _add_custom_options(self, parser):
from argparse import ArgumentParser
parser.add_argument("-m", "--mode", dest="mode", default='debug',
help=MultiLanguage.get_string('COMPILE_ARG_MODE'))
parser.add_argument("-j", "--jobs", dest="jobs", type=int,
help=MultiLanguage.get_string('COMPILE_ARG_JOBS'))
parser.add_argument("-o", "--output-dir", dest="output_dir",
help=MultiLanguage.get_string('COMPILE_ARG_OUTPUT'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_ANDROID'))
group.add_argument("--ap", dest="android_platform",
help=MultiLanguage.get_string('COMPILE_ARG_AP'))
group.add_argument("--build-type", dest="build_type",
help=MultiLanguage.get_string('COMPILE_ARG_BUILD_TYPE'))
group.add_argument("--app-abi", dest="app_abi",
help=MultiLanguage.get_string('COMPILE_ARG_APP_ABI'))
group.add_argument("--ndk-toolchain", dest="toolchain",
help=MultiLanguage.get_string('COMPILE_ARG_TOOLCHAIN'))
group.add_argument("--ndk-cppflags", dest="cppflags",
help=MultiLanguage.get_string('COMPILE_ARG_CPPFLAGS'))
group.add_argument("--no-apk", dest="no_apk", action="store_true",
help=MultiLanguage.get_string('COMPILE_ARG_NO_APK'))
group.add_argument("--no-sign", dest="no_sign", action="store_true",
help=MultiLanguage.get_string('COMPILE_ARG_NO_SIGN'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_WIN'))
group.add_argument("--vs", dest="vs_version", type=int,
help=MultiLanguage.get_string('COMPILE_ARG_VS'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_WEB'))
group.add_argument("--source-map", dest="source_map", action="store_true",
help=MultiLanguage.get_string('COMPILE_ARG_SOURCE_MAP'))
group.add_argument("--advanced", dest="advanced", action="store_true",
help=MultiLanguage.get_string('COMPILE_ARG_ADVANCE'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_IOS_MAC'))
group.add_argument("-t", "--target", dest="target_name",
help=MultiLanguage.get_string('COMPILE_ARG_TARGET'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_IOS'))
group.add_argument("--sign-identity", dest="sign_id",
help=MultiLanguage.get_string('COMPILE_ARG_IOS_SIGN'))
group.add_argument("-sdk", dest="use_sdk", metavar="USE_SDK", nargs='?', default='iphonesimulator',
help=MultiLanguage.get_string('COMPILE_ARG_IOS_SDK'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_LUA_JS'))
group.add_argument("--no-res", dest="no_res", action="store_true",
help=MultiLanguage.get_string('COMPILE_ARG_NO_RES'))
group.add_argument("--compile-script", dest="compile_script", type=int, choices=[0, 1],
help=MultiLanguage.get_string('COMPILE_ARG_COMPILE_SCRIPT'))
group = parser.add_argument_group(MultiLanguage.get_string('COMPILE_ARG_GROUP_LUA'))
group.add_argument("--lua-encrypt", dest="lua_encrypt", action="store_true",
help=MultiLanguage.get_string('COMPILE_ARG_LUA_ENCRYPT'))
group.add_argument("--lua-encrypt-key", dest="lua_encrypt_key",
help=MultiLanguage.get_string('COMPILE_ARG_LUA_ENCRYPT_KEY'))
group.add_argument("--lua-encrypt-sign", dest="lua_encrypt_sign",
help=MultiLanguage.get_string('COMPILE_ARG_LUA_ENCRYPT_SIGN'))
category = self.plugin_category()
name = self.plugin_name()
usage = "\n\t%%prog %s %s -p <platform> [-s src_dir][-m <debug|release>]" \
"\nSample:" \
"\n\t%%prog %s %s -p android" % (category, name, category, name)
def _check_custom_options(self, args):
# get the mode parameter
available_modes = [ 'release', 'debug' ]
self._mode = self.check_param(args.mode, 'debug', available_modes,
MultiLanguage.get_string('COMPILE_ERROR_WRONG_MODE_FMT',
available_modes))
# android arguments
available_build_types = [ 'cmake', 'none']
self._build_type = self.check_param(args.build_type, 'cmake', available_build_types,
MultiLanguage.get_string('COMPILE_ERROR_WRONG_BUILD_TYPE_FMT',
available_build_types))
self._no_apk = args.no_apk
self._no_sign = args.no_sign
self.app_abi = None
if args.app_abi:
self.app_abi = " ".join(args.app_abi.split(":"))
self.cppflags = None
if args.cppflags:
self.cppflags = args.cppflags
self.ndk_toolchain = None
if args.toolchain:
self.ndk_toolchain = args.toolchain
# Win32 arguments
self.vs_version = args.vs_version
# iOS/Mac arguments
self.xcode_target_name = None
if args.target_name is not None:
self.xcode_target_name = args.target_name
if args.compile_script is not None:
self._compile_script = bool(args.compile_script)
else:
self._compile_script = (self._mode == "release")
self._ap = args.android_platform
if args.jobs is not None:
self._jobs = args.jobs
else:
self._jobs = self.get_num_of_cpu()
self._has_sourcemap = args.source_map
self._web_advanced = args.advanced
self._no_res = args.no_res
if args.output_dir is None:
self._output_dir = self._get_output_dir()
else:
if os.path.isabs(args.output_dir):
self._output_dir = args.output_dir
else:
self._output_dir = os.path.abspath(args.output_dir)
self._sign_id = args.sign_id
self._use_sdk = args.use_sdk
if self._project._is_lua_project():
self._lua_encrypt = args.lua_encrypt
self._lua_encrypt_key = args.lua_encrypt_key
self._lua_encrypt_sign = args.lua_encrypt_sign
self.end_warning = ""
self._gen_custom_step_args()
def check_param(self, value, default_value, available_values, error_msg, ignore_case=True):
if value is None:
return default_value
if ignore_case:
check_value = value.lower()
right_values = []
for v in available_values:
right_values.append(v.lower())
else:
check_value = value
right_values = available_values
if check_value in right_values:
return check_value
else:
raise cocos.CCPluginError(error_msg, cocos.CCPluginError.ERROR_WRONG_ARGS)
def get_num_of_cpu(self):
try:
return multiprocessing.cpu_count()
except Exception:
print(MultiLanguage.get_string('COMPILE_DETECT_CPU_FAILED'))
return 1
def _get_output_dir(self):
project_dir = self._project.get_project_dir()
cur_platform = self._platforms.get_current_platform()
if self._project._is_script_project():
if self._project._is_js_project() and self._platforms.is_web_active():
cur_platform = CCPluginCompile.WEB_PLATFORM_FOLDER_NAME
if self._mode == 'debug':
output_dir = os.path.join(project_dir, CCPluginCompile.OUTPUT_DIR_SCRIPT_DEBUG, cur_platform)
else:
output_dir = os.path.join(project_dir, CCPluginCompile.OUTPUT_DIR_SCRIPT_RELEASE, cur_platform)
else:
output_dir = os.path.join(project_dir, CCPluginCompile.OUTPUT_DIR_NATIVE, self._mode, cur_platform)
return output_dir
def _gen_custom_step_args(self):
self._custom_step_args = {
"project-path": self._project.get_project_dir(),
"platform-project-path": self._platforms.project_path(),
"build-mode": self._mode,
"output-dir": self._output_dir
}
if self._platforms.is_android_active():
self._custom_step_args["ndk-build-type"] = self._build_type
def _build_cfg_path(self):
cur_cfg = self._platforms.get_current_config()
if self._platforms.is_win32_active():
ret = self._platforms.project_path()
elif self._platforms.is_ios_active():
ret = os.path.join(self._platforms.project_path(), "ios")
elif self._platforms.is_mac_active():
ret = os.path.join(self._platforms.project_path(), "mac")
else:
ret = self._platforms.project_path()
return ret
def _update_build_cfg(self):
build_cfg_dir = self._build_cfg_path()
cfg_file_path = os.path.join(build_cfg_dir, CCPluginCompile.BUILD_CONFIG_FILE)
if not os.path.isfile(cfg_file_path):
return
key_of_copy = None
key_of_must_copy = None
if self._platforms.is_android_active():
from build_android import AndroidBuilder
key_of_copy = AndroidBuilder.CFG_KEY_COPY_TO_ASSETS
key_of_must_copy = AndroidBuilder.CFG_KEY_MUST_COPY_TO_ASSERTS
elif self._platforms.is_win32_active():
key_of_copy = CCPluginCompile.CFG_KEY_WIN32_COPY_FILES
key_of_must_copy = CCPluginCompile.CFG_KEY_WIN32_MUST_COPY_FILES
if key_of_copy is None and key_of_must_copy is None:
return
try:
outfile = None
open_file = open(cfg_file_path)
cfg_info = json.load(open_file)
open_file.close()
open_file = None
changed = False
if key_of_copy is not None:
if cfg_info.has_key(key_of_copy):
src_list = cfg_info[key_of_copy]
ret_list = self._convert_cfg_list(src_list, build_cfg_dir)
cfg_info[CCPluginCompile.CFG_KEY_COPY_RESOURCES] = ret_list
del cfg_info[key_of_copy]
changed = True
if key_of_must_copy is not None:
if cfg_info.has_key(key_of_must_copy):
src_list = cfg_info[key_of_must_copy]
ret_list = self._convert_cfg_list(src_list, build_cfg_dir)
cfg_info[CCPluginCompile.CFG_KEY_MUST_COPY_RESOURCES] = ret_list
del cfg_info[key_of_must_copy]
changed = True
if changed:
# backup the old-cfg
split_list = os.path.splitext(CCPluginCompile.BUILD_CONFIG_FILE)
file_name = split_list[0]
ext_name = split_list[1]
bak_name = file_name + "-for-v0.1" + ext_name
bak_file_path = os.path.join(build_cfg_dir, bak_name)
if os.path.exists(bak_file_path):
os.remove(bak_file_path)
os.rename(cfg_file_path, bak_file_path)
# write the new data to file
with open(cfg_file_path, 'w') as outfile:
json.dump(cfg_info, outfile, sort_keys = True, indent = 4)
outfile.close()
outfile = None
finally:
if open_file is not None:
open_file.close()
if outfile is not None:
outfile.close()
def _convert_cfg_list(self, src_list, build_cfg_dir):
ret = []
for element in src_list:
ret_element = {}
if str(element).endswith("/"):
sub_str = element[0:len(element)-1]
ret_element["from"] = sub_str
ret_element["to"] = ""
else:
element_full_path = os.path.join(build_cfg_dir, element)
if os.path.isfile(element_full_path):
to_dir = ""
else:
to_dir = os.path.basename(element)
ret_element["from"] = element
ret_element["to"] = to_dir
ret.append(ret_element)
return ret
def _is_debug_mode(self):
return self._mode == 'debug'
def _remove_file_with_ext(self, work_dir, ext):
if not os.path.exists(work_dir):
return
file_list = os.listdir(work_dir)
for f in file_list:
full_path = os.path.join(work_dir, f)
if os.path.isdir(full_path):
self._remove_file_with_ext(full_path, ext)
elif os.path.isfile(full_path):
name, cur_ext = os.path.splitext(f)
if cur_ext == ext:
os.remove(full_path)
def compile_lua_scripts(self, src_dir, dst_dir, build_64):
if not self._project._is_lua_project():
return False
if not self._compile_script and not self._lua_encrypt:
return False
cocos_cmd_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "cocos")
rm_ext = ".lua"
compile_cmd = "\"%s\" luacompile -s \"%s\" -d \"%s\"" % (cocos_cmd_path, src_dir, dst_dir)
if not self._compile_script:
compile_cmd = "%s --disable-compile" % compile_cmd
elif build_64:
compile_cmd = "%s --bytecode-64bit" % compile_cmd
if self._lua_encrypt:
add_para = ""
if self._lua_encrypt_key is not None:
add_para = "%s -k %s" % (add_para, self._lua_encrypt_key)
if self._lua_encrypt_sign is not None:
add_para = "%s -b %s" % (add_para, self._lua_encrypt_sign)
compile_cmd = "%s -e %s" % (compile_cmd, add_para)
# run compile command
self._run_cmd(compile_cmd)
# remove the source scripts
self._remove_file_with_ext(dst_dir, rm_ext)
return True
def compile_js_scripts(self, src_dir, dst_dir):
if not self._project._is_js_project():
return False
if not self._compile_script:
return False
cocos_cmd_path = os.path.join(os.path.dirname(os.path.abspath(sys.argv[0])), "cocos")
rm_ext = ".js"
compile_cmd = "\"%s\" jscompile -s \"%s\" -d \"%s\"" % (cocos_cmd_path, src_dir, dst_dir)
# run compile command
self._run_cmd(compile_cmd)
# remove the source scripts
# self._remove_file_with_ext(dst_dir, rm_ext)
return True
def add_warning_at_end(self, warning_str):
if warning_str is None or len(warning_str) == 0:
return
self.end_warning = "%s\n%s" % (self.end_warning, warning_str)
def is_valid_path(self, p):
if (p is not None) and os.path.exists(p):
ret = True
else:
ret = False
return ret
def get_engine_version_num(self):
# 1. get engine version from .cocos_project.json
engine_ver_str = self._project.get_proj_config(cocos_project.Project.KEY_ENGINE_VERSION)
# 2. engine version is not found. find from source file
if engine_ver_str is None:
engine_dir = self.get_engine_dir()
if engine_dir is not None:
engine_ver_str = utils.get_engine_version(engine_dir)
if engine_ver_str is None:
return None
version_pattern = r'cocos2d-x-([\d]+)\.([\d]+)'
match = re.match(version_pattern, engine_ver_str)
if match:
return ((int)(match.group(1)), (int)(match.group(2)))
else:
return None
def build_android(self):
if not self._platforms.is_android_active():
return
project_dir = self._project.get_project_dir()
build_mode = self._mode
output_dir = self._output_dir
# get the android project path
cfg_obj = self._platforms.get_current_config()
project_android_dir = cfg_obj.proj_path
ide_name = 'Android Studio'
cocos.Logging.info(MultiLanguage.get_string('COMPILE_INFO_ANDROID_PROJPATH_FMT', (ide_name, project_android_dir)))
# Check whether the gradle of the project is support ndk or not
# Get the engine version of the project
engine_version_num = self.get_engine_version_num()
if engine_version_num is None:
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_UNKNOWN_ENGINE_VERSION'))
# Gradle supports NDK build from engine 3.15
main_ver = engine_version_num[0]
minor_ver = engine_version_num[1]
if main_ver > 3 or (main_ver == 3 and minor_ver >= 15):
gradle_support_ndk = True
from build_android import AndroidBuilder
builder = AndroidBuilder(self._verbose, project_android_dir,
self._no_res, self._project, self._mode, self._build_type,
self.app_abi, gradle_support_ndk)
args_ndk_copy = self._custom_step_args.copy()
target_platform = self._platforms.get_current_platform()
# update the project with the android platform
builder.update_project(self._ap)
if not self._project._is_script_project() or self._project._is_native_support():
if self._build_type != "none" and not gradle_support_ndk:
# build native code
cocos.Logging.info(MultiLanguage.get_string('COMPILE_INFO_BUILD_NATIVE'))
ndk_build_param = [
"-j%s" % self._jobs
]
if self.app_abi:
abi_param = "APP_ABI=\"%s\"" % self.app_abi
ndk_build_param.append(abi_param)
if self.ndk_toolchain:
toolchain_param = "NDK_TOOLCHAIN=%s" % self.ndk_toolchain
ndk_build_param.append(toolchain_param)
self._project.invoke_custom_step_script(cocos_project.Project.CUSTOM_STEP_PRE_NDK_BUILD, target_platform, args_ndk_copy)
modify_mk = False
app_mk = os.path.join(project_android_dir, "app/jni/Application.mk")
mk_content = None
if self.cppflags and os.path.exists(app_mk):
# record the content of Application.mk
f = open(app_mk)
mk_content = f.read()
f.close()
# Add cpp flags
f = open(app_mk, "a")
f.write("\nAPP_CPPFLAGS += %s" % self.cppflags)
f.close()
modify_mk = True
try:
builder.do_ndk_build(ndk_build_param, self._mode, self._build_type, self)
except Exception as e:
if e.__class__.__name__ == 'CCPluginError':
raise e
else:
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_NDK_BUILD_FAILED'),
cocos.CCPluginError.ERROR_BUILD_FAILED)
finally:
# roll-back the Application.mk
if modify_mk:
f = open(app_mk, "w")
f.write(mk_content)
f.close()
self._project.invoke_custom_step_script(cocos_project.Project.CUSTOM_STEP_POST_NDK_BUILD, target_platform, args_ndk_copy)
# build apk
if not self._no_apk:
cocos.Logging.info(MultiLanguage.get_string('COMPILE_INFO_BUILD_APK'))
self.apk_path = builder.do_build_apk(build_mode, self._no_apk, self._no_sign, output_dir, self._custom_step_args, self._ap, self)
self.android_package, self.android_activity = builder.get_apk_info()
cocos.Logging.info(MultiLanguage.get_string('COMPILE_INFO_BUILD_SUCCEED'))
def _remove_res(self, target_path):
build_cfg_dir = self._build_cfg_path()
cfg_file = os.path.join(build_cfg_dir, CCPluginCompile.BUILD_CONFIG_FILE)
if os.path.exists(cfg_file) and os.path.isfile(cfg_file):
# have config file
open_file = open(cfg_file)
cfg_info = json.load(open_file)
open_file.close()
if cfg_info.has_key("remove_res"):
remove_list = cfg_info["remove_res"]
for f in remove_list:
res = os.path.join(target_path, f)
if os.path.isdir(res):
# is a directory
if f.endswith('/'):
# remove files & dirs in it
for sub_file in os.listdir(res):
sub_file_fullpath = os.path.join(res, sub_file)
if os.path.isfile(sub_file_fullpath):
os.remove(sub_file_fullpath)
elif os.path.isdir(sub_file_fullpath):
shutil.rmtree(sub_file_fullpath)
else:
# remove the dir
shutil.rmtree(res)
elif os.path.isfile(res):
# is a file, remove it
os.remove(res)
def get_engine_dir(self):
engine_dir = self._project.get_proj_config(CCPluginCompile.PROJ_CFG_KEY_ENGINE_DIR)
if engine_dir is None:
proj_dir = self._project.get_project_dir()
if self._project._is_js_project():
check_dir = os.path.join(proj_dir, "frameworks", "cocos2d-x")
if os.path.isdir(check_dir):
# the case for jsb in cocos2d-x engine
engine_dir = check_dir
else:
# the case for jsb in cocos2d-js engine
engine_dir = proj_dir
elif self._project._is_lua_project():
engine_dir = os.path.join(proj_dir, "frameworks", "cocos2d-x")
else:
engine_dir = os.path.join(proj_dir, "cocos2d")
else:
engine_dir = os.path.join(self._project.get_project_dir(), engine_dir)
return engine_dir
def backup_dir(self, dir_path):
backup_dir = "%s%s" % (dir_path, CCPluginCompile.BACKUP_SUFFIX)
if os.path.exists(backup_dir):
shutil.rmtree(backup_dir)
shutil.copytree(dir_path, backup_dir)
def reset_backup_dir(self, dir_path):
backup_dir = "%s%s" % (dir_path, CCPluginCompile.BACKUP_SUFFIX)
if os.path.exists(dir_path):
shutil.rmtree(dir_path)
os.rename(backup_dir, dir_path)
def get_engine_js_dir(self):
engine_js_dir = None
isFound = False
check_script_dir = os.path.join(self._project.get_project_dir(), "script")
if os.path.isdir(check_script_dir):
# JS script already copied into the project dir
engine_js_dir = check_script_dir
isFound = True
else:
for js_dir in CCPluginCompile.ENGINE_JS_DIRS:
engine_js_dir = os.path.join(self.get_engine_dir(), js_dir)
if os.path.isdir(engine_js_dir):
isFound = True
break
if isFound:
return engine_js_dir
else:
return None
def _get_export_options_plist_path(self):
project_dir = self._project.get_project_dir()
possible_sub_paths = [ 'proj.ios', 'proj.ios_mac/ios', 'frameworks/runtime-src/proj.ios_mac/ios' ]
ios_project_dir = None
for sub_path in possible_sub_paths:
ios_project_dir = os.path.join(project_dir, sub_path)
if os.path.exists(ios_project_dir):
break
if ios_project_dir is None:
return None
return os.path.join(ios_project_dir, 'exportOptions.plist')
def get_available_devenv(self, required_versions, min_ver, specify_vs_ver=None):
if required_versions is None or len(required_versions) == 0:
if specify_vs_ver is None:
# Not specify VS version, find newest version
needUpgrade, commandPath = utils.get_newest_devenv(min_ver)
else:
# Have specified VS version
if specify_vs_ver < min_ver:
# Specified version is lower than required, raise error
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_LOW_VS_VER'),
cocos.CCPluginError.ERROR_WRONG_ARGS)
else:
# Get the specified VS
commandPath = utils.get_devenv_path(specify_vs_ver)
if specify_vs_ver > min_ver:
needUpgrade = True
else:
needUpgrade = False
else:
needUpgrade = False
if specify_vs_ver is None:
# find VS in required versions
commandPath = None
for v in required_versions:
commandPath = utils.get_devenv_path(v)
if commandPath is not None:
break
else:
# use specified VS version
if specify_vs_ver in required_versions:
commandPath = utils.get_devenv_path(specify_vs_ver)
else:
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_WRONG_VS_VER_FMT', specify_vs_ver),
cocos.CCPluginError.ERROR_WRONG_ARGS)
if commandPath is None:
message = MultiLanguage.get_string('COMPILE_ERROR_VS_NOT_FOUND')
raise cocos.CCPluginError(message, cocos.CCPluginError.ERROR_TOOLS_NOT_FOUND)
return (needUpgrade, commandPath)
def build_web(self):
if not self._platforms.is_web_active():
return
project_dir = self._platforms.project_path()
# store env for run
cfg_obj = self._platforms.get_current_config()
if cfg_obj.run_root_dir is not None:
self.run_root = cfg_obj.run_root_dir
else:
self.run_root = project_dir
if cfg_obj.sub_url is not None:
self.sub_url = cfg_obj.sub_url
else:
self.sub_url = '/'
output_dir = CCPluginCompile.OUTPUT_DIR_SCRIPT_RELEASE
if self._is_debug_mode():
output_dir = CCPluginCompile.OUTPUT_DIR_SCRIPT_DEBUG
if not self._web_advanced:
return
self.sub_url = '%s%s/%s/' % (self.sub_url, output_dir, CCPluginCompile.WEB_PLATFORM_FOLDER_NAME)
f = open(os.path.join(project_dir, "project.json"))
project_json = json.load(f)
f.close()
engine_dir = os.path.join(project_json["engineDir"])
realEngineDir = os.path.normpath(os.path.join(project_dir, engine_dir))
publish_dir = os.path.normpath(os.path.join(project_dir, output_dir, CCPluginCompile.WEB_PLATFORM_FOLDER_NAME))
# need to config in options of command
buildOpt = {
"outputFileName" : "game.min.js",
"debug": "true" if self._is_debug_mode() else "false",
"compilationLevel" : "advanced" if self._web_advanced else "simple",
"sourceMapOpened" : True if self._has_sourcemap else False
}
if os.path.exists(publish_dir):
shutil.rmtree(publish_dir)
os.makedirs(publish_dir)
# generate build.xml
build_web.gen_buildxml(project_dir, project_json, publish_dir, buildOpt)
outputJsPath = os.path.join(publish_dir, buildOpt["outputFileName"])
if os.path.exists(outputJsPath) == True:
os.remove(outputJsPath)
# call closure compiler
ant_root = cocos.check_environment_variable('ANT_ROOT')
ant_path = os.path.join(ant_root, 'ant')
self._run_cmd("%s -f %s" % (ant_path, os.path.join(publish_dir, 'build.xml')))
# handle sourceMap
sourceMapPath = os.path.join(publish_dir, "sourcemap")
if os.path.exists(sourceMapPath):
smFile = open(sourceMapPath)
try:
smContent = smFile.read()
finally:
smFile.close()
dir_to_replace = project_dir
if cocos.os_is_win32():
dir_to_replace = project_dir.replace('\\', '\\\\')
smContent = smContent.replace(dir_to_replace, os.path.relpath(project_dir, publish_dir))
smContent = smContent.replace(realEngineDir, os.path.relpath(realEngineDir, publish_dir))
smContent = smContent.replace('\\\\', '/')
smContent = smContent.replace('\\', '/')
smFile = open(sourceMapPath, "w")
smFile.write(smContent)
smFile.close()
# handle project.json
del project_json["engineDir"]
del project_json["modules"]
del project_json["jsList"]
project_json_output_file = open(os.path.join(publish_dir, "project.json"), "w")
project_json_output_file.write(json.dumps(project_json))
project_json_output_file.close()
# handle index.html
indexHtmlFile = open(os.path.join(project_dir, "index.html"))
try:
indexContent = indexHtmlFile.read()
finally:
indexHtmlFile.close()
reg1 = re.compile(r'<script\s+src\s*=\s*("|\')[^"\']*CCBoot\.js("|\')\s*><\/script>')
indexContent = reg1.sub("", indexContent)
mainJs = project_json.get("main") or "main.js"
indexContent = indexContent.replace(mainJs, buildOpt["outputFileName"])
indexHtmlOutputFile = open(os.path.join(publish_dir, "index.html"), "w")
indexHtmlOutputFile.write(indexContent)
indexHtmlOutputFile.close()
# copy res dir
if cfg_obj.copy_res is None:
dst_dir = os.path.join(publish_dir, 'res')
src_dir = os.path.join(project_dir, 'res')
if os.path.exists(dst_dir):
shutil.rmtree(dst_dir)
shutil.copytree(src_dir, dst_dir)
else:
for cfg in cfg_obj.copy_res:
cocos.copy_files_with_config(cfg, project_dir, publish_dir)
# copy to the output directory if necessary
pub_dir = os.path.normcase(publish_dir)
out_dir = os.path.normcase(os.path.normpath(self._output_dir))
if pub_dir != out_dir:
cpy_cfg = {
"from" : pub_dir,
"to" : out_dir
}
cocos.copy_files_with_config(cpy_cfg, pub_dir, out_dir)
def check_platform(self, platform):
if platform == 'mac':
if not self._platforms.is_mac_active() or not cocos.os_is_mac:
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_BUILD_ON_MAC'),
cocos.CCPluginError.ERROR_WRONG_ARGS)
if platform == 'ios':
if not self._platforms.is_ios_active() or not cocos.os_is_mac:
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_BUILD_ON_MAC'),
cocos.CCPluginError.ERROR_WRONG_ARGS)
if platform == 'win32':
if not self._platforms.is_win32_active or not cocos.os_is_win32:
raise cocos.CCPluginError(MultiLanguage.get_string('COMPILE_ERROR_BUILD_ON_WIN'),
cocos.CCPluginError.ERROR_WRONG_ARGS)
if platform == 'linux':
if not self._platforms.is_linux_active():
raise cocos.CCPluginError("Please build on linux")
def compile_script(self, script_path, platform):
"""
Compile and encrypt script files if needed.
"""
if not self._project._is_script_project:
return
if self._project._is_lua_project:
build_64bit = True if platform != 'win32' else False
build_32bit = True if platform != 'mac' else False
if build_64bit:
folder_64bit = os.path.join(script_path, '64bit')
self.compile_lua_scripts(script_path, folder_64bit, True)
if build_32bit:
self.compile_lua_scripts(script_path, script_path, False)
# mac only support 64bit, so should remove .lua files not int folder_64bit
if platform == 'mac' and self._compile_script:
self._remove_file_with_ext(script_path, '.lua')
# self.compile_js_script(path)
# if only support 64bit, then move to
def build(self, platform):
if self._platforms.is_android_active():
self.build_android()
return
self.check_platform(platform)
project_dir = self._project.get_project_dir()
cfg_obj = self._platforms.get_current_config()
if cfg_obj.cmake_path is not None:
cmakefile_dir = os.path.join(project_dir, cfg_obj.cmake_path)
else:
cmakefile_dir = project_dir
# get the project name
if cfg_obj.project_name is not None:
self.project_name = cfg_obj.project_name
else:
f = open(os.path.join(cmakefile_dir, 'CMakeLists.txt'), 'r')
regexp_set_app_name = re.compile(r'\s*set\s*\(\s*APP_NAME', re.IGNORECASE)
for line in f.readlines():
if regexp_set_app_name.search(line):
self.project_name = re.search('APP_NAME ([^\)]+)\)', line, re.IGNORECASE).group(1)
break
if hasattr(self, 'project_name') == False:
raise cocos.CCPluginError("Couldn't find APP_NAME in CMakeLists.txt")
if cfg_obj.build_dir is not None:
build_dir = os.path.join(project_dir, cfg_obj.build_dir)
else:
build_dir = os.path.join(project_dir, '%s-build' % platform)
if not os.path.exists(build_dir):
os.makedirs(build_dir)
# compile codes
build_mode = 'Debug' if self._is_debug_mode() else 'Release'
with cocos.pushd(build_dir):
# iOS need to generate Xcode project file first
if platform == 'ios':
engine_dir = self.get_engine_dir()
self._run_cmd('cmake %s -GXcode -DCMAKE_SYSTEM_NAME=iOS -DCMAKE_OSX_SYSROOT=%s' %
( os.path.relpath(cmakefile_dir, build_dir), self._use_sdk ) )
elif platform == 'mac':
self._run_cmd('cmake -GXcode %s' % os.path.relpath(cmakefile_dir, build_dir))
elif platform == "win32":
ret = utils.get_newest_devenv(self.vs_version)
if ret is not None:
ver_num = int(float(ret[2]))
generator = self.CMAKE_VS_GENERATOR_MAP[str(ver_num)]
if generator is not None:
if ver_num >= 16:
# for vs2019 x64 is the default target
self._run_cmd('cmake %s -G "%s" -A win32' %
(os.path.relpath(cmakefile_dir, build_dir), generator))
else:
self._run_cmd('cmake %s -G "%s"' %
(os.path.relpath(cmakefile_dir, build_dir), generator))
else:
cocos.Logging.warning(MultiLanguage.get_string("COMPILE_VS_VERSION_NOT_REGISTER") % (ret[2]))
self._run_cmd('cmake %s' % os.path.relpath(cmakefile_dir, build_dir) )
else:
cocos.Logging.warning(MultiLanguage.get_string("COMPILE_VS_VERSION"))
self._run_cmd('cmake %s' % os.path.relpath(cmakefile_dir, build_dir) )
else:
self._run_cmd('cmake %s' % os.path.relpath(cmakefile_dir, build_dir) )
self._run_cmd('cmake --build . --config %s' % build_mode)
# move file
output_dir = self._output_dir
if os.path.exists(output_dir):
shutil.rmtree(output_dir)
os.makedirs(output_dir)
if cfg_obj.build_result_dir is not None:
result_dir = os.path.join(build_dir, 'bin', cfg_obj.build_result_dir, self.project_name)
else:
result_dir = os.path.join(build_dir, 'bin', self.project_name)
if os.path.exists(os.path.join(result_dir, build_mode)):
result_dir = os.path.join(result_dir, build_mode)
cocos.copy_files_in_dir(result_dir, output_dir)
self.run_root = output_dir
# set application path and application name
if platform == 'mac' or platform == 'ios':
self.app_path = os.path.join(output_dir, self.project_name + '.app')
else:
self.app_path = output_dir
self.app_name = self.project_name
if platform == 'win32':
self.app_name = self.app_name + '.exe'
script_resource_path = os.path.join(self.app_path, 'src')
if platform == 'mac':
script_resource_path = os.path.join(self.app_path, 'Contents/Resources/src')
cocos.Logging.info(MultiLanguage.get_string('COMPILE_INFO_BUILD_SUCCEED'))
def _get_build_cfg(self):
build_cfg_dir = self._build_cfg_path()
build_cfg = os.path.join(build_cfg_dir, CCPluginCompile.BUILD_CONFIG_FILE)
if not os.path.exists(build_cfg):
message = MultiLanguage.get_string('COMPILE_ERROR_FILE_NOT_FOUND_FMT', build_cfg)
raise cocos.CCPluginError(message, cocos.CCPluginError.ERROR_PATH_NOT_FOUND)
f = open(build_cfg)
return json.load(f)
def _copy_resources(self, dst_path):
data = self._get_build_cfg()
if data.has_key(CCPluginCompile.CFG_KEY_MUST_COPY_RESOURCES):
if self._no_res:
fileList = data[CCPluginCompile.CFG_KEY_MUST_COPY_RESOURCES]
else:
fileList = data[CCPluginCompile.CFG_KEY_COPY_RESOURCES] + data[CCPluginCompile.CFG_KEY_MUST_COPY_RESOURCES]
else:
fileList = data[CCPluginCompile.CFG_KEY_COPY_RESOURCES]
for cfg in fileList:
cocos.copy_files_with_config(cfg, self._build_cfg_path(), dst_path)
def checkFileByExtention(self, ext, path):
filelist = os.listdir(path)
for fullname in filelist:
name, extention = os.path.splitext(fullname)
if extention == ext:
return name, fullname
return (None, None)
def run(self, argv, dependencies):
self.parse_args(argv)
cocos.Logging.info(MultiLanguage.get_string('COMPILE_INFO_BUILD_MODE_FMT', self._mode))
self._update_build_cfg()
target_platform = self._platforms.get_current_platform()
args_build_copy = self._custom_step_args.copy()
language = self._project.get_language()
action_str = 'compile_%s' % language
target_str = 'compile_for_%s' % target_platform
cocos.DataStatistic.stat_event('compile', action_str, target_str)
# invoke the custom step: pre-build
self._project.invoke_custom_step_script(cocos_project.Project.CUSTOM_STEP_PRE_BUILD, target_platform, args_build_copy)
self.build_web()
self.build(target_platform)
# invoke the custom step: post-build
self._project.invoke_custom_step_script(cocos_project.Project.CUSTOM_STEP_POST_BUILD, target_platform, args_build_copy)
if len(self.end_warning) > 0:
cocos.Logging.warning(self.end_warning)