diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index fe54ad6..a77fe96 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -257,15 +257,32 @@ jobs: run: | SET PKG_CONFIG_PATH=%CD%\clib\lib\pkgconfig SET CMAKE_PREFIX_PATH=%CD%\clib - SET PATH=%PATH%;%CD%\clib\bin + SET PATH=%CD%\clib\bin;%PATH% SET OPENSSL_LIB_DIR=%CD%\clib\lib SET OPENSSL_INCLUDE_DIR=%CD%\clib\include cargo build --features all --profile release-with-debug -vv || exit 1 + - name: Download lld-rust + continue-on-error: true + run: | + COPY /Y scripts\download_lld-rust.bat || exit 1 + CALL download_lld-rust.bat || exit 1 + - name: Package files + continue-on-error: true + run: | + SET PATH=%CD%\clib\bin;%PATH% + COPY /Y clib\ssl\cert.pem cert.pem + python scripts\pack_prog.py -o pixiv_downloader.7z -a cert.pem %CD%/target/release-with-debug/pixiv_downloader.exe || exit 1 + - name: Upload files + continue-on-error: true + uses: actions/upload-artifact@v3 + with: + name: pixiv_downloader + path: ./pixiv_downloader.7z - name: Test run: | SET PKG_CONFIG_PATH=%CD%\clib\lib\pkgconfig SET CMAKE_PREFIX_PATH=%CD%\clib - SET PATH=%PATH%;%CD%\clib\bin + SET PATH=%CD%\clib\bin;%PATH% SET OPENSSL_LIB_DIR=%CD%\clib\lib SET OPENSSL_INCLUDE_DIR=%CD%\clib\include cargo test --features all --profile release-with-debug --verbose -- --show-output || exit 1 diff --git a/scripts/download_lld-rust.bat b/scripts/download_lld-rust.bat new file mode 100644 index 0000000..d4c5dab --- /dev/null +++ b/scripts/download_lld-rust.bat @@ -0,0 +1,11 @@ +@ECHO OFF +SETLOCAL +SET TOP=%CD% +SET SCRIPTS_DIR=%CD%\scripts +SET DOWNLOAD_RESOURCE=%SCRIPTS_DIR%\download_resource.bat +SET VERSION=v0.0.2 +SET FILE=ldd-x86_64-msvc-%VERSION%.7z +CALL %DOWNLOAD_RESOURCE% -o "%FILE%" "https://github.com/lifegpc/ldd-rust/releases/download/%VERSION%/%FILE%" || EXIT %ERRORLEVEL% +7z e "%FILE%" ldd.exe || EXIT %ERRORLEVEL% +MOVE /Y ldd.exe ldd-rust.exe || EXIT %ERRORLEVEL% +ENDLOCAL diff --git a/scripts/pack_prog.py b/scripts/pack_prog.py new file mode 100644 index 0000000..e65870b --- /dev/null +++ b/scripts/pack_prog.py @@ -0,0 +1,225 @@ +from argparse import ArgumentParser, Namespace +from os import system, devnull, environ, remove, makedirs, listdir, chdir +from typing import List +from sys import exit +from subprocess import Popen, PIPE +from re import search, IGNORECASE +from os.path import splitext, exists, abspath, isdir, isfile, join, basename +from tempfile import NamedTemporaryFile +from shutil import copy2 + + +def add_path_ext(path: str) -> str: + p, n = splitext(path) + if n != '': + return path + else: + pext = environ['PATHEXT'] + pextl = pext.split(';') + for ext in pextl: + if ext == '': + continue + if exists(p + ext): + return p + ext + return path + + +def check_pdb(path: str) -> str: + p = splitext(path)[0] + '.pdb' + if exists(p): + return p + + +def check_needed_prog(): + if system(f'ldd-rust --help > {devnull}'): + return False + if system(f'7z --help > {devnull}'): + return False + return True + + +def check_prog(prog: str) -> List[str]: + r = Popen(f'ldd-rust {prog}', stdout=PIPE, stderr=PIPE) + out: bytes = r.communicate()[0] + r.wait() + out += r.communicate()[0] + if not r.returncode: + sl = out.splitlines(False) + rl = [] + for r in sl: + r = r.decode() + rs = search(r'=?-?> (.+) ?(\(0x[0-9a-f]+\))?$', r, IGNORECASE) + if rs is not None: + rl.append(abspath(rs.groups()[0])) + else: + raise ValueError(f'Can not find path for {r}.') + return rl + return None + + +def getUnixPath(path: str) -> str: + rs = search(r'^[A-Z]:', path, IGNORECASE) + if rs is None: + return path.replace('\\', '/') + return '/' + path[0].lower() + path[2:].replace('\\', '/') + + +def getWindowsPath(path: str) -> str: + rs = search(r'^[\\/][A-Z][\\/]', path, IGNORECASE) + if rs is None: + return path.replace('/', '\\') + return path[1].upper() + ":" + path[2:].replace('/', '\\') + + +def listdirs(loc: str, ignore_hidden_files: bool = True): + bl = listdir(loc) + r = [] + for i in bl: + if i.startswith('.'): + if ignore_hidden_files or i == '.' or i == '..': + continue + p = join(loc, i) + if isfile(p): + r.append(p) + elif isdir(p): + r += listdirs(p) + return r + + +def remove_dirs(loc: str): + bl = listdirs(loc, False) + for i in bl: + if isfile(i): + remove(i) + elif isdir(i): + try: + remove_dirs(i) + except Exception: + remove_dirs(i) + remove(loc) + +class Prog: + def __init__(self): + self._loc = [] + self.strip = False + self.num_cpu = None + + def add_dep(self, path: str): + path_w = getWindowsPath(path) + if path_w.upper().startswith('C:\\WINDOWS'): + return + if path_w not in self._loc: + print(f'add dependence: "{path_w}"') + self._loc.append(path_w) + + def add_prog(self, path: str): + pro = add_path_ext(path) + pro_w = getWindowsPath(pro) + if pro_w not in self._loc: + print(f'add program: "{pro_w}"') + self._loc.append(pro_w) + + def add_file(self, path: str): + p = getWindowsPath(path) + if p not in self._loc: + print(f'add file: "{p}"') + self._loc.append(p) + + def to_7z(self, output: str): + if self.strip: + makedirs('temp', exist_ok=True) + p = NamedTemporaryFile(delete=False) + for i in self._loc: + if not self.strip: + p.write((i + '\n').encode('UTF8')) + else: + bn = basename(i) + dest = f'temp/{bn}' + print(f'Copying {i} to {dest}') + copy2(i, dest) + pr = Popen(['strip', dest]) + pr.wait() + p.write((bn + '\n').encode('UTF8')) + fp = p.name + p.close() + output = getWindowsPath(abspath(output)) + try: + num_cpu = '' if self.num_cpu is None else f' -mmt{self.num_cpu}' + if self.strip: + chdir('temp') + system(f'7z a{num_cpu} -mx9 -y {output} @{fp}') + if self.strip: + chdir('../') + except Exception: + remove(fp) + from traceback import print_exc + print_exc() + remove(fp) + if self.strip: + try: + remove_dirs('temp') + except Exception: + pass + + +def main(prog: List[str], output: str = None, adds: List[str] = None, + pdbs: List[str] = None, args: Namespace = None): + if output is None: + output = 'programs.7z' + if not check_needed_prog(): + print('ldd and 7z is needed.') + p = Prog() + for pro in prog: + pro = abspath(pro) + pro = add_path_ext(pro) + # pro_u = getUnixPath(pro) + rel = check_prog(pro) + if rel is None: + print(f'Can not get dependencies for {pro},') + exit(-1) + p.add_prog(pro) + for i in rel: + p.add_dep(i) + if adds is not None: + for f in adds: + p.add_file(f) + if pdbs: + for i in pdbs: + pro = abspath(i) + pro = add_path_ext(pro) + rel = check_prog(pro) + if rel is None: + print(f'Can not get dependencies for {pro},') + exit(-1) + fn = check_pdb(pro) + if fn: + p.add_file(fn) + for i in rel: + fn = check_pdb(i) + if fn: + p.add_file(fn) + if args and args.strip: + p.strip = True + if args and args.num_cpu: + p.num_cpu = args.num_cpu + p.to_7z(output) + + +p = ArgumentParser(description='Pack programs into a 7-zip file.') +p.add_argument('PROG', action='append', help='Program to pack', nargs='*', default=[]) +p.add_argument('-p', '--pdb', action='append', help='Program\'s PDB file to pack', default=[], dest='pdb') +p.add_argument('-o', '--output', help='Output file', default='programs.7z', dest='output') +p.add_argument('-a', '--add', action='append', help='Additional files to pack', default=[], dest='add') +p.add_argument('-s', '--strip', help='Strip before package', default=False, action='store_true', dest='strip') +p.add_argument('-n', '--num_cpu', help='Number of CPU to use', default=None, type=int, dest='num_cpu') + + +if __name__ == '__main__': + try: + args = p.parse_args() + main(args.PROG[0], args.output, args.add, args.pdb, args) + except Exception: + from traceback import print_exc + from sys import exit + print_exc() + exit(-1)