1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# 4# Hardlink only packages used in the build from one directory to another, 5# and updates the RemotePackageRepository file at the same time. 6# 7# Copyright 2017-2020 Augustin Cavalier <waddlesplash> 8# Distributed under the terms of the MIT License. 9 10import sys, os, subprocess, re, hashlib 11from distutils.version import LooseVersion 12 13if len(sys.argv) != 5: 14 print("usage: hardlink_packages.py [arch] [jam RemotePackageRepository file] " 15 + "[prebuilt packages directory] [destination root directory]") 16 print("note that the [jam RemotePackageRepository file] will be modified.") 17 print("note that [target directory] is assumed to have a 'packages' subdirectory, " 18 + " and a repo.info.template file (using $ARCH$)") 19 sys.exit(1) 20 21if subprocess.run(['package_repo'], None, None, None, 22 subprocess.DEVNULL, subprocess.PIPE).returncode != 1: 23 print("package_repo command does not seem to exist.") 24 sys.exit(1) 25 26args_arch = sys.argv[1] 27args_jamf = sys.argv[2] 28args_src = sys.argv[3] 29args_dst = sys.argv[4] 30 31if not args_dst.endswith('/'): 32 args_dst = args_dst + '/' 33if not args_src.endswith('/'): 34 args_src = args_src + '/' 35 36args_dst_packages = args_dst + 'packages/' 37 38packageVersions = [] 39for filename in os.listdir(args_src): 40 if (not (filename.endswith("-" + args_arch + ".hpkg")) and 41 not (filename.endswith("-any.hpkg"))): 42 continue 43 packageVersions.append(filename) 44 45# Read RemotePackageRepository file and hardlink relevant packages 46pattern = re.compile("^[a-z0-9]") 47newFileForJam = [] 48packageFiles = [] 49errorsOccurred = False 50with open(args_jamf) as f: 51 for line in f: 52 pkg = line.strip() 53 if (len(pkg) == 0): 54 continue 55 if not (pattern.match(pkg)): 56 # not a package (probably a Jam directive) 57 newFileForJam.append(line) 58 continue 59 60 try: 61 pkgname = pkg[:pkg.index('-')] 62 except: 63 pkgname = '' 64 if (len(pkgname) == 0): 65 # no version, likely a source/debuginfo listing 66 newFileForJam.append(line) 67 continue 68 69 greatestVersion = None 70 for pkgVersion in packageVersions: 71 if (pkgVersion.startswith(pkgname + '-') and 72 ((greatestVersion == None) 73 or (LooseVersion(pkgVersion) > LooseVersion(greatestVersion)))): 74 greatestVersion = pkgVersion 75 if (greatestVersion == None): 76 print("not found: " + pkg) 77 newFileForJam.append(line) 78 errorsOccurred = True 79 continue 80 else: 81 # found it, so hardlink it 82 if not (os.path.exists(args_dst_packages + greatestVersion)): 83 os.link(args_src + greatestVersion, args_dst_packages + greatestVersion) 84 if ('packages/' + greatestVersion) in packageFiles: 85 print("error: duplicated package: " + pkgname) 86 errorsOccurred = True 87 packageFiles.append('packages/' + greatestVersion) 88 # also hardlink the source package, if one exists 89 srcpkg = greatestVersion.replace("-" + args_arch + ".hpkg", 90 "-source.hpkg").replace('-', '_source-', 1) 91 if os.path.exists(args_src + srcpkg): 92 if not os.path.exists(args_dst_packages + srcpkg): 93 os.link(args_src + srcpkg, args_dst_packages + srcpkg) 94 if ('packages/' + srcpkg) not in packageFiles: 95 packageFiles.append('packages/' + srcpkg) 96 newFileForJam.append("\t" + greatestVersion[:greatestVersion.rfind('-')] + "\n"); 97 98if errorsOccurred: 99 sys.exit(1) 100 101finalizedNewFile = "".join(newFileForJam).encode('UTF-8') 102with open(args_jamf, 'wb') as f: 103 f.write(finalizedNewFile) 104 105listhash = hashlib.sha256(finalizedNewFile).hexdigest() 106try: 107 os.mkdir(args_dst + listhash) 108except: 109 print("dir " + listhash + " already exists. No changes?") 110 sys.exit(1) 111 112repodir = args_dst + listhash + '/' 113os.symlink('../packages', repodir + 'packages') 114 115with open(args_dst + 'repo.info.template', 'r') as ritf: 116 repoInfoTemplate = ritf.read() 117 118repoInfoTemplate = repoInfoTemplate.replace("$ARCH$", args_arch) 119with open(repodir + 'repo.info', 'w') as rinf: 120 rinf.write(repoInfoTemplate) 121 122packageFiles.sort() 123with open(repodir + 'package.list', 'w') as pkgl: 124 pkgl.write("\n".join(packageFiles)) 125 126if os.system('cd ' + repodir + ' && package_repo create repo.info ' + " ".join(packageFiles)) != 0: 127 print("failed to create package repo.") 128 sys.exit(1) 129 130if os.system('cd ' + repodir + ' && sha256sum repo >repo.sha256') != 0: 131 print("failed to checksum package repo.") 132 sys.exit(1) 133