axmol/tools/console/bin/adxe_project.py

502 lines
18 KiB
Python
Raw Normal View History

2021-05-18 18:08:59 +08:00
import os
import re
import json
import adxe
from MultiLanguage import MultiLanguage
class Project(object):
CPP = 'cpp'
LUA = 'lua'
JS = 'js'
CONFIG = '.adxeproj.json'
KEY_PROJ_TYPE = 'project_type'
KEY_HAS_NATIVE = 'has_native'
KEY_CUSTOM_STEP_SCRIPT = "custom_step_script"
KEY_ENGINE_VERSION = "engine_version"
CUSTOM_STEP_PRE_BUILD = "pre-build"
CUSTOM_STEP_POST_BUILD = "post-build"
CUSTOM_STEP_PRE_NDK_BUILD = "pre-ndk-build"
CUSTOM_STEP_POST_NDK_BUILD = "post-ndk-build"
CUSTOM_STEP_PRE_COPY_ASSETS = "pre-copy-assets"
CUSTOM_STEP_POST_COPY_ASSETS = "post-copy-assets"
CUSTOM_STEP_PRE_ANT_BUILD = "pre-ant-build"
CUSTOM_STEP_POST_ANT_BUILD = "post-ant-build"
@staticmethod
def list_for_display():
return [x.lower() for x in Project.language_list()]
@staticmethod
def language_list():
return (Project.CPP, Project.LUA, Project.JS)
def __init__(self, project_dir):
# parse the config file
self.info = self._parse_project_json(project_dir)
def _parse_project_json(self, src_dir):
proj_path = self._find_project_dir(src_dir)
# config file is not found
if proj_path == None:
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_NOT_FOUND_FMT',
os.path.join(src_dir, Project.CONFIG)),
adxe.CCPluginError.ERROR_PATH_NOT_FOUND)
project_json = os.path.join(proj_path, Project.CONFIG)
try:
f = open(project_json)
project_info = json.load(f)
f.close()
except Exception:
if f is not None:
f.close()
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_BROKEN_FMT',
project_json),
adxe.CCPluginError.ERROR_PARSE_FILE)
if project_info is None:
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_PARSE_FAILED_FMT',
Project.CONFIG), adxe.CCPluginError.ERROR_PARSE_FILE)
if not adxe.dict_contains(project_info, Project.KEY_PROJ_TYPE):
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_GET_VALUE_FAILED_FMT',
(Project.KEY_PROJ_TYPE, Project.CONFIG)),
adxe.CCPluginError.ERROR_WRONG_CONFIG)
lang = project_info[Project.KEY_PROJ_TYPE]
lang = lang.lower()
# The config is invalid
if not (lang in Project.language_list()):
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_INVALID_LANG_FMT',
(Project.KEY_PROJ_TYPE, ', '.join(Project.list_for_display()))),
adxe.CCPluginError.ERROR_WRONG_CONFIG)
# record the dir & language of the project
self._project_dir = proj_path
self._project_lang = lang
# if is script project, record whether it has native or not
self._has_native = False
if (self._is_script_project() and adxe.dict_contains(project_info, Project.KEY_HAS_NATIVE)):
self._has_native = project_info[Project.KEY_HAS_NATIVE]
# if has custom step script, record it
self._custom_step = None
if (adxe.dict_contains(project_info, Project.KEY_CUSTOM_STEP_SCRIPT)):
script_path = project_info[Project.KEY_CUSTOM_STEP_SCRIPT]
if not os.path.isabs(script_path):
script_path = os.path.join(self._project_dir, script_path)
if os.path.isfile(script_path):
import sys
script_dir, script_name = os.path.split(script_path)
sys.path.append(script_dir)
self._custom_step = __import__(os.path.splitext(script_name)[0])
adxe.Logging.info(MultiLanguage.get_string('PROJECT_INFO_FOUND_CUSTOM_STEP_FMT', script_path))
else:
adxe.Logging.warning(MultiLanguage.get_string('PROJECT_WARNING_CUSTOM_SCRIPT_NOT_FOUND_FMT',
script_path))
self._custom_step = None
return project_info
def invoke_custom_step_script(self, event, tp, args):
try:
if self._custom_step is not None:
self._custom_step.handle_event(event, tp, args)
except Exception as e:
adxe.Logging.warning(MultiLanguage.get_string('PROJECT_WARNING_CUSTOM_STEP_FAILED_FMT', e))
raise e
def _find_project_dir(self, start_path):
path = start_path
while True:
if adxe.os_is_win32():
# windows root path, eg. c:\
if re.match(".+:\\\\$", path):
break
else:
# unix like use '/' as root path
if path == '/' :
break
cfg_path = os.path.join(path, Project.CONFIG)
if (os.path.exists(cfg_path) and os.path.isfile(cfg_path)):
return path
path = os.path.dirname(path)
return None
def get_proj_config(self, key):
project_json = os.path.join(self._project_dir, Project.CONFIG)
f = open(project_json)
project_info = json.load(f)
f.close()
ret = None
if adxe.dict_contains(project_info, key):
ret = project_info[key]
return ret
def write_proj_config(self, key, value):
project_json = os.path.join(self._project_dir, Project.CONFIG)
if os.path.isfile(project_json):
f = open(project_json)
project_info = json.load(f)
f.close()
if project_info is None:
project_info = {}
project_info[key] = value
outfile = open(project_json, "w")
json.dump(project_info, outfile, sort_keys = True, indent = 4)
outfile.close()
def get_project_dir(self):
return self._project_dir
def get_language(self):
return self._project_lang
def has_android_libs(self):
if self._is_script_project():
proj_android_path = os.path.join(self.get_project_dir(), "frameworks", "runtime-src", "proj.android", "libs")
else:
proj_android_path = os.path.join(self.get_project_dir(), "proj.android", "libs")
return os.path.isdir(proj_android_path)
def _is_native_support(self):
return self._has_native
def _is_script_project(self):
return self._is_lua_project() or self._is_js_project()
def _is_cpp_project(self):
return self._project_lang == Project.CPP
def _is_lua_project(self):
return self._project_lang == Project.LUA
def _is_js_project(self):
return self._project_lang == Project.JS
class Platforms(object):
ANDROID = 'android'
IOS = 'ios'
MAC = 'mac'
WEB = 'web'
WIN32 = 'win32'
LINUX = 'linux'
CFG_CLASS_MAP = {
ANDROID : "adxe_project.AndroidConfig",
IOS : "adxe_project.iOSConfig",
MAC : "adxe_project.MacConfig",
WEB : "adxe_project.WebConfig",
WIN32 : "adxe_project.Win32Config",
LINUX : "adxe_project.LinuxConfig",
}
@staticmethod
def list_for_display():
return [x.lower() for x in Platforms.list()]
@staticmethod
def list():
return Platforms.CFG_CLASS_MAP.keys()
def __init__(self, project, current, proj_dir = None):
self._project = project
proj_info = self._project.info
self._gen_available_platforms(proj_info, proj_dir)
self._current = None
if current is not None:
current_lower = current.lower()
if current_lower in self._available_platforms.keys():
self._current = current_lower
else:
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_INVALID_PLATFORM_FMT',
(self._available_platforms.keys(), current)),
adxe.CCPluginError.ERROR_WRONG_ARGS)
def _filter_platforms(self, platforms):
ret = []
platforms_for_os = {
"linux" : [ Platforms.WEB, Platforms.LINUX, Platforms.ANDROID ],
"mac" : [ Platforms.WEB, Platforms.IOS, Platforms.MAC, Platforms.ANDROID ],
"win32" : [ Platforms.WEB, Platforms.WIN32, Platforms.ANDROID ]
}
for p in platforms:
if adxe.os_is_linux():
if p in platforms_for_os["linux"]:
ret.append(p)
if adxe.os_is_mac():
if p in platforms_for_os["mac"]:
ret.append(p)
if adxe.os_is_win32():
if p in platforms_for_os["win32"]:
ret.append(p)
return ret
def _gen_available_platforms(self, proj_info, proj_dir):
# generate the platform list for different projects
if self._project._is_lua_project():
if self._project._is_native_support():
platform_list = [ Platforms.ANDROID, Platforms.WIN32, Platforms.IOS, Platforms.MAC, Platforms.LINUX ]
else:
if self._project.has_android_libs():
platform_list = [ Platforms.ANDROID ]
else:
platform_list = []
elif self._project._is_js_project():
if self._project._is_native_support():
platform_list = [ Platforms.ANDROID, Platforms.WIN32, Platforms.IOS, Platforms.MAC, Platforms.WEB, Platforms.LINUX ]
else:
if self._project.has_android_libs():
platform_list = [ Platforms.ANDROID, Platforms.WEB ]
else:
platform_list = [ Platforms.WEB ]
elif self._project._is_cpp_project():
platform_list = [ Platforms.ANDROID, Platforms.WIN32, Platforms.IOS, Platforms.MAC, Platforms.LINUX ]
# filter the available platform list
platform_list = self._filter_platforms(platform_list)
# check the real available platforms
self._available_platforms = {}
root_path = self._project.get_project_dir()
for p in platform_list:
cfg_class = adxe.get_class(Platforms.CFG_CLASS_MAP[p])
if cfg_class is None:
continue
cfg_key = "%s_cfg" % p
if adxe.dict_contains(proj_info, cfg_key):
cfg_obj = cfg_class(root_path, self._project._is_script_project(), proj_info[cfg_key])
else:
cfg_obj = cfg_class(root_path, self._project._is_script_project())
if proj_dir is not None:
cfg_obj.proj_path = os.path.join(root_path, proj_dir)
if cfg_obj._is_available():
self._available_platforms[p] = cfg_obj
# don't have available platforms
if len(self._available_platforms) == 0:
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_NO_AVAILABLE_PLATFORMS'),
adxe.CCPluginError.ERROR_WRONG_CONFIG)
def get_current_platform(self):
return self._current
def get_available_platforms(self):
return self._available_platforms
def none_active(self):
return self._current is None
def is_android_active(self):
return self._current == Platforms.ANDROID
def is_ios_active(self):
return self._current == Platforms.IOS
def is_mac_active(self):
return self._current == Platforms.MAC
def is_web_active(self):
return self._current == Platforms.WEB
def is_win32_active(self):
return self._current == Platforms.WIN32
def is_linux_active(self):
return self._current == Platforms.LINUX
def get_current_config(self):
if self.none_active():
return None
return self._available_platforms[self._current]
def project_path(self):
if self._current is None:
return None
cfg_obj = self._available_platforms[self._current]
return cfg_obj.proj_path
def _has_one(self):
return len(self._available_platforms) == 1
def select_one(self):
if self._has_one():
self._current = self._available_platforms.keys()[0]
return
raise adxe.CCPluginError(MultiLanguage.get_string('PROJECT_SPECIFY_PLATFORM_FMT',
str(self._available_platforms.keys())),
adxe.CCPluginError.ERROR_WRONG_CONFIG)
class PlatformConfig(object):
KEY_PROJ_PATH = "project_path"
def __init__(self, proj_root_path, is_script, cfg_info = None):
self._proj_root_path = proj_root_path
self._is_script = is_script
if cfg_info is None:
self._use_default()
else:
self._parse_info(cfg_info)
def _use_default(self):
pass
def _parse_info(self, cfg_info):
if adxe.dict_contains(cfg_info, PlatformConfig.KEY_PROJ_PATH):
self.proj_path = os.path.join(self._proj_root_path, cfg_info[PlatformConfig.KEY_PROJ_PATH])
else:
self.proj_path = None
def _is_available(self):
ret = True
if self.proj_path is None or not os.path.isdir(self.proj_path):
ret = False
return ret
class AndroidConfig(PlatformConfig):
def _use_default(self):
if self._is_script:
self.proj_path = os.path.join(self._proj_root_path, "frameworks", "runtime-src", "proj.android")
else:
self.proj_path = os.path.join(self._proj_root_path, "proj.android")
def _parse_info(self, cfg_info):
super(AndroidConfig, self)._parse_info(cfg_info)
def _is_available(self):
proj_android_existed = super(AndroidConfig, self)._is_available()
return proj_android_existed
class LinuxConfig(PlatformConfig):
KEY_CMAKE_PATH = "cmake_path"
KEY_BUILD_DIR = "build_dir"
KEY_PROJECT_NAME = "project_name"
KEY_BUILD_RESULT_DIR = "build_result_dir"
def _use_default(self):
if self._is_script:
self.proj_path = os.path.join(self._proj_root_path, "frameworks", "runtime-src", "proj.linux")
else:
self.proj_path = os.path.join(self._proj_root_path, "proj.linux")
self.cmake_path = None
self.build_dir = None
self.project_name = None
self.build_result_dir = None
def _parse_info(self, cfg_info):
super(LinuxConfig, self)._parse_info(cfg_info)
if adxe.dict_contains(cfg_info, LinuxConfig.KEY_CMAKE_PATH):
self.cmake_path = cfg_info[LinuxConfig.KEY_CMAKE_PATH]
else:
self.cmake_path = None
if adxe.dict_contains(cfg_info, LinuxConfig.KEY_BUILD_DIR):
self.build_dir = cfg_info[LinuxConfig.KEY_BUILD_DIR]
else:
self.build_dir = None
if adxe.dict_contains(cfg_info, LinuxConfig.KEY_PROJECT_NAME):
self.project_name = cfg_info[LinuxConfig.KEY_PROJECT_NAME]
else:
self.project_name = None
if adxe.dict_contains(cfg_info, LinuxConfig.KEY_BUILD_RESULT_DIR):
self.build_result_dir = cfg_info[LinuxConfig.KEY_BUILD_RESULT_DIR]
else:
self.build_result_dir = None
def _is_available(self):
ret = super(LinuxConfig, self)._is_available()
return ret
class MacConfig(LinuxConfig):
def _use_default(self):
super(MacConfig, self)._use_default()
if self._is_script:
self.proj_path = os.path.join(self._proj_root_path, "frameworks", "runtime-src", "proj.ios_mac")
else:
self.proj_path = os.path.join(self._proj_root_path, "proj.ios_mac")
class iOSConfig(LinuxConfig):
def _use_default(self):
super(iOSConfig, self)._use_default()
if self._is_script:
self.proj_path = os.path.join(self._proj_root_path, "frameworks", "runtime-src", "proj.ios_mac")
else:
self.proj_path = os.path.join(self._proj_root_path, "proj.ios_mac")
class Win32Config(LinuxConfig):
def _use_default(self):
super(Win32Config, self)._use_default()
if self._is_script:
self.proj_path = os.path.join(self._proj_root_path, "frameworks", "runtime-src", "proj.win32")
else:
self.proj_path = os.path.join(self._proj_root_path, "proj.win32")
class WebConfig(PlatformConfig):
KEY_SUB_URL = "sub_url"
KEY_RUN_ROOT_DIR = "run_root_dir"
KEY_COPY_RESOURCES = "copy_resources"
def _use_default(self):
self.proj_path = self._proj_root_path
self.run_root_dir = self._proj_root_path
self.copy_res = None
self.sub_url = None
def _parse_info(self, cfg_info):
super(WebConfig, self)._parse_info(cfg_info)
if adxe.dict_contains(cfg_info, WebConfig.KEY_SUB_URL):
self.sub_url = cfg_info[WebConfig.KEY_SUB_URL]
else:
self.sub_url = None
if adxe.dict_contains(cfg_info, WebConfig.KEY_RUN_ROOT_DIR):
self.run_root_dir = os.path.join(self._proj_root_path, cfg_info[WebConfig.KEY_RUN_ROOT_DIR])
else:
self.run_root_dir = None
if adxe.dict_contains(cfg_info, WebConfig.KEY_COPY_RESOURCES):
self.copy_res = cfg_info[WebConfig.KEY_COPY_RESOURCES]
else:
self.copy_res = None
def _is_available(self):
ret = super(WebConfig, self)._is_available()
if ret:
index_path = os.path.join(self.proj_path, "index.html")
ret = os.path.isfile(index_path)
return ret