commit | author | age
|
de89cf
|
1 |
#!/usr/bin/python3.9 |
27cdce
|
2 |
|
AL |
3 |
# |
|
4 |
# This file and its contents are supplied under the terms of the |
|
5 |
# Common Development and Distribution License ("CDDL"), version 1.0. |
|
6 |
# You may only use this file in accordance with the terms of version |
|
7 |
# 1.0 of the CDDL. |
|
8 |
# |
|
9 |
# A full copy of the text of the CDDL should have accompanied this |
|
10 |
# source. A copy of the CDDL is also available via the Internet at |
|
11 |
# http://www.illumos.org/license/CDDL. |
|
12 |
# |
|
13 |
|
|
14 |
# |
|
15 |
# Copyright 2021 Aurelien Larcher |
|
16 |
# |
|
17 |
|
|
18 |
import argparse |
|
19 |
import os |
|
20 |
import re |
|
21 |
import sys |
|
22 |
import json |
|
23 |
|
|
24 |
from bass.component import Component |
|
25 |
from bass.makefiles import Item |
|
26 |
from bass.makefiles import Keywords |
|
27 |
from bass.makefiles import Makefile as MK |
|
28 |
|
|
29 |
# Refactoring rules |
|
30 |
#----------------------------------------------------------------------------- |
|
31 |
# They should be called in-order to avoid unsatisfied assumptions. |
|
32 |
def format_component(path, verbose): |
|
33 |
mk = MK(path) |
|
34 |
kw = Keywords() |
|
35 |
refactor000(mk) |
|
36 |
refactor001(mk) |
|
37 |
refactor002(mk) |
|
38 |
mk.write() |
|
39 |
|
|
40 |
|
|
41 |
#----------------------------------------------------------------------------- |
|
42 |
# 000: Use WS_* variables instead $(WS_TOP)/* |
|
43 |
# If $(WS_TOP)/make-rules is found in an include then replace with the |
|
44 |
# variable $(WS_RULES). Do the same for other variables. |
|
45 |
def refactor000(mk): |
|
46 |
for i in iter(mk.includes): |
b688b7
|
47 |
r = re.match(r"^\$\(WS_TOP\)\/(.*)\/(.*).mk", i.value()) |
27cdce
|
48 |
if r is not None: |
AL |
49 |
subdir = r.group(1) |
|
50 |
mkfile = r.group(2) |
b688b7
|
51 |
print("000: Fix include " + i.value()) |
27cdce
|
52 |
i.set_value(os.path.join(MK.directory_variable(subdir), mkfile+".mk")) |
AL |
53 |
mk.contents[i.line()] = i.include_line() |
4bf6ad
|
54 |
mk.update() |
27cdce
|
55 |
|
AL |
56 |
|
|
57 |
#----------------------------------------------------------------------------- |
|
58 |
# 001: Use common.mk |
|
59 |
# If common.mk is not included then: |
|
60 |
# 1. infer the build system and set the BUILD_STYLE. |
|
61 |
# 2. set the BUILD_BITS from the existing targets. |
|
62 |
# 3. erase default target and keep the custom ones. |
b688b7
|
63 |
# 4. fix known target typos |
27cdce
|
64 |
def refactor001(mk): |
AL |
65 |
kw = Keywords() |
b688b7
|
66 |
if mk.has_variable('BUILD_STYLE') or mk.has_mk_include('common'): |
27cdce
|
67 |
return |
AL |
68 |
# Build style |
|
69 |
build_style = None |
|
70 |
for i in iter(mk.includes): |
|
71 |
r = re.match(r"^\$\(WS_MAKE_RULES\)/(.*).mk$", i.value()) |
|
72 |
if r is not None: |
|
73 |
build_style = r.group(1) if r.group(1) in kw.variables['BUILD_STYLE'] else None |
|
74 |
if build_style is not None: |
|
75 |
mk.set_variable('BUILD_STYLE', build_style) |
|
76 |
break |
|
77 |
if build_style is None: |
|
78 |
raise ValueError("Variable BUILD_STYLE cannot be defined") |
|
79 |
else: |
|
80 |
print("001: Setting build style to '" + build_style + "'") |
|
81 |
build_style = mk.variable('BUILD_STYLE').value() |
|
82 |
# Build bits |
|
83 |
mk_bits = mk.run("print-value-MK_BITS")[0] |
|
84 |
if mk_bits not in kw.variables["MK_BITS"]: |
|
85 |
raise ValueError("Variable MK_BITS cannot be defined") |
|
86 |
else: |
|
87 |
print("001: Setting make bits to '" + mk_bits + "'") |
|
88 |
# Check targets |
b688b7
|
89 |
new_mk_bits = None |
27cdce
|
90 |
new_targets = {} |
AL |
91 |
for t, u in iter(mk.targets.items()): |
|
92 |
# We do not know how to handle target with defined steps yet |
|
93 |
if len(u.str) > 1: |
|
94 |
continue |
b688b7
|
95 |
# Amend typos |
AL |
96 |
if t == 'test' and u.value() == MK.value('NO_TEST'): |
|
97 |
print("001: Fix typo $(NO_TEST) -> $(NO_TESTS)") |
|
98 |
u.set_value(MK.value('NO_TESTS')) |
27cdce
|
99 |
# Process target |
AL |
100 |
found = False |
|
101 |
for v in kw.targets[t]: |
|
102 |
v = MK.value(v.replace(MK.value("MK_BITS"), mk_bits)) |
|
103 |
# If the target dependency is one of the default values |
b688b7
|
104 |
if u.value() == v: |
27cdce
|
105 |
found = True |
AL |
106 |
w = MK.target_value(t, mk_bits) |
b688b7
|
107 |
#print(w) |
27cdce
|
108 |
if v == w: |
AL |
109 |
print("001: Use default target '"+t+"'") |
|
110 |
u.str = None |
|
111 |
else: |
b688b7
|
112 |
print("001: Define target '"+t+"': "+u.value()) |
27cdce
|
113 |
new_targets[t] = u |
AL |
114 |
break |
|
115 |
if not found: |
871dfc
|
116 |
# Some Python/Perl makefiles actually use NO_ARCH target with MK_BITS=32, or BITS was not set |
AL |
117 |
if mk_bits == '32' or mk_bits == '64': |
b688b7
|
118 |
ok_bits = ( 'NO_ARCH', '64', '32_and_64', '64_and_32' ) |
AL |
119 |
for b in ok_bits: |
|
120 |
if u.value() == MK.target_value(t, b): |
|
121 |
if not new_mk_bits: |
|
122 |
new_mk_bits = b |
|
123 |
elif b != new_mk_bits: |
|
124 |
raise ValueError("001: Inconsistent target '"+t+"': "+u.value()) |
|
125 |
u.str = None |
|
126 |
break |
27cdce
|
127 |
else: |
b688b7
|
128 |
raise ValueError("001: Unknown target '"+t+"' bitness: "+u.value()) |
0d28f0
|
129 |
if new_mk_bits: |
AL |
130 |
print("001: Changing make bits from "+mk_bits+" to '"+new_mk_bits+"'") |
|
131 |
mk_bits = new_mk_bits |
27cdce
|
132 |
# Collect items |
AL |
133 |
rem_lines = set() |
|
134 |
rem_includes = [ MK.makefile_path("prep"), MK.makefile_path("ips")] |
|
135 |
new_includes = [] |
|
136 |
include_shared_mk = None |
|
137 |
include_common_mk = None |
|
138 |
for i in iter(mk.includes): |
|
139 |
if i.value() not in rem_includes: |
|
140 |
if i.value() == MK.makefile_path(build_style): |
|
141 |
i.set_value(MK.makefile_path("common")) |
|
142 |
include_common_mk = i |
b688b7
|
143 |
elif re.match(r".*/shared-macros.mk$", i.value()): |
27cdce
|
144 |
include_shared_mk = i |
AL |
145 |
new_includes.append(i) |
|
146 |
else: |
|
147 |
rem_lines.add(i.line()) |
|
148 |
mk.includes = new_includes |
|
149 |
if include_common_mk is None: |
|
150 |
raise ValueError("Include directive of common.mk not found") |
|
151 |
if include_shared_mk is None: |
|
152 |
raise ValueError("Include directive of shared-macros.mk not found") |
|
153 |
# Add lines to skip for default targets |
|
154 |
for u in mk.targets.values(): |
|
155 |
if u.str is None: |
|
156 |
rem_lines.add(u.line()) |
|
157 |
# Update content |
|
158 |
contents = mk.contents[0:include_shared_mk.line()] |
|
159 |
# Add build macros |
|
160 |
contents.append(Keywords.assignment('BUILD_STYLE', build_style)) |
|
161 |
contents.append(Keywords.assignment('BUILD_BITS', mk_bits)) |
|
162 |
# Write metadata lines |
|
163 |
for idx, line in enumerate(mk.contents[include_shared_mk.line():include_common_mk.line()]): |
|
164 |
if (include_shared_mk.line() + idx) in rem_lines: |
|
165 |
continue |
|
166 |
contents.append(line) |
|
167 |
# Write new targets |
|
168 |
for t in ["build", "install", "test"]: |
|
169 |
if t in new_targets.keys(): |
b688b7
|
170 |
contents.append(Keywords.target_variable_assignment(t, new_targets[t].value())) |
27cdce
|
171 |
rem_lines.add(new_targets[t].line()) |
AL |
172 |
# Add common include |
|
173 |
contents.append(include_common_mk.include_line()) |
|
174 |
# Write lines after common.mk |
|
175 |
for idx, line in enumerate(mk.contents[include_common_mk.line()+1:]): |
|
176 |
if (include_common_mk.line()+1+idx) in rem_lines: |
|
177 |
continue |
|
178 |
contents.append(line) |
|
179 |
mk.update(contents) |
|
180 |
|
|
181 |
|
|
182 |
#----------------------------------------------------------------------------- |
|
183 |
# 002: Indent COMPONENT_ variables |
|
184 |
def refactor002(mk): |
|
185 |
for k,i in iter(mk.variables.items()): |
|
186 |
if re.match("^COMPONENT_", k): |
|
187 |
idx = i.line() |
|
188 |
lines = i.variable_assignment(k) |
|
189 |
for i in range(0, i.length()): |
|
190 |
mk.contents[idx + i] = lines[i] |
|
191 |
mk.update() |
|
192 |
|
|
193 |
|
4bf6ad
|
194 |
# Update rules |
AL |
195 |
#----------------------------------------------------------------------------- |
|
196 |
# U000: Update to default OpenSSL |
|
197 |
# If openssl is a dependency and the openssl package version is not set |
|
198 |
# 1. update the dependency to the next openssl X.Y |
|
199 |
# 2. add macros USE_OPENSSLXY to the makefile |
|
200 |
def update000(mk): |
|
201 |
curr_version = '1.0' |
|
202 |
next_version = '1.1' |
|
203 |
curr_macro = 'USE_OPENSSL'+curr_version.replace('.','') |
|
204 |
next_macro = 'USE_OPENSSL'+next_version.replace('.','') |
|
205 |
curr_openssl_pkg = 'library/security/openssl' |
|
206 |
next_openssl_pkg = 'library/security/openssl-11' |
|
207 |
reqs = mk.required_packages() |
|
208 |
has_openssl_deps=False |
|
209 |
for p in reqs.split(): |
|
210 |
if p == curr_openssl_pkg: |
|
211 |
has_openssl_deps=True |
|
212 |
if not has_openssl_deps: |
|
213 |
return |
|
214 |
# Check whether current version is enforced |
|
215 |
for line in iter(mk.contents): |
|
216 |
if re.match("^"+curr_macro+"[\s]*=[\s]*yes", line): |
|
217 |
return |
|
218 |
print("U000: update to next openssl") |
|
219 |
# Replace dependency |
|
220 |
for idx, line in enumerate(mk.contents): |
|
221 |
if re.match(r"REQUIRED_PACKAGES(.*)"+curr_openssl_pkg+"[\s]*$", line): |
|
222 |
mk.contents[idx] = line.replace(curr_openssl_pkg, next_openssl_pkg) |
|
223 |
break |
|
224 |
# Add macro before shared-macros |
|
225 |
include_shared_macros_mk = mk.get_mk_include('shared-macros') |
|
226 |
if not include_shared_macros_mk: |
|
227 |
raise ValueError('include shared_macros.mk not found') |
|
228 |
mk.set_variable(next_macro, 'yes', include_shared_macros_mk.line()) |
|
229 |
mk.update() |
|
230 |
|
|
231 |
|
27cdce
|
232 |
#----------------------------------------------------------------------------- |
AL |
233 |
# Update component makefile for revision or version bump |
|
234 |
def update_component(path, version, verbose): |
|
235 |
format_component(path, verbose) |
|
236 |
# Nothing to bump, just update the Makefile to current format |
|
237 |
if version is None: |
|
238 |
return |
|
239 |
mk = MK(path) |
4bf6ad
|
240 |
# Apply default update rules |
AL |
241 |
update000(mk) |
|
242 |
# Check current version |
27cdce
|
243 |
if not mk.has_variable('COMPONENT_VERSION'): |
AL |
244 |
raise ValueError('COMPONENT_VERSION not found') |
|
245 |
newvers = str(version) |
|
246 |
current = mk.variable('COMPONENT_VERSION').value() |
81e725
|
247 |
version_has_changed = False |
27cdce
|
248 |
# Bump revision only |
AL |
249 |
if newvers == '0' or newvers == current: |
|
250 |
print("Bump COMPONENT_REVISION") |
|
251 |
if mk.has_variable('COMPONENT_REVISION'): |
|
252 |
try: |
|
253 |
component_revision = int(mk.variable('COMPONENT_REVISION').value()) |
|
254 |
except ValueError: |
|
255 |
print('COMPONENT_REVISION field malformed: {}'.format(component_revision)) |
|
256 |
# Change value |
|
257 |
mk.set_variable('COMPONENT_REVISION', str(component_revision+1)) |
|
258 |
else: |
|
259 |
# Add value set to 1 after COMPONENT_VERSION |
|
260 |
mk.set_variable('COMPONENT_REVISION', str(1), line=mk.variable('COMPONENT_VERSION').line()+1) |
|
261 |
# Update to given version and remove revision |
|
262 |
else: |
81e725
|
263 |
if newvers == 'latest': |
AL |
264 |
if mk.build_style() == 'setup.py': |
|
265 |
print("Trying to get latest version from PyPI") |
|
266 |
js = mk.get_pypi_data() |
|
267 |
try: |
|
268 |
newvers = js['info']['version'] |
|
269 |
except KeyError: |
|
270 |
print("Unable to find version") |
|
271 |
return None |
27cdce
|
272 |
print("Bump COMPONENT_VERSION to " + newvers) |
81e725
|
273 |
version_has_changed = True |
27cdce
|
274 |
mk.set_variable('COMPONENT_VERSION', newvers) |
AL |
275 |
if mk.has_variable('COMPONENT_REVISION'): |
|
276 |
mk.remove_variable('COMPONENT_REVISION') |
|
277 |
# Update makefile |
|
278 |
mk.write() |
|
279 |
|
81e725
|
280 |
if not version_has_changed: |
AL |
281 |
return |
|
282 |
|
|
283 |
# Try to update archive checksum |
|
284 |
if mk.uses_pypi(): |
|
285 |
print("Trying to get checksum from PyPI") |
|
286 |
js = mk.get_pypi_data() |
|
287 |
try: |
|
288 |
verblock = js['releases'][newvers] |
|
289 |
except KeyError: |
|
290 |
print("Unknown version '%s'" % newvers) |
|
291 |
return None |
|
292 |
# Index 0 is for the pypi package and index 1 for the source archive |
|
293 |
sha256 = verblock[1]['digests']['sha256'] |
|
294 |
print("Found: "+str(sha256)) |
|
295 |
mk.set_archive_hash(sha256) |
|
296 |
# Update makefile |
|
297 |
mk.write() |
|
298 |
|
27cdce
|
299 |
|
AL |
300 |
def main(): |
|
301 |
parser = argparse.ArgumentParser() |
|
302 |
parser.add_argument('--path', default='components', |
|
303 |
help='Directory holding components') |
|
304 |
parser.add_argument('--bump', nargs='?', default=None, const=0, |
|
305 |
help='Bump component to given version') |
|
306 |
parser.add_argument('-v', '--verbose', action='store_true', |
|
307 |
default=False, help='Verbose output') |
|
308 |
args = parser.parse_args() |
|
309 |
|
|
310 |
path = args.path |
|
311 |
version = args.bump |
|
312 |
verbose = args.verbose |
|
313 |
|
|
314 |
update_component(path=path, version=version, verbose=verbose) |
|
315 |
|
|
316 |
|
|
317 |
if __name__ == '__main__': |
|
318 |
main() |