blob: 2e3e4e8c790e425b0a99bceaf8b76cc757760ba0 [file] [log] [blame]
Patrick Williams92b42cb2022-09-03 06:53:57 -05001#
2# Copyright OpenEmbedded Contributors
3#
4# SPDX-License-Identifier: MIT
5#
6
7inherit package
8
9IMAGE_PKGTYPE ?= "rpm"
10
Patrick Williamsac13d5f2023-11-24 18:59:46 -060011RPM = "rpm"
12RPMBUILD = "rpmbuild"
13RPMBUILD_COMPMODE ?= "${@'w19T%d.zstdio' % int(d.getVar('ZSTD_THREADS'))}"
Patrick Williams92b42cb2022-09-03 06:53:57 -050014
15PKGWRITEDIRRPM = "${WORKDIR}/deploy-rpms"
16
Patrick Williamsac13d5f2023-11-24 18:59:46 -060017# Maintaining the perfile dependencies has significant overhead when writing the
Patrick Williams92b42cb2022-09-03 06:53:57 -050018# packages. When set, this value merges them for efficiency.
19MERGEPERFILEDEPS = "1"
20
21# Filter dependencies based on a provided function.
22def filter_deps(var, f):
23 import collections
24
25 depends_dict = bb.utils.explode_dep_versions2(var)
26 newdeps_dict = collections.OrderedDict()
27 for dep in depends_dict:
28 if f(dep):
29 newdeps_dict[dep] = depends_dict[dep]
30 return bb.utils.join_deps(newdeps_dict, commasep=False)
31
32# Filter out absolute paths (typically /bin/sh and /usr/bin/env) and any perl
33# dependencies for nativesdk packages.
34def filter_nativesdk_deps(srcname, var):
35 if var and srcname.startswith("nativesdk-"):
36 var = filter_deps(var, lambda dep: not dep.startswith('/') and dep != 'perl' and not dep.startswith('perl('))
37 return var
38
39# Construct per file dependencies file
40def write_rpm_perfiledata(srcname, d):
41 workdir = d.getVar('WORKDIR')
42 packages = d.getVar('PACKAGES')
43 pkgd = d.getVar('PKGD')
44
45 def dump_filerdeps(varname, outfile, d):
46 outfile.write("#!/usr/bin/env python3\n\n")
47 outfile.write("# Dependency table\n")
48 outfile.write('deps = {\n')
49 for pkg in packages.split():
50 dependsflist_key = 'FILE' + varname + 'FLIST' + ":" + pkg
51 dependsflist = (d.getVar(dependsflist_key) or "")
52 for dfile in dependsflist.split():
53 key = "FILE" + varname + ":" + dfile + ":" + pkg
54 deps = filter_nativesdk_deps(srcname, d.getVar(key) or "")
55 depends_dict = bb.utils.explode_dep_versions(deps)
56 file = dfile.replace("@underscore@", "_")
57 file = file.replace("@closebrace@", "]")
58 file = file.replace("@openbrace@", "[")
59 file = file.replace("@tab@", "\t")
60 file = file.replace("@space@", " ")
61 file = file.replace("@at@", "@")
62 outfile.write('"' + pkgd + file + '" : "')
63 for dep in depends_dict:
64 ver = depends_dict[dep]
65 if dep and ver:
Patrick Williamsac13d5f2023-11-24 18:59:46 -060066 ver = ver.replace("(", "")
67 ver = ver.replace(")", "")
Patrick Williams92b42cb2022-09-03 06:53:57 -050068 outfile.write(dep + " " + ver + " ")
69 else:
70 outfile.write(dep + " ")
71 outfile.write('",\n')
72 outfile.write('}\n\n')
73 outfile.write("import sys\n")
74 outfile.write("while 1:\n")
75 outfile.write("\tline = sys.stdin.readline().strip()\n")
76 outfile.write("\tif not line:\n")
77 outfile.write("\t\tsys.exit(0)\n")
78 outfile.write("\tif line in deps:\n")
79 outfile.write("\t\tprint(deps[line] + '\\n')\n")
80
81 # OE-core dependencies a.k.a. RPM requires
82 outdepends = workdir + "/" + srcname + ".requires"
83
84 dependsfile = open(outdepends, 'w')
85
86 dump_filerdeps('RDEPENDS', dependsfile, d)
87
88 dependsfile.close()
89 os.chmod(outdepends, 0o755)
90
91 # OE-core / RPM Provides
92 outprovides = workdir + "/" + srcname + ".provides"
93
94 providesfile = open(outprovides, 'w')
95
96 dump_filerdeps('RPROVIDES', providesfile, d)
97
98 providesfile.close()
99 os.chmod(outprovides, 0o755)
100
101 return (outdepends, outprovides)
102
103
104python write_specfile () {
105 import oe.packagedata
Patrick Williams73bd93f2024-02-20 08:07:48 -0600106 import os,pwd,grp,stat
Patrick Williams92b42cb2022-09-03 06:53:57 -0500107
108 # append information for logs and patches to %prep
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600109 def add_prep(d, spec_files_bottom):
Patrick Williams92b42cb2022-09-03 06:53:57 -0500110 if d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and bb.data.inherits_class('archiver', d):
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600111 spec_files_bottom.append('%%prep -n %s' % d.getVar('PN'))
Patrick Williams92b42cb2022-09-03 06:53:57 -0500112 spec_files_bottom.append('%s' % "echo \"include logs and patches, Please check them in SOURCES\"")
113 spec_files_bottom.append('')
114
115 # append the name of tarball to key word 'SOURCE' in xxx.spec.
116 def tail_source(d):
117 if d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and bb.data.inherits_class('archiver', d):
118 ar_outdir = d.getVar('ARCHIVER_OUTDIR')
119 if not os.path.exists(ar_outdir):
120 return
121 source_list = os.listdir(ar_outdir)
122 source_number = 0
123 for source in source_list:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600124 # do_deploy_archives may have already run (from sstate) meaning a .src.rpm may already
Patrick Williams92b42cb2022-09-03 06:53:57 -0500125 # exist in ARCHIVER_OUTDIR so skip if present.
126 if source.endswith(".src.rpm"):
127 continue
128 # The rpmbuild doesn't need the root permission, but it needs
129 # to know the file's user and group name, the only user and
130 # group in fakeroot is "root" when working in fakeroot.
131 f = os.path.join(ar_outdir, source)
132 os.chown(f, 0, 0)
133 spec_preamble_top.append('Source%s: %s' % (source_number, source))
134 source_number += 1
135
136 # In RPM, dependencies are of the format: pkg <>= Epoch:Version-Release
137 # This format is similar to OE, however there are restrictions on the
138 # characters that can be in a field. In the Version field, "-"
139 # characters are not allowed. "-" is allowed in the Release field.
140 #
141 # We translate the "-" in the version to a "+", by loading the PKGV
142 # from the dependent recipe, replacing the - with a +, and then using
143 # that value to do a replace inside of this recipe's dependencies.
144 # This preserves the "-" separator between the version and release, as
145 # well as any "-" characters inside of the release field.
146 #
147 # All of this has to happen BEFORE the mapping_rename_hook as
148 # after renaming we cannot look up the dependencies in the packagedata
149 # store.
150 def translate_vers(varname, d):
151 depends = d.getVar(varname)
152 if depends:
153 depends_dict = bb.utils.explode_dep_versions2(depends)
154 newdeps_dict = {}
155 for dep in depends_dict:
156 verlist = []
157 for ver in depends_dict[dep]:
158 if '-' in ver:
159 subd = oe.packagedata.read_subpkgdata_dict(dep, d)
160 if 'PKGV' in subd:
161 pv = subd['PV']
162 pkgv = subd['PKGV']
163 reppv = pkgv.replace('-', '+')
Andrew Geissler517393d2023-01-13 08:55:19 -0600164 if ver.startswith(pv):
165 ver = ver.replace(pv, reppv)
166 ver = ver.replace(pkgv, reppv)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500167 if 'PKGR' in subd:
168 # Make sure PKGR rather than PR in ver
169 pr = '-' + subd['PR']
170 pkgr = '-' + subd['PKGR']
171 if pkgr not in ver:
172 ver = ver.replace(pr, pkgr)
173 verlist.append(ver)
174 else:
175 verlist.append(ver)
176 newdeps_dict[dep] = verlist
177 depends = bb.utils.join_deps(newdeps_dict)
178 d.setVar(varname, depends.strip())
179
180 # We need to change the style the dependency from BB to RPM
181 # This needs to happen AFTER the mapping_rename_hook
182 def print_deps(variable, tag, array, d):
183 depends = variable
184 if depends:
185 depends_dict = bb.utils.explode_dep_versions2(depends)
186 for dep in depends_dict:
187 for ver in depends_dict[dep]:
188 ver = ver.replace('(', '')
189 ver = ver.replace(')', '')
190 array.append("%s: %s %s" % (tag, dep, ver))
191 if not len(depends_dict[dep]):
192 array.append("%s: %s" % (tag, dep))
193
194 def walk_files(walkpath, target, conffiles, dirfiles):
195 # We can race against the ipk/deb backends which create CONTROL or DEBIAN directories
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600196 # when packaging. We just ignore these files which are created in
Patrick Williams92b42cb2022-09-03 06:53:57 -0500197 # packages-split/ and not package/
198 # We have the odd situation where the CONTROL/DEBIAN directory can be removed in the middle of
199 # of the walk, the isdir() test would then fail and the walk code would assume its a file
200 # hence we check for the names in files too.
201 for rootpath, dirs, files in os.walk(walkpath):
Patrick Williams73bd93f2024-02-20 08:07:48 -0600202 def get_attr(path):
203 stat_f = os.stat(rootpath + "/" + path, follow_symlinks=False)
204 mode = stat.S_IMODE(stat_f.st_mode)
205 try:
206 owner = pwd.getpwuid(stat_f.st_uid).pw_name
207 except Exception as e:
208 bb.error("Content of /etc/passwd in sysroot:\n{}".format(
209 open(d.getVar("RECIPE_SYSROOT") +"/etc/passwd").read()))
210 raise e
211 try:
212 group = grp.getgrgid(stat_f.st_gid).gr_name
213 except Exception as e:
214 bb.error("Content of /etc/group in sysroot:\n{}".format(
215 open(d.getVar("RECIPE_SYSROOT") +"/etc/group").read()))
216 raise e
217 return "%attr({:o},{},{}) ".format(mode, owner, group)
218
219 def escape_chars(p):
220 return p.replace("%", "%%").replace("\\", "\\\\").replace('"', '\\"')
221
Patrick Williams92b42cb2022-09-03 06:53:57 -0500222 path = rootpath.replace(walkpath, "")
223 if path.endswith("DEBIAN") or path.endswith("CONTROL"):
224 continue
Patrick Williams92b42cb2022-09-03 06:53:57 -0500225
226 # Treat all symlinks to directories as normal files.
227 # os.walk() lists them as directories.
228 def move_to_files(dir):
229 if os.path.islink(os.path.join(rootpath, dir)):
230 files.append(dir)
231 return True
232 else:
233 return False
234 dirs[:] = [dir for dir in dirs if not move_to_files(dir)]
235
236 # Directory handling can happen in two ways, either DIRFILES is not set at all
237 # in which case we fall back to the older behaviour of packages owning all their
238 # directories
239 if dirfiles is None:
240 for dir in dirs:
241 if dir == "CONTROL" or dir == "DEBIAN":
242 continue
Patrick Williams73bd93f2024-02-20 08:07:48 -0600243 p = path + '/' + dir
Patrick Williams92b42cb2022-09-03 06:53:57 -0500244 # All packages own the directories their files are in...
Patrick Williams73bd93f2024-02-20 08:07:48 -0600245 target.append(get_attr(dir) + '%dir "' + escape_chars(p) + '"')
246 elif path:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500247 # packages own only empty directories or explict directory.
248 # This will prevent the overlapping of security permission.
Patrick Williams73bd93f2024-02-20 08:07:48 -0600249 attr = get_attr(path)
250 if (not files and not dirs) or path in dirfiles:
251 target.append(attr + '%dir "' + escape_chars(path) + '"')
Patrick Williams92b42cb2022-09-03 06:53:57 -0500252
253 for file in files:
254 if file == "CONTROL" or file == "DEBIAN":
255 continue
Patrick Williams73bd93f2024-02-20 08:07:48 -0600256 attr = get_attr(file)
257 p = path + '/' + file
258 if conffiles.count(p):
259 target.append(attr + '%config "' + escape_chars(p) + '"')
Patrick Williams92b42cb2022-09-03 06:53:57 -0500260 else:
Patrick Williams73bd93f2024-02-20 08:07:48 -0600261 target.append(attr + '"' + escape_chars(p) + '"')
Patrick Williams92b42cb2022-09-03 06:53:57 -0500262
263 # Prevent the prerm/postrm scripts from being run during an upgrade
264 def wrap_uninstall(scriptvar):
265 scr = scriptvar.strip()
266 if scr.startswith("#!"):
267 pos = scr.find("\n") + 1
268 else:
269 pos = 0
270 scr = scr[:pos] + 'if [ "$1" = "0" ] ; then\n' + scr[pos:] + '\nfi'
271 return scr
272
273 def get_perfile(varname, pkg, d):
274 deps = []
275 dependsflist_key = 'FILE' + varname + 'FLIST' + ":" + pkg
276 dependsflist = (d.getVar(dependsflist_key) or "")
277 for dfile in dependsflist.split():
278 key = "FILE" + varname + ":" + dfile + ":" + pkg
279 depends = d.getVar(key)
280 if depends:
281 deps.append(depends)
282 return " ".join(deps)
283
284 def append_description(spec_preamble, text):
285 """
286 Add the description to the spec file.
287 """
288 import textwrap
289 dedent_text = textwrap.dedent(text).strip()
290 # Bitbake saves "\n" as "\\n"
291 if '\\n' in dedent_text:
292 for t in dedent_text.split('\\n'):
293 spec_preamble.append(t.strip())
294 else:
295 spec_preamble.append('%s' % textwrap.fill(dedent_text, width=75))
296
297 packages = d.getVar('PACKAGES')
298 if not packages or packages == '':
299 bb.debug(1, "No packages; nothing to do")
300 return
301
302 pkgdest = d.getVar('PKGDEST')
303 if not pkgdest:
304 bb.fatal("No PKGDEST")
305
306 outspecfile = d.getVar('OUTSPECFILE')
307 if not outspecfile:
308 bb.fatal("No OUTSPECFILE")
309
310 # Construct the SPEC file...
311 srcname = d.getVar('PN')
312 localdata = bb.data.createCopy(d)
313 localdata.setVar('OVERRIDES', d.getVar("OVERRIDES", False) + ":" + srcname)
314 srcsummary = (localdata.getVar('SUMMARY') or localdata.getVar('DESCRIPTION') or ".")
315 srcversion = localdata.getVar('PKGV').replace('-', '+')
316 srcrelease = localdata.getVar('PKGR')
317 srcepoch = (localdata.getVar('PKGE') or "")
318 srclicense = localdata.getVar('LICENSE')
319 srcsection = localdata.getVar('SECTION')
320 srcmaintainer = localdata.getVar('MAINTAINER')
321 srchomepage = localdata.getVar('HOMEPAGE')
322 srcdescription = localdata.getVar('DESCRIPTION') or "."
Andrew Geissler517393d2023-01-13 08:55:19 -0600323 srccustomtagschunk = oe.packagedata.get_package_additional_metadata("rpm", localdata)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500324
325 srcdepends = d.getVar('DEPENDS')
326 srcrdepends = ""
327 srcrrecommends = ""
328 srcrsuggests = ""
329 srcrprovides = ""
330 srcrreplaces = ""
331 srcrconflicts = ""
332 srcrobsoletes = ""
333
334 srcrpreinst = []
335 srcrpostinst = []
336 srcrprerm = []
337 srcrpostrm = []
338
339 spec_preamble_top = []
340 spec_preamble_bottom = []
341
342 spec_scriptlets_top = []
343 spec_scriptlets_bottom = []
344
345 spec_files_top = []
346 spec_files_bottom = []
347
348 perfiledeps = (d.getVar("MERGEPERFILEDEPS") or "0") == "0"
349 extra_pkgdata = (d.getVar("RPM_EXTRA_PKGDATA") or "0") == "1"
350
351 for pkg in packages.split():
352 localdata = bb.data.createCopy(d)
353
354 root = "%s/%s" % (pkgdest, pkg)
355
356 localdata.setVar('ROOT', '')
357 localdata.setVar('ROOT_%s' % pkg, root)
358 pkgname = localdata.getVar('PKG:%s' % pkg)
359 if not pkgname:
360 pkgname = pkg
361 localdata.setVar('PKG', pkgname)
362
363 localdata.setVar('OVERRIDES', d.getVar("OVERRIDES", False) + ":" + pkg)
364
Andrew Geissler517393d2023-01-13 08:55:19 -0600365 conffiles = oe.package.get_conffiles(pkg, d)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500366 dirfiles = localdata.getVar('DIRFILES')
367 if dirfiles is not None:
368 dirfiles = dirfiles.split()
369
370 splitname = pkgname
371
372 splitsummary = (localdata.getVar('SUMMARY') or localdata.getVar('DESCRIPTION') or ".")
373 splitversion = (localdata.getVar('PKGV') or "").replace('-', '+')
374 splitrelease = (localdata.getVar('PKGR') or "")
375 splitepoch = (localdata.getVar('PKGE') or "")
376 splitlicense = (localdata.getVar('LICENSE') or "")
377 splitsection = (localdata.getVar('SECTION') or "")
378 splitdescription = (localdata.getVar('DESCRIPTION') or ".")
Andrew Geissler517393d2023-01-13 08:55:19 -0600379 splitcustomtagschunk = oe.packagedata.get_package_additional_metadata("rpm", localdata)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500380
381 translate_vers('RDEPENDS', localdata)
382 translate_vers('RRECOMMENDS', localdata)
383 translate_vers('RSUGGESTS', localdata)
384 translate_vers('RPROVIDES', localdata)
385 translate_vers('RREPLACES', localdata)
386 translate_vers('RCONFLICTS', localdata)
387
388 # Map the dependencies into their final form
Andrew Geissler517393d2023-01-13 08:55:19 -0600389 oe.packagedata.mapping_rename_hook(localdata)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500390
391 splitrdepends = localdata.getVar('RDEPENDS') or ""
392 splitrrecommends = localdata.getVar('RRECOMMENDS') or ""
393 splitrsuggests = localdata.getVar('RSUGGESTS') or ""
394 splitrprovides = localdata.getVar('RPROVIDES') or ""
395 splitrreplaces = localdata.getVar('RREPLACES') or ""
396 splitrconflicts = localdata.getVar('RCONFLICTS') or ""
397 splitrobsoletes = ""
398
399 splitrpreinst = localdata.getVar('pkg_preinst')
400 splitrpostinst = localdata.getVar('pkg_postinst')
401 splitrprerm = localdata.getVar('pkg_prerm')
402 splitrpostrm = localdata.getVar('pkg_postrm')
403
404
405 if not perfiledeps:
406 # Add in summary of per file dependencies
407 splitrdepends = splitrdepends + " " + get_perfile('RDEPENDS', pkg, d)
408 splitrprovides = splitrprovides + " " + get_perfile('RPROVIDES', pkg, d)
409
410 splitrdepends = filter_nativesdk_deps(srcname, splitrdepends)
411
412 # Gather special src/first package data
413 if srcname == splitname:
414 archiving = d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and \
415 bb.data.inherits_class('archiver', d)
416 if archiving and srclicense != splitlicense:
417 bb.warn("The SRPM produced may not have the correct overall source license in the License tag. This is due to the LICENSE for the primary package and SRPM conflicting.")
418
419 srclicense = splitlicense
420 srcrdepends = splitrdepends
421 srcrrecommends = splitrrecommends
422 srcrsuggests = splitrsuggests
423 srcrprovides = splitrprovides
424 srcrreplaces = splitrreplaces
425 srcrconflicts = splitrconflicts
426
427 srcrpreinst = splitrpreinst
428 srcrpostinst = splitrpostinst
429 srcrprerm = splitrprerm
430 srcrpostrm = splitrpostrm
431
432 file_list = []
433 walk_files(root, file_list, conffiles, dirfiles)
434 if not file_list and localdata.getVar('ALLOW_EMPTY', False) != "1":
435 bb.note("Not creating empty RPM package for %s" % splitname)
436 else:
437 spec_files_top.append('%files')
438 if extra_pkgdata:
439 package_rpm_extra_pkgdata(splitname, spec_files_top, localdata)
440 spec_files_top.append('%defattr(-,-,-,-)')
441 if file_list:
442 bb.note("Creating RPM package for %s" % splitname)
443 spec_files_top.extend(file_list)
444 else:
445 bb.note("Creating empty RPM package for %s" % splitname)
446 spec_files_top.append('')
447 continue
448
449 # Process subpackage data
450 spec_preamble_bottom.append('%%package -n %s' % splitname)
451 spec_preamble_bottom.append('Summary: %s' % splitsummary)
452 if srcversion != splitversion:
453 spec_preamble_bottom.append('Version: %s' % splitversion)
454 if srcrelease != splitrelease:
455 spec_preamble_bottom.append('Release: %s' % splitrelease)
456 if srcepoch != splitepoch:
457 spec_preamble_bottom.append('Epoch: %s' % splitepoch)
458 spec_preamble_bottom.append('License: %s' % splitlicense)
459 spec_preamble_bottom.append('Group: %s' % splitsection)
460
461 if srccustomtagschunk != splitcustomtagschunk:
462 spec_preamble_bottom.append(splitcustomtagschunk)
463
464 # Replaces == Obsoletes && Provides
465 robsoletes = bb.utils.explode_dep_versions2(splitrobsoletes)
466 rprovides = bb.utils.explode_dep_versions2(splitrprovides)
467 rreplaces = bb.utils.explode_dep_versions2(splitrreplaces)
468 for dep in rreplaces:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600469 if dep not in robsoletes:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500470 robsoletes[dep] = rreplaces[dep]
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600471 if dep not in rprovides:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500472 rprovides[dep] = rreplaces[dep]
473 splitrobsoletes = bb.utils.join_deps(robsoletes, commasep=False)
474 splitrprovides = bb.utils.join_deps(rprovides, commasep=False)
475
476 print_deps(splitrdepends, "Requires", spec_preamble_bottom, d)
477 if splitrpreinst:
478 print_deps(splitrdepends, "Requires(pre)", spec_preamble_bottom, d)
479 if splitrpostinst:
480 print_deps(splitrdepends, "Requires(post)", spec_preamble_bottom, d)
481 if splitrprerm:
482 print_deps(splitrdepends, "Requires(preun)", spec_preamble_bottom, d)
483 if splitrpostrm:
484 print_deps(splitrdepends, "Requires(postun)", spec_preamble_bottom, d)
485
486 print_deps(splitrrecommends, "Recommends", spec_preamble_bottom, d)
487 print_deps(splitrsuggests, "Suggests", spec_preamble_bottom, d)
488 print_deps(splitrprovides, "Provides", spec_preamble_bottom, d)
489 print_deps(splitrobsoletes, "Obsoletes", spec_preamble_bottom, d)
490 print_deps(splitrconflicts, "Conflicts", spec_preamble_bottom, d)
491
492 spec_preamble_bottom.append('')
493
494 spec_preamble_bottom.append('%%description -n %s' % splitname)
495 append_description(spec_preamble_bottom, splitdescription)
496
497 spec_preamble_bottom.append('')
498
499 # Now process scriptlets
500 if splitrpreinst:
501 spec_scriptlets_bottom.append('%%pre -n %s' % splitname)
502 spec_scriptlets_bottom.append('# %s - preinst' % splitname)
503 spec_scriptlets_bottom.append(splitrpreinst)
504 spec_scriptlets_bottom.append('')
505 if splitrpostinst:
506 spec_scriptlets_bottom.append('%%post -n %s' % splitname)
507 spec_scriptlets_bottom.append('# %s - postinst' % splitname)
508 spec_scriptlets_bottom.append(splitrpostinst)
509 spec_scriptlets_bottom.append('')
510 if splitrprerm:
511 spec_scriptlets_bottom.append('%%preun -n %s' % splitname)
512 spec_scriptlets_bottom.append('# %s - prerm' % splitname)
513 scriptvar = wrap_uninstall(splitrprerm)
514 spec_scriptlets_bottom.append(scriptvar)
515 spec_scriptlets_bottom.append('')
516 if splitrpostrm:
517 spec_scriptlets_bottom.append('%%postun -n %s' % splitname)
518 spec_scriptlets_bottom.append('# %s - postrm' % splitname)
519 scriptvar = wrap_uninstall(splitrpostrm)
520 spec_scriptlets_bottom.append(scriptvar)
521 spec_scriptlets_bottom.append('')
522
523 # Now process files
524 file_list = []
525 walk_files(root, file_list, conffiles, dirfiles)
526 if not file_list and localdata.getVar('ALLOW_EMPTY', False) != "1":
527 bb.note("Not creating empty RPM package for %s" % splitname)
528 else:
529 spec_files_bottom.append('%%files -n %s' % splitname)
530 if extra_pkgdata:
531 package_rpm_extra_pkgdata(splitname, spec_files_bottom, localdata)
532 spec_files_bottom.append('%defattr(-,-,-,-)')
533 if file_list:
534 bb.note("Creating RPM package for %s" % splitname)
535 spec_files_bottom.extend(file_list)
536 else:
537 bb.note("Creating empty RPM package for %s" % splitname)
538 spec_files_bottom.append('')
539
540 del localdata
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600541
542 add_prep(d, spec_files_bottom)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500543 spec_preamble_top.append('Summary: %s' % srcsummary)
544 spec_preamble_top.append('Name: %s' % srcname)
545 spec_preamble_top.append('Version: %s' % srcversion)
546 spec_preamble_top.append('Release: %s' % srcrelease)
547 if srcepoch and srcepoch.strip() != "":
548 spec_preamble_top.append('Epoch: %s' % srcepoch)
549 spec_preamble_top.append('License: %s' % srclicense)
550 spec_preamble_top.append('Group: %s' % srcsection)
551 spec_preamble_top.append('Packager: %s' % srcmaintainer)
552 if srchomepage:
553 spec_preamble_top.append('URL: %s' % srchomepage)
554 if srccustomtagschunk:
555 spec_preamble_top.append(srccustomtagschunk)
556 tail_source(d)
557
558 # Replaces == Obsoletes && Provides
559 robsoletes = bb.utils.explode_dep_versions2(srcrobsoletes)
560 rprovides = bb.utils.explode_dep_versions2(srcrprovides)
561 rreplaces = bb.utils.explode_dep_versions2(srcrreplaces)
562 for dep in rreplaces:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600563 if dep not in robsoletes:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500564 robsoletes[dep] = rreplaces[dep]
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600565 if dep not in rprovides:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500566 rprovides[dep] = rreplaces[dep]
567 srcrobsoletes = bb.utils.join_deps(robsoletes, commasep=False)
568 srcrprovides = bb.utils.join_deps(rprovides, commasep=False)
569
570 print_deps(srcdepends, "BuildRequires", spec_preamble_top, d)
571 print_deps(srcrdepends, "Requires", spec_preamble_top, d)
572 if srcrpreinst:
573 print_deps(srcrdepends, "Requires(pre)", spec_preamble_top, d)
574 if srcrpostinst:
575 print_deps(srcrdepends, "Requires(post)", spec_preamble_top, d)
576 if srcrprerm:
577 print_deps(srcrdepends, "Requires(preun)", spec_preamble_top, d)
578 if srcrpostrm:
579 print_deps(srcrdepends, "Requires(postun)", spec_preamble_top, d)
580
581 print_deps(srcrrecommends, "Recommends", spec_preamble_top, d)
582 print_deps(srcrsuggests, "Suggests", spec_preamble_top, d)
583 print_deps(srcrprovides, "Provides", spec_preamble_top, d)
584 print_deps(srcrobsoletes, "Obsoletes", spec_preamble_top, d)
585 print_deps(srcrconflicts, "Conflicts", spec_preamble_top, d)
586
587 spec_preamble_top.append('')
588
589 spec_preamble_top.append('%description')
590 append_description(spec_preamble_top, srcdescription)
591
592 spec_preamble_top.append('')
593
594 if srcrpreinst:
595 spec_scriptlets_top.append('%pre')
596 spec_scriptlets_top.append('# %s - preinst' % srcname)
597 spec_scriptlets_top.append(srcrpreinst)
598 spec_scriptlets_top.append('')
599 if srcrpostinst:
600 spec_scriptlets_top.append('%post')
601 spec_scriptlets_top.append('# %s - postinst' % srcname)
602 spec_scriptlets_top.append(srcrpostinst)
603 spec_scriptlets_top.append('')
604 if srcrprerm:
605 spec_scriptlets_top.append('%preun')
606 spec_scriptlets_top.append('# %s - prerm' % srcname)
607 scriptvar = wrap_uninstall(srcrprerm)
608 spec_scriptlets_top.append(scriptvar)
609 spec_scriptlets_top.append('')
610 if srcrpostrm:
611 spec_scriptlets_top.append('%postun')
612 spec_scriptlets_top.append('# %s - postrm' % srcname)
613 scriptvar = wrap_uninstall(srcrpostrm)
614 spec_scriptlets_top.append(scriptvar)
615 spec_scriptlets_top.append('')
616
617 # Write the SPEC file
618 specfile = open(outspecfile, 'w')
619
620 # RPMSPEC_PREAMBLE is a way to add arbitrary text to the top
621 # of the generated spec file
622 external_preamble = d.getVar("RPMSPEC_PREAMBLE")
623 if external_preamble:
624 specfile.write(external_preamble + "\n")
625
626 for line in spec_preamble_top:
627 specfile.write(line + "\n")
628
629 for line in spec_preamble_bottom:
630 specfile.write(line + "\n")
631
632 for line in spec_scriptlets_top:
633 specfile.write(line + "\n")
634
635 for line in spec_scriptlets_bottom:
636 specfile.write(line + "\n")
637
638 for line in spec_files_top:
639 specfile.write(line + "\n")
640
641 for line in spec_files_bottom:
642 specfile.write(line + "\n")
643
644 specfile.close()
645}
646# Otherwise allarch packages may change depending on override configuration
647write_specfile[vardepsexclude] = "OVERRIDES"
648
649# Have to list any variables referenced as X_<pkg> that aren't in pkgdata here
650RPMEXTRAVARS = "PACKAGE_ADD_METADATA_RPM"
651write_specfile[vardeps] += "${@gen_packagevar(d, 'RPMEXTRAVARS')}"
652
653python do_package_rpm () {
654 workdir = d.getVar('WORKDIR')
655 tmpdir = d.getVar('TMPDIR')
656 pkgd = d.getVar('PKGD')
Patrick Williams92b42cb2022-09-03 06:53:57 -0500657 if not workdir or not pkgd or not tmpdir:
658 bb.error("Variables incorrectly set, unable to package")
659 return
660
661 packages = d.getVar('PACKAGES')
662 if not packages or packages == '':
663 bb.debug(1, "No packages; nothing to do")
664 return
665
666 # Construct the spec file...
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600667 # If the spec file already exist, and has not been stored into
Patrick Williams92b42cb2022-09-03 06:53:57 -0500668 # pseudo's files.db, it maybe cause rpmbuild src.rpm fail,
669 # so remove it before doing rpmbuild src.rpm.
670 srcname = d.getVar('PN')
671 outspecfile = workdir + "/" + srcname + ".spec"
672 if os.path.isfile(outspecfile):
673 os.remove(outspecfile)
674 d.setVar('OUTSPECFILE', outspecfile)
675 bb.build.exec_func('write_specfile', d)
676
677 perfiledeps = (d.getVar("MERGEPERFILEDEPS") or "0") == "0"
678 if perfiledeps:
679 outdepends, outprovides = write_rpm_perfiledata(srcname, d)
680
681 # Setup the rpmbuild arguments...
682 rpmbuild = d.getVar('RPMBUILD')
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600683 rpmbuild_compmode = d.getVar('RPMBUILD_COMPMODE')
Patrick Williams73bd93f2024-02-20 08:07:48 -0600684 rpmbuild_extra_params = d.getVar('RPMBUILD_EXTRA_PARAMS') or ""
Patrick Williams92b42cb2022-09-03 06:53:57 -0500685
686 # Too many places in dnf stack assume that arch-independent packages are "noarch".
687 # Let's not fight against this.
688 package_arch = (d.getVar('PACKAGE_ARCH') or "").replace("-", "_")
689 if package_arch == "all":
690 package_arch = "noarch"
691
Patrick Williams92b42cb2022-09-03 06:53:57 -0500692 d.setVar('PACKAGE_ARCH_EXTEND', package_arch)
693 pkgwritedir = d.expand('${PKGWRITEDIRRPM}/${PACKAGE_ARCH_EXTEND}')
694 d.setVar('RPM_PKGWRITEDIR', pkgwritedir)
695 bb.debug(1, 'PKGWRITEDIR: %s' % d.getVar('RPM_PKGWRITEDIR'))
696 pkgarch = d.expand('${PACKAGE_ARCH_EXTEND}${HOST_VENDOR}-linux')
697 bb.utils.mkdirhier(pkgwritedir)
698 os.chmod(pkgwritedir, 0o755)
699
700 cmd = rpmbuild
701 cmd = cmd + " --noclean --nodeps --short-circuit --target " + pkgarch + " --buildroot " + pkgd
702 cmd = cmd + " --define '_topdir " + workdir + "' --define '_rpmdir " + pkgwritedir + "'"
703 cmd = cmd + " --define '_builddir " + d.getVar('B') + "'"
704 cmd = cmd + " --define '_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm'"
705 cmd = cmd + " --define '_use_internal_dependency_generator 0'"
706 cmd = cmd + " --define '_binaries_in_noarch_packages_terminate_build 0'"
707 cmd = cmd + " --define '_build_id_links none'"
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600708 cmd = cmd + " --define '_source_payload %s'" % rpmbuild_compmode
709 cmd = cmd + " --define '_binary_payload %s'" % rpmbuild_compmode
Patrick Williams92b42cb2022-09-03 06:53:57 -0500710 cmd = cmd + " --define 'clamp_mtime_to_source_date_epoch 1'"
711 cmd = cmd + " --define 'use_source_date_epoch_as_buildtime 1'"
712 cmd = cmd + " --define '_buildhost reproducible'"
713 cmd = cmd + " --define '__font_provides %{nil}'"
714 if perfiledeps:
715 cmd = cmd + " --define '__find_requires " + outdepends + "'"
716 cmd = cmd + " --define '__find_provides " + outprovides + "'"
717 else:
718 cmd = cmd + " --define '__find_requires %{nil}'"
719 cmd = cmd + " --define '__find_provides %{nil}'"
720 cmd = cmd + " --define '_unpackaged_files_terminate_build 0'"
721 cmd = cmd + " --define 'debug_package %{nil}'"
722 cmd = cmd + " --define '_tmppath " + workdir + "'"
Patrick Williams73bd93f2024-02-20 08:07:48 -0600723 cmd = cmd + " --define '_use_weak_usergroup_deps 1'"
724 cmd = cmd + " --define '_passwd_path " + "/completely/bogus/path" + "'"
725 cmd = cmd + " --define '_group_path " + "/completely/bogus/path" + "'"
726 cmd = cmd + rpmbuild_extra_params
Patrick Williams92b42cb2022-09-03 06:53:57 -0500727 if d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and bb.data.inherits_class('archiver', d):
728 cmd = cmd + " --define '_sourcedir " + d.getVar('ARCHIVER_OUTDIR') + "'"
729 cmdsrpm = cmd + " --define '_srcrpmdir " + d.getVar('ARCHIVER_RPMOUTDIR') + "'"
730 cmdsrpm = cmdsrpm + " -bs " + outspecfile
731 # Build the .src.rpm
732 d.setVar('SBUILDSPEC', cmdsrpm + "\n")
733 d.setVarFlag('SBUILDSPEC', 'func', '1')
734 bb.build.exec_func('SBUILDSPEC', d)
735 cmd = cmd + " -bb " + outspecfile
736
737 # rpm 4 creates various empty directories in _topdir, let's clean them up
738 cleanupcmd = "rm -rf %s/BUILDROOT %s/SOURCES %s/SPECS %s/SRPMS" % (workdir, workdir, workdir, workdir)
739
740 # Build the rpm package!
741 d.setVar('BUILDSPEC', cmd + "\n" + cleanupcmd + "\n")
742 d.setVarFlag('BUILDSPEC', 'func', '1')
743 bb.build.exec_func('BUILDSPEC', d)
744
745 if d.getVar('RPM_SIGN_PACKAGES') == '1':
746 bb.build.exec_func("sign_rpm", d)
747}
748
749python () {
750 if d.getVar('PACKAGES') != '':
751 deps = ' rpm-native:do_populate_sysroot virtual/fakeroot-native:do_populate_sysroot'
752 d.appendVarFlag('do_package_write_rpm', 'depends', deps)
753 d.setVarFlag('do_package_write_rpm', 'fakeroot', '1')
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600754
755 # Needed to ensure PKG_xxx renaming of dependency packages works
756 d.setVarFlag('do_package_write_rpm', 'deptask', "do_packagedata")
757 d.setVarFlag('do_package_write_rpm', 'rdeptask', "do_packagedata")
Patrick Williams92b42cb2022-09-03 06:53:57 -0500758}
759
760SSTATETASKS += "do_package_write_rpm"
761do_package_write_rpm[sstate-inputdirs] = "${PKGWRITEDIRRPM}"
762do_package_write_rpm[sstate-outputdirs] = "${DEPLOY_DIR_RPM}"
763# Take a shared lock, we can write multiple packages at the same time...
764# but we need to stop the rootfs/solver from running while we do...
765do_package_write_rpm[sstate-lockfile-shared] += "${DEPLOY_DIR_RPM}/rpm.lock"
766
767python do_package_write_rpm_setscene () {
768 sstate_setscene(d)
769}
770addtask do_package_write_rpm_setscene
771
772python do_package_write_rpm () {
773 bb.build.exec_func("read_subpackage_metadata", d)
774 bb.build.exec_func("do_package_rpm", d)
775}
776
777do_package_write_rpm[dirs] = "${PKGWRITEDIRRPM}"
778do_package_write_rpm[cleandirs] = "${PKGWRITEDIRRPM}"
779do_package_write_rpm[depends] += "${@oe.utils.build_depends_string(d.getVar('PACKAGE_WRITE_DEPS'), 'do_populate_sysroot')}"
780addtask package_write_rpm after do_packagedata do_package do_deploy_source_date_epoch before do_build
781do_build[rdeptask] += "do_package_write_rpm"
782
783PACKAGEINDEXDEPS += "rpm-native:do_populate_sysroot"
784PACKAGEINDEXDEPS += "createrepo-c-native:do_populate_sysroot"