import os import os.path import shutil import subprocess import platform import sys import hashlib def getFullToolPath(name): ''' 获取工具的目录 ''' return getFullPath('tools', name) def getFullGameApk(name): ''' 获取游戏的原始包 ''' return getFullPath('game', name, name + '.apk') def getFullSDKPath(sdk): ''' 获取sdk的目录 ''' return getFullPath('sdk', sdk) def getFullLogSDKPath(sdk): ''' 获取logsdk的目录 ''' return getFullPath('log_sdk', sdk) def getFullOaidSDKPath(version): ''' 获取logsdk的目录 ''' return getFullPath('oaid_sdk', version) def getDecompliePath(game, sdk, subChannel, cache): ''' 获取解包的目录 ''' return getFullPath('gen', game, sdk, subChannel, cache) def getSubChannelPath(game, sdk, subChannel): ''' 获取子渠道的目录 ''' return getFullPath('game', game, sdk, subChannel) def getChannelPath(game, sdk): ''' 获取渠道的目录 ''' return getFullPath('game', game, sdk) def getFullGamePath(game): ''' 获取游戏的目录 ''' return getFullPath('game', game) def getFullInternalPath(): ''' 获取内部目录 ''' return os.path.join(getCurrentPath(), 'internal') def getFullPath(type, *name): path = os.path.join(getCurrentPath(), type) for n in name: path = os.path.join(path, str(n)) return path def getCurrentPath(): ''' 当前目录 ''' return sys.path[0] def execFormatCmd(cmd, cd = None): ''' 执行cmd命令 返回值:None —— 子进程尚未结束; ==0 —— 子进程正常退出; > 0—— 子进程异常退出,returncode对应于出错码; < 0—— 子进程被信号杀掉了。 ''' '''print(cmd) p = os.popen(cmd) print(p.read())''' ret = 0 try: s = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, cwd=cd) stdoutput, erroutput = s.communicate() if platform.system() == 'Windows': stdoutput = stdoutput.decode('gbk') erroutput = erroutput.decode('gbk') ''' None —— 子进程尚未结束; ==0 —— 子进程正常退出; > 0—— 子进程异常退出,returncode对应于出错码; < 0—— 子进程被信号杀掉了。 ''' ret = s.returncode if ret: print('*******ERROR*******') print(stdoutput) print(erroutput) print('*******************') cmd = 'error::' + cmd + ' !!!exec Fail!!! ' else: print(stdoutput) print(erroutput) cmd = cmd + ' !!!exec success!!! ' print(cmd) except Exception as e: print('Exception ' + e) return 1 return ret def execJarCmd(jar, params): ''' 执行jar ''' return execFormatCmd('java -jar "%s" %s' % (jar, params)) def copyFileAllDir(fromDir, toDir, delete = False, support = None): ''' 拷贝目录下所有文件文件 ''' #print('copy all file %s --> %s' % (fromDir, toDir)) ret = copyDir(fromDir, toDir, delete, support) if ret: return ret if delete: deleteFolder(fromDir) return 0 def copyDir(fromDir, toDir, delete = False, support = None): ''' 拷贝目录下所有文件文件 ''' #print('copy all file %s --> %s' % (fromDir, toDir)) if not os.path.exists(fromDir): print('%s not exists!' % fromDir) return 1 if not os.path.isdir(fromDir): print('%s not a dir!' % fromDir) return 1 for fromFile in os.listdir(fromDir): fromFilePath = os.path.join(fromDir, fromFile) toFilePath = os.path.join(toDir, fromFile) if os.path.isdir(fromFilePath): # 不支持的类型 if support is not None and fromFile not in support: continue ret = copyDir(fromFilePath, toFilePath, delete, support) if ret: return ret else: ret = copyFile(fromFilePath, toFilePath, delete) if ret: return ret return 0 def copyFile(formFile, toFile, delete = False): ''' 拷贝文件 ''' if not os.path.isfile(formFile): print('----> %s not a file!' % formFile) return 1 fpath, fname = os.path.split(toFile) #分离文件名和路径 if not os.path.exists(fpath): #print('%s not exists, crate' % fpath) os.makedirs(fpath) #创建路径 '''if os.path.exists(toFile) and os.path.getsize(formFile) > 104857600 and equalsFile(formFile, toFile): print('skip copy %s --> %s' % (formFile, toFile)) if delete: os.remove(formFile) print('remove %s' % formFile) return 0''' if delete: shutil.move(formFile, toFile) #移动文件 #print('move %s --> %s' % (formFile, toFile)) else: shutil.copyfile(formFile, toFile) #复制文件 #print('copy %s --> %s' % (formFile, toFile)) return 0 def replaceContent(file, oldText, newText): ''' 全局替换 ''' with openFile(file, 'r+') as f: t = f.read() t = t.replace(oldText, newText) #读写偏移位置移到最开始处 f.seek(0, 0) #清空内容 f.truncate() f.write(t) def readFile(file): ''' 读取文件内容 ''' content = '' with openFile(file, 'r') as f: content = f.read() return content def createFile(file, content): ''' 创建文件 ''' fpath, fname = os.path.split(file) #分离文件名和路径 if not os.path.exists(fpath): os.makedirs(fpath) with openFile(file, 'w') as f: f.write(content) f.close() def openFile(file, mode): return open(file, mode, encoding='UTF-8') def deleteFolder(folder): ''' 删除目录以及目录下的文件 ''' if not os.path.exists(folder): return #print('remove %s ...' % folder) shutil.rmtree(folder) #print('removed %s' % folder) def getAAPTPath(): ''' 获取aapt ''' return getToolWithSystem('aapt') def getAAPT2Path(): ''' 获取aapt2 ''' return getToolWithSystem('aapt2') def getAndroidCompileToolPath(): ''' 获取android.jar ''' return getFullToolPath('android.jar') def getDxPath(): ''' 获取dx.jar ''' return getFullToolPath('dx.jar') def getD8Path(): ''' 获取d8.jar ''' return getFullToolPath('d8.jar') def getAlignPath(): ''' 获取zipalign ''' return getToolWithSystem('zipalign') def getMultiDexPath(): ''' 获取multidex.jar ''' return getFullToolPath('android-support-multidex.jar') def getApkToolPath(): ''' 获取apktool.jar ''' return getFullToolPath('apktool_2.4.0.jar') def getBaksmaliPath(): ''' 获取baksmali.jar ''' return getFullToolPath('baksmali-2.3.jar') def getApksignerPath(): ''' 获取apksigner.jar ''' return getFullToolPath('apksigner.jar') def getWallePath(): ''' 获取walle-cli-all.jar ''' return getFullToolPath('walle-cli-all.jar') def getOutApkPath(game, sdk, subChannel, cache): ''' 获取输出的apk的目录 ''' return getFullPath('target', game, sdk, cache, '%s_%s_%s.apk' % (game, sdk, subChannel)) def getAlignApkPath(game, sdk, subChannel, cache): ''' 获取输出的apk的目录 ''' return getFullPath('target', game, sdk, cache, '%s_%s_%s_align.apk' % (game, sdk, subChannel)) def getRenameApkPath(game, sdk, cache, name): ''' 重命名输出的apk名称 ''' return getFullPath('target', game, sdk, cache, '%s.apk' % name) def getSignApkPath(game, sdk, subChannel, cache): ''' 获取输出的apk的目录 ''' return getFullPath('target', game, sdk, cache, '%s_%s_%s_signed.apk' % (game, sdk, subChannel)) def getPackagePath(basePath, packageName): ''' 包名对应的目录 ''' packageNameSplit = packageName.split('.') newPath = basePath for item in packageNameSplit: newPath = os.path.join(newPath, item) return newPath def getToolWithSystem(tool): ''' 获取系统相关工具 ''' system = getSystemPath() suffix = getSystemSuffix() return os.path.join(getFullToolPath(system), tool + suffix) def getSystemPath(): ''' 获取系统目录 ''' if platform.system() == 'Windows': return 'win' elif platform.system() == 'Darwin': print ('---------macos----------') return 'macos' else: return 'linux' def getSystemSuffix(): ''' 系统工具后缀 ''' if platform.system() == 'Windows': return '.exe' else: return '' def getApplicationSmaliIndex(file): ''' 获取application.smali的MultiDex信息 ''' content = ('invoke-super', '->attachBaseContext(Landroid/content/Context;)V') index = -1 with openFile(file, 'r') as f: line = f.readline() while line: if content[0] in line and content[1] in line: index = f.tell() break line = f.readline() return index def insertApplicationSmali(file, index): ''' 获取application.smali插入MultiDex的信息 ''' if index == -1: # append content = '''# virtual methods .method protected attachBaseContext(Landroid/content/Context;)V .locals 0 .param p1, "context" # Landroid/content/Context; invoke-super {p0, p1}, Landroid/app/Application;->attachBaseContext(Landroid/content/Context;)V invoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V return-void .end method''' with openFile(file, 'a') as f: f.write(content) else: # insert content = '\n\tinvoke-static {p0}, Landroid/support/multidex/MultiDex;->install(Landroid/content/Context;)V\n' with openFile(file, 'r+') as f: f.seek(index) last = f.read() f.seek(index) f.write(content) f.write(last) return 0 def changeVersion(yml, versionCode = None, versionName = None, targetSdkVersion = None): if versionCode is None and versionName is None and targetSdkVersion is None: return 0 tag = ['versionCode:', 'versionName:', 'targetSdkVersion:'] with openFile(yml, 'r+') as f: content = '' line = f.readline() while line: if versionCode is not None and tag[0] in line: content += ' versionCode: \'%s\'\n' % versionCode elif versionName is not None and tag[1] in line: content += ' versionName: %s\n' % versionName elif targetSdkVersion is not None and tag[2] in line: content += ' targetSdkVersion: \'%s\'\n' % targetSdkVersion else: content += line line = f.readline() f.seek(0, 0) f.truncate() f.write(content) return 0 def changeMinSdkVersion(yml, minSdkVersion): with openFile(yml, 'r+') as f: content = '' line = f.readline() while line: if 'minSdkVersion' in line: start = line.index("'") version = int(line[start+1:-2]) if version < minSdkVersion: content += ' minSdkVersion: \'%s\'\n' % minSdkVersion else: return 0 else: content += line line = f.readline() f.seek(0, 0) f.truncate() f.write(content) return 0 def list_files(src, resFiles): ''' 列出目录下所有的文件 ''' if os.path.exists(src): if os.path.isfile(src): resFiles.append(src) elif os.path.isdir(src): for f in os.listdir(src): list_files(os.path.join(src, f), resFiles) return resFiles def getFileMd5(f): ''' 获取文件的md5 ''' m = hashlib.md5() while True: data = f.read(1024) #将文件分块读取 if not data: break m.update(data) return m.hexdigest() def equalsFile(file1, file2): ''' 比较两个文件是否是同一个文件 ''' with open(file1, 'rb') as f1, open(file2, 'rb') as f2: file1Md5 = getFileMd5(f1) file2Md5 = getFileMd5(f2) #print('file1Md5:',file1Md5) #print('file2Md5:',file2Md5) return file1Md5 == file2Md5 def getExecPermission(file): ''' linux下获取执行权限 ''' if platform.system() == 'Windows': return 0 return execFormatCmd('chmod +x "%s"' % file) def copySdkSmaliCode(game, sdk, subChannel, config): ''' 拷贝代码 ''' print('copySdkSmaliCode --> %s' % sdk) sdkPath = getFullSDKPath(sdk) xmyFile = os.path.join(sdkPath, 'smali') decompliePath = getDecompliePath(game, sdk, subChannel, config['cache']) smaliPath = os.path.join(decompliePath, 'smali') ret = copyDir(xmyFile, smaliPath) return ret def copyGameSmaliCode(game, sdk, subChannel, config): ''' 拷贝代码 ''' print('copyGameSmaliCode --> %s' % sdk) if not config['aapt2disable']: print('aapt2disable = false ~~~') return 0 path = os.path.join(getCurrentPath(),"game_script",game,"gameSmali") print('copyDir = gameSmali~~~') decompliePath = getDecompliePath(game, sdk, subChannel, config['cache']) smaliPath = os.path.join(decompliePath, 'smali') ret = copyDir(path, smaliPath) return ret