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