1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
|
#!/usr/bin/env python
import json
import os
import subprocess
import sys
import urllib.request
import zipfile
from pathlib import Path
from xml.etree import ElementTree as ET
def _normalize_dep(dep):
dep = dep.strip()
if not dep:
return ""
if "(" in dep:
dep = dep.split("(", 1)[0]
return dep.strip()
def _package_contains(pkg, needle):
needle = _normalize_dep(needle)
if not needle:
return False
target = needle if needle.lower().endswith(".lpk") else f"{needle}.lpk"
return any(f.get("Name", "").lower() == target.lower() for f in pkg["packages"])
def _match_packages(packages, name):
name_l = name.lower()
matches = [
p
for p in packages
if p.get("DisplayName", "").lower() == name_l
or p.get("Name", "").lower() == name_l
]
if not matches:
matches = [p for p in packages if _package_contains(p, name)]
return matches
def _get_dependencies(packages, pkg, seen):
pkg_name = pkg.get("Name", "")
if pkg_name in seen:
return []
seen.add(pkg_name)
deps = []
for pkg_file in pkg.get("packages", []):
dep_str = pkg_file.get("DependenciesAsString", "")
for dep in dep_str.split(","):
dep = _normalize_dep(dep)
if not dep:
continue
for candidate in packages:
if candidate.get("Name") == pkg_name:
continue
if _package_contains(candidate, dep):
deps.append(candidate)
deps.extend(_get_dependencies(packages, candidate, seen))
return deps
def _is_compatible(laz_compat, laz_version):
if not laz_compat:
return True
laz_compat = laz_compat.lower()
laz_version = laz_version.lower()
if "trunk" in laz_compat:
return True
if laz_version in laz_compat:
return True
version_parts = laz_version.split(".")
if len(version_parts) >= 2:
major_minor = f"{version_parts[0]}.{version_parts[1]}"
if f"{major_minor}.x" in laz_compat or f"{major_minor}." in laz_compat:
return True
return False
def _get_lazarus_version(lazarusdir):
try:
result = subprocess.run(
["lazbuild", f"--lazarusdir={lazarusdir}", "--version"],
capture_output=True,
text=True,
check=True,
)
version = result.stdout.strip().split()[0]
return version
except (subprocess.CalledProcessError, IndexError):
return None
def main():
repo_base = os.environ["LAZARUS_PKG_REPO"]
temp_dir = Path(os.environ["LAZARUS_PKG_TMP"])
lazarusdir = os.environ["LAZARUSDIR"]
project_file = Path("heidisql.lpi")
laz_version = _get_lazarus_version(lazarusdir)
if not project_file.exists():
raise SystemExit("heidisql.lpi not found")
tree = ET.parse(project_file)
required = []
for elem in tree.findall(".//RequiredPackages/Item/PackageName"):
value = elem.get("Value")
if value:
required.append(value.strip())
required = [r for r in required if r]
print("Required packages from LPI:", ", ".join(required) if required else "none")
if not required:
sys.exit(0)
with urllib.request.urlopen(f"{repo_base}/packagelist.json") as resp:
package_list = json.load(resp)
packages = []
for key, value in package_list.items():
if not key.startswith("PackageData"):
continue
idx = key[len("PackageData") :]
pkg = dict(value)
pkg["PackageBaseDir"] = pkg.get("PackageBaseDir", "").replace("\\", "")
files = []
for entry in package_list.get(f"PackageFiles{idx}", []):
entry = dict(entry)
entry["RelativeFilePath"] = entry.get("RelativeFilePath", "").replace(
"\\", ""
)
files.append(entry)
pkg["packages"] = files
packages.append(pkg)
to_install = []
seen_names = set()
def _add_pkg(pkg):
key = pkg.get("DisplayName") or pkg.get("Name")
if key and key not in seen_names:
seen_names.add(key)
to_install.append(pkg)
for req in required:
matches = _match_packages(packages, req)
if not matches:
print(f"Skipping non-repo package: {req}")
continue
for pkg in matches:
for dep in _get_dependencies(packages, pkg, set()):
_add_pkg(dep)
_add_pkg(pkg)
if not to_install:
print("No Lazarus packages to install from repository.")
sys.exit(0)
print("Installing Lazarus packages from repository:")
for pkg in to_install:
print(f" - {pkg.get('DisplayName') or pkg.get('Name')}")
temp_dir.mkdir(parents=True, exist_ok=True)
for pkg in to_install:
repo_file = pkg.get("RepositoryFileName")
if not repo_file:
raise SystemExit(f"Missing RepositoryFileName for {pkg.get('DisplayName')}")
url = f"{repo_base}/{repo_file}"
zip_path = temp_dir / repo_file
print(f"Downloading {url}")
with urllib.request.urlopen(url) as resp, open(zip_path, "wb") as out:
out.write(resp.read())
extract_dir = temp_dir / (pkg.get("RepositoryFileHash") or repo_file)
extract_dir.mkdir(parents=True, exist_ok=True)
with zipfile.ZipFile(zip_path) as zf:
zf.extractall(extract_dir)
base_dir = extract_dir / pkg.get("PackageBaseDir", "")
for pkg_file in pkg.get("packages", []):
pkg_path = (
base_dir
/ pkg_file.get("RelativeFilePath", "")
/ pkg_file.get("Name", "")
)
if laz_version:
laz_compat = pkg_file.get("LazCompatibility", "")
if not _is_compatible(laz_compat, laz_version):
print(f"Skipping incompatible package: {pkg_path} (LazCompatibility: {laz_compat}, Lazarus: {laz_version})")
continue
print(f"Building {pkg_path}")
subprocess.run(
["lazbuild", f"--lazarusdir={lazarusdir}", str(pkg_path)],
check=True,
)
if __name__ == "__main__":
main()
|