axmol/tools/console/bin/axmol.py

1163 lines
36 KiB
Python

#!/usr/bin/python
# ----------------------------------------------------------------------------
# cocos-console: command line tool manager for cocos2d-x
#
# Author: Ricardo Quesada
# Copyright 2013 (C) Zynga, Inc
#
# License: MIT
# ----------------------------------------------------------------------------
'''
Command line tool manager for cocos
'''
__docformat__ = 'restructuredtext'
# python
import sys
import os
import subprocess
from contextlib import contextmanager
import axmol_project
import shutil
import string
import locale
import gettext
import json
import utils
import re
# FIXME: MultiLanguage should be deprecated in favor of gettext
from MultiLanguage import MultiLanguage
COCOS2D_CONSOLE_VERSION = '2.3'
def dict_contains(dict,key):
if(sys.version_info.major >= 3):
return (key in dict)
else:
return dict.has_key(key)
def encode_with(text,encoding):
if(sys.version_info.major >= 3):
return text
else:
return unicode(text, encoding)
def str_join(s, v):
if(sys.version_info.major >= 3):
return s.join(v)
else:
return string.join(v, s)
def isunicode(text):
if(sys.version_info.major >= 3):
return isinstance(text, str)
else:
return isinstance(text, unicode)
def transcode(text,encoding):
if(sys.version_info.major >= 3 and encoding=="utf-8"):
return text
else:
return text.encode(encoding)
def get_input(prompt):
if(sys.version_info.major >= 3):
return input(prompt)
else:
return raw_input(prompt)
class Cocos2dIniParser:
def __init__(self):
if(sys.version_info.major >= 3):
import configparser # import ConfigParser
self._cp = configparser.ConfigParser(allow_no_value=True)
else:
import ConfigParser
self._cp = ConfigParser.ConfigParser(allow_no_value=True)
self._cp.optionxform = str
# read global config file
self.cocos2d_path = os.path.dirname(os.path.abspath(sys.argv[0]))
self._cp.read(os.path.join(self.cocos2d_path, "axmol.ini"))
# XXX: override with local config ??? why ???
# self._cp.read("~/.cocos2d-js/axmol.ini")
def parse_plugins(self):
classes = {}
for s in self._cp.sections():
if s == 'plugins':
for classname in self._cp.options(s):
plugin_class = get_class(classname)
category = plugin_class.plugin_category()
name = plugin_class.plugin_name()
if name is None:
print(MultiLanguage.get_string('COCOS_PARSE_PLUGIN_WARNING_FMT', classname))
if len(category) == 0:
key = name
else:
# combine category & name as key
# eg. 'project_new'
key = category + '_' + name
classes[key] = plugin_class
_check_dependencies(classes)
return classes
def _sanitize_path(self, path):
if len(path) == 0:
return None
path = os.path.expanduser(path)
path = os.path.abspath(os.path.join(self.cocos2d_path, path))
if not os.path.isdir(path):
Logging.warning(MultiLanguage.get_string('COCOS_WARNING_INVALID_DIR_IN_INI_FMT', path))
return None
return path
def get_plugins_path(self):
path = self._cp.get('paths', 'plugins')
if not os.path.isabs(path):
path = os.path.join(get_current_path(), path)
path = self._sanitize_path(path)
return path
def get_cocos2dx_path(self):
cocos2d_x = self._cp.get('paths', 'cocos2d_x')
cocos2d_x = self._sanitize_path(cocos2d_x)
return cocos2d_x
def get_templates_path(self):
templates = self._cp.get('paths', 'templates')
templates = self._sanitize_path(templates)
return templates
def get_cocos2dx_mode(self):
mode = self._cp.get('global', 'cocos2d_x_mode')
if mode is None or len(mode) == 0:
mode = 'source'
if mode not in ('source', 'precompiled', 'distro'):
Logging.warning(MultiLanguage.get_string('COCOS_WARNING_INVALID_MODE_FMT', mode))
mode = 'source'
return mode
def is_statistic_enabled(self):
try:
ret = self._cp.getboolean('global', 'enable_stat')
except:
ret = True
return ret
class Logging:
# TODO maybe the right way to do this is to use something like colorama?
RED = '\033[31m'
GREEN = '\033[32m'
YELLOW = '\033[33m'
MAGENTA = '\033[35m'
RESET = '\033[0m'
@staticmethod
def _print(s, color=None):
if color and sys.stdout.isatty() and sys.platform != 'win32':
print(color + s + Logging.RESET)
else:
print(s)
@staticmethod
def debug(s):
Logging._print(s, Logging.MAGENTA)
@staticmethod
def info(s):
Logging._print(s, Logging.GREEN)
@staticmethod
def warning(s):
Logging._print(s, Logging.YELLOW)
@staticmethod
def error(s):
Logging._print(s, Logging.RED)
class CCPluginError(Exception):
ERROR_WRONG_ARGS = 11 # wrong arguments
ERROR_PATH_NOT_FOUND = 12 # path not found
ERROR_BUILD_FAILED = 13 # build failed
ERROR_RUNNING_CMD = 14 # error when running command
ERROR_CMD_NOT_FOUND = 15 # command not found
ERROR_ENV_VAR_NOT_FOUND = 16 # environment variable not found
ERROR_TOOLS_NOT_FOUND = 17 # depend on tools not found
ERROR_PARSE_FILE = 18 # error when parse files
ERROR_WRONG_CONFIG = 19 # configuration is wrong
ERROR_OTHERS = 101 # other errors
def __init__(self, err_args, err_no=1):
super(CCPluginError, self).__init__(err_args)
self.error_no = err_no
def get_error_no(self):
return self.error_no
class CMDRunner(object):
@staticmethod
def run_cmd(command, verbose, cwd=None):
if verbose:
Logging.debug(MultiLanguage.get_string('COCOS_DEBUG_RUNNING_CMD_FMT', ''.join(command)))
else:
log_path = CCPlugin._log_path()
command += ' >"%s" 2>&1' % log_path
sys.stdout.flush()
ret = subprocess.call(command, shell=True, cwd=cwd)
if ret != 0:
message = MultiLanguage.get_string('COCOS_ERROR_RUNNING_CMD_RET_FMT', str(ret))
if not verbose:
message += (MultiLanguage.get_string('COCOS_ERROR_CHECK_LOG_FMT', log_path))
raise CCPluginError(message, CCPluginError.ERROR_RUNNING_CMD)
@staticmethod
def output_for(command, verbose):
if verbose:
Logging.debug(MultiLanguage.get_string('COCOS_DEBUG_RUNNING_CMD_FMT', command))
else:
log_path = CCPlugin._log_path()
try:
return subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
except subprocess.CalledProcessError as e:
output = e.output
message = MultiLanguage.get_string('COCOS_ERROR_RUNNING_CMD')
if not verbose:
with open(log_path, 'w') as f:
f.write(output)
message += MultiLanguage.get_string('COCOS_ERROR_CHECK_LOG_FMT', log_path)
else:
Logging.error(output)
raise CCPluginError(message, CCPluginError.ERROR_RUNNING_CMD)
@staticmethod
def convert_path_to_cmd(path):
""" Escape paths which include spaces to correct style which bash(mac) and cmd(windows) can treat correctly.
eg: on mac: convert '/usr/xxx/apache-ant 1.9.3' to '/usr/xxx/apache-ant\ 1.9.3'
eg: on windows: convert '"c:\apache-ant 1.9.3"\bin' to '"c:\apache-ant 1.9.3\bin"'
"""
ret = path
if os_is_mac():
ret = path.replace("\ ", " ").replace(" ", "\ ")
if os_is_win32():
ret = "\"%s\"" % (path.replace("\"", ""))
# print("!!!!! Convert %s to %s\n" % (path, ret))
return ret
@staticmethod
def convert_path_to_python(path):
""" Escape paths which include spaces to correct style which python can treat correctly.
eg: on mac: convert '/usr/xxx/apache-ant\ 1.9.3' to '/usr/xxx/apache-ant 1.9.3'
eg: on windows: convert '"c:\apache-ant 1.9.3"\bin' to 'c:\apache-ant 1.9.3\bin'
"""
ret = path
if os_is_mac():
ret = path.replace("\ ", " ")
if os_is_win32():
ret = ret.replace("\"", "")
# print("!!!!! Convert %s to %s\n" % (path, ret))
return ret
class DataStatistic(object):
'''
In order to improve axmol, we periodically send anonymous data about how you use axmol.
You can turn off this function by change the value of "enable_stat" in axmol.ini.
Information collected will be used to develop new features and improve axmol.
Since no personally identifiable information is collected,
the anonymous data will not be meaningful to anyone outside of Chukong Inc.
'''
inited = False
stat_obj = None
key_last_state = 'last_stat_enabled'
key_agreement_shown = 'agreement_shown'
@classmethod
def get_cfg_file_path(cls):
return os.path.join(os.path.expanduser('~/.axmol'), 'local_cfg.json')
@classmethod
def get_cfg_value(cls, key, default_value):
local_cfg_file = cls.get_cfg_file_path()
if not os.path.isfile(local_cfg_file):
cur_info = None
else:
try:
f = open(local_cfg_file)
cur_info = json.load(f)
f.close()
except:
cur_info = None
ret = default_value
if cur_info is not None:
if key in cur_info:
ret = cur_info[key]
return ret
@classmethod
def set_cfg_value(cls, key, value):
# get current local config info
cfg_file = cls.get_cfg_file_path()
if not os.path.isfile(cfg_file):
cur_info = {}
else:
try:
f = open(cfg_file)
cur_info = json.load(f)
f.close()
except:
cur_info = {}
# set the value in config
cur_info[key] = value
# make config directory if it's not already there
cfg_dir = os.path.dirname(cfg_file)
if not os.path.exists(cfg_dir):
os.makedirs(cfg_dir)
# write the config
f = open(cfg_file, 'w')
json.dump(cur_info, f, sort_keys=True, indent=4)
f.close()
# get the stat agreed or not
@classmethod
def is_agreement_shown(cls):
return cls.get_cfg_value(cls.key_agreement_shown, False)
@classmethod
def change_agree_stat(cls, agreed):
cls.set_cfg_value(cls.key_agreement_shown, True)
# write the config to ini
ini_file = os.path.join(get_current_path(), "axmol.ini")
f = open(ini_file)
old_lines = f.readlines()
f.close()
new_str = 'enable_stat=%s' % ('true' if agreed else 'false')
new_lines = []
for line in old_lines:
new_line = re.sub('enable_stat[ \t]*=(.*)$', new_str, line)
new_lines.append(new_line)
f = open(ini_file, 'w', newline='\n')
f.writelines(new_lines)
f.close()
@classmethod
def show_stat_agreement(cls, skip_agree_value=None):
if cls.is_agreement_shown():
return
if skip_agree_value is None:
# show the agreement
input_value = get_input(MultiLanguage.get_string('COCOS_AGREEMENT'))
agreed = (input_value.lower() != 'n' and input_value.lower() != 'no')
else:
# --agreement is used to skip the input
agreed = skip_agree_value
cls.change_agree_stat(agreed)
# change the last time statistics status in local config file.
@classmethod
def change_last_state(cls, enabled):
cls.set_cfg_value(cls.key_last_state, enabled)
# get the last time statistics status in local config file.
@classmethod
def get_last_state(cls):
return cls.get_cfg_value(cls.key_last_state, True)
@classmethod
def init_stat_obj(cls):
if cls.inited == False:
# get the axmol_stat module
m = None
try:
m = __import__("axmol_stat")
except:
pass
if m is not None:
stat_cls = getattr(m, "Statistic")
cls.stat_obj = stat_cls(STAT_VERSION)
# axmol_stat is found
if cls.stat_obj is not None:
# check config in axmol.ini
parser = Cocos2dIniParser()
cur_enabled = parser.is_statistic_enabled()
# get last time is enabled or not
last_enabled = cls.get_last_state()
if not cur_enabled:
# statistics is disabled
if last_enabled:
cls.stat_obj.send_event('switch', 'off', 'stat_closed')
cls.stat_obj = None
# update last time status
if cur_enabled != last_enabled:
cls.change_last_state(cur_enabled)
# try to send the cached events
if cls.stat_obj is not None:
cls.stat_obj.send_cached_events()
cls.inited = True
return cls.stat_obj
@classmethod
def stat_event(cls, category, action, label):
try:
cls.init_stat_obj()
if cls.stat_obj is None:
return
cls.stat_obj.send_event(category, action, label)
except:
pass
@classmethod
def terminate_stat(cls):
try:
if cls.stat_obj is None:
return
cls.stat_obj.terminate_stat()
except:
pass
#
# Plugins should be a sublass of CCPlugin
#
class CCPlugin(object):
def _run_cmd(self, command, cwd=None):
CMDRunner.run_cmd(command, self._verbose, cwd)
def _output_for(self, command):
return CMDRunner.output_for(command, self._verbose)
@classmethod
def get_cocos2d_path(cls):
"""returns the path where axmol is installed"""
#
# 1: Check for config.ini
#
parser = Cocos2dIniParser()
cocos2dx_path = parser.get_cocos2dx_path()
if cocos2dx_path is not None:
return cocos2dx_path
#
# 2: default engine path
#
# possible path of console
# /Users/myself/axmol/tools/console/bin
# if so, we have to remove the last 3 segments
path = cls.get_console_path()
path = os.path.abspath(path)
cocos2dx_path = os.path.abspath(os.path.join(
path, os.path.pardir, os.path.pardir, os.path.pardir))
if os.path.isdir(cocos2dx_path):
return cocos2dx_path
if cls.get_cocos2d_mode() != "distro": # if cls.get_cocos2d_mode() is not "distro":
# In 'distro' mode this is not a warning since
# the source code is not expected to be installed
Logging.warning(MultiLanguage.get_string('COCOS_WARNING_ENGINE_NOT_FOUND'))
return None
@classmethod
def get_console_path(cls):
"""returns the path where cocos console is installed"""
run_path = encode_with(get_current_path(), "utf-8")
return run_path
@classmethod
def get_templates_paths(cls):
"""returns a set of paths where templates are installed"""
parser = Cocos2dIniParser()
templates_path = parser.get_templates_path()
paths = []
#
# 1: Check for config.ini
#
if templates_path is not None:
paths.append(templates_path)
#
# 2: Path defined by walking the cocos2d path
#
path = cls.get_cocos2d_path()
if path is not None:
# Try one: cocos2d-x/templates (assuming it is using cocos2d-x's setup.py)
# Try two: cocos2d-x/../../templates
possible_paths = [['templates'], ['..', '..', 'templates']]
for p in possible_paths:
p = str_join(os.sep, p)
template_path = os.path.abspath(os.path.join(path, p))
try:
if os.path.isdir(template_path):
paths.append(template_path)
except Exception as e:
Logging.info(MultiLanguage.get_string('COCOS_INFO_CHECK_TEMPLATE_PATH_FAILED_FMT', template_path))
Logging.info("%s" % e)
pass
#
# 3: Templates can be in ~/.cocos2d/templates as well
#
user_path = os.path.expanduser("~/.axmol/templates")
if os.path.isdir(user_path):
paths.append(user_path)
if len(paths) == 0:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_TEMPLATE_NOT_FOUND'),
CCPluginError.ERROR_PATH_NOT_FOUND)
# remove duplicates
from collections import OrderedDict
ordered = OrderedDict.fromkeys(paths)
paths = ordered.keys()
return paths
@classmethod
def get_cocos2d_mode(cls):
parser = Cocos2dIniParser()
return parser.get_cocos2dx_mode()
@staticmethod
def _log_path():
log_dir = os.path.expanduser("~/.axmol")
if not os.path.exists(log_dir):
os.mkdir(log_dir)
return os.path.join(log_dir, "axmol.log")
# the list of plugins this plugin needs to run before itself.
# ie: if it returns ('a', 'b'), the plugin 'a' will run first, then 'b'
# and after that, the plugin itself.
# they all share the same command line arguments
@staticmethod
def depends_on():
return None
# returns the plugin category,
# default is empty string.
@staticmethod
def plugin_category():
return ""
# returns the plugin name
@staticmethod
def plugin_name():
pass
# returns help
@staticmethod
def brief_description():
pass
# Constructor
def __init__(self):
pass
# Setup common options. If a subclass needs custom options,
# override this method and call super.
def init(self, args):
self._verbose = (not args.quiet)
self._platforms = axmol_project.Platforms(self._project, args.platform, args.proj_dir)
if self._platforms.none_active():
self._platforms.select_one()
# Run it
def run(self, argv, dependencies):
pass
# If a plugin needs to add custom parameters, override this method.
# There's no need to call super
def _add_custom_options(self, parser):
pass
# If a plugin needs to check custom parameters values after parsing them,
# override this method.
# There's no need to call super
def _check_custom_options(self, args):
pass
def parse_args(self, argv):
from argparse import ArgumentParser
# FIXME:
# CCPlugin should not parse any argument. Plugins are responsoble for doing it
parser = ArgumentParser(prog="cocos %s" % self.__class__.plugin_name(),
description=self.__class__.brief_description())
parser.add_argument("-s", "--src",
dest="src_dir",
help=MultiLanguage.get_string('COCOS_HELP_ARG_SRC'))
parser.add_argument("-q", "--quiet",
action="store_true",
dest="quiet",
help=MultiLanguage.get_string('COCOS_HELP_ARG_QUIET'))
platform_list = axmol_project.Platforms.list_for_display()
parser.add_argument("-p", "--platform",
dest="platform",
help=MultiLanguage.get_string('COCOS_HELP_ARG_PLATFORM'))
parser.add_argument("--list-platforms",
action="store_true",
dest="listplatforms",
help=_("List available platforms"))
parser.add_argument("--proj-dir",
dest="proj_dir",
help=MultiLanguage.get_string('COCOS_HELP_ARG_PROJ_DIR'))
self._add_custom_options(parser)
(args, unkonw) = parser.parse_known_args(argv)
if args.src_dir is None:
self._project = axmol_project.Project(os.path.abspath(os.getcwd()))
else:
self._project = axmol_project.Project(
os.path.abspath(args.src_dir))
args.src_dir = self._project.get_project_dir()
if args.src_dir is None:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_PROJECT_NOT_FOUND'),
CCPluginError.ERROR_WRONG_ARGS)
if args.platform:
args.platform = args.platform.lower()
if args.platform not in platform_list:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_UNKNOWN_PLATFORM_FMT', args.platform),
CCPluginError.ERROR_WRONG_ARGS)
if args.listplatforms and self._project is not None:
platforms = axmol_project.Platforms(self._project, args.platform, args.proj_dir)
p = list(platforms.get_available_platforms().keys())
print('{"platforms":' + json.dumps(p) + '}')
sys.exit(0)
self.init(args)
self._check_custom_options(args)
def get_current_path():
if getattr(sys, 'frozen', None):
ret = os.path.realpath(os.path.dirname(sys.executable))
else:
ret = os.path.realpath(os.path.dirname(__file__))
return ret
# get_class from: http://stackoverflow.com/a/452981
def get_class(kls):
parts = kls.split('.')
module = ".".join(parts[:-1])
if len(parts) == 1:
m = sys.modules[__name__]
m = getattr(m, parts[0])
else:
m = __import__(module)
for comp in parts[1:]:
m = getattr(m, comp)
return m
def _check_dependencies_exist(dependencies, classes, plugin_name):
for dep in dependencies:
if dep not in classes:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_INVALID_DEPENDENCY_FMT',
(plugin_name, dep)),
CCPluginError.ERROR_CMD_NOT_FOUND)
def _check_dependencies(classes):
for k in classes:
plugin = classes[k]
dependencies = plugin.depends_on()
if dependencies is not None:
_check_dependencies_exist(dependencies, classes, k)
# common functions
def check_environment_variable(var):
''' Checking the environment variable, if found then return it's value, else raise error
'''
try:
value = os.environ[var]
except Exception:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_ENV_NOT_DEFINED_FMT', var),
CCPluginError.ERROR_ENV_VAR_NOT_FOUND)
return value
def get_xcode_version():
commands = [
"xcodebuild",
"-version"
]
child = subprocess.Popen(commands, stdout=subprocess.PIPE)
xcode = None
version = None
for line in child.stdout:
line = line.decode('utf8')
if 'Xcode' in line:
xcode, version = str.split(line, ' ')
child.wait()
if xcode is None:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_XCODE_NOT_INSTALLED'),
CCPluginError.ERROR_TOOLS_NOT_FOUND)
return version
def app_is_installed(adb_cmd, pack_name):
list_pack_cmd = "%s shell 'pm list packages'" % (adb_cmd)
desired_name = "package:%s" % (pack_name)
child = subprocess.Popen(list_pack_cmd, stdout=subprocess.PIPE, shell=True)
for line in child.stdout:
if desired_name == line.strip():
return True
return False
def version_compare(a, op, b):
'''Compares two version numbers to see if a op b is true
op is operator
op can be ">", "<", "==", "!=", ">=", "<="
a and b are version numbers (dot separated)
a and b can be string, float or int
Please note that: 3 == 3.0 == 3.0.0 ... ("==" is not a simple string cmp)
'''
allowed = [">", "<", "==", "!=", ">=", "<="]
if op not in allowed:
raise ValueError("op must be one of {}".format(allowed))
# Use recursion to simplify operators:
if op[0] == "<": # Reverse args and inequality sign:
return version_compare(b, op.replace("<",">"), a)
if op == ">=":
return version_compare(a,"==",b) or version_compare(a,">",b)
if op == "!=":
return not version_compare(a,"==",b)
# We now have 1 of 2 base cases, "==" or ">":
assert op in ["==", ">"]
a = [int(x) for x in str(a).split(".")]
b = [int(x) for x in str(b).split(".")]
for i in range(max(len(a), len(b))):
ai, bi = 0, 0 # digits
if len(a) > i:
ai = a[i]
if len(b) > i:
bi = b[i]
if ai > bi:
if op == ">":
return True
else: # op "=="
return False
if ai < bi:
# Both "==" and ">" are False:
return False
if op == ">":
return False # op ">" and all digits were equal
return True # op "==" and all digits were equal
def copy_files_in_dir(src, dst):
for item in os.listdir(src):
path = os.path.join(src, item)
if os.path.isfile(path):
path = add_path_prefix(path)
copy_dst = add_path_prefix(dst)
shutil.copy(path, copy_dst)
if os.path.isdir(path):
new_dst = os.path.join(dst, item)
if not os.path.isdir(new_dst):
os.makedirs(add_path_prefix(new_dst))
copy_files_in_dir(path, new_dst)
def copy_files_with_config(config, src_root, dst_root):
src_dir = config["from"]
dst_dir = config["to"]
src_dir = os.path.join(src_root, src_dir)
dst_dir = os.path.join(dst_root, dst_dir)
include_rules = None
if "include" in config:
include_rules = config["include"]
include_rules = convert_rules(include_rules)
exclude_rules = None
if "exclude" in config:
exclude_rules = config["exclude"]
exclude_rules = convert_rules(exclude_rules)
copy_files_with_rules(
src_dir, src_dir, dst_dir, include_rules, exclude_rules)
def copy_files_with_rules(src_rootDir, src, dst, include=None, exclude=None):
if os.path.isfile(src):
if not os.path.exists(dst):
os.makedirs(add_path_prefix(dst))
copy_src = add_path_prefix(src)
copy_dst = add_path_prefix(dst)
shutil.copy(copy_src, copy_dst)
return
if (include is None) and (exclude is None):
if not os.path.exists(dst):
os.makedirs(add_path_prefix(dst))
copy_files_in_dir(src, dst)
elif (include is not None):
# have include
for name in os.listdir(src):
abs_path = os.path.join(src, name)
rel_path = os.path.relpath(abs_path, src_rootDir)
if os.path.isdir(abs_path):
sub_dst = os.path.join(dst, name)
copy_files_with_rules(
src_rootDir, abs_path, sub_dst, include=include)
elif os.path.isfile(abs_path):
if _in_rules(rel_path, include):
if not os.path.exists(dst):
os.makedirs(add_path_prefix(dst))
abs_path = add_path_prefix(abs_path)
copy_dst = add_path_prefix(dst)
shutil.copy(abs_path, copy_dst)
elif (exclude is not None):
# have exclude
for name in os.listdir(src):
abs_path = os.path.join(src, name)
rel_path = os.path.relpath(abs_path, src_rootDir)
if os.path.isdir(abs_path):
sub_dst = os.path.join(dst, name)
copy_files_with_rules(
src_rootDir, abs_path, sub_dst, exclude=exclude)
elif os.path.isfile(abs_path):
if not _in_rules(rel_path, exclude):
if not os.path.exists(dst):
os.makedirs(add_path_prefix(dst))
abs_path = add_path_prefix(abs_path)
copy_dst = add_path_prefix(dst)
shutil.copy(abs_path, copy_dst)
def _in_rules(rel_path, rules):
ret = False
path_str = rel_path.replace("\\", "/")
for rule in rules:
if re.match(rule, path_str):
ret = True
return ret
def convert_rules(rules):
ret_rules = []
for rule in rules:
ret = rule.replace('.', '\\.')
ret = ret.replace('*', '.*')
ret = "%s" % ret
ret_rules.append(ret)
return ret_rules
def os_is_win32():
return sys.platform == 'win32'
def os_is_32bit_windows():
if not os_is_win32():
return False
arch = os.environ['PROCESSOR_ARCHITECTURE'].lower()
archw = "PROCESSOR_ARCHITEW6432" in os.environ
return (arch == "x86" and not archw)
def os_is_mac():
return sys.platform == 'darwin'
def os_is_linux():
return 'linux' in sys.platform
def add_path_prefix(path_str):
if not os_is_win32():
return path_str
if path_str.startswith("\\\\?\\"):
return path_str
ret = "\\\\?\\" + os.path.abspath(path_str)
ret = ret.replace("/", "\\")
return ret
# get from http://stackoverflow.com/questions/6194499/python-os-system-pushd
@contextmanager
def pushd(newDir):
previousDir = os.getcwd()
os.chdir(newDir)
yield
os.chdir(previousDir)
def help():
print(MultiLanguage.get_string('COCOS_HELP_BRIEF_FMT',
(sys.argv[0], COCOS2D_CONSOLE_VERSION)))
print(MultiLanguage.get_string('COCOS_HELP_AVAILABLE_CMD'))
parse = Cocos2dIniParser()
classes = parse.parse_plugins()
max_name = max(len(classes[key].plugin_name(
) + classes[key].plugin_category()) for key in classes.keys())
max_name += 4
for key in classes.keys():
plugin_class = classes[key]
category = plugin_class.plugin_category()
category = (category + ' ') if len(category) > 0 else ''
name = plugin_class.plugin_name()
print("\t%s%s%s%s" % (category, name,
' ' * (max_name - len(name + category)),
plugin_class.brief_description()))
print(MultiLanguage.get_string('COCOS_HELP_AVAILABLE_ARGS_FMT',
MultiLanguage.get_available_langs()))
print(MultiLanguage.get_string('COCOS_HELP_EXAMPLE'))
def show_version():
print(COCOS_ENGINE_VERSION)
print("Cocos Console %s" % COCOS2D_CONSOLE_VERSION)
def run_plugin(command, argv, plugins):
run_directly = False
if len(argv) > 0:
if argv[0] in ['--help', '-h']:
run_directly = True
plugin = plugins[command]()
if run_directly:
plugin.run(argv, None)
else:
dependencies = plugin.depends_on()
dependencies_objects = {}
if dependencies is not None:
for dep_name in dependencies:
# FIXME check there's not circular dependencies
dependencies_objects[dep_name] = run_plugin(
dep_name, argv, plugins)
# don't print this info. Not useful to users, and generates noise when parsing output
# Logging.info(MultiLanguage.get_string('COCOS_INFO_RUNNING_PLUGIN_FMT', plugin.__class__.plugin_name()))
plugin.run(argv, dependencies_objects)
return plugin
def _check_python_version():
major_ver = sys.version_info[0]
minor_ver = sys.version_info[1]
ret = True
if major_ver < 2:
ret = False
elif minor_ver < 7:
ret = False
if not ret:
print(MultiLanguage.get_string('COCOS_PYTHON_VERSION_TIP_FMT') % (major_ver, minor_ver))
return ret
# gettext
language = None
encoding = None
try:
locale.setlocale(locale.LC_ALL, '') # use user's preferred locale
language, encoding = locale.getlocale()
except:
pass
if language is not None:
filename = "language_%s.mo" % language[0:2]
try:
trans = gettext.GNUTranslations(open(filename, "rb"))
except IOError:
trans = gettext.NullTranslations()
trans.install()
_ = trans.gettext
else:
_ = MultiLanguage.get_string
if __name__ == "__main__":
# Parse the arguments, specify the language
language_arg = '--ol'
if language_arg in sys.argv:
idx = sys.argv.index(language_arg)
if idx == (len(sys.argv) - 1):
Logging.error(MultiLanguage.get_string('COCOS_ERROR_OL_NO_VALUE'))
sys.exit(CCPluginError.ERROR_WRONG_ARGS)
# set specified language
MultiLanguage.set_language(sys.argv[idx+1])
# remove the argument '--ol' & the value
sys.argv.pop(idx)
sys.argv.pop(idx)
agreement_arg = '--agreement'
skip_agree_value = None
if agreement_arg in sys.argv:
idx = sys.argv.index(agreement_arg)
if idx == (len(sys.argv) - 1):
Logging.error(MultiLanguage.get_string('COCOS_ERROR_AGREEMENT_NO_VALUE'))
sys.exit(CCPluginError.ERROR_WRONG_ARGS)
# get the argument value
agree_value = sys.argv[idx+1]
if agree_value.lower() == 'n':
skip_agree_value = False
else:
skip_agree_value = True
# remove the argument '--agreement' & the value
sys.argv.pop(idx)
sys.argv.pop(idx)
# Get the engine version for the DataStat
cur_path = get_current_path()
engine_path = os.path.normpath(os.path.join(cur_path, '../../../'))
COCOS_ENGINE_VERSION = utils.get_engine_version(engine_path)
STAT_VERSION = COCOS_ENGINE_VERSION
ver_pattern = r"axmol-(.*)"
match = re.match(ver_pattern, COCOS_ENGINE_VERSION)
if match:
STAT_VERSION = match.group(1)
force_disable_stats = True # Temp disable stats
if force_disable_stats:
DataStatistic.change_agree_stat(False)
else:
DataStatistic.show_stat_agreement(skip_agree_value)
DataStatistic.stat_event('axmol', 'start', 'invoked')
if not _check_python_version():
DataStatistic.terminate_stat()
sys.exit(CCPluginError.ERROR_TOOLS_NOT_FOUND)
parser = Cocos2dIniParser()
plugins_path = parser.get_plugins_path()
sys.path.append(plugins_path)
if len(sys.argv) == 1 or sys.argv[1] in ('-h', '--help'):
help()
DataStatistic.terminate_stat()
sys.exit(0)
if len(sys.argv) > 1 and sys.argv[1] in ('-v', '--version'):
show_version()
DataStatistic.terminate_stat()
sys.exit(0)
try:
plugins = parser.parse_plugins()
command = sys.argv[1]
argv = sys.argv[2:]
# try to find plugin by name
if command in plugins:
DataStatistic.stat_event('axmol', 'running_command', command)
run_plugin(command, argv, plugins)
else:
# try to find plugin by category_name, so the len(sys.argv) at
# least 3.
if len(sys.argv) > 2:
# combine category & name as key
# eg. 'project_new'
command = sys.argv[1] + '_' + sys.argv[2]
argv = sys.argv[3:]
if command in plugins:
DataStatistic.stat_event('axmol', 'running_command', command)
run_plugin(command, argv, plugins)
else:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_CMD_NOT_FOUND_FMT',
' '.join(sys.argv[1:])),
CCPluginError.ERROR_CMD_NOT_FOUND)
else:
raise CCPluginError(MultiLanguage.get_string('COCOS_ERROR_CMD_NOT_FOUND_FMT', command),
CCPluginError.ERROR_CMD_NOT_FOUND)
except Exception as e:
# FIXME don't know how to handle this. Can't catch cocos2d.CCPluginError
# as it's not defined that way in this file, but the plugins raise it
# with that name.
if e.__class__.__name__ == 'CCPluginError':
Logging.error(' '.join(e.args))
# import traceback
# print '-' * 60
# traceback.print_exc(file=sys.stdout)
# print '-' * 60
err_no = e.get_error_no()
sys.exit(err_no)
else:
raise
finally:
DataStatistic.terminate_stat()