#!/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 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 [-s src_dir][-m ]" \ "\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>') 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)