#!/usr/bin/python
#-*- coding: UTF-8 -*-
# ----------------------------------------------------------------------------
# Generate Axis Simulator
#
# Copyright 2015 (C) zhangbin
#
# License: MIT
# ----------------------------------------------------------------------------
import os
import re
import shutil
import string
import sys
import axis
from MultiLanguage import MultiLanguage
import utils
from datetime import date
from axis import CCPluginError, Logging
from argparse import ArgumentParser
class SimulatorCompiler(axis.CCPlugin):
SIMULATOR_PROJ_PATH = 'tools/simulator'
SIMULATOR_SLN_PATH = "frameworks/runtime-src/proj.win32/simulator.sln"
SIMULATOR_XCODE_PATH = "frameworks/runtime-src/proj.ios_mac/simulator.xcodeproj"
COCOS_CMD_PATH = 'tools/console/bin/axis'
DEFAULT_OUTPUT_FOLDER_NAME = 'simulator'
@staticmethod
def plugin_name():
return "gen-simulator"
@staticmethod
def brief_description():
return MultiLanguage.get_string('GEN_SIM_BRIEF')
def parse_args(self, argv):
parser = ArgumentParser(prog="axis %s" % self.__class__.plugin_name(),
description=self.__class__.brief_description())
parser.add_argument('-c', '--clean', dest='do_clean', action='store_true',
help=MultiLanguage.get_string('GEN_SIM_ARG_CLEAN'))
parser.add_argument('-e', dest='engine_path', help=MultiLanguage.get_string('GEN_SIM_ARG_ENGINE'))
parser.add_argument('-m', "--mode", dest='compile_mode', type=str, choices=['debug', 'release'],
help=MultiLanguage.get_string('GEN_SIM_ARG_MODE'))
parser.add_argument('-o', '--output', dest='out_dir',
help=MultiLanguage.get_string('GEN_SIM_ARG_OUTPUT'))
parser.add_argument('-p', dest='platform', action="append", choices=['ios', 'mac', 'android', 'win32'],
help=MultiLanguage.get_string('GEN_SIM_ARG_PLATFORM'))
parser.add_argument('--vs', dest='vs_version', type=int,
help=MultiLanguage.get_string('GEN_SIM_ARG_VS'))
(args, unknown) = parser.parse_known_args()
self.init(args)
return args
def init(self, args):
if getattr(sys, 'frozen', None):
self.cur_dir = os.path.realpath(os.path.dirname(sys.executable))
self.default_engine_path = os.path.join(self.cur_dir, os.pardir, os.pardir, os.pardir)
else:
self.cur_dir = os.path.realpath(os.path.dirname(__file__))
self.default_engine_path = os.path.join(self.cur_dir, os.pardir, os.pardir, os.pardir, os.pardir)
self.default_engine_path = os.path.normpath(self.default_engine_path)
if args.engine_path is None:
self.engine_root = self.default_engine_path
else:
engine_path = os.path.expanduser(args.engine_path)
if os.path.isabs(engine_path):
self.engine_root = os.path.normpath(engine_path)
else:
self.engine_root = os.path.normpath(os.path.abspath(engine_path))
if not os.path.isdir(self.engine_root):
raise CCPluginError(MultiLanguage.get_string('GEN_SIM_ERROR_WRONG_PATH_FMT', self.engine_root),
CCPluginError.ERROR_WRONG_ARGS)
self.simulator_abs_path = os.path.join(self.engine_root, SimulatorCompiler.SIMULATOR_PROJ_PATH)
self.cocos_bin = os.path.join(self.engine_root, SimulatorCompiler.COCOS_CMD_PATH)
engine_version = utils.get_engine_version(self.engine_root)
# get the short version after "cocos2d-x-"
self.engine_version = engine_version[10:]
# get the full path of output dir.
if args.out_dir is None:
self.simulator_output_dir = os.path.join(self.engine_root, SimulatorCompiler.DEFAULT_OUTPUT_FOLDER_NAME)
else:
out_dir = os.path.expanduser(args.out_dir)
if os.path.isabs(out_dir):
self.simulator_output_dir = os.path.normpath(out_dir)
else:
self.simulator_output_dir = os.path.normpath(os.path.abspath(out_dir))
# get arguments
self.is_clean_before_build = args.do_clean
if args.compile_mode is None:
self.mode = 'debug'
else:
self.mode = args.compile_mode
if args.platform is None:
self.build_ios = True
self.build_mac = True
self.build_win = True
self.build_android = True
else:
self.build_ios = False
self.build_mac = False
self.build_win = False
self.build_android = False
if 'win32' in args.platform:
self.build_win = True
if 'ios' in args.platform:
self.build_ios = True
if 'mac' in args.platform:
self.build_mac = True
if 'android' in args.platform:
self.build_android = True
self.build_log = ""
self.vs_version = args.vs_version
self._verbose = True
def replace_keyword_with_content(self,content,old,new):
found_string = string.find(content,old) >= 0
return found_string, string.replace(content,old,new)
def get_content_from_file(self,file_path):
if not os.path.isfile(file_path):
raise CCPluginError(MultiLanguage.get_string('GEN_SIM_ERROR_FILE_NOT_FOUND_FMT', file_path),
CCPluginError.ERROR_PATH_NOT_FOUND)
with open(file_path) as f:
return f.read()
def write_content_to_file(self,content, file_path):
if not os.path.isfile(file_path):
raise CCPluginError(MultiLanguage.get_string('GEN_SIM_ERROR_FILE_NOT_FOUND_FMT', file_path),
CCPluginError.ERROR_PATH_NOT_FOUND)
with open(file_path, 'w') as f:
f.write(content)
def replace_keyword_with_file(self,file_path,keyword_map):
content = self.get_content_from_file(file_path)
found = False
for k in keyword_map:
old, new = k, keyword_map[k]
has_found, content = self.replace_keyword_with_content(content, old, new)
if has_found :
found = has_found
if found == True:
self.write_content_to_file(content,file_path)
def get_keywords(self):
osx_keyword = {
"AX_TARGET_OS_IPHONE,":"AX_TARGET_OS_IPHONE,\n\"AXIS_DEBUG=1\",",
"AX_TARGET_OS_MAC,":"AX_TARGET_OS_MAC,\n\"AXIS_DEBUG=1\",",
"AXIS_DEBUG=0":"AXIS_DEBUG=1",
}
win_keyword = {
"_WINDOWS":"_WINDOWS;AXIS_DEBUG=1",
}
if axis.os_is_mac():
return osx_keyword
if axis.os_is_win32():
return win_keyword
return {}
def convert_path_to_win32(self,path):
return path.replace("/","\\")
def get_depend_project_file_list(self):
file_list = []
if axis.os_is_mac() and (self.build_mac or self.build_ios):
IOS_MAC_PROJECT_SUFFIX = "project.pbxproj"
IOS_MAC_PROJECT_REFERENCES_TAG = 'ProjectRef ='
IOS_MAC_PROJECT_NAME_RE = r'\w+.xcodeproj'
IOS_MAC_PROJECT_PATH_RE = r'name = %s; path = (.)*.xcodeproj'
project_file_path = os.path.join(self.simulator_abs_path,
SimulatorCompiler.SIMULATOR_XCODE_PATH,
IOS_MAC_PROJECT_SUFFIX)
contents_str = self.get_content_from_file(project_file_path)
lines = re.split(r'\n', contents_str)
simulator_mac_project_path = os.path.dirname(os.path.dirname(project_file_path))
project_references = []
for l in lines:
if IOS_MAC_PROJECT_REFERENCES_TAG in l:
ret = re.search(IOS_MAC_PROJECT_NAME_RE, l)
if ret: project_references.append(ret.group(0))
for references in project_references:
re_str = IOS_MAC_PROJECT_PATH_RE % references
ret = re.search(re_str, contents_str)
if ret:
match_str = ret.group(0)
match_str = match_str.replace("name = %s; path = " % references, "")
match_str = match_str.replace('"', "")
file_list.append(os.path.join(simulator_mac_project_path, match_str, IOS_MAC_PROJECT_SUFFIX))
if axis.os_is_win32() and self.build_android:
WIN32_PROJECT_TAG = "Project(\""
project_file_path = os.path.join(self.simulator_abs_path, SimulatorCompiler.SIMULATOR_SLN_PATH)
simulator_win32_project_path = os.path.dirname(project_file_path)
content_str = self.get_content_from_file(project_file_path)
lines = content_str.split('\n')
for l in lines:
if l.startswith(WIN32_PROJECT_TAG):
ret = re.compile('"(.*?)"').findall(l.split(',')[1])
if ret:
path = self.convert_path_to_win32(os.path.join(simulator_win32_project_path, ret[0]))
file_list.append(path)
return file_list
def compile_for_osx(self):
if self.is_clean_before_build:
project_directory = os.path.join(self.simulator_abs_path, "frameworks/runtime-src/proj.ios_mac/")
command = "xcodebuild -alltargets -configuration %s clean" % ("Debug" if self.mode == 'debug' else 'Release')
self._run_cmd(command, project_directory)
command = ' '.join([
"mkdir -p %s" % (os.path.join(self.simulator_abs_path, "src")),
" && %s compile -p mac -m %s -o \"%s\" --no-res --compile-script 0" % (self.cocos_bin
, "debug" if self.mode == 'debug' else "release"
, os.path.join(self.simulator_output_dir,"mac")),
" && strip %s" % (os.path.join(self.simulator_output_dir,"mac/Simulator.app/Contents/MacOS/Simulator")),
])
self._run_cmd(command, self.simulator_abs_path)
self.build_log += MultiLanguage.get_string('GEN_SIM_BUILD_SUCCESS_FMT', ('Mac', self.mode))
def compile_for_ios(self):
if self.is_clean_before_build:
project_directory = os.path.join(self.simulator_abs_path, "frameworks/runtime-src/proj.ios_mac/")
command = "xcodebuild -alltargets -configuration %s clean" % ("Debug" if self.mode =='debug' else 'Release')
self._run_cmd(command, project_directory)
command = ' '.join([
" %s compile -p ios -m %s -o \"%s\" --no-res --compile-script 0" % (self.cocos_bin
, "debug" if self.mode == 'debug' else "release"
, os.path.join(self.simulator_output_dir,"ios")),
" && strip %s" % (os.path.join(self.simulator_output_dir,"ios","Simulator.app/Simulator")),
" && rm -fr %s" % (os.path.join(self.simulator_output_dir,"ios","Simulator.app.dSYM")),
])
self._run_cmd(command, self.simulator_abs_path)
self.build_log += MultiLanguage.get_string('GEN_SIM_BUILD_SUCCESS_FMT', ('iOS', self.mode))
def compile_for_win32(self):
win32_output_dir = os.path.join(self.simulator_output_dir, "win32")
win32_output_dir = self.convert_path_to_win32(win32_output_dir)
if not os.path.isdir(win32_output_dir):
os.makedirs(win32_output_dir)
# get the vs version should be used
if self.vs_version is None:
ver_param = ''
else:
ver_param = '--vs %d' % self.vs_version
if self.mode == 'debug':
win32_src_dir = os.path.join(self.simulator_abs_path,"runtime/win32/")
win32_src_dir = self.convert_path_to_win32(win32_src_dir)
win32_dll_dir = self.convert_path_to_win32(os.path.join(os.path.dirname(self.cur_dir),"dll/"))
command = ' '.join([
" %s compile -p win32 -m debug --no-res --compile-script 0 %s" % (self.cocos_bin, ver_param),
" && xcopy /Y %s*.dll %s" % (win32_src_dir, win32_output_dir),
" && xcopy /Y %s*.exe %s" % (win32_src_dir, win32_output_dir),
" && if exist %s*.dll xcopy /Y %s*.dll %s" % (win32_dll_dir,win32_dll_dir,win32_output_dir)
])
else:
command = " %s compile -p win32 -m release --no-res --compile-script 0 -o %s %s" % (self.cocos_bin,win32_output_dir,ver_param)
self._run_cmd(command, self.simulator_abs_path)
self.build_log += MultiLanguage.get_string('GEN_SIM_BUILD_SUCCESS_FMT', ('Win32', self.mode))
def compile_for_android(self):
rename_command = ' '.join([
"mv %s %s" % (os.path.join(self.simulator_output_dir,"android","simulator-debug.apk"),
os.path.join(self.simulator_output_dir,"android","Simulator.apk"))
])
command = ' '.join([
" %s compile -p android --mode %s -o \"%s\" --no-res --compile-script 0" % (self.cocos_bin
, "debug" if self.mode == 'debug' else "release"
, os.path.join(self.simulator_output_dir,"android")),
"&& %s" % (rename_command),
])
self._run_cmd(command, self.simulator_abs_path)
self.build_log += MultiLanguage.get_string('GEN_SIM_BUILD_SUCCESS_FMT', ('Android', self.mode))
def do_compile(self):
if axis.os_is_mac():
if self.build_mac:
self.compile_for_osx()
if self.build_ios:
self.compile_for_ios()
if axis.os_is_win32():
if self.build_win:
self.compile_for_win32()
if self.build_android:
self.compile_for_android()
def change_cocos2d_debug_macro_to_1(self, file_list):
keyword = self.get_keywords()
for file_path in file_list:
self.replace_keyword_with_file(file_path, keyword)
def update_bundle_version(self):
build_date = date.today().strftime("%Y%m%d")
if axis.os_is_mac() and self.build_mac:
# mac
info_plist_path = os.path.join(self.simulator_abs_path, "frameworks/runtime-src/proj.ios_mac/mac/Info.plist")
info_plist_content = self.get_content_from_file(info_plist_path)
match = re.compile('CFBundleVersion(\s)*(.*?)').findall(info_plist_content)
if len(match):
build_date_tag = "%s" % match[0][1]
keyword_map = { build_date_tag : "%s" % build_date }
self.replace_keyword_with_file(info_plist_path, keyword_map)
match = re.compile('CFBundleShortVersionString(\s)*(.*?)').findall(info_plist_content)
if len(match):
build_date_tag = "%s" % match[0][1]
keyword_map = { build_date_tag : "%s" % self.engine_version }
self.replace_keyword_with_file(info_plist_path, keyword_map)
if axis.os_is_win32() and self.build_win:
# win32
game_rc_path = os.path.join(self.simulator_abs_path,"frameworks/runtime-src/proj.win32/game.rc")
game_rc_content = self.get_content_from_file(game_rc_path)
match = re.compile('"Version[^\(]*\(.*\)"').findall(game_rc_content)
if len(match):
build_info_str = match[0]
target_str = '"Version %s (%s)"' % (self.engine_version, build_date)
keyword_map = { build_info_str : target_str}
self.replace_keyword_with_file(game_rc_path,keyword_map)
def backup_files(self, files):
for f in files:
full_path = os.path.abspath(f)
if not os.path.isfile(full_path):
continue
backup_file_path = '%s.bak' % full_path
shutil.copyfile(full_path, backup_file_path)
def rollback_files(self, files):
for f in files:
full_path = os.path.abspath(f)
backup_file_path = '%s.bak' % full_path
if not os.path.isfile(backup_file_path):
continue
try:
shutil.copyfile(backup_file_path, full_path)
os.remove(backup_file_path)
except:
Logging.warning(MultiLanguage.get_string('GEN_SIM_ROLL_BACK_FAIL_FMT',
(full_path, backup_file_path, full_path)))
pass
def run(self, argv, dependencies):
self.parse_args(argv)
if self.is_clean_before_build:
utils.rmdir(self.simulator_output_dir)
# backup some files
modify_files = self.get_depend_project_file_list()
if axis.os_is_mac() and self.build_mac:
modify_files.append(os.path.join(self.simulator_abs_path, 'frameworks/runtime-src/proj.ios_mac/mac/Info.plist'))
if axis.os_is_win32() and self.build_win:
modify_files.append(os.path.join(self.simulator_abs_path, 'frameworks/runtime-src/proj.win32/game.rc'))
self.backup_files(modify_files)
try:
# modify bundle version
self.update_bundle_version()
# modify project config files
self.change_cocos2d_debug_macro_to_1(modify_files)
# compile simulator
self.do_compile()
except Exception as e:
raise e
finally:
# roll back modified files
self.rollback_files(modify_files)
Logging.info("")
Logging.info(self.build_log)
Logging.info("")
return 0