mirror of https://github.com/axmolengine/axmol.git
Add missing binaries
This commit is contained in:
parent
d2d2005027
commit
2eea90545d
|
@ -0,0 +1,18 @@
|
|||
#!/bin/bash -l
|
||||
|
||||
AXMOL_CONSOLE_BIN_DIRECTORY=$(dirname "$0")
|
||||
AXMOL_CONSOLE_BIN_DIRECTORY=$(cd "$AXMOL_CONSOLE_BIN_DIRECTORY" && pwd -P)
|
||||
|
||||
if hash python3 2>/dev/null; then
|
||||
PYTHON=python3
|
||||
elif hash python2 2>/dev/null; then
|
||||
PYTHON=python2
|
||||
elif hash python 2>/dev/null; then
|
||||
PYTHON=python
|
||||
else
|
||||
echo "Python 2+ required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$PYTHON "$AXMOL_CONSOLE_BIN_DIRECTORY/axmol.py" "$@"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
@echo off
|
||||
rem Ensure vswhere installed for cmdline tool could find vs2017+
|
||||
pip install vswhere
|
||||
python "%~dp0/axmol.py" %*
|
|
@ -0,0 +1,55 @@
|
|||
#
|
||||
# axmol command line tool configuration file
|
||||
#
|
||||
|
||||
[global]
|
||||
# there are 3 modes
|
||||
# "source", which means that the cocos2d-x source code is being used for "new" and other plugins.
|
||||
# "precompiled", which means that cocos2d-x precompiled libraries and headers will be used for "new" and other plugins
|
||||
# "distro", which means that cocos2d-x precompiled libraries and headers won't be copied when using "new" and other plugins
|
||||
# Default: source. Distros and other installers must override this setting
|
||||
cocos2d_x_mode=source
|
||||
|
||||
# Enable/Disable the data statistics
|
||||
# If the value is 'false' or 'no', statistics is disabled.
|
||||
# Otherwise, it's enabled.
|
||||
enable_stat=false
|
||||
|
||||
[plugins]
|
||||
# What are the plugins that must be enabled
|
||||
plugin_new.CCPluginNew
|
||||
plugin_compile.CCPluginCompile
|
||||
plugin_run.CCPluginRun
|
||||
plugin_deploy.CCPluginDeploy
|
||||
plugin_luacompile.CCPluginLuaCompile
|
||||
# plugin_generate.LibsCompiler
|
||||
plugin_generate.SimulatorCompiler
|
||||
#plugin_generate.TemplateGenerator
|
||||
; plugin_package.CCPluginPackage
|
||||
#plugin_gui.CCPluginGUI
|
||||
#plugin_version.CCPluginVersion
|
||||
#plugin_install.CCPluginInstall
|
||||
#plugin_update.CCPluginUpdate
|
||||
#plugin_clean.CCPluginClean
|
||||
#plugin_dist.CCPluginDist
|
||||
#plugin_test.CCPluginTest
|
||||
# To add a new plugin add it's classname here
|
||||
|
||||
|
||||
[paths]
|
||||
# where cocos2d-x is installed
|
||||
# example: /usr/local/axmol
|
||||
# eg: this file must exist: /usr/local/axmol/core/axmol.h
|
||||
# Default: empty. Installers will populate it
|
||||
cocos2d_x=
|
||||
|
||||
# where are the cocos2d-x's templates installed
|
||||
# example: /home/user/templates
|
||||
# eg: this directory must exist: /home/user/templates/cpp-template-default
|
||||
# Default: empty. Installers will populate it
|
||||
templates=
|
||||
|
||||
# where are the plugins installed
|
||||
# but distros can override this directory
|
||||
# Default: ../plugins. Installers can replace it if needed
|
||||
plugins=../plugins
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,515 @@
|
|||
import os
|
||||
import re
|
||||
import json
|
||||
import axmol
|
||||
from MultiLanguage import MultiLanguage
|
||||
|
||||
class Project(object):
|
||||
CPP = 'cpp'
|
||||
LUA = 'lua'
|
||||
JS = 'js'
|
||||
|
||||
CONFIG = '.axproj.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 axmol.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_NOT_FOUND_FMT',
|
||||
os.path.join(src_dir, Project.CONFIG)),
|
||||
axmol.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 axmol.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_BROKEN_FMT',
|
||||
project_json),
|
||||
axmol.CCPluginError.ERROR_PARSE_FILE)
|
||||
|
||||
if project_info is None:
|
||||
raise axmol.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_PARSE_FAILED_FMT',
|
||||
Project.CONFIG), axmol.CCPluginError.ERROR_PARSE_FILE)
|
||||
|
||||
if not axmol.dict_contains(project_info, Project.KEY_PROJ_TYPE):
|
||||
raise axmol.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_GET_VALUE_FAILED_FMT',
|
||||
(Project.KEY_PROJ_TYPE, Project.CONFIG)),
|
||||
axmol.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 axmol.CCPluginError(MultiLanguage.get_string('PROJECT_CFG_INVALID_LANG_FMT',
|
||||
(Project.KEY_PROJ_TYPE, ', '.join(Project.list_for_display()))),
|
||||
axmol.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 axmol.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 (axmol.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])
|
||||
axmol.Logging.info(MultiLanguage.get_string('PROJECT_INFO_FOUND_CUSTOM_STEP_FMT', script_path))
|
||||
else:
|
||||
axmol.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:
|
||||
axmol.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 axmol.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 axmol.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'
|
||||
TVOS = 'tvos'
|
||||
MAC = 'mac'
|
||||
WEB = 'web'
|
||||
WIN32 = 'win32'
|
||||
LINUX = 'linux'
|
||||
|
||||
CFG_CLASS_MAP = {
|
||||
ANDROID : "axmol_project.AndroidConfig",
|
||||
IOS : "axmol_project.iOSConfig",
|
||||
TVOS : "axmol_project.tvOSConfig",
|
||||
MAC : "axmol_project.MacConfig",
|
||||
WEB : "axmol_project.WebConfig",
|
||||
WIN32 : "axmol_project.Win32Config",
|
||||
LINUX : "axmol_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 axmol.CCPluginError(MultiLanguage.get_string('PROJECT_INVALID_PLATFORM_FMT',
|
||||
(self._available_platforms.keys(), current)),
|
||||
axmol.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.TVOS, Platforms.MAC, Platforms.ANDROID ],
|
||||
"win32" : [ Platforms.WEB, Platforms.WIN32, Platforms.ANDROID ]
|
||||
}
|
||||
for p in platforms:
|
||||
if axmol.os_is_linux():
|
||||
if p in platforms_for_os["linux"]:
|
||||
ret.append(p)
|
||||
if axmol.os_is_mac():
|
||||
if p in platforms_for_os["mac"]:
|
||||
ret.append(p)
|
||||
if axmol.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.TVOS, 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.TVOS, 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.TVOS, 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 = axmol.get_class(Platforms.CFG_CLASS_MAP[p])
|
||||
if cfg_class is None:
|
||||
continue
|
||||
|
||||
cfg_key = "%s_cfg" % p
|
||||
if axmol.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 axmol.CCPluginError(MultiLanguage.get_string('PROJECT_NO_AVAILABLE_PLATFORMS'),
|
||||
axmol.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_tvos_active(self):
|
||||
return self._current == Platforms.TVOS
|
||||
|
||||
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 axmol.CCPluginError(MultiLanguage.get_string('PROJECT_SPECIFY_PLATFORM_FMT',
|
||||
str(self._available_platforms.keys())),
|
||||
axmol.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 axmol.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 axmol.dict_contains(cfg_info, LinuxConfig.KEY_CMAKE_PATH):
|
||||
self.cmake_path = cfg_info[LinuxConfig.KEY_CMAKE_PATH]
|
||||
else:
|
||||
self.cmake_path = None
|
||||
|
||||
if axmol.dict_contains(cfg_info, LinuxConfig.KEY_BUILD_DIR):
|
||||
self.build_dir = cfg_info[LinuxConfig.KEY_BUILD_DIR]
|
||||
else:
|
||||
self.build_dir = None
|
||||
|
||||
if axmol.dict_contains(cfg_info, LinuxConfig.KEY_PROJECT_NAME):
|
||||
self.project_name = cfg_info[LinuxConfig.KEY_PROJECT_NAME]
|
||||
else:
|
||||
self.project_name = None
|
||||
|
||||
if axmol.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 tvOSConfig(LinuxConfig):
|
||||
def _use_default(self):
|
||||
super(tvOSConfig, 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 axmol.dict_contains(cfg_info, WebConfig.KEY_SUB_URL):
|
||||
self.sub_url = cfg_info[WebConfig.KEY_SUB_URL]
|
||||
else:
|
||||
self.sub_url = None
|
||||
|
||||
if axmol.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 axmol.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
|
|
@ -0,0 +1,569 @@
|
|||
#!/usr/bin/python
|
||||
# ----------------------------------------------------------------------------
|
||||
# statistics: Statistics the user behaviors of axmol-console by google analytics
|
||||
#
|
||||
# Author: Bin Zhang
|
||||
#
|
||||
# License: MIT
|
||||
# ----------------------------------------------------------------------------
|
||||
'''
|
||||
Statistics the user behaviors of axmol-console by google analytics
|
||||
'''
|
||||
|
||||
import axmol
|
||||
import uuid
|
||||
import locale
|
||||
|
||||
import urllib
|
||||
import platform
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import socket
|
||||
import hashlib
|
||||
import datetime
|
||||
import zlib
|
||||
|
||||
import multiprocessing
|
||||
|
||||
urlEncode = None
|
||||
|
||||
if sys.version_info.major >= 3:
|
||||
import http.client as httplib
|
||||
urlEncode = urllib.parse.urlencode
|
||||
else:
|
||||
import httplib
|
||||
urlEncode = urllib.urlencode
|
||||
|
||||
# GA related Constants
|
||||
|
||||
GA_HOST = 'www.google-analytics.com'
|
||||
GA_PATH = '/collect'
|
||||
GA_APIVERSION = '1'
|
||||
APPNAME = 'AxisConsole'
|
||||
|
||||
TIMEOUT_VALUE = 0.5
|
||||
|
||||
# formal tracker ID
|
||||
GA_TRACKERID = 'UA-60734607-3'
|
||||
|
||||
# debug tracker ID
|
||||
# GA_TRACKERID = 'UA-60530469-4'
|
||||
|
||||
# BI related Constants
|
||||
BI_HOST = 'ark.cocounion.com'
|
||||
BI_PATH = '/as'
|
||||
BI_APPID = '433748803'
|
||||
|
||||
GA_ENABLED = True
|
||||
BI_ENABLED = False
|
||||
|
||||
class Fields(object):
|
||||
API_VERSION = 'v'
|
||||
TRACKING_ID = 'tid'
|
||||
HIT_TYPE = 't'
|
||||
CLIENT_ID = 'cid'
|
||||
EVENT_CATEGORY = 'ec'
|
||||
EVENT_ACTION = 'ea'
|
||||
EVENT_LABEL = 'el'
|
||||
EVENT_VALUE = 'ev'
|
||||
APP_NAME = 'an'
|
||||
APP_VERSION = 'av'
|
||||
USER_LANGUAGE = 'ul'
|
||||
USER_AGENT = 'ua'
|
||||
SCREEN_NAME = "cd"
|
||||
SCREEN_RESOLUTION = "sr"
|
||||
|
||||
|
||||
GA_CACHE_EVENTS_FILE = 'cache_events'
|
||||
GA_CACHE_EVENTS_BAK_FILE = 'cache_event_bak'
|
||||
|
||||
local_cfg_path = os.path.expanduser('~/.axmol')
|
||||
local_cfg_file = os.path.join(local_cfg_path, GA_CACHE_EVENTS_FILE)
|
||||
local_cfg_bak_file = os.path.join(local_cfg_path, GA_CACHE_EVENTS_BAK_FILE)
|
||||
file_in_use_lock = multiprocessing.Lock()
|
||||
bak_file_in_use_lock = multiprocessing.Lock()
|
||||
|
||||
|
||||
BI_CACHE_EVENTS_FILE = 'bi_cache_events'
|
||||
bi_cfg_file = os.path.join(local_cfg_path, BI_CACHE_EVENTS_FILE)
|
||||
bi_file_in_use_lock = multiprocessing.Lock()
|
||||
|
||||
def get_user_id():
|
||||
node = uuid.getnode()
|
||||
mac = uuid.UUID(int = node).hex[-12:]
|
||||
|
||||
uid = hashlib.md5(mac.encode('utf-8')).hexdigest()
|
||||
return uid
|
||||
|
||||
def get_language():
|
||||
lang, encoding = locale.getdefaultlocale()
|
||||
return lang
|
||||
|
||||
def get_user_agent():
|
||||
ret_str = None
|
||||
if axmol.os_is_win32():
|
||||
ver_info = sys.getwindowsversion()
|
||||
ver_str = '%d.%d' % (ver_info[0], ver_info[1])
|
||||
if axmol.os_is_32bit_windows():
|
||||
arch_str = "WOW32"
|
||||
else:
|
||||
arch_str = "WOW64"
|
||||
ret_str = "Mozilla/5.0 (Windows NT %s; %s) Chrome/103.0.5060.114 Safari/537.36" % (ver_str, arch_str)
|
||||
elif axmol.os_is_mac():
|
||||
ver_str = (platform.mac_ver()[0]).replace('.', '_')
|
||||
ret_str = "Mozilla/5.0 (Macintosh; Intel Mac OS X %s) Chrome/103.0.5060.114 Safari/537.36" % ver_str
|
||||
elif axmol.os_is_linux():
|
||||
arch_str = platform.machine()
|
||||
ret_str = "Mozilla/5.0 (X11; Linux %s) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36" % arch_str
|
||||
|
||||
return ret_str
|
||||
|
||||
def get_system_info():
|
||||
if axmol.os_is_win32():
|
||||
ret_str = "windows"
|
||||
ret_str += "_%s" % platform.release()
|
||||
if axmol.os_is_32bit_windows():
|
||||
ret_str += "_%s" % "32bit"
|
||||
else:
|
||||
ret_str += "_%s" % "64bit"
|
||||
elif axmol.os_is_mac():
|
||||
ret_str = "mac_%s" % (platform.mac_ver()[0]).replace('.', '_')
|
||||
elif axmol.os_is_linux():
|
||||
ret_str = "linux_%s" % platform.linux_distribution()[0]
|
||||
else:
|
||||
ret_str = "unknown"
|
||||
|
||||
return ret_str
|
||||
|
||||
def get_python_version():
|
||||
return "python_%s" % platform.python_version()
|
||||
|
||||
def get_time_stamp():
|
||||
utc_dt = datetime.datetime.utcnow()
|
||||
local_dt = utc_dt + datetime.timedelta(hours=8)
|
||||
epoch = datetime.datetime(1970,1,1)
|
||||
local_ts = (local_dt - epoch).total_seconds()
|
||||
ret = '%d' % int(local_ts)
|
||||
|
||||
return ret
|
||||
|
||||
def get_static_params(engine_version):
|
||||
static_params = {
|
||||
Fields.API_VERSION: GA_APIVERSION,
|
||||
Fields.TRACKING_ID: GA_TRACKERID,
|
||||
Fields.CLIENT_ID: get_user_id(),
|
||||
Fields.APP_NAME: APPNAME,
|
||||
Fields.HIT_TYPE: "event",
|
||||
Fields.USER_LANGUAGE: get_language(),
|
||||
Fields.APP_VERSION: engine_version,
|
||||
Fields.SCREEN_NAME: get_system_info(),
|
||||
Fields.SCREEN_RESOLUTION: get_python_version()
|
||||
}
|
||||
agent_str = get_user_agent()
|
||||
if agent_str is not None:
|
||||
static_params[Fields.USER_AGENT] = agent_str
|
||||
|
||||
return static_params
|
||||
|
||||
def gen_bi_event(event, event_value):
|
||||
time_stamp = get_time_stamp()
|
||||
if event_value == 0:
|
||||
is_cache_event = '1'
|
||||
else:
|
||||
is_cache_event = '0'
|
||||
|
||||
category = event[0]
|
||||
action = event[1]
|
||||
label = event[2]
|
||||
|
||||
event_name = category
|
||||
params = {
|
||||
'cached_event' : is_cache_event
|
||||
}
|
||||
if category == 'axmol':
|
||||
if action == 'start':
|
||||
event_name = 'axmol_invoked'
|
||||
elif action == 'running_command':
|
||||
event_name = 'running_command'
|
||||
params['command'] = label
|
||||
else:
|
||||
params['category'] = category
|
||||
params['action'] = action
|
||||
params['label'] = label
|
||||
elif category == 'new':
|
||||
event_name = 'new_project'
|
||||
params['language'] = action
|
||||
params['template'] = label
|
||||
elif category == 'new_engine_ver':
|
||||
event_name = 'engine_info'
|
||||
params['version'] = action
|
||||
params['engine_type'] = label
|
||||
elif category == 'compile':
|
||||
params['language'] = action
|
||||
params['target_platform'] = label
|
||||
else:
|
||||
params['category'] = category
|
||||
params['action'] = action
|
||||
params['label'] = label
|
||||
|
||||
if len(event) >= 4:
|
||||
appear_time = event[3]
|
||||
else:
|
||||
appear_time = time_stamp
|
||||
ret = {
|
||||
'u' : {
|
||||
'28' : get_user_id(),
|
||||
'34' : get_python_version()
|
||||
},
|
||||
'p' : params,
|
||||
's' : time_stamp,
|
||||
'e' : event_name,
|
||||
't' : appear_time
|
||||
}
|
||||
|
||||
return ret
|
||||
|
||||
def get_bi_params(events, event_value, multi_events=False, engine_version=''):
|
||||
if axmol.os_is_win32():
|
||||
system_str = 'windows'
|
||||
ver_info = sys.getwindowsversion()
|
||||
ver_str = '%d.%d' % (ver_info[0], ver_info[1])
|
||||
if axmol.os_is_32bit_windows():
|
||||
arch_str = "_32bit"
|
||||
else:
|
||||
arch_str = "_64bit"
|
||||
system_ver = '%s%s' % (ver_str, arch_str)
|
||||
elif axmol.os_is_mac():
|
||||
system_str = 'mac'
|
||||
system_ver = (platform.mac_ver()[0])
|
||||
elif axmol.os_is_linux():
|
||||
system_str = 'linux'
|
||||
system_ver = platform.machine()
|
||||
else:
|
||||
system_str = 'unknown'
|
||||
system_ver = 'unknown'
|
||||
|
||||
events_param = []
|
||||
if multi_events:
|
||||
for e in events:
|
||||
events_param.append(gen_bi_event(e, event_value))
|
||||
else:
|
||||
events_param.append(gen_bi_event(events, event_value))
|
||||
|
||||
params = {
|
||||
'device': {
|
||||
'10' : system_ver,
|
||||
'11' : system_str
|
||||
},
|
||||
'app': {
|
||||
'7' : BI_APPID,
|
||||
'8' : engine_version,
|
||||
'9' : get_language()
|
||||
},
|
||||
'time' : get_time_stamp(),
|
||||
'events' : events_param
|
||||
}
|
||||
|
||||
return params
|
||||
|
||||
def cache_event(event, is_ga=True, multi_events=False):
|
||||
if is_ga:
|
||||
cache_ga_event(event)
|
||||
else:
|
||||
cache_bi_event(event, multi_events)
|
||||
|
||||
# BI cache events related methods
|
||||
def cache_bi_event(event, multi_events=False):
|
||||
bi_file_in_use_lock.acquire()
|
||||
|
||||
outFile = None
|
||||
try:
|
||||
# get current cached events
|
||||
cache_events = get_bi_cached_events(need_lock=False)
|
||||
|
||||
if multi_events:
|
||||
need_cache_size = len(event)
|
||||
else:
|
||||
need_cache_size = 1
|
||||
|
||||
# delete the oldest events if there are too many events.
|
||||
events_size = len(cache_events)
|
||||
if events_size >= Statistic.MAX_CACHE_EVENTS:
|
||||
start_idx = events_size - (Statistic.MAX_CACHE_EVENTS - need_cache_size)
|
||||
cache_events = cache_events[start_idx:]
|
||||
|
||||
# cache the new event
|
||||
if multi_events:
|
||||
for e in event:
|
||||
cache_events.append(e)
|
||||
else:
|
||||
cache_events.append(event)
|
||||
|
||||
# write file
|
||||
outFile = open(bi_cfg_file, 'w')
|
||||
json.dump(cache_events, outFile)
|
||||
outFile.close()
|
||||
except:
|
||||
if outFile is not None:
|
||||
outFile.close()
|
||||
finally:
|
||||
bi_file_in_use_lock.release()
|
||||
|
||||
def get_bi_cached_events(need_lock=True):
|
||||
if not os.path.isfile(bi_cfg_file):
|
||||
cached_events = []
|
||||
else:
|
||||
f = None
|
||||
try:
|
||||
if need_lock:
|
||||
bi_file_in_use_lock.acquire()
|
||||
|
||||
f = open(bi_cfg_file)
|
||||
cached_events = json.load(f)
|
||||
f.close()
|
||||
|
||||
if not isinstance(cached_events, list):
|
||||
cached_events = []
|
||||
except:
|
||||
cached_events = []
|
||||
finally:
|
||||
if f is not None:
|
||||
f.close()
|
||||
|
||||
if need_lock:
|
||||
bi_file_in_use_lock.release()
|
||||
|
||||
return cached_events
|
||||
|
||||
# GA cache events related methods
|
||||
def get_ga_cached_events(is_bak=False, need_lock=True):
|
||||
if is_bak:
|
||||
cfg_file = local_cfg_bak_file
|
||||
lock = bak_file_in_use_lock
|
||||
else:
|
||||
cfg_file = local_cfg_file
|
||||
lock = file_in_use_lock
|
||||
|
||||
if not os.path.isfile(cfg_file):
|
||||
cached_events = []
|
||||
else:
|
||||
f = None
|
||||
try:
|
||||
if need_lock:
|
||||
lock.acquire()
|
||||
|
||||
f = open(cfg_file)
|
||||
cached_events = json.load(f)
|
||||
f.close()
|
||||
|
||||
if not isinstance(cached_events, list):
|
||||
cached_events = []
|
||||
except:
|
||||
cached_events = []
|
||||
finally:
|
||||
if f is not None:
|
||||
f.close()
|
||||
if need_lock:
|
||||
lock.release()
|
||||
|
||||
return cached_events
|
||||
|
||||
def cache_ga_event(event):
|
||||
file_in_use_lock.acquire()
|
||||
|
||||
outFile = None
|
||||
try:
|
||||
# get current cached events
|
||||
cache_events = get_ga_cached_events(is_bak=False, need_lock=False)
|
||||
|
||||
# delete the oldest events if there are too many events.
|
||||
events_size = len(cache_events)
|
||||
if events_size >= Statistic.MAX_CACHE_EVENTS:
|
||||
start_idx = events_size - (Statistic.MAX_CACHE_EVENTS - 1)
|
||||
cache_events = cache_events[start_idx:]
|
||||
|
||||
# cache the new event
|
||||
cache_events.append(event)
|
||||
|
||||
# write file
|
||||
outFile = open(local_cfg_file, 'w')
|
||||
json.dump(cache_events, outFile)
|
||||
outFile.close()
|
||||
except:
|
||||
if outFile is not None:
|
||||
outFile.close()
|
||||
finally:
|
||||
file_in_use_lock.release()
|
||||
|
||||
def pop_bak_ga_cached_event():
|
||||
bak_file_in_use_lock.acquire()
|
||||
events = get_ga_cached_events(is_bak=True, need_lock=False)
|
||||
|
||||
if len(events) > 0:
|
||||
e = events[0]
|
||||
events = events[1:]
|
||||
outFile = None
|
||||
try:
|
||||
outFile = open(local_cfg_bak_file, 'w')
|
||||
json.dump(events, outFile)
|
||||
outFile.close()
|
||||
except:
|
||||
if outFile:
|
||||
outFile.close()
|
||||
else:
|
||||
e = None
|
||||
|
||||
bak_file_in_use_lock.release()
|
||||
|
||||
return e
|
||||
|
||||
def do_send_ga_cached_event(engine_version):
|
||||
e = pop_bak_ga_cached_event()
|
||||
while(e is not None):
|
||||
do_send(e, 0, is_ga=True, multi_events=False, engine_version=engine_version)
|
||||
e = pop_bak_ga_cached_event()
|
||||
|
||||
def get_params_str(event, event_value, is_ga=True, multi_events=False, engine_version=''):
|
||||
if is_ga:
|
||||
params = get_static_params(engine_version)
|
||||
params[Fields.EVENT_CATEGORY] = 'ax-' + event[0]
|
||||
params[Fields.EVENT_ACTION] = event[1]
|
||||
params[Fields.EVENT_LABEL] = event[2]
|
||||
params[Fields.EVENT_VALUE] = '%d' % event_value
|
||||
params_str = urlEncode(params)
|
||||
else:
|
||||
params = get_bi_params(event, event_value, multi_events, engine_version)
|
||||
strParam = json.dumps(params)
|
||||
params_str = zlib.compress(strParam, 9)
|
||||
|
||||
return params_str
|
||||
|
||||
def do_http_request(event, event_value, is_ga=True, multi_events=False, engine_version=''):
|
||||
ret = False
|
||||
conn = None
|
||||
try:
|
||||
params_str = get_params_str(event, event_value, is_ga, multi_events, engine_version)
|
||||
if is_ga:
|
||||
host_url = GA_HOST
|
||||
host_path = GA_PATH
|
||||
else:
|
||||
host_url = BI_HOST
|
||||
host_path = BI_PATH
|
||||
|
||||
socket.setdefaulttimeout(TIMEOUT_VALUE)
|
||||
|
||||
conn = httplib.HTTPConnection(host_url, timeout=TIMEOUT_VALUE)
|
||||
conn.request(method="POST", url=host_path, body=params_str)
|
||||
|
||||
response = conn.getresponse()
|
||||
res = response.status
|
||||
if res >= 200 and res < 300:
|
||||
# status is 2xx mean the request is success.
|
||||
ret = True
|
||||
else:
|
||||
ret = False
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
return ret
|
||||
|
||||
def do_send(event, event_value, is_ga=True, multi_events=False, engine_version=''):
|
||||
try:
|
||||
ret = do_http_request(event, event_value, is_ga, multi_events, engine_version)
|
||||
if not ret:
|
||||
# request failed, cache the event
|
||||
cache_event(event, is_ga, multi_events)
|
||||
except:
|
||||
pass
|
||||
|
||||
class Statistic(object):
|
||||
|
||||
MAX_CACHE_EVENTS = 50
|
||||
MAX_CACHE_PROC = 5
|
||||
|
||||
def __init__(self, engine_version):
|
||||
self.process_pool = []
|
||||
self.engine_version = engine_version
|
||||
if axmol.os_is_win32():
|
||||
multiprocessing.freeze_support()
|
||||
|
||||
def send_cached_events(self):
|
||||
try:
|
||||
# send GA cached events
|
||||
if GA_ENABLED:
|
||||
events = get_ga_cached_events()
|
||||
event_size = len(events)
|
||||
if event_size == 0:
|
||||
return
|
||||
|
||||
# rename the file
|
||||
if os.path.isfile(local_cfg_bak_file):
|
||||
os.remove(local_cfg_bak_file)
|
||||
os.rename(local_cfg_file, local_cfg_bak_file)
|
||||
|
||||
# create processes to handle the events
|
||||
proc_num = min(event_size, Statistic.MAX_CACHE_PROC)
|
||||
for i in range(proc_num):
|
||||
p = multiprocessing.Process(target=do_send_ga_cached_event, args=(self.engine_version,))
|
||||
p.start()
|
||||
self.process_pool.append(p)
|
||||
|
||||
# send BI cached events
|
||||
if BI_ENABLED:
|
||||
events = get_bi_cached_events()
|
||||
event_size = len(events)
|
||||
if event_size == 0:
|
||||
return
|
||||
|
||||
# remove the cached events file
|
||||
if os.path.isfile(bi_cfg_file):
|
||||
os.remove(bi_cfg_file)
|
||||
|
||||
p = multiprocessing.Process(target=do_send, args=(events, 0, False, True, self.engine_version,))
|
||||
p.start()
|
||||
self.process_pool.append(p)
|
||||
except:
|
||||
pass
|
||||
|
||||
def send_event(self, category, action, label):
|
||||
try:
|
||||
event = [ category, action, label ]
|
||||
|
||||
# send event to GA
|
||||
if GA_ENABLED:
|
||||
p = multiprocessing.Process(target=do_send, args=(event, 1, True, False, self.engine_version,))
|
||||
p.start()
|
||||
self.process_pool.append(p)
|
||||
|
||||
# send event to BI
|
||||
if BI_ENABLED:
|
||||
# add timestamp
|
||||
event.append(get_time_stamp())
|
||||
p = multiprocessing.Process(target=do_send, args=(event, 1, False, False, self.engine_version,))
|
||||
p.start()
|
||||
self.process_pool.append(p)
|
||||
except:
|
||||
pass
|
||||
|
||||
def terminate_stat(self):
|
||||
# terminate sub-processes
|
||||
if len(self.process_pool) > 0:
|
||||
alive_count = 0
|
||||
for p in self.process_pool:
|
||||
if p.is_alive():
|
||||
alive_count += 1
|
||||
|
||||
if alive_count > 0:
|
||||
time.sleep(1)
|
||||
for p in self.process_pool:
|
||||
if p.is_alive():
|
||||
p.terminate()
|
||||
|
||||
# remove the backup file
|
||||
if os.path.isfile(local_cfg_bak_file):
|
||||
os.remove(local_cfg_bak_file)
|
Loading…
Reference in New Issue