Created
March 31, 2020 03:48
-
-
Save thilong/60dace8b17e21afed28ab521721ad15d to your computer and use it in GitHub Desktop.
把aar文件解包并合并的python脚本
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/local/bin/python3 | |
# -*- coding: utf-8 -*- | |
import sys, os, shutil, zipfile | |
import xml.etree.ElementTree as xmlDoc | |
from enum import Enum | |
ignoreFiles = ['R.txt', 'proguard.txt'] | |
def log(LEVEL, msg): | |
print('['+LEVEL+']\t'+msg) | |
def logFileContent(path): | |
content = '' | |
with open(path, 'r') as fp: | |
content = fp.read() | |
print('[文件]\t'+path + ' : ') | |
print(content) | |
class HMSFileType(Enum): | |
Unknown = 0 | |
Jar = 1 | |
AndroidManifest = 2 | |
R = 3 | |
Value = 4 | |
Regular = 5 | |
class HMSFile: | |
fileType = HMSFileType.Unknown #文件类型 | |
name = '' #文件名 | |
path = '' #相对于工作路径的路径 | |
copyTo = '' #拷贝到的目标路径 | |
def __init__(self): | |
self.fileType = HMSFileType.Unknown | |
self.name = '' | |
self.path = '' | |
self.copyTo = '' | |
def shouldIgnore(self) -> bool: | |
return self.name in ignoreFiles | |
def isCombineable(self) -> bool: | |
return self.fileType == HMSFileType.AndroidManifest or self.fileType == HMSFileType.Value | |
def process(self,rootPath) -> bool: | |
fullTo = os.path.join(rootPath, self.copyTo) | |
if self.shouldIgnore(): | |
log('忽略', self.path) | |
return True | |
if self.fileType == HMSFileType.AndroidManifest and os.path.exists(fullTo): | |
return self.process_AndroidManifest(rootPath) | |
if self.fileType == HMSFileType.Value and os.path.exists(fullTo): | |
return self.process_Values(rootPath) | |
fullFrom = os.path.join(rootPath, self.path) | |
fullToFolder = os.path.dirname(fullTo) | |
if not os.path.exists(fullToFolder): | |
os.makedirs(fullToFolder) | |
shutil.copy(fullFrom, fullTo) | |
return os.path.exists(fullTo) | |
def process_AndroidManifest(self,rootPath) -> bool: | |
log('合并', self.path) | |
fromPath = os.path.join(rootPath, self.path) | |
toPath = os.path.join(rootPath, self.copyTo) | |
fromTree = xmlDoc.parse(fromPath) | |
toTree = xmlDoc.parse(toPath) | |
toRoot = toTree.getroot() | |
logFileContent(fromPath) | |
for node in fromTree.getroot(): | |
if node.tag == 'uses-sdk': | |
continue | |
elif node.tag == 'application': | |
toAppNode = toRoot.find('application') | |
for appNode in node: | |
toAppNode.append(appNode) | |
else: | |
toRoot.append(node) | |
xmlStr = str(xmlDoc.tostring(toRoot, encoding='utf-8'), encoding='utf-8') | |
xmlStr = xmlStr.replace('xmlns:ns0', 'xmlns:android') | |
xmlStr = xmlStr.replace(' ns0:', ' android:') | |
with open(toPath, 'w+') as outputXmlFp: | |
outputXmlFp.write(xmlStr) | |
return True | |
def process_Values(self,rootPath) -> bool: | |
log('合并', self.path) | |
fromPath = os.path.join(rootPath, self.path) | |
toPath = os.path.join(rootPath, self.copyTo) | |
fromTree = xmlDoc.parse(fromPath) | |
toTree = xmlDoc.parse(toPath) | |
toRoot = toTree.getroot() | |
for node in fromTree.getroot(): | |
toRoot.append(node) | |
xmlStr = str(xmlDoc.tostring(toRoot, encoding='utf-8'), encoding='utf-8') | |
xmlStr = xmlStr.replace('xmlns:ns0', 'xmlns:android') | |
xmlStr = xmlStr.replace(' ns0:', ' android:') | |
with open(toPath, 'w+') as outputXmlFp: | |
outputXmlFp.write(xmlStr) | |
return True | |
class HMSLibraryType(Enum): | |
UNKNOWN = 0 | |
JAR = 1 | |
AAR = 2 | |
class HMSLibrary: | |
fileType = None | |
parentFolder = '' #所在文件夹 | |
name = '' #文件名 | |
nameWithoutExt = '' #无后缀的文件名 | |
extractFolder = '' #解压后的文件夹名 | |
files = [] #解压后需要处理的文件 | |
def __init__(self, path): | |
self.parentFolder = os.path.dirname(path) | |
self.name = os.path.basename(path) | |
self.files = [] | |
self.nameWithoutExt = '' | |
self.extractFolder = '' | |
if self.name.lower().endswith('.aar'): | |
self.fileType = HMSLibraryType.AAR | |
self.nameWithoutExt = self.name[:-4] | |
elif self.name.lower().endswith('jar'): | |
self.fileType = HMSLibraryType.JAR | |
self.nameWithoutExt = self.name[:-4] | |
else: | |
self.fileType = HMSLibraryType.UNKNOWN | |
def prepareForCopy(self) -> bool: | |
if self.fileType == HMSLibraryType.JAR: | |
child = HMSFile() | |
child.fileType = HMSFileType.Jar | |
child.name = self.name | |
child.copyTo = os.path.join('output/libs', self.name) | |
child.path = self.name | |
self.files.append(child) | |
return True | |
try: | |
extractPath = os.path.join(self.parentFolder, self.nameWithoutExt) | |
zf = zipfile.ZipFile(os.path.join(self.parentFolder, self.name)) | |
zf.extractall(path = extractPath) | |
self.extractFolder = self.nameWithoutExt | |
except: | |
return False | |
self.prepareFiles('') | |
return True | |
def prepareFiles(self, inFolder): | |
folderPath = os.path.join(self.parentFolder, self.extractFolder, inFolder) | |
for subFileName in os.listdir(folderPath): | |
subFilePath = os.path.join(folderPath, subFileName) | |
if os.path.isdir(subFilePath): | |
self.prepareFiles(os.path.join(inFolder, subFileName)) | |
elif os.path.isfile(subFilePath): | |
relativePath = os.path.join(inFolder, subFileName) | |
hfile = HMSFile() | |
hfile.name = subFileName | |
hfile.path = os.path.join(self.extractFolder,inFolder, subFileName) | |
hfile.copyTo = os.path.join('output/resources/', relativePath) | |
if relativePath == 'classes.jar': | |
hfile.copyTo = os.path.join('output/libs/', self.extractFolder + '.jar') | |
hfile.fileType = HMSFileType.Jar | |
elif relativePath == 'AndroidManifest.xml': | |
hfile.copyTo = os.path.join('output', 'AndroidManifest.xml') | |
hfile.fileType = HMSFileType.AndroidManifest | |
elif '/values' in relativePath: | |
hfile.fileType = HMSFileType.Value | |
else: | |
hfile.fileType = HMSFileType.Regular | |
self.files.append(hfile) | |
def cleanup(self): | |
if len(self.extractFolder) > 0: | |
shutil.rmtree(os.path.join(self.parentFolder, self.extractFolder), ignore_errors=True) | |
class HMSRepackApp: | |
workingRoot = '' | |
abortOnError = True | |
shouldCleanup = True | |
libraries = [] | |
completedFiles = [] | |
def __init__(self, pathToWork, abortOnError = True, shouldCleanup = True): | |
self.workingRoot = pathToWork | |
self.abortOnError = abortOnError | |
self.shouldCleanup = shouldCleanup | |
outputPath = os.path.join(self.workingRoot, 'output') | |
if os.path.exists(outputPath): | |
shutil.rmtree(outputPath, ignore_errors=True) | |
def prepareFiles(self) -> bool: | |
for fileName in os.listdir(self.workingRoot): | |
filePath = os.path.join(self.workingRoot, fileName) | |
if not os.path.isfile(filePath): | |
continue | |
lib = HMSLibrary(filePath) | |
if lib.fileType != HMSLibraryType.UNKNOWN: | |
self.libraries.append(lib) | |
if lib.prepareForCopy() == False and self.abortOnError: | |
self.cleanup() | |
return False | |
return True | |
def checkHasConflict(self, lfile) -> bool: | |
if lfile.isCombineable(): | |
return False | |
for cFile in self.completedFiles: | |
if cFile.copyTo == lfile.copyTo and not lfile.shouldIgnore(): | |
log('警告','文件拷贝存在冲突: ' + cFile.path + ' 与 ' + lfile.path) | |
return True | |
return False | |
def processFiles(self) -> bool: | |
for lib in self.libraries: | |
for lfile in lib.files: | |
if self.checkHasConflict(lfile): | |
if self.abortOnError: | |
return False | |
if lfile.process(self.workingRoot) == False: | |
log('错误','文件处理发生错误 ' + lfile.path) | |
if self.abortOnError: | |
return False | |
else: | |
self.completedFiles.append(lfile) | |
return True | |
#解决agcp与agconnect-core的类冲突 | |
def mod_agcp(self): | |
for fileName in os.listdir(os.path.join(self.workingRoot, 'output/libs')): | |
if not fileName.startswith('com.huawei.agconnect.agcp'): | |
continue | |
oldJarPath = os.path.join(self.workingRoot, 'output/libs', fileName) | |
newJarPath = os.path.join(self.workingRoot, 'output/libs', fileName[0:-4] + '.mod.jar') | |
log('修改', oldJarPath + ' -> ' + newJarPath) | |
zin = zipfile.ZipFile (oldJarPath, 'r') | |
zout = zipfile.ZipFile (newJarPath, 'w') | |
for item in zin.infolist(): | |
buffer = zin.read(item.filename) | |
if not item.filename.startswith('com/huawei/agconnect/config'): | |
zout.writestr(item, buffer) | |
zout.close() | |
zin.close() | |
os.remove(oldJarPath) | |
def run(self) -> bool: | |
ret = True | |
while True: | |
if not self.prepareFiles(): | |
ret = False | |
break | |
if not self.processFiles(): | |
ret = False | |
break | |
break | |
self.mod_agcp() | |
if self.shouldCleanup: | |
self.cleanup() | |
return ret | |
def cleanup(self): | |
for lib in self.libraries: | |
lib.cleanup() | |
app = HMSRepackApp('/Users/aidoo/Downloads/HMS_Eclipse', shouldCleanup=True) | |
app.run() | |
print('done') | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment