def do_build(self): #各个渠道的打包 for app_id in self.batch_info.keys(): app_id = utils.non_unicode_str(app_id) channels = self.batch_info[app_id] for channel_id in channels: Logging.debug_msg('----------------') apk_rollback_obj = utils.FileRollback() try: channel_str = self.get_string(channel_id) self.build_result['%s_%s' % (app_id, channel_str)] = '' self.build_one_apk(app_id, channel_str, apk_rollback_obj) except: Logging.warn_msg('打包失败') finally: Logging.debug_msg('finally :') if not self.no_rollback: apk_rollback_obj.do_rollback() Logging.debug_msg('----------------\n') Logging.debug_msg('\n打包结果汇总 :') for key in self.build_result.keys(): ids = key.split('_') value = self.build_result[key] if value and os.path.isfile(utils.get_sys_encode_str(value)): Logging.debug_msg( 'APP_ID : %s, CHANNEL_ID : %s。打包成功。apk 路径 : %s' % (ids[0], ids[1], value)) else: Logging.debug_msg('APP_ID : %s, CHANNEL_ID : %s。打包失败。' % (ids[0], ids[1])) #打开目录 os.system("start explorer %s" % self.output_path)
def build_apk_gradle(self, rollback_obj, cfg_dir, apk_cfg_info, apk_name): # 最终 apk 文件名格式为:GAMENAME_PKGNAME_APPID_CHANNELID_VERNAME_VERCODE.apk # Logging.debug_msg('修改apk文件名 game_name=%s pkg_name=%s app_id=%s channel_id=%s ver_name=%s ver_code=%s' % (game_name, pkg_name, app_id, channel_id, ver_name, ver_code)) # 修改签名文件 Logging.debug_msg('修改签名文件') keystore_path = utils.flat_path( os.path.join( cfg_dir, apk_cfg_info[PackAPK.CFG_SIGN][PackAPK.CFG_SIGN_FILE])) keystore_pass = apk_cfg_info[PackAPK.CFG_SIGN][ PackAPK.CFG_SIGN_PASSWORD] alias = apk_cfg_info[PackAPK.CFG_SIGN][PackAPK.CFG_SIGN_ALIAS] alias_pass = apk_cfg_info[PackAPK.CFG_SIGN][ PackAPK.CFG_SIGN_ALIAS_PASSWORD] self._modify_gradle_config(rollback_obj, self.output_path, apk_name, keystore_path, keystore_pass, alias, alias_pass) gradle_cmd = "cd %s;gradle clean;gradle aR" % self.proj_androidStudio_path try: utils.run_shell(gradle_cmd) except: Logging.warn_msg('gradle 命令行打包失败') Logging.debug_msg('gradle配置文件修改完成')
def _do_add_check_files(self, key, value): unix_key = key.replace('\\', '/') if unix_key not in self.check_files: self.check_files[unix_key] = value else: Logging.warn_msg("%s 冲突:%s 和 %s" % (unix_key, self.check_files[unix_key], value))
def do_build(self): try: for app_id in self.batch_info.keys(): app_id = utils.non_unicode_str(app_id) channels = self.batch_info[app_id] for channel_id in channels: Logging.debug_msg('----------------') apk_rollback_obj = utils.FileRollback() try: channel_str = self.get_string(channel_id) self.build_result['%s_%s' % (app_id, channel_str)] = '' self.build_one_apk(app_id, channel_str, apk_rollback_obj) except: Logging.warn_msg('打包失败') finally: if not self.no_rollback: apk_rollback_obj.do_rollback() Logging.debug_msg('----------------\n') Logging.debug_msg('\n打包结果汇总 :') for key in self.build_result.keys(): ids = key.split('_') value = self.build_result[key] if value and os.path.isfile(utils.get_sys_encode_str(value)): Logging.debug_msg( 'APP_ID : %s, CHANNEL_ID : %s。打包成功。apk 路径 : %s' % (ids[0], ids[1], value)) else: Logging.debug_msg('APP_ID : %s, CHANNEL_ID : %s。打包失败。' % (ids[0], ids[1])) except Exception: raise finally: if os.path.isdir(self.temp_dir): shutil.rmtree(self.temp_dir)
def do_build(self): #各个游戏,各个渠道 for game in self.batch_info.keys(): game = utils.non_unicode_str(game) channels = self.batch_info[game] for channel_id in channels: Logging.debug_msg('----------------' + channel_id) try: channel_str = utils.non_unicode_str(channel_id) apk_cfg_file = os.path.join(self.root_dir, game, channel_str, 'package.json') if not os.path.isfile(apk_cfg_file): Logging.warn_msg('未找到 %s 文件' % apk_cfg_file) return pack_cfg_info = self.parse_json(apk_cfg_file) if pack_cfg_info is None: Logging.warn_msg('解析文件 %s 出错' % apk_cfg_file) return strResPath = utils.non_unicode_str( pack_cfg_info['res_path']) res_path = utils.flat_path( os.path.join(self.root_dir, game, channel_str, strResPath)) strTargetpath = utils.non_unicode_str( pack_cfg_info['target_path']) target_path = utils.flat_path( os.path.join(self.root_dir, strTargetpath)) copy_cfg = { 'from': '.', 'to': '.', 'exclude': ['**/.DS_Store'] } excopy.copy_files_with_config(copy_cfg, res_path, target_path) except: Logging.warn_msg('资源拷贝失败') finally: Logging.debug_msg('finally :') Logging.debug_msg('----------------\n')
def needCompress(key): global PngMap # folder, basename = os.path.split(key) # pattern = r"([a-f0-9\-]+)\.[a-f0-9\-]{5}\.png" # m = re.match(pattern, basename) # if not m: # Logging.warn_msg("The file path %s is not in right format." % key) # return False # # checkKey = os.path.join(folder, "%s.png" % m.group(1)) checkKey = key.replace('\\', '/') if PngMap.has_key(checkKey): srcPath = os.path.dirname(PngMap[checkKey]) srcPath = srcPath.lstrip("db://assets/") base, ext = os.path.splitext(srcPath) if (ext == ".plist"): srcPath = base + '.png' # 检查不需要压缩的文件夹 unDirs = Uncompress.get('dirs', []) for d in unDirs: if srcPath.startswith(d): Logging.warn_msg("%s in uncompress dir : %s" % (key, d)) return False # 检查不需要压缩的文件 unFiles = Uncompress.get('files', []) for f in unFiles: if srcPath == f: Logging.warn_msg("%s is uncompress file %s" % (key, f)) return False Logging.debug_msg("Compress image : %s (%s)" % (key, srcPath)) return True else: Logging.warn_msg("%s is not found in PngMap.json" % checkKey) return False
def build_one_apk(self, app_id, channel_id, rollback_obj): apk_cfg_file = os.path.join(self.root_dir, app_id, channel_id, 'package.json') if not os.path.isfile(apk_cfg_file): Logging.warn_msg('未找到 %s 文件,打包失败1' % apk_cfg_file) return Logging.debug_msg('开始使用配置文件 %s 打包' % apk_cfg_file) apk_cfg_info = self._parse_json(apk_cfg_file) if apk_cfg_info is None: Logging.warn_msg('解析文件 %s 出错,打包失败2' % apk_cfg_file) return cfg_dir = os.path.dirname(apk_cfg_file) check_ret = self._check_cfg_info(apk_cfg_info, cfg_dir) if check_ret: Logging.warn_msg(check_ret + ',打包失败5') return # gradle需要修改 package.json 配置文件 rollback_obj.record_file(apk_cfg_file) game_name = utils.non_unicode_str(apk_cfg_info[PackAPK.CFG_GAME_NAME]) pkg_name = utils.non_unicode_str(apk_cfg_info[PackAPK.CFG_PKG_NAME]) ver_name = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_VERSION_NAME]) ver_code = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_VERSION_CODE]) app_scheme = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_APP_SCHEME]) # 修改 strings.xml self._modify_strings_xml(game_name, app_scheme) # 修改渠道号 isThirdPart = apk_cfg_info.get(PackAPK.CFG_THIRD_PART, False) self._modify_apihelper(apk_cfg_info[PackAPK.CFG_CHANNEL_ID], isThirdPart, rollback_obj) # 修改 java 文件中 R 的包名 self._modify_java_files(pkg_name, rollback_obj) # 修改 AndroidManifest.xml self._modify_manifest(pkg_name, ver_name, ver_code, rollback_obj) # 替换 res self._modify_res(apk_cfg_info, cfg_dir, rollback_obj) # 替换 libs self._modify_libs(apk_cfg_info, cfg_dir, rollback_obj) # 最终 apk 文件名格式为:GAMENAME_PKGNAME_APPID_CHANNELID_VERNAME_VERCODE.apk Logging.debug_msg('生成apk文件名') name_infos = [ game_name, pkg_name, app_id, channel_id, ver_name, ver_code ] # 文件名增加时间戳 Logging.debug_msg('文件名增加时间戳') cur_time_str = datetime.datetime.fromtimestamp( time.time()).strftime('%Y%m%d_%H%M%S') name_infos.append(cur_time_str) apk_name = '_'.join(name_infos) + '.apk' out_file_path = os.path.join(self.output_path, apk_name) if not os.path.isdir(self.output_path): os.makedirs(self.output_path) # 进行 gradle 打包 Logging.debug_msg('进行 gradle 打包') try: self.build_apk_gradle(rollback_obj, cfg_dir, apk_cfg_info, apk_name) self.build_result['%s_%s' % (app_id, channel_id)] = out_file_path except: print 'traceback.format_exc():\n%s' % traceback.format_exc() return
def build_one_apk(self, app_id, channel_id, rollback_obj): apk_cfg_file = os.path.join(self.root_dir, app_id, channel_id, 'package.json') if not os.path.isfile(apk_cfg_file): Logging.warn_msg('未找到 %s 文件,打包失败' % apk_cfg_file) return Logging.debug_msg('开始使用配置文件 %s 打包' % apk_cfg_file) apk_cfg_info = self._parse_json(apk_cfg_file) if apk_cfg_info is None: Logging.warn_msg('解析文件 %s 出错,打包失败' % apk_cfg_file) return cfg_dir = os.path.dirname(apk_cfg_file) check_gt_ret = self._check_gt_info(apk_cfg_info, cfg_dir) if check_gt_ret: Logging.warn_msg(check_gt_ret + ', 打包失败') return check_ret = self._check_cfg_info(apk_cfg_info, cfg_dir) if check_ret: Logging.warn_msg(check_ret + ',打包失败') return # 备份文件和文件夹 rollback_obj.record_file(utils.flat_path(self.library_manifest)) rollback_obj.record_file(utils.flat_path(self.manifest)) rollback_obj.record_folder(os.path.join(self.proj_android_path, 'res')) game_name = utils.non_unicode_str(apk_cfg_info[PackAPK.CFG_GAME_NAME]) pkg_name = utils.non_unicode_str(apk_cfg_info[PackAPK.CFG_PKG_NAME]) gaode_key = utils.non_unicode_str(apk_cfg_info[PackAPK.CFG_GAODE_KEY]) ver_name = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_VERSION_NAME]) ver_code = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_VERSION_CODE]) app_scheme = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_APP_SCHEME]) need_remove_agora = True # Android 去掉视频 SDK apk_cfg_info.get(PackAPK.CFG_NO_AGORA, False) # 修改 java 文件中 R 的包名 self._modify_java_files(pkg_name, rollback_obj) # 对需要修改包名的文件进行包名替换 self._change_pkg_name(pkg_name, rollback_obj) # 修改 bugly appid if apk_cfg_info.has_key(PackAPK.CFG_BUGLY_ID): bugly_appid = utils.non_unicode_str( apk_cfg_info[PackAPK.CFG_BUGLY_ID]) else: bugly_appid = "" if len(bugly_appid) > 0: self._modify_bugly(self.library_manifest, bugly_appid, channel_id) # 修改高德 key if len(gaode_key) > 0: self._modify_gaode_key(self.library_manifest, gaode_key) # 修改 Agora id if apk_cfg_info.has_key(PackAPK.CFG_AGORA_ID): self._modify_agora_id(self.library_manifest, apk_cfg_info[PackAPK.CFG_AGORA_ID]) # 修改 AndroidManifest.xml self._modify_manifest(pkg_name, ver_name, ver_code) # 替换 java res for p in apk_cfg_info[PackAPK.CFG_JAVA_RES].split(','): java_res_path = utils.flat_path(os.path.join(cfg_dir, p.strip())) copy_cfg = {'from': '.', 'to': 'res', 'exclude': ['**/.DS_Store']} excopy.copy_files_with_config(copy_cfg, java_res_path, self.proj_android_path) # 替换 个推通知图标push.png # isTurnOffPush = apk_cfg_info.get(PackAPK.CFG_TURNOFF_PUSH, None) # if isTurnOffPush != "1": push_res_path = utils.flat_path(os.path.join(cfg_dir, "../pushRes")) proj_getui_path = utils.flat_path( os.path.join(self.proj_android_path, '../library.pushgetui.android')) if not os.path.isdir(proj_getui_path): raise_known_error("未找到 getui 工程文件夹 %s" % proj_getui_path) copy_cfg = {'from': '.', 'to': 'res', 'exclude': ['**/.DS_Store']} rollback_obj.record_folder(os.path.join(proj_getui_path, "res")) if os.path.isdir(push_res_path): excopy.copy_files_with_config(copy_cfg, push_res_path, proj_getui_path) else: # raise_known_error('文件夹 %s 不存在,放置个推通知图标pushRes/drawable-xxhdpi/push.png|push_small.png' % push_res_path, KnownError.ERROR_PATH_NOT_FOUND) res_path = utils.flat_path( os.path.join(self.proj_android_path, "res")) pushSmallSrc_path = utils.flat_path( os.path.join(res_path, "mipmap-hdpi/ic_launcher.png")) pushSrc_path = utils.flat_path( os.path.join(res_path, "mipmap-xxxhdpi/ic_launcher.png")) pushDes_path = utils.flat_path( os.path.join(proj_getui_path, "res/drawable-xxhdpi/push.png")) pushSmallDes_path = utils.flat_path( os.path.join(proj_getui_path, "res/drawable-xxhdpi/push_small.png")) # excopy.copy_files_with_config(copy_cfg, push_res_path, proj_getui_path) shutil.copyfile(pushSrc_path, pushDes_path) shutil.copyfile(pushSmallSrc_path, pushSmallDes_path) Logging.debug_msg('个推通知图标替换完成') # 修改 strings.xml self._modify_strings_xml(game_name, app_scheme) # 获取并清理临时的 assets 文件夹 temp_assets_path = os.path.join(self.temp_dir, '%s_%s_assets' % (app_id, channel_id)) if os.path.isdir(temp_assets_path): shutil.rmtree(temp_assets_path) Logging.debug_msg('开始处理资源文件') # 拷贝大厅的资源文件 for p in PackAPK.DEFAULT_HALL_DIRS: copy_cfg = { 'from': p, 'to': p, 'exclude': ['**/.DS_Store', 'LuaDebugjit.lua'] } excopy.copy_files_with_config(copy_cfg, self.res_root_path, temp_assets_path) # 拷贝游戏通用组件的各个部分 game_common_parts = apk_cfg_info.get(PackAPK.CFG_GAME_COMMON_PARTS, None) self._copy_game_common_parts(game_common_parts, temp_assets_path) # 拷贝指定的游戏文件 games_dirs = [] games = apk_cfg_info.get(PackAPK.CFG_GAMES, None) if isinstance(games, list) and len(games) > 0: games_dirs.extend(games) if len(games_dirs) > 0: if not os.path.isdir(self.games_root_path): raise_known_error('文件夹 %s 不存在' % self.games_root_path, KnownError.ERROR_PATH_NOT_FOUND) for game in games_dirs: copy_cfg = { 'from': '%s' % game, 'to': 'games/%s' % game, 'exclude': ['**/.DS_Store', '*.git'] } excopy.copy_files_with_config(copy_cfg, self.games_root_path, temp_assets_path) # 替换游戏配置文件 for p in apk_cfg_info[PackAPK.CFG_FILES].split(','): cfg_path = utils.flat_path(os.path.join(cfg_dir, p.strip())) copy_cfg = {'from': '.', 'to': 'src', 'exclude': ['**/.DS_Store']} excopy.copy_files_with_config(copy_cfg, cfg_path, temp_assets_path) # cfg_package.lua文件末尾写入打包文件夹名称时间戳 tmpFile = open(os.path.join(temp_assets_path, "src/cfg_package.lua"), 'a') tmpFile.write('\nPACKAGE_TIMESTAMP=' + '\"' + self.packageTimestamp + '\"') tmpFile.close() # 替换 res 文件夹 if apk_cfg_info.has_key(PackAPK.CFG_RES_FILES): for p in apk_cfg_info[PackAPK.CFG_RES_FILES].split(','): res_path = utils.flat_path(os.path.join(cfg_dir, p.strip())) copy_cfg = { 'from': '.', 'to': 'res', 'exclude': ['**/.DS_Store'] } excopy.copy_files_with_config(copy_cfg, res_path, temp_assets_path) # 删除不需要的品牌资源文件夹 for brand in utils.get_support_brands(): brand_path = utils.flat_path( os.path.join(temp_assets_path, 'res', brand)) if brand != self.brand and os.path.isdir(brand_path): shutil.rmtree(brand_path) # 删除不属于该地区的地图资源文件 if apk_cfg_info.has_key(PackAPK.CFG_REGION) and apk_cfg_info[ PackAPK.CFG_REGION] != 'all': region_list = [] new_list = apk_cfg_info[PackAPK.CFG_REGION].split(',') for r in new_list: if r.strip() != '': region_list.append(r) # 超过1个地区时需要带上全国地图 if len(region_list) > 1: region_list.append('0') keep_name_list = [] for region in region_list: keep_name = 'map_%s' % region keep_name_list.append(keep_name) map_path = utils.flat_path( os.path.join(temp_assets_path, 'res/hall/map')) if os.path.isdir(map_path): for f in os.listdir(map_path): full_path = os.path.join(map_path, f) if f not in keep_name_list and os.path.isdir(full_path): shutil.rmtree(full_path) # 如果不显示全国地图,则删除云动画的图片 if len(region_list) <= 1: cloud1_path = os.path.join(map_path, "yun1.png") cloud2_path = os.path.join(map_path, "yun2.png") os.remove(cloud1_path) os.remove(cloud2_path) # 清理 assets 文件夹,只保留需要的文件 assets_path = os.path.join(self.proj_android_path, 'assets') file_recorder = utils.FileRollback() for f in PackAPK.NEED_BACKUP_ASSETS: file_recorder.record_file( utils.flat_path(os.path.join(assets_path, f))) if os.path.isdir(assets_path): shutil.rmtree(assets_path) if not self.no_rollback: file_recorder.do_rollback() # 进行加密或者直接拷贝资源文件 if os.path.isdir(temp_assets_path): # 生成 native 环境信息文件 params = None if need_remove_agora: # 需要移除 agora params = {"no_agora": "true"} utils.write_native_info( os.path.join(temp_assets_path, 'src/NativeInfo.lua'), params) if not self.no_encrypt: self._do_encrypt(temp_assets_path, assets_path) else: copy_cfg = { 'from': '.', 'to': '.', 'exclude': ['**/.DS_Store'] } excopy.copy_files_with_config(copy_cfg, temp_assets_path, assets_path) f = open(os.path.join(assets_path, 'src/myflag'), 'w') f.write('') f.close() Logging.debug_msg('资源文件处理结束') # 处理 SDK 相关的配置 Logging.debug_msg('处理 SDK 相关的配置开始') sdks_cfg = apk_cfg_info.get(PackAPK.CFG_SDKS, {}) remove_weixin = apk_cfg_info.get(PackAPK.CFG_REMOVE_WEIXIN, False) remove_youqu = apk_cfg_info.get(PackAPK.CFG_REMOVE_YOUQU, False) sdk_mgr = SDKManager.SDKManager(rollback_obj, pkg_name, sdks_cfg, remove_weixin, remove_youqu, self.build_gradle, self.proj_android_path, self.proj_androidStudio_path) sdk_mgr.prepare_sdk() Logging.debug_msg('处理 SDK 相关的配置结束') # 检查是否需要删除 agora 的库文件 if need_remove_agora: self._do_remove_agora(rollback_obj) # 最终 apk 文件名格式为:GAMENAME_PKGNAME_APPID_CHANNELID_VERNAME_VERCODE.apk Logging.debug_msg('生成apk文件名') name_infos = [pkg_name, app_id, channel_id, ver_name, ver_code] remark = utils.non_unicode_str(apk_cfg_info.get( PackAPK.CFG_REMARK, "")) if len(remark) is not 0: name_infos.append(remark) if self.no_encrypt: name_infos.append('NO_ENCRYPT') # 是否删除 agora # if need_remove_agora: # name_infos.append("NO_AGORA") # 文件名增加时间戳 Logging.debug_msg('文件名增加时间戳') cur_time_str = datetime.datetime.fromtimestamp( time.time()).strftime('%Y%m%d_%H%M%S') name_infos.append(cur_time_str) apk_name = '_'.join(name_infos) + '.apk' out_file_path = os.path.join(self.output_path, apk_name) if not os.path.isdir(self.output_path): os.makedirs(self.output_path) if self.build_gradle: # 进行 gradle 打包 Logging.debug_msg('进行 gradle 打包') try: self.build_apk_gradle(rollback_obj, cfg_dir, apk_cfg_info, apk_name) self.build_result['%s_%s' % (app_id, channel_id)] = out_file_path except: print 'traceback.format_exc():\n%s' % traceback.format_exc() return else: # 进行 ant 打包 Logging.debug_msg('进行 ant 打包') keystore_path = utils.flat_path( os.path.join( cfg_dir, apk_cfg_info[PackAPK.CFG_SIGN][PackAPK.CFG_SIGN_FILE])) keystore_pass = apk_cfg_info[PackAPK.CFG_SIGN][ PackAPK.CFG_SIGN_PASSWORD] alias = apk_cfg_info[PackAPK.CFG_SIGN][PackAPK.CFG_SIGN_ALIAS] alias_pass = apk_cfg_info[PackAPK.CFG_SIGN][ PackAPK.CFG_SIGN_ALIAS_PASSWORD] ant_cmd = PackAPK.ANT_CMD_FMT % (self.ant_bin, self.build_xml, self.sdk_root, keystore_path, alias, keystore_pass, alias_pass) try: utils.run_shell(ant_cmd) except: Logging.warn_msg('打包失败') return # 将 apk 包拷贝到指定位置 build_path = utils.flat_path( os.path.join(self.proj_android_path, 'bin/%s-release.apk' % self.proj_name)) shutil.copy(utils.get_sys_encode_str(build_path), utils.get_sys_encode_str(out_file_path)) Logging.debug_msg('生成的 apk 文件路径:%s' % out_file_path) self.build_result['%s_%s' % (app_id, channel_id)] = out_file_path try: shutil.rmtree(temp_assets_path) except: pass