123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529 |
- # -*- coding:utf-8 -*-
- import os, io, platform, subprocess, zipfile, sys
- from xml.etree import ElementTree as ET
- import file_utils, path_utils
- androidNS = 'http://schemas.android.com/apk/res/android'
- def update():
- if len(sys.argv) < 2:
- print("参数数量不正确")
- exit(1)
- config = sys.argv[1]
- update_channel(config)
- pass
- def update_channel(channel):
- channel_path = path_utils.get_sdk_channel_path(channel)
- file_utils.delete_folder(channel_path)
- svn_up_cmd = "svn up %s" % channel_path
- print("svn_up_cmd:%s" % svn_up_cmd)
- ret, result = exec_common_cmd(svn_up_cmd)
- if ret:
- print("svn update fail.")
- return
- if not os.path.exists(channel_path):
- print("no such channel")
- return
- pretreatment_project = os.path.join(channel_path, "pretreatment_project")
- if not os.path.exists(pretreatment_project):
- os.mkdir(pretreatment_project)
- # 渠道使用jar包接入
- jars_path = os.path.join(channel_path, 'jars')
- base_manifest_path = path_utils.get_sdk_channel_path('SDKManifest.xml')
- pre_manifest_path = os.path.join(pretreatment_project, 'SDKManifest.xml')
- if not os.path.exists(jars_path):
- file_utils.copy_file(base_manifest_path, pre_manifest_path)
- else:
- file_utils.copyAllFile(jars_path, pretreatment_project)
- # 渠道使用aar接入
- aars_path = os.path.join(channel_path, 'aars')
- if not os.path.exists(aars_path):
- os.mkdir(aars_path)
- # 复制平台SDK 到aar 目录
- if channel != 'share_sdk':
- platform_sdk_path = path_utils.get_full_path('platform_sdk', 'hnyy')
- file_utils.copyAllFile(platform_sdk_path, aars_path)
- # 解析 所有aar包
- decompile_aar(aars_path, pretreatment_project)
- # 渠道使用gradle 依赖包接入
- sdk_dependencies_gralde_path = os.path.join(channel_path, 'dependencies', 'build.gradle')
- if os.path.exists(sdk_dependencies_gralde_path):
- execute_gradlew(sdk_dependencies_gralde_path, pretreatment_project)
- pack_jar(pretreatment_project)
- remove_app_name(pretreatment_project)
- def decompile_aar(channel_aar_dir, pretreatment_project):
- for lib in os.listdir(channel_aar_dir):
- if lib.endswith(".aar"):
- aar_decom_dir = os.path.join(channel_aar_dir, lib.split('.aar')[0])
- if os.path.exists(aar_decom_dir):
- file_utils.delete_folder(aar_decom_dir)
- os.mkdir(aar_decom_dir)
- aar_path = os.path.join(channel_aar_dir, lib)
- zip_file = zipfile.ZipFile(aar_path)
- for names in zip_file.namelist():
- zip_file.extract(names, aar_decom_dir)
- zip_file.close()
- print("copy sdk resource:%s" % aar_decom_dir)
- copy_aar_jar_to_libs(aar_decom_dir, pretreatment_project, str(lib))
- sdk_base_xml = os.path.join(pretreatment_project, "SDKManifest.xml")
- aar_decom_dir_manifest_xml = os.path.join(aar_decom_dir, 'AndroidManifest.xml')
- print("merge sdk manifest:%s" % aar_decom_dir_manifest_xml)
- merge_manifest_aar_to_sdk(aar_decom_dir_manifest_xml, sdk_base_xml)
- elif lib.endswith(".jar"):
- pre_libs_dir = os.path.join(pretreatment_project, "lib")
- if not os.path.exists(pre_libs_dir):
- os.makedirs(pre_libs_dir)
- jar_path = os.path.join(channel_aar_dir, lib)
- lib_path = os.path.join(pre_libs_dir, lib)
- print("copy sdk jar:%s" % jar_path)
- file_utils.copy_file(jar_path, lib_path)
- else:
- print('not support type')
- file_utils.delete_folder(lib)
- continue
- def merge_manifest_aar_to_sdk(manifest_from, manifest_to):
- """
- Merge aars AndroidManifest.xml to the sdk SdkManifest.xml
- """
- if not os.path.exists(manifest_to) or not os.path.exists(manifest_from):
- print("the manifest file is not exists.manifestTo:%s;manifestFrom:%s", manifest_to, manifest_from)
- return False
- ET.register_namespace('android', androidNS)
- targetTree = ET.parse(manifest_to)
- # 获取xml根节点
- targetRoot = targetTree.getroot()
- ET.register_namespace('android', androidNS)
- aarTree = ET.parse(manifest_from)
- aarRoot = aarTree.getroot()
- f = open(manifest_to)
- targetContent = f.read()
- f.close()
- permissionConfigNode = targetRoot.find('permissionConfig')
- for child in list(aarRoot):
- # 子节点对应的values,uses-feature,uses-permission,permission
- key = '{' + androidNS + '}name'
- val = child.get(key)
- if val != None and len(val) > 0:
- attrIndex = targetContent.find(val)
- # values不存在添加
- if -1 == attrIndex:
- permissionConfigNode.append(child)
- aarAppNode = aarRoot.find('application')
- sdkAppConfigNode = targetRoot.find('applicationConfig')
- if aarAppNode is not None:
- for aarChild in list(aarAppNode):
- key = '{' + androidNS + '}name'
- val = aarChild.get(key)
- if val is not None and len(val) > 0:
- attrIndex = targetContent.find(val)
- if -1 == attrIndex:
- sdkAppConfigNode.append(aarChild)
- targetTree.write(manifest_to, 'UTF-8')
- return True
- def copy_aar_jar_to_libs(aar_decom_dir, pretreatment_project, aar_name):
- pre_assets_dir = os.path.join(pretreatment_project, "assets")
- if not os.path.exists(pre_assets_dir):
- os.makedirs(pre_assets_dir)
- pre_libs_dir = os.path.join(pretreatment_project, "lib")
- if not os.path.exists(pre_libs_dir):
- os.makedirs(pre_libs_dir)
- pre_base_jni_dir = os.path.join(pretreatment_project, "jniLibs")
- if not os.path.exists(pre_base_jni_dir):
- os.makedirs(pre_base_jni_dir)
- pre_res_dir = os.path.join(pretreatment_project, "res")
- if not os.path.exists(pre_res_dir):
- os.makedirs(pre_res_dir)
- for f in os.listdir(aar_decom_dir):
- # 分割最后一个“.”
- name = aar_name.rsplit(".", 1)[0]
- pre_libs_name = os.path.join(pre_libs_dir, name + ".jar")
- if f.endswith(".jar"):
- file_utils.copy_file(os.path.join(aar_decom_dir, f), pre_libs_name)
- if "assets" == f:
- file_utils.copyAllFile(os.path.join(aar_decom_dir, f), pre_assets_dir)
- if "jni" == f:
- decom_jni_dir = os.path.join(aar_decom_dir, f)
- for jniF in os.listdir(decom_jni_dir):
- if "armeabi" == jniF or "armeabi-v7a" == jniF or "x86" == jniF or "arm64-v8a" == jniF \
- or "x86_64" == jniF:
- pre_jni_dir = os.path.join(pre_base_jni_dir, jniF)
- if not os.path.exists(pre_jni_dir):
- os.makedirs(pre_jni_dir)
- file_utils.copyAllFile(os.path.join(decom_jni_dir, jniF), pre_jni_dir)
- if "libs" == f:
- decom_libs_dir = os.path.join(aar_decom_dir, f)
- # 部分aar包中的classes.jar里面并没有class,而是在aar包中libs里面的classes.jar
- for libF in os.listdir(decom_libs_dir):
- if libF == "classes.jar":
- # 如果SDK libs里面已经存在先删除,在复制一次
- if os.path.exists(pre_libs_name):
- file_utils.del_file(pre_libs_name)
- file_utils.copyAllFile(os.path.join(decom_libs_dir, libF), pre_libs_name)
- else:
- file_utils.copyAllFile(os.path.join(decom_libs_dir, libF), os.path.join(pre_libs_dir, libF))
- if "res" == f:
- copy_res_to_apk(os.path.join(aar_decom_dir, f), pre_res_dir)
- def copy_res_to_apk(copyFrom, copyTo):
- """
- Copy two resource folders
- """
- if not os.path.exists(copyFrom):
- print("the copyFrom %s is not exists.", copyFrom)
- return
- if not os.path.exists(copyTo):
- os.makedirs(copyTo)
- if os.path.isfile(copyFrom) and not merge_res_xml(copyFrom, copyTo):
- file_utils.copy_file(copyFrom, copyTo)
- print("copyResToApk:copyFrom-->%s;copyTo-->%s;", copyFrom, copyTo)
- return
- # 卢-->修改复制资源,并合并和删除重复资源
- for f in os.listdir(copyFrom):
- sourcefile = os.path.join(copyFrom, f)
- targetfile = os.path.join(copyTo, f)
- if f == 'abc_action_menu_layout.xml' or f == 'abc_screen_toolbar.xml':
- file_utils.delete_folder(sourcefile)
- continue
- if os.path.isfile(sourcefile):
- if not os.path.exists(copyTo):
- os.makedirs(copyTo)
- delete_same_res(sourcefile, copyTo, 'attrs.xml')
- delete_same_res(sourcefile, copyTo, 'colors.xml')
- delete_same_res(sourcefile, copyTo, 'dimens.xml')
- delete_same_res(sourcefile, copyTo, 'drawables.xml')
- delete_same_res(sourcefile, copyTo, 'ids.xml')
- delete_same_res(sourcefile, copyTo, 'integers.xml')
- delete_same_res(sourcefile, copyTo, 'public.xml')
- delete_same_res(sourcefile, copyTo, 'strings.xml')
- delete_same_res(sourcefile, copyTo, 'styles.xml')
- if merge_res_xml(sourcefile, targetfile):
- continue
- destfilestream = open(targetfile, 'wb')
- sourcefilestream = open(sourcefile, 'rb')
- destfilestream.write(sourcefilestream.read())
- destfilestream.close()
- sourcefilestream.close()
- if os.path.isdir(sourcefile):
- copy_res_to_apk(sourcefile, targetfile)
- def merge_res_xml(copyFrom, copyTo):
- """
- Merge all android res xml
- """
- if not os.path.exists(copyTo):
- return False
- fromName = os.path.basename(copyFrom)
- if not fromName.endswith('.xml'):
- return False
- f = io.open(copyTo, 'r', encoding='utf-8')
- targetContent = f.read()
- f.close()
- fromTree = ET.parse(copyFrom)
- fromRoot = fromTree.getroot()
- toTree = ET.parse(copyTo)
- toRoot = toTree.getroot()
- for fromNode in list(fromRoot):
- val = fromNode.get('name')
- if val is not None and len(val) > 0:
- # 如果出现name和parent字段一样也会删除,加上name=
- # <style name="Theme.AppCompat.Light">
- # <style name="m4399ad.Dialog.NoTitleBar" parent="Theme.AppCompat.Light">
- valMatched = 'name="' + val + '"'
- attrIndex = targetContent.find(valMatched)
- if -1 == attrIndex:
- toRoot.append(fromNode)
- # else:
- # log_utils.warning("The node %s is already exists in %s", val, copyTo)
- else:
- if fromNode.tag == "declare-styleable":
- for toNode in list(toRoot):
- if toNode.get("name") == fromNode.get("name"):
- for fNode in list(fromNode.iter("attr")):
- print(fNode)
- toNode.append(fNode)
- # 删除valus.xml中的无法编译的字段
- for toNode in list(toRoot):
- for declareNode in list(toNode.iter('declare-styleable')):
- declareName = declareNode.get('name')
- if (declareName == 'ActionBar' or declareName == 'Toolbar' or declareName == 'ActionMode'
- or declareName == 'Spinner' or declareName == 'LinearLayoutCompat'
- or declareName == 'AlignTextView' or declareName == 'TextSeekBar'):
- toRoot.remove(declareNode)
- continue
- toTree.write(copyTo, 'UTF-8')
- return True
- def delete_same_res(sourcefile, copyTo, filename):
- sourceName = os.path.basename(sourcefile)
- if not sourceName.endswith('.xml'):
- return
- targetfile = os.path.join(copyTo, filename)
- if not os.path.exists(targetfile):
- return
- fromTree = ET.parse(sourcefile)
- fromRoot = fromTree.getroot()
- tf = open(targetfile)
- targetContent = tf.read()
- tf.close()
- if filename == 'attrs.xml':
- for child in list(fromRoot):
- for declareNode in list(child.iter('declare-styleable')):
- if -1 == targetContent.find('"' + declareNode.get("name") + '"'):
- print("no same")
- continue
- else:
- for attrNode in list(declareNode.iter('attr')):
- attrName = attrNode.get('name')
- if attrName != None and len(attrName) > 0:
- attrMatched = '"' + attrName + '"'
- attrIndex = targetContent.find(attrMatched)
- if -1 == attrIndex:
- continue
- else:
- # for childNo in list(fromRoot):
- # if childNo.get("name") == attrName:
- # print(attrName)
- # fromRoot.remove(childNo)
- declareNode.remove(attrNode)
- else:
- for child in list(fromRoot):
- # 使用到com.android.support:appcompat-v7都要删除
- for itemNode in list(child.iter('item')):
- itemName = itemNode.get('name')
- if (
- itemName == 'buttonGravity' or itemName == 'subtitleTextStyle' or itemName == 'subtitleTextAppearance'
- or itemName == 'collapseIcon' or itemName == 'contentInsetStart' or itemName == 'contentInsetEnd'
- or itemName == 'contentInsetStartWithNavigation' or itemName == 'collapseContentDescription'
- or itemName == 'titleTextStyle' or itemName == 'titleMargin' or itemName == 'titleTextAppearance'
- or itemName == 'background' or itemName == 'backgroundSplit' or itemName == 'backgroundStacked'
- or itemName == 'displayOptions' or itemName == 'divider' or itemName == 'elevation'
- or itemName == 'popupTheme' or itemName == 'maxButtonHeight' or itemName == 'dividerPadding'
- or itemName == 'showDividers' or itemName == 'closeItemLayout'):
- child.remove(itemNode)
- continue
- name = child.get('name')
- if name != None and len(name) > 0:
- val = '"' + name + '"'
- index = targetContent.find(val)
- if -1 == index:
- continue
- else:
- fromRoot.remove(child)
- fromTree.write(sourcefile, 'UTF-8')
- def pack_jar(channel_path):
- """
- 打包所有的jar
- :param platform:
- :param apk_decompile_tmp_dir:
- :param channel_path:
- :param sdk_name:
- :return:
- """
- temp_gen_path = os.path.join(channel_path, 'gen')
- print('[temp_gen_path]: %s ' % temp_gen_path)
- if not os.path.exists(temp_gen_path):
- os.makedirs(temp_gen_path)
- dx = path_utils.get_d8_path()
- dex_cmd = ' --release --output %s' % temp_gen_path
- # 找到所有lib依赖,编译成class.dex
- sdk_lib_path = os.path.join(channel_path, 'lib')
- lib_list = ''
- for sdk_lib in file_utils.iterate_dir_path(sdk_lib_path):
- if sdk_lib.endswith('.DS_Store'):
- continue
- lib_list += ' ' + sdk_lib
- dex_cmd = dex_cmd + lib_list
- print('packaging all jar ...')
- ret = exec_jar_cmd(dx, dex_cmd)
- if ret:
- return ret
- print('baksmali classes.dex ...')
- out_dex = os.path.join(temp_gen_path, 'classes.dex')
- bak_smali_path = path_utils.get_baksmali_path()
- out_smali_path = os.path.join(temp_gen_path, 'out')
- ret = exec_jar_cmd(bak_smali_path, 'd "%s" -o "%s"' % (out_dex, out_smali_path))
- if ret:
- return ret
- # 将生成的文件拷贝到目标目录
- print('copy all smali ...')
- temp_smali_path = os.path.join(channel_path, 'smali_classes2')
- ret = file_utils.copy_file_all_dir(out_smali_path, temp_smali_path, True)
- if ret:
- return ret
- def exec_jar_cmd(jar, params):
- cmd = 'java -jar "%s" %s' % (jar, params)
- print("[exec_jar_cmd]:%s" % cmd)
- ret, result = exec_common_cmd(cmd)
- return ret
- def exec_common_cmd(cmd, cd=None):
- """
- 执行cmd命令
- 返回值:None —— 子进程尚未结束;
- ==0 —— 子进程正常退出;
- > 0—— 子进程异常退出,return code对应于出错码;
- < 0—— 子进程被信号杀掉了。
- """
- '''print(cmd)
- p = os.popen(cmd)
- print(p.read())''
- '''
- try:
- s = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cd, encoding='utf-8')
- std_output, err_output = s.communicate()
- if platform.system() == 'Windows':
- std_output = std_output.decode('gbk')
- err_output = err_output.decode('gbk')
- '''
- None —— 子进程尚未结束;
- ==0 —— 子进程正常退出;
- > 0—— 子进程异常退出,return code对应于出错码;
- < 0—— 子进程被信号杀掉了。
- '''
- ret = s.returncode
- if ret:
- print('*******ERROR*******')
- print(std_output)
- print(err_output)
- print('*******************')
- cmd = 'error::' + cmd + ' !!!exec Fail!!! '
- else:
- print(std_output)
- print(err_output)
- cmd = cmd + ' !!!exec success!!! '
- print(cmd)
- except Exception as e:
- print(e)
- return 1, e
- return ret, std_output
- def remove_app_name(pretreatment_project):
- value_xml = os.path.join(pretreatment_project, 'res', 'values', 'values.xml')
- print('remove app_name for :%s' % value_xml)
- fromTree = ET.parse(value_xml)
- fromRoot = fromTree.getroot()
- for child in list(fromRoot):
- if child.get('name') == 'app_name':
- print('remove app name')
- fromRoot.remove(child)
- fromTree.write(value_xml, 'UTF-8')
- def execute_gradlew(sdk_dependencies_gralde_path, pretreatment_project):
- sdk_dependencies_gralde_dir = os.path.dirname(sdk_dependencies_gralde_path)
- dependencies_libs_dir = os.path.join(sdk_dependencies_gralde_dir, "libs")
- os.makedirs(dependencies_libs_dir)
- cmd = '''
- cd %s && \
- /opt/mixsdk/tool/gradle/gradle-7.5.1/bin/gradle copyToLibs \
- ''' % sdk_dependencies_gralde_dir
- print(cmd)
- ret, result = exec_common_cmd(cmd)
- if not ret:
- decompile_aar(dependencies_libs_dir, pretreatment_project)
- if __name__ == "__main__":
- update()
|