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