Преглед на файлове

【软件版本】
V
【模块名称】
APK自动编译脚本
【问题原因】
win_subprocess.py脚本为CreateProcessW版本。
【修改描述】

【测试结果】

sat23 преди 3 години
родител
ревизия
38bfb18c72
променени са 4 файла, в които са добавени 605 реда и са изтрити 0 реда
  1. BIN
      APK自动编译/apk-release.xlsx
  2. 235 0
      APK自动编译/app-auto-release.py
  3. 215 0
      APK自动编译/basessh2.py
  4. 155 0
      APK自动编译/win_subprocess.py

BIN
APK自动编译/apk-release.xlsx


+ 235 - 0
APK自动编译/app-auto-release.py

@@ -0,0 +1,235 @@
+# -*- coding:utf-8 -*-
+# 请使用Python 2.7版本运行;
+from __future__ import unicode_literals
+import os
+import re  # 正则表达式;
+import sys
+import time
+import datetime
+import shutil
+from basessh2 import baseSSH2
+import subprocess
+import xlrd
+import xlwt
+from xlwt import XFStyle, Pattern
+import win_subprocess #此版本对应宽字节,subprocess为acssi;
+
+class ExcelParser:
+    def __init__(self, excelPath):
+        self.__xlspath = excelPath
+        self.xlsData = []
+
+    def readExcel(self, excelPath=None):
+        if excelPath is not None:
+            if type(excelPath) == str:
+                self.__xlspath = excelPath.decode('utf-8')
+
+        '文件是否存在'
+        if not os.path.exists(self.__xlspath):
+            return False
+
+        '打开excel文件'
+        wb = xlrd.open_workbook(filename=self.__xlspath)
+        if wb is None:
+            return False
+
+        '获取所有sheet'
+        sheet = None
+        for item in wb.sheet_names():
+            sheet = wb.sheet_by_name(item)
+            self.paserExcel(sheet)
+
+    def paserExcel(self, sheet):
+        if sheet.name == "release":
+            for i in range(1, sheet.nrows):
+                rowDict = {}
+                '获取每行内容:url\版本号\debug svn revision\src svn revision\是否释放'
+                rowData = tuple(sheet.row_values(i))
+                if rowData.__len__() == 5:
+                    if rowData[0].__len__() > 0:
+                        # 需要去除空格;
+                        rowDict['url'] = rowData[0].strip()
+                        rowDict['apk_version'] = str(rowData[1]).strip()
+                        rowDict['apk_revision'] = str(rowData[2]).strip('.0')
+                        rowDict['src_revision'] = str(rowData[3]).strip('.0')
+                        rowDict['sqa_result'] = rowData[4].strip('.0')
+                        self.xlsData.append(rowDict)
+                    # endif
+                # endif
+            # endfor
+        else:
+            print u"未找到对应的sheet名称:release"
+
+
+# 远程服务器SVN操作;
+class remoteSVN:
+    def __init__(self, host, user, pwd):
+        self.__user = user
+        self.__pwd = pwd
+        self.__host = host
+        self.__ssh2 = baseSSH2()
+        self.__ssh2.init_ssh2(user, pwd, host)
+
+    # 获取指定版本信息;
+    def getRevisionInfo(self, revision, url):
+        cmd = 'svn info -r %s %s' % (revision, url)
+        return self.__ssh2.execute_cmd(cmd)
+
+    # 获取指定版本log;
+    def getRevisionLog(self, revision, url):
+        cmd = 'svn log -r %s %s' % (revision, url)
+        return self.__ssh2.execute_cmd(cmd)
+
+    # 更新指定目录;
+    def update(self, dir, revision=''):
+        if revision.__len__() > 0:
+            cmd = 'svn up -r %s %s' % (revision, dir)
+        else:
+            cmd = 'svn up %s' % (dir)
+        return self.__ssh2.execute_cmd(cmd)
+
+    # 提交指定目录;
+    def commit(self):
+        pass
+
+
+# 本地SVN操作,必须安装命令行;
+class localSVN:
+    def __init__(self):
+        print 'local svn'
+
+    # 不使用重定向;
+    def __cmdExecute2(self, cmd):
+        # 定义文件名称;
+        file_name = str(time.time())
+        # 输出到文件中;
+        cmd = cmd + ' >> ' + file_name
+        print u'执行cmd:', cmd
+        proc = subprocess.Popen(cmd, shell=True)
+        # 等待完成;
+        proc.wait()
+        # 读取文件内容;
+        data = ''
+        with open(file_name, mode="rb") as f:
+            data = f.read()
+
+        os.remove(file_name)
+        print u"cmd结果:", data
+        return data
+
+    def __cmdExecute(self, cmd):
+        print u'执行cmd:', cmd
+        proc = win_subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
+        # 等待完成;
+        stdout, stderr = proc.communicate()
+        if proc.returncode == 0:
+            print u'执行成功'
+        # 返回执行结果;
+        # print u"__cmdExecute结果:%s" % stdout
+        return stdout
+
+    def revisionInfo(self, revision, url):
+        cmd = 'svn info -r %s %s' % (revision, url)
+        return self.__cmdExecute(cmd)
+
+    def revisionLog(self, revision, url):
+        cmd = 'svn log -r %s %s' % (revision, url)
+        return self.__cmdExecute(cmd)
+
+    def checkout(self, dir, url, revision=''):
+        # dir中文必须是unicode的转gbk;
+        if revision.__len__() > 0:
+            cmd = 'svn co -r %s %s %s' % (revision, url, dir)
+        else:
+            cmd = 'svn co %s %s' % (url, dir)
+        return self.__cmdExecute(cmd)
+
+    def export(self, dir, url, revision=''):
+        # dir中文必须是unicode的转gbk;
+        if revision.__len__() > 0:
+            cmd = 'svn export -r %s %s %s' % (revision, url, dir.encode("gbk"))
+        else:
+            cmd = 'svn export %s %s' % (url, dir.encode("gbk"))
+        return self.__cmdExecute(cmd)
+
+    def update(self, dir, revision=''):  # revision只有根目录才有效,即有.svn的目录才有效;
+        dir = dir.replace('\\', '/')
+        # dir中文必须是unicode的转gbk;
+        if revision.__len__() > 0:
+            cmd = 'svn up %s -r %s' % (dir, revision)
+        else:
+            cmd = 'svn up %s' % (dir)
+        return self.__cmdExecute(cmd)
+
+    def revert(self, dir, revision=''):
+        # dir中文必须是unicode的转gbk;
+        if revision.__len__() > 0:
+            cmd = 'svn revert -r %s %s' % (revision, dir)
+        else:
+            cmd = 'svn revert %s' % (dir)
+        return self.__cmdExecute(cmd.encode("gbk"))
+
+    def add(self, file):
+        # file中文必须是unicode的转gbk;
+        cmd = 'svn add %s' % (file.encode("gbk"))
+        return self.__cmdExecute(cmd)
+
+    def commit(self, file, desc):
+        # file/desc中文必须是unicode的转gbk;
+        cmd = "svn commit -m %s %s" % (desc.encode("gbk"), file.encode("gbk"))
+        return self.__cmdExecute(cmd)
+
+
+if __name__ == "__main__":
+    print "start main"
+    if 1:
+        'checkout或更新代码'
+        lsvn = localSVN()
+        # 注意:中文字符串,必须加u表示unicode;
+        debug_dir = u"F:\\Moka-Code\\APK自动编译相关\\debug"
+        debug_url = u"https://odm-design-center-hz.tclking.com/svn/scbc_apps/trunk/app/apk/01debug/general"
+        release_dir = u"F:\\Moka-Code\\APK自动编译相关\\release"
+        release_url = u"https://odm-design-center-hz.tclking.com/svn/scbc_apps/trunk/app/apk/02release/general"
+        '先Clean'
+        if os.path.exists(debug_dir + "\\.svn"):
+            lsvn.update(debug_dir)
+        else:
+            lsvn.checkout(debug_dir, debug_url)
+
+        '先Clean'
+        if os.path.exists(release_dir + "\\.svn"):
+            lsvn.update(release_dir)
+        else:
+            lsvn.checkout(release_dir, release_url)
+
+        '读取excel表记录'
+        excel = ExcelParser(u"F:\\Moka-Code\\APK自动编译相关\\apk-release.xlsx")
+        excel.readExcel()
+        print excel.xlsData
+        for item in excel.xlsData:
+            if item['sqa_result'] == u"释放":
+                '获取apk名称'
+                apk_name = item['url'][debug_url.__len__() + 1:-1]
+                '更新到释放的版本'
+                # lsvn.revert(debug_dir+"\\"+apk_name, item['apk_revision'])
+                lsvn.update(debug_dir + "\\" + apk_name, item['apk_revision'])
+            # endif
+        # endfor
+
+        # endfor
+    if 0:
+        rsvn = remoteSVN("10.201.44.65", "app", "app2021")
+        # bolean, data = getRevisionLog('10168', 'https://odm-design-center-hz.tclking.com/svn/scbc_apps/trunk/app/apk/01debug/general/TVMeetingMode')
+        # bolean,data = update('/home/data/ApkAutoCompile/cacheAPK/general$', '10168')
+        bolean, data = rsvn.update('/home/data/ApkAutoCompile/cacheAPK/general$')
+        print data
+    if 0:
+        lsvn = localSVN()
+        # data = lsvn.checkout(u'F:\\Moka-Code\\APK自动编译相关\\general','https://odm-design-center-hz.tclking.com/svn/scbc_apps/trunk/app/apk/01debug/general','10168')
+        # data = lsvn.export(u'F:\\Moka-Code\\APK自动编译相关\\general','https://odm-design-center-hz.tclking.com/svn/scbc_apps/trunk/app/apk/01debug/general')
+        # time.sleep(10)
+        # data = lsvn.update(u'F:\\Moka-Code\\APK自动编译相关\\general', '10168')
+        # data = lsvn.update(u'F:\\Moka-Code\\APK自动编译相关\\general', '')
+        data = lsvn.add(u'F:\\Moka-Code\\APK自动编译相关\\general\\1.txt')
+        data = lsvn.commit(u'F:\\Moka-Code\\APK自动编译相关\\general\\1.txt', u'测试')
+        print data

+ 215 - 0
APK自动编译/basessh2.py

@@ -0,0 +1,215 @@
+# -*- coding:utf-8 -*-
+import os
+import sys
+import time
+import datetime
+import socket
+import hashlib  # md5验证;
+from ssh2.session import Session
+from ssh2.sftp import LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR
+
+
+class baseSSH2:
+    def __init__(self):
+        self.__user = ""
+        self.__pwd = ""
+        self.__host = ""
+        self.__port = 22
+
+    '''
+    函数:创建tcp sock连接;
+    参数:无
+    返回:成功创建tcp连接返回sock,否则返回None
+    注意:创建成功后的sock,在外部使用完后必须调用sock.close()释放;
+    '''
+
+    def __create_sock(self):
+        # 创建tcp连接;
+        try:
+            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+            sock.connect((self.__host, int(self.__port)))  # 没有返回值;
+            return sock
+        except Exception, e:
+            print u'创建ssh2 socket失败', e
+            return None
+
+    '''
+    函数:初始化ssh2所需参数;
+    参数:user、pwd、host、port
+    返回:无
+    '''
+
+    def init_ssh2(self, user, pwd, host, port=22):
+        self.__host = host
+        self.__port = port
+        self.__user = user
+        self.__pwd = pwd
+
+    '''
+    函数:执行命令;
+    参数:cmd
+    返回:执行成功,返回True及执行结果;
+    '''
+
+    def execute_cmd(self, cmd):
+        print u"ssh2.execute_cmd=",cmd
+        sock = self.__create_sock()
+        if sock is None:
+            return False, ""
+
+        # 创建ssh2会话连接;
+        session = Session()
+        # 关联tcp socket;
+        if session.handshake(sock) is None:
+            sock.close()
+            print u'建立ssh2 会话失败'
+            return False, ""
+        # 以用户+密码方式建立认证连接;
+        if session.userauth_password(self.__user, self.__pwd) is None:
+            sock.close()
+            print u'登录ssh2 会话失败'
+            return False, ""
+
+        strdata = ''
+        # 创建会话通道;
+        channel = session.open_session()
+        channel.execute(cmd)
+        size, data = channel.read()
+        strdata = data
+        while size > 0:
+            size, data = channel.read()
+            strdata += data
+        channel.close()
+        # print("Exit status: %s" % channel.get_exit_status())
+        sock.close()
+
+        return True if channel.get_exit_status() == 0 else False, strdata
+
+    '''
+    函数:下载文件;
+    参数:ftp_path要下载的文件路径, local_path要保存的文件路径;
+    返回:执行成功,返回True;
+    '''
+
+    def sftp_download(self, ftp_path, local_path):
+        sock = self.__create_sock()
+        if sock is None:
+            return False
+
+        # 创建ssh2会话连接;
+        session = Session()
+        # 关联tcp socket;
+        if session.handshake(sock) is None:
+            sock.close()
+            print u'建立ssh2 会话失败'
+            return False
+        # 以用户+密码方式建立认证连接;
+        if session.userauth_password(self.__user, self.__pwd) is None:
+            sock.close()
+            print u'登录ssh2 会话失败'
+            return False
+
+        sftp = session.sftp_init()
+        # now = datetime.time()
+        # print("Starting read for remote file %s" % ftp_path)
+        try:
+            with sftp.open(ftp_path, LIBSSH2_FXF_READ, LIBSSH2_SFTP_S_IRUSR) as fh, open(local_path, 'wb+') as lh:
+                for size, data in fh:
+                    lh.write(data)
+                    # print 'download size=',size
+            lh.close()
+            # print("Finished file read in %s" % (datetime.time() - now))
+            sock.close()
+            return True
+        except Exception, e:
+            print u'下载失败:',e
+            return False
+
+    '''
+    函数:计算文件md5值;
+    参数:file要计算的文件路径;
+    返回:执行成功返回md5值,否则返回None;
+    '''
+
+    def get_md5_sum(self, sftp_file):
+        cmd = 'md5sum %s' % sftp_file
+        boolean, data = self.execute_cmd(cmd)
+        if boolean is False:
+            return None
+        # 分组;
+        str_list = data.split('  ')
+        if str_list.__len__() != 2:
+            return None
+
+        return str_list[0]
+
+    '''
+    函数:下载文件,并验证md5是否正确;
+    参数:ftp_path要下载的文件路径, local_path要保存的文件路径;
+    返回:执行成功,返回True;
+    '''
+    def sftp_download_md5(self, ftp_path, local_path):
+        # 先计算md5值;
+        sftp_md5 = self.get_md5_sum(ftp_path)
+        if sftp_md5 is None:
+            print 'sftp_md5值空'
+            return False
+        
+        # 删除本地文件;
+        try:
+            os.remove(local_path)
+            time.sleep(10)
+        except Exception, e:
+            print u"删除文件失败:",e
+        
+        print "SFTP MD5=", sftp_md5
+        # 下载文件后计算;
+        if self.sftp_download(ftp_path, local_path) is True:
+            local_md5 = ""
+            md5_local = hashlib.md5()
+            time.sleep(5)
+            file_data = []
+            if os.path.exists(local_path):
+                with open(local_path, mode='rb') as f:
+                    while True:
+                        data = f.read(8192)
+                        if not data:
+                            break
+                        md5_local.update(data)
+                local_md5 = md5_local.hexdigest()
+                print u"本地MD5=", local_md5
+
+            return True if sftp_md5 == local_md5 else False
+
+        print u'下载文件失败'
+        return False
+
+def API_sftp_download(host, user, pwd, ftp_path, ftp_file, local_path, local_file):
+    myssh2 = baseSSH2()
+    myssh2.init_ssh2(user, pwd, host)
+    # 判断ftp上的文件是否存在;
+    cmd = "ls %s" % ftp_path
+    status, data = myssh2.execute_cmd(cmd)
+    if status is True and data.find(ftp_file) >= 0:
+        # 下载后验证md5是否正确;
+        return myssh2.sftp_download_md5(ftp_path + ftp_file, local_path + local_file)
+    return False
+        
+
+
+if __name__ == "__main__":
+    host = "10.201.251.254"
+    user = "wjf"
+    pwd = "wjf2019"
+    myssh2 = baseSSH2()
+    myssh2.init_ssh2(user, pwd, host)
+    # cmd = "md5sum /home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip"
+    # bolean, data = myssh2.execute_cmd(cmd)
+    # print data.split('  ')[0]
+    # print u'MD5值=', myssh2.get_md5_sum(
+    #     "/home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip")
+    # myssh2.sftp_download("rt2851/Buildimg/V8-T841T01-LF1V001/Images/USB/build.prop", "D:\\sat\\build.prop")
+    # myssh2.sftp_download("/home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip", "D:\\sat\\a.img")
+    if myssh2.sftp_download_md5("/home/RT2841_2851_dailybuild/DailyBuild_RT2851_0509/signed-ota_rt2851_update.zip", "D:\\sat\\b.img"):
+        print u"下载文件成功"
+        

+ 155 - 0
APK自动编译/win_subprocess.py

@@ -0,0 +1,155 @@
+import ctypes
+import subprocess
+import _subprocess
+import os
+from ctypes import byref, windll, c_char_p, c_wchar_p, c_void_p, \
+    Structure, sizeof, c_wchar, WinError
+from ctypes.wintypes import BYTE, WORD, LPWSTR, BOOL, DWORD, LPVOID, \
+    HANDLE
+
+##
+## Types
+##
+
+CREATE_UNICODE_ENVIRONMENT = 0x00000400
+LPCTSTR = c_char_p
+LPTSTR = c_wchar_p
+LPSECURITY_ATTRIBUTES = c_void_p
+LPBYTE = ctypes.POINTER(BYTE)
+
+
+class STARTUPINFOW(Structure):
+    _fields_ = [
+        ("cb", DWORD), ("lpReserved", LPWSTR),
+        ("lpDesktop", LPWSTR), ("lpTitle", LPWSTR),
+        ("dwX", DWORD), ("dwY", DWORD),
+        ("dwXSize", DWORD), ("dwYSize", DWORD),
+        ("dwXCountChars", DWORD), ("dwYCountChars", DWORD),
+        ("dwFillAtrribute", DWORD), ("dwFlags", DWORD),
+        ("wShowWindow", WORD), ("cbReserved2", WORD),
+        ("lpReserved2", LPBYTE), ("hStdInput", HANDLE),
+        ("hStdOutput", HANDLE), ("hStdError", HANDLE),
+    ]
+
+
+LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW)
+
+
+class PROCESS_INFORMATION(Structure):
+    _fields_ = [
+        ("hProcess", HANDLE), ("hThread", HANDLE),
+        ("dwProcessId", DWORD), ("dwThreadId", DWORD),
+    ]
+
+
+LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION)
+
+
+class DUMMY_HANDLE(ctypes.c_void_p):
+
+    def __init__(self, *a, **kw):
+        super(DUMMY_HANDLE, self).__init__(*a, **kw)
+        self.closed = False
+
+    def Close(self):
+        if not self.closed:
+            windll.kernel32.CloseHandle(self)
+            self.closed = True
+
+    def __int__(self):
+        return self.value
+
+
+CreateProcessW = windll.kernel32.CreateProcessW
+CreateProcessW.argtypes = [
+    LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES,
+    LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR,
+    LPSTARTUPINFOW, LPPROCESS_INFORMATION,
+]
+CreateProcessW.restype = BOOL
+
+
+##
+## Patched functions/classes
+##
+
+def CreateProcess(executable, args, _p_attr, _t_attr,
+                  inherit_handles, creation_flags, env, cwd,
+                  startup_info):
+    """Create a process supporting unicode executable and args for win32
+
+    Python implementation of CreateProcess using CreateProcessW for Win32
+
+    """
+
+    si = STARTUPINFOW(
+        dwFlags=startup_info.dwFlags,
+        wShowWindow=startup_info.wShowWindow,
+        cb=sizeof(STARTUPINFOW),
+        ## XXXvlab: not sure of the casting here to ints.
+        hStdInput=int(startup_info.hStdInput),
+        hStdOutput=int(startup_info.hStdOutput),
+        hStdError=int(startup_info.hStdError),
+    )
+
+    wenv = None
+    if env is not None:
+        ## LPCWSTR seems to be c_wchar_p, so let's say CWSTR is c_wchar
+        env = (unicode("").join([
+            unicode("%s=%s\0") % (k, v)
+            for k, v in env.items()])) + unicode("\0")
+        wenv = (c_wchar * len(env))()
+        wenv.value = env
+
+    pi = PROCESS_INFORMATION()
+    creation_flags |= CREATE_UNICODE_ENVIRONMENT
+
+    if CreateProcessW(executable, args, None, None,
+                      inherit_handles, creation_flags,
+                      wenv, cwd, byref(si), byref(pi)):
+        return (DUMMY_HANDLE(pi.hProcess), DUMMY_HANDLE(pi.hThread),
+                pi.dwProcessId, pi.dwThreadId)
+    raise WinError()
+
+
+class Popen(subprocess.Popen):
+    """This superseeds Popen and corrects a bug in cPython 2.7 implem"""
+
+    def _execute_child(self, args, executable, preexec_fn, close_fds,
+                       cwd, env, universal_newlines,
+                       startupinfo, creationflags, shell, to_close,
+                       p2cread, p2cwrite,
+                       c2pread, c2pwrite,
+                       errread, errwrite):
+        """Code from part of _execute_child from Python 2.7 (9fbb65e)
+
+        There are only 2 little changes concerning the construction of
+        the the final string in shell mode: we preempt the creation of
+        the command string when shell is True, because original function
+        will try to encode unicode args which we want to avoid to be able to
+        sending it as-is to ``CreateProcess``.
+
+        """
+        if not isinstance(args, subprocess.types.StringTypes):
+            args = subprocess.list2cmdline(args)
+
+        if startupinfo is None:
+            startupinfo = subprocess.STARTUPINFO()
+        if shell:
+            startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW
+            startupinfo.wShowWindow = _subprocess.SW_HIDE
+            comspec = os.environ.get("COMSPEC", unicode("cmd.exe"))
+            args = unicode('{} /c "{}"').format(comspec, args)
+            if (_subprocess.GetVersion() >= 0x80000000 or
+                    os.path.basename(comspec).lower() == "command.com"):
+                w9xpopen = self._find_w9xpopen()
+                args = unicode('"%s" %s') % (w9xpopen, args)
+                creationflags |= _subprocess.CREATE_NEW_CONSOLE
+
+        super(Popen, self)._execute_child(args, executable,
+                                          preexec_fn, close_fds, cwd, env, universal_newlines,
+                                          startupinfo, creationflags, False, to_close, p2cread,
+                                          p2cwrite, c2pread, c2pwrite, errread, errwrite)
+
+
+_subprocess.CreateProcess = CreateProcess