blob: 2fc18fe98c1ea71c2e097e672304ad20b18bbc2e [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
106
107 # append information for logs and patches to %prep
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600108 def add_prep(d, spec_files_bottom):
Patrick Williams92b42cb2022-09-03 06:53:57 -0500109 if d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and bb.data.inherits_class('archiver', d):
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600110 spec_files_bottom.append('%%prep -n %s' % d.getVar('PN'))
Patrick Williams92b42cb2022-09-03 06:53:57 -0500111 spec_files_bottom.append('%s' % "echo \"include logs and patches, Please check them in SOURCES\"")
112 spec_files_bottom.append('')
113
114 # append the name of tarball to key word 'SOURCE' in xxx.spec.
115 def tail_source(d):
116 if d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and bb.data.inherits_class('archiver', d):
117 ar_outdir = d.getVar('ARCHIVER_OUTDIR')
118 if not os.path.exists(ar_outdir):
119 return
120 source_list = os.listdir(ar_outdir)
121 source_number = 0
122 for source in source_list:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600123 # do_deploy_archives may have already run (from sstate) meaning a .src.rpm may already
Patrick Williams92b42cb2022-09-03 06:53:57 -0500124 # exist in ARCHIVER_OUTDIR so skip if present.
125 if source.endswith(".src.rpm"):
126 continue
127 # The rpmbuild doesn't need the root permission, but it needs
128 # to know the file's user and group name, the only user and
129 # group in fakeroot is "root" when working in fakeroot.
130 f = os.path.join(ar_outdir, source)
131 os.chown(f, 0, 0)
132 spec_preamble_top.append('Source%s: %s' % (source_number, source))
133 source_number += 1
134
135 # In RPM, dependencies are of the format: pkg <>= Epoch:Version-Release
136 # This format is similar to OE, however there are restrictions on the
137 # characters that can be in a field. In the Version field, "-"
138 # characters are not allowed. "-" is allowed in the Release field.
139 #
140 # We translate the "-" in the version to a "+", by loading the PKGV
141 # from the dependent recipe, replacing the - with a +, and then using
142 # that value to do a replace inside of this recipe's dependencies.
143 # This preserves the "-" separator between the version and release, as
144 # well as any "-" characters inside of the release field.
145 #
146 # All of this has to happen BEFORE the mapping_rename_hook as
147 # after renaming we cannot look up the dependencies in the packagedata
148 # store.
149 def translate_vers(varname, d):
150 depends = d.getVar(varname)
151 if depends:
152 depends_dict = bb.utils.explode_dep_versions2(depends)
153 newdeps_dict = {}
154 for dep in depends_dict:
155 verlist = []
156 for ver in depends_dict[dep]:
157 if '-' in ver:
158 subd = oe.packagedata.read_subpkgdata_dict(dep, d)
159 if 'PKGV' in subd:
160 pv = subd['PV']
161 pkgv = subd['PKGV']
162 reppv = pkgv.replace('-', '+')
Andrew Geissler517393d2023-01-13 08:55:19 -0600163 if ver.startswith(pv):
164 ver = ver.replace(pv, reppv)
165 ver = ver.replace(pkgv, reppv)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500166 if 'PKGR' in subd:
167 # Make sure PKGR rather than PR in ver
168 pr = '-' + subd['PR']
169 pkgr = '-' + subd['PKGR']
170 if pkgr not in ver:
171 ver = ver.replace(pr, pkgr)
172 verlist.append(ver)
173 else:
174 verlist.append(ver)
175 newdeps_dict[dep] = verlist
176 depends = bb.utils.join_deps(newdeps_dict)
177 d.setVar(varname, depends.strip())
178
179 # We need to change the style the dependency from BB to RPM
180 # This needs to happen AFTER the mapping_rename_hook
181 def print_deps(variable, tag, array, d):
182 depends = variable
183 if depends:
184 depends_dict = bb.utils.explode_dep_versions2(depends)
185 for dep in depends_dict:
186 for ver in depends_dict[dep]:
187 ver = ver.replace('(', '')
188 ver = ver.replace(')', '')
189 array.append("%s: %s %s" % (tag, dep, ver))
190 if not len(depends_dict[dep]):
191 array.append("%s: %s" % (tag, dep))
192
193 def walk_files(walkpath, target, conffiles, dirfiles):
194 # We can race against the ipk/deb backends which create CONTROL or DEBIAN directories
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600195 # when packaging. We just ignore these files which are created in
Patrick Williams92b42cb2022-09-03 06:53:57 -0500196 # packages-split/ and not package/
197 # We have the odd situation where the CONTROL/DEBIAN directory can be removed in the middle of
198 # of the walk, the isdir() test would then fail and the walk code would assume its a file
199 # hence we check for the names in files too.
200 for rootpath, dirs, files in os.walk(walkpath):
201 path = rootpath.replace(walkpath, "")
202 if path.endswith("DEBIAN") or path.endswith("CONTROL"):
203 continue
204 path = path.replace("%", "%%%%%%%%")
205
206 # Treat all symlinks to directories as normal files.
207 # os.walk() lists them as directories.
208 def move_to_files(dir):
209 if os.path.islink(os.path.join(rootpath, dir)):
210 files.append(dir)
211 return True
212 else:
213 return False
214 dirs[:] = [dir for dir in dirs if not move_to_files(dir)]
215
216 # Directory handling can happen in two ways, either DIRFILES is not set at all
217 # in which case we fall back to the older behaviour of packages owning all their
218 # directories
219 if dirfiles is None:
220 for dir in dirs:
221 if dir == "CONTROL" or dir == "DEBIAN":
222 continue
223 dir = dir.replace("%", "%%%%%%%%")
224 # All packages own the directories their files are in...
225 target.append('%dir "' + path + '/' + dir + '"')
226 else:
227 # packages own only empty directories or explict directory.
228 # This will prevent the overlapping of security permission.
229 if path and not files and not dirs:
230 target.append('%dir "' + path + '"')
231 elif path and path in dirfiles:
232 target.append('%dir "' + path + '"')
233
234 for file in files:
235 if file == "CONTROL" or file == "DEBIAN":
236 continue
237 file = file.replace("%", "%%%%%%%%")
238 if conffiles.count(path + '/' + file):
239 target.append('%config "' + path + '/' + file + '"')
240 else:
241 target.append('"' + path + '/' + file + '"')
242
243 # Prevent the prerm/postrm scripts from being run during an upgrade
244 def wrap_uninstall(scriptvar):
245 scr = scriptvar.strip()
246 if scr.startswith("#!"):
247 pos = scr.find("\n") + 1
248 else:
249 pos = 0
250 scr = scr[:pos] + 'if [ "$1" = "0" ] ; then\n' + scr[pos:] + '\nfi'
251 return scr
252
253 def get_perfile(varname, pkg, d):
254 deps = []
255 dependsflist_key = 'FILE' + varname + 'FLIST' + ":" + pkg
256 dependsflist = (d.getVar(dependsflist_key) or "")
257 for dfile in dependsflist.split():
258 key = "FILE" + varname + ":" + dfile + ":" + pkg
259 depends = d.getVar(key)
260 if depends:
261 deps.append(depends)
262 return " ".join(deps)
263
264 def append_description(spec_preamble, text):
265 """
266 Add the description to the spec file.
267 """
268 import textwrap
269 dedent_text = textwrap.dedent(text).strip()
270 # Bitbake saves "\n" as "\\n"
271 if '\\n' in dedent_text:
272 for t in dedent_text.split('\\n'):
273 spec_preamble.append(t.strip())
274 else:
275 spec_preamble.append('%s' % textwrap.fill(dedent_text, width=75))
276
277 packages = d.getVar('PACKAGES')
278 if not packages or packages == '':
279 bb.debug(1, "No packages; nothing to do")
280 return
281
282 pkgdest = d.getVar('PKGDEST')
283 if not pkgdest:
284 bb.fatal("No PKGDEST")
285
286 outspecfile = d.getVar('OUTSPECFILE')
287 if not outspecfile:
288 bb.fatal("No OUTSPECFILE")
289
290 # Construct the SPEC file...
291 srcname = d.getVar('PN')
292 localdata = bb.data.createCopy(d)
293 localdata.setVar('OVERRIDES', d.getVar("OVERRIDES", False) + ":" + srcname)
294 srcsummary = (localdata.getVar('SUMMARY') or localdata.getVar('DESCRIPTION') or ".")
295 srcversion = localdata.getVar('PKGV').replace('-', '+')
296 srcrelease = localdata.getVar('PKGR')
297 srcepoch = (localdata.getVar('PKGE') or "")
298 srclicense = localdata.getVar('LICENSE')
299 srcsection = localdata.getVar('SECTION')
300 srcmaintainer = localdata.getVar('MAINTAINER')
301 srchomepage = localdata.getVar('HOMEPAGE')
302 srcdescription = localdata.getVar('DESCRIPTION') or "."
Andrew Geissler517393d2023-01-13 08:55:19 -0600303 srccustomtagschunk = oe.packagedata.get_package_additional_metadata("rpm", localdata)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500304
305 srcdepends = d.getVar('DEPENDS')
306 srcrdepends = ""
307 srcrrecommends = ""
308 srcrsuggests = ""
309 srcrprovides = ""
310 srcrreplaces = ""
311 srcrconflicts = ""
312 srcrobsoletes = ""
313
314 srcrpreinst = []
315 srcrpostinst = []
316 srcrprerm = []
317 srcrpostrm = []
318
319 spec_preamble_top = []
320 spec_preamble_bottom = []
321
322 spec_scriptlets_top = []
323 spec_scriptlets_bottom = []
324
325 spec_files_top = []
326 spec_files_bottom = []
327
328 perfiledeps = (d.getVar("MERGEPERFILEDEPS") or "0") == "0"
329 extra_pkgdata = (d.getVar("RPM_EXTRA_PKGDATA") or "0") == "1"
330
331 for pkg in packages.split():
332 localdata = bb.data.createCopy(d)
333
334 root = "%s/%s" % (pkgdest, pkg)
335
336 localdata.setVar('ROOT', '')
337 localdata.setVar('ROOT_%s' % pkg, root)
338 pkgname = localdata.getVar('PKG:%s' % pkg)
339 if not pkgname:
340 pkgname = pkg
341 localdata.setVar('PKG', pkgname)
342
343 localdata.setVar('OVERRIDES', d.getVar("OVERRIDES", False) + ":" + pkg)
344
Andrew Geissler517393d2023-01-13 08:55:19 -0600345 conffiles = oe.package.get_conffiles(pkg, d)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500346 dirfiles = localdata.getVar('DIRFILES')
347 if dirfiles is not None:
348 dirfiles = dirfiles.split()
349
350 splitname = pkgname
351
352 splitsummary = (localdata.getVar('SUMMARY') or localdata.getVar('DESCRIPTION') or ".")
353 splitversion = (localdata.getVar('PKGV') or "").replace('-', '+')
354 splitrelease = (localdata.getVar('PKGR') or "")
355 splitepoch = (localdata.getVar('PKGE') or "")
356 splitlicense = (localdata.getVar('LICENSE') or "")
357 splitsection = (localdata.getVar('SECTION') or "")
358 splitdescription = (localdata.getVar('DESCRIPTION') or ".")
Andrew Geissler517393d2023-01-13 08:55:19 -0600359 splitcustomtagschunk = oe.packagedata.get_package_additional_metadata("rpm", localdata)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500360
361 translate_vers('RDEPENDS', localdata)
362 translate_vers('RRECOMMENDS', localdata)
363 translate_vers('RSUGGESTS', localdata)
364 translate_vers('RPROVIDES', localdata)
365 translate_vers('RREPLACES', localdata)
366 translate_vers('RCONFLICTS', localdata)
367
368 # Map the dependencies into their final form
Andrew Geissler517393d2023-01-13 08:55:19 -0600369 oe.packagedata.mapping_rename_hook(localdata)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500370
371 splitrdepends = localdata.getVar('RDEPENDS') or ""
372 splitrrecommends = localdata.getVar('RRECOMMENDS') or ""
373 splitrsuggests = localdata.getVar('RSUGGESTS') or ""
374 splitrprovides = localdata.getVar('RPROVIDES') or ""
375 splitrreplaces = localdata.getVar('RREPLACES') or ""
376 splitrconflicts = localdata.getVar('RCONFLICTS') or ""
377 splitrobsoletes = ""
378
379 splitrpreinst = localdata.getVar('pkg_preinst')
380 splitrpostinst = localdata.getVar('pkg_postinst')
381 splitrprerm = localdata.getVar('pkg_prerm')
382 splitrpostrm = localdata.getVar('pkg_postrm')
383
384
385 if not perfiledeps:
386 # Add in summary of per file dependencies
387 splitrdepends = splitrdepends + " " + get_perfile('RDEPENDS', pkg, d)
388 splitrprovides = splitrprovides + " " + get_perfile('RPROVIDES', pkg, d)
389
390 splitrdepends = filter_nativesdk_deps(srcname, splitrdepends)
391
392 # Gather special src/first package data
393 if srcname == splitname:
394 archiving = d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and \
395 bb.data.inherits_class('archiver', d)
396 if archiving and srclicense != splitlicense:
397 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.")
398
399 srclicense = splitlicense
400 srcrdepends = splitrdepends
401 srcrrecommends = splitrrecommends
402 srcrsuggests = splitrsuggests
403 srcrprovides = splitrprovides
404 srcrreplaces = splitrreplaces
405 srcrconflicts = splitrconflicts
406
407 srcrpreinst = splitrpreinst
408 srcrpostinst = splitrpostinst
409 srcrprerm = splitrprerm
410 srcrpostrm = splitrpostrm
411
412 file_list = []
413 walk_files(root, file_list, conffiles, dirfiles)
414 if not file_list and localdata.getVar('ALLOW_EMPTY', False) != "1":
415 bb.note("Not creating empty RPM package for %s" % splitname)
416 else:
417 spec_files_top.append('%files')
418 if extra_pkgdata:
419 package_rpm_extra_pkgdata(splitname, spec_files_top, localdata)
420 spec_files_top.append('%defattr(-,-,-,-)')
421 if file_list:
422 bb.note("Creating RPM package for %s" % splitname)
423 spec_files_top.extend(file_list)
424 else:
425 bb.note("Creating empty RPM package for %s" % splitname)
426 spec_files_top.append('')
427 continue
428
429 # Process subpackage data
430 spec_preamble_bottom.append('%%package -n %s' % splitname)
431 spec_preamble_bottom.append('Summary: %s' % splitsummary)
432 if srcversion != splitversion:
433 spec_preamble_bottom.append('Version: %s' % splitversion)
434 if srcrelease != splitrelease:
435 spec_preamble_bottom.append('Release: %s' % splitrelease)
436 if srcepoch != splitepoch:
437 spec_preamble_bottom.append('Epoch: %s' % splitepoch)
438 spec_preamble_bottom.append('License: %s' % splitlicense)
439 spec_preamble_bottom.append('Group: %s' % splitsection)
440
441 if srccustomtagschunk != splitcustomtagschunk:
442 spec_preamble_bottom.append(splitcustomtagschunk)
443
444 # Replaces == Obsoletes && Provides
445 robsoletes = bb.utils.explode_dep_versions2(splitrobsoletes)
446 rprovides = bb.utils.explode_dep_versions2(splitrprovides)
447 rreplaces = bb.utils.explode_dep_versions2(splitrreplaces)
448 for dep in rreplaces:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600449 if dep not in robsoletes:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500450 robsoletes[dep] = rreplaces[dep]
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600451 if dep not in rprovides:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500452 rprovides[dep] = rreplaces[dep]
453 splitrobsoletes = bb.utils.join_deps(robsoletes, commasep=False)
454 splitrprovides = bb.utils.join_deps(rprovides, commasep=False)
455
456 print_deps(splitrdepends, "Requires", spec_preamble_bottom, d)
457 if splitrpreinst:
458 print_deps(splitrdepends, "Requires(pre)", spec_preamble_bottom, d)
459 if splitrpostinst:
460 print_deps(splitrdepends, "Requires(post)", spec_preamble_bottom, d)
461 if splitrprerm:
462 print_deps(splitrdepends, "Requires(preun)", spec_preamble_bottom, d)
463 if splitrpostrm:
464 print_deps(splitrdepends, "Requires(postun)", spec_preamble_bottom, d)
465
466 print_deps(splitrrecommends, "Recommends", spec_preamble_bottom, d)
467 print_deps(splitrsuggests, "Suggests", spec_preamble_bottom, d)
468 print_deps(splitrprovides, "Provides", spec_preamble_bottom, d)
469 print_deps(splitrobsoletes, "Obsoletes", spec_preamble_bottom, d)
470 print_deps(splitrconflicts, "Conflicts", spec_preamble_bottom, d)
471
472 spec_preamble_bottom.append('')
473
474 spec_preamble_bottom.append('%%description -n %s' % splitname)
475 append_description(spec_preamble_bottom, splitdescription)
476
477 spec_preamble_bottom.append('')
478
479 # Now process scriptlets
480 if splitrpreinst:
481 spec_scriptlets_bottom.append('%%pre -n %s' % splitname)
482 spec_scriptlets_bottom.append('# %s - preinst' % splitname)
483 spec_scriptlets_bottom.append(splitrpreinst)
484 spec_scriptlets_bottom.append('')
485 if splitrpostinst:
486 spec_scriptlets_bottom.append('%%post -n %s' % splitname)
487 spec_scriptlets_bottom.append('# %s - postinst' % splitname)
488 spec_scriptlets_bottom.append(splitrpostinst)
489 spec_scriptlets_bottom.append('')
490 if splitrprerm:
491 spec_scriptlets_bottom.append('%%preun -n %s' % splitname)
492 spec_scriptlets_bottom.append('# %s - prerm' % splitname)
493 scriptvar = wrap_uninstall(splitrprerm)
494 spec_scriptlets_bottom.append(scriptvar)
495 spec_scriptlets_bottom.append('')
496 if splitrpostrm:
497 spec_scriptlets_bottom.append('%%postun -n %s' % splitname)
498 spec_scriptlets_bottom.append('# %s - postrm' % splitname)
499 scriptvar = wrap_uninstall(splitrpostrm)
500 spec_scriptlets_bottom.append(scriptvar)
501 spec_scriptlets_bottom.append('')
502
503 # Now process files
504 file_list = []
505 walk_files(root, file_list, conffiles, dirfiles)
506 if not file_list and localdata.getVar('ALLOW_EMPTY', False) != "1":
507 bb.note("Not creating empty RPM package for %s" % splitname)
508 else:
509 spec_files_bottom.append('%%files -n %s' % splitname)
510 if extra_pkgdata:
511 package_rpm_extra_pkgdata(splitname, spec_files_bottom, localdata)
512 spec_files_bottom.append('%defattr(-,-,-,-)')
513 if file_list:
514 bb.note("Creating RPM package for %s" % splitname)
515 spec_files_bottom.extend(file_list)
516 else:
517 bb.note("Creating empty RPM package for %s" % splitname)
518 spec_files_bottom.append('')
519
520 del localdata
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600521
522 add_prep(d, spec_files_bottom)
Patrick Williams92b42cb2022-09-03 06:53:57 -0500523 spec_preamble_top.append('Summary: %s' % srcsummary)
524 spec_preamble_top.append('Name: %s' % srcname)
525 spec_preamble_top.append('Version: %s' % srcversion)
526 spec_preamble_top.append('Release: %s' % srcrelease)
527 if srcepoch and srcepoch.strip() != "":
528 spec_preamble_top.append('Epoch: %s' % srcepoch)
529 spec_preamble_top.append('License: %s' % srclicense)
530 spec_preamble_top.append('Group: %s' % srcsection)
531 spec_preamble_top.append('Packager: %s' % srcmaintainer)
532 if srchomepage:
533 spec_preamble_top.append('URL: %s' % srchomepage)
534 if srccustomtagschunk:
535 spec_preamble_top.append(srccustomtagschunk)
536 tail_source(d)
537
538 # Replaces == Obsoletes && Provides
539 robsoletes = bb.utils.explode_dep_versions2(srcrobsoletes)
540 rprovides = bb.utils.explode_dep_versions2(srcrprovides)
541 rreplaces = bb.utils.explode_dep_versions2(srcrreplaces)
542 for dep in rreplaces:
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600543 if dep not in robsoletes:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500544 robsoletes[dep] = rreplaces[dep]
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600545 if dep not in rprovides:
Patrick Williams92b42cb2022-09-03 06:53:57 -0500546 rprovides[dep] = rreplaces[dep]
547 srcrobsoletes = bb.utils.join_deps(robsoletes, commasep=False)
548 srcrprovides = bb.utils.join_deps(rprovides, commasep=False)
549
550 print_deps(srcdepends, "BuildRequires", spec_preamble_top, d)
551 print_deps(srcrdepends, "Requires", spec_preamble_top, d)
552 if srcrpreinst:
553 print_deps(srcrdepends, "Requires(pre)", spec_preamble_top, d)
554 if srcrpostinst:
555 print_deps(srcrdepends, "Requires(post)", spec_preamble_top, d)
556 if srcrprerm:
557 print_deps(srcrdepends, "Requires(preun)", spec_preamble_top, d)
558 if srcrpostrm:
559 print_deps(srcrdepends, "Requires(postun)", spec_preamble_top, d)
560
561 print_deps(srcrrecommends, "Recommends", spec_preamble_top, d)
562 print_deps(srcrsuggests, "Suggests", spec_preamble_top, d)
563 print_deps(srcrprovides, "Provides", spec_preamble_top, d)
564 print_deps(srcrobsoletes, "Obsoletes", spec_preamble_top, d)
565 print_deps(srcrconflicts, "Conflicts", spec_preamble_top, d)
566
567 spec_preamble_top.append('')
568
569 spec_preamble_top.append('%description')
570 append_description(spec_preamble_top, srcdescription)
571
572 spec_preamble_top.append('')
573
574 if srcrpreinst:
575 spec_scriptlets_top.append('%pre')
576 spec_scriptlets_top.append('# %s - preinst' % srcname)
577 spec_scriptlets_top.append(srcrpreinst)
578 spec_scriptlets_top.append('')
579 if srcrpostinst:
580 spec_scriptlets_top.append('%post')
581 spec_scriptlets_top.append('# %s - postinst' % srcname)
582 spec_scriptlets_top.append(srcrpostinst)
583 spec_scriptlets_top.append('')
584 if srcrprerm:
585 spec_scriptlets_top.append('%preun')
586 spec_scriptlets_top.append('# %s - prerm' % srcname)
587 scriptvar = wrap_uninstall(srcrprerm)
588 spec_scriptlets_top.append(scriptvar)
589 spec_scriptlets_top.append('')
590 if srcrpostrm:
591 spec_scriptlets_top.append('%postun')
592 spec_scriptlets_top.append('# %s - postrm' % srcname)
593 scriptvar = wrap_uninstall(srcrpostrm)
594 spec_scriptlets_top.append(scriptvar)
595 spec_scriptlets_top.append('')
596
597 # Write the SPEC file
598 specfile = open(outspecfile, 'w')
599
600 # RPMSPEC_PREAMBLE is a way to add arbitrary text to the top
601 # of the generated spec file
602 external_preamble = d.getVar("RPMSPEC_PREAMBLE")
603 if external_preamble:
604 specfile.write(external_preamble + "\n")
605
606 for line in spec_preamble_top:
607 specfile.write(line + "\n")
608
609 for line in spec_preamble_bottom:
610 specfile.write(line + "\n")
611
612 for line in spec_scriptlets_top:
613 specfile.write(line + "\n")
614
615 for line in spec_scriptlets_bottom:
616 specfile.write(line + "\n")
617
618 for line in spec_files_top:
619 specfile.write(line + "\n")
620
621 for line in spec_files_bottom:
622 specfile.write(line + "\n")
623
624 specfile.close()
625}
626# Otherwise allarch packages may change depending on override configuration
627write_specfile[vardepsexclude] = "OVERRIDES"
628
629# Have to list any variables referenced as X_<pkg> that aren't in pkgdata here
630RPMEXTRAVARS = "PACKAGE_ADD_METADATA_RPM"
631write_specfile[vardeps] += "${@gen_packagevar(d, 'RPMEXTRAVARS')}"
632
633python do_package_rpm () {
634 workdir = d.getVar('WORKDIR')
635 tmpdir = d.getVar('TMPDIR')
636 pkgd = d.getVar('PKGD')
Patrick Williams92b42cb2022-09-03 06:53:57 -0500637 if not workdir or not pkgd or not tmpdir:
638 bb.error("Variables incorrectly set, unable to package")
639 return
640
641 packages = d.getVar('PACKAGES')
642 if not packages or packages == '':
643 bb.debug(1, "No packages; nothing to do")
644 return
645
646 # Construct the spec file...
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600647 # If the spec file already exist, and has not been stored into
Patrick Williams92b42cb2022-09-03 06:53:57 -0500648 # pseudo's files.db, it maybe cause rpmbuild src.rpm fail,
649 # so remove it before doing rpmbuild src.rpm.
650 srcname = d.getVar('PN')
651 outspecfile = workdir + "/" + srcname + ".spec"
652 if os.path.isfile(outspecfile):
653 os.remove(outspecfile)
654 d.setVar('OUTSPECFILE', outspecfile)
655 bb.build.exec_func('write_specfile', d)
656
657 perfiledeps = (d.getVar("MERGEPERFILEDEPS") or "0") == "0"
658 if perfiledeps:
659 outdepends, outprovides = write_rpm_perfiledata(srcname, d)
660
661 # Setup the rpmbuild arguments...
662 rpmbuild = d.getVar('RPMBUILD')
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600663 rpmbuild_compmode = d.getVar('RPMBUILD_COMPMODE')
Patrick Williams92b42cb2022-09-03 06:53:57 -0500664
665 # Too many places in dnf stack assume that arch-independent packages are "noarch".
666 # Let's not fight against this.
667 package_arch = (d.getVar('PACKAGE_ARCH') or "").replace("-", "_")
668 if package_arch == "all":
669 package_arch = "noarch"
670
Patrick Williams92b42cb2022-09-03 06:53:57 -0500671 d.setVar('PACKAGE_ARCH_EXTEND', package_arch)
672 pkgwritedir = d.expand('${PKGWRITEDIRRPM}/${PACKAGE_ARCH_EXTEND}')
673 d.setVar('RPM_PKGWRITEDIR', pkgwritedir)
674 bb.debug(1, 'PKGWRITEDIR: %s' % d.getVar('RPM_PKGWRITEDIR'))
675 pkgarch = d.expand('${PACKAGE_ARCH_EXTEND}${HOST_VENDOR}-linux')
676 bb.utils.mkdirhier(pkgwritedir)
677 os.chmod(pkgwritedir, 0o755)
678
679 cmd = rpmbuild
680 cmd = cmd + " --noclean --nodeps --short-circuit --target " + pkgarch + " --buildroot " + pkgd
681 cmd = cmd + " --define '_topdir " + workdir + "' --define '_rpmdir " + pkgwritedir + "'"
682 cmd = cmd + " --define '_builddir " + d.getVar('B') + "'"
683 cmd = cmd + " --define '_build_name_fmt %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm'"
684 cmd = cmd + " --define '_use_internal_dependency_generator 0'"
685 cmd = cmd + " --define '_binaries_in_noarch_packages_terminate_build 0'"
686 cmd = cmd + " --define '_build_id_links none'"
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600687 cmd = cmd + " --define '_source_payload %s'" % rpmbuild_compmode
688 cmd = cmd + " --define '_binary_payload %s'" % rpmbuild_compmode
Patrick Williams92b42cb2022-09-03 06:53:57 -0500689 cmd = cmd + " --define 'clamp_mtime_to_source_date_epoch 1'"
690 cmd = cmd + " --define 'use_source_date_epoch_as_buildtime 1'"
691 cmd = cmd + " --define '_buildhost reproducible'"
692 cmd = cmd + " --define '__font_provides %{nil}'"
693 if perfiledeps:
694 cmd = cmd + " --define '__find_requires " + outdepends + "'"
695 cmd = cmd + " --define '__find_provides " + outprovides + "'"
696 else:
697 cmd = cmd + " --define '__find_requires %{nil}'"
698 cmd = cmd + " --define '__find_provides %{nil}'"
699 cmd = cmd + " --define '_unpackaged_files_terminate_build 0'"
700 cmd = cmd + " --define 'debug_package %{nil}'"
701 cmd = cmd + " --define '_tmppath " + workdir + "'"
702 if d.getVarFlag('ARCHIVER_MODE', 'srpm') == '1' and bb.data.inherits_class('archiver', d):
703 cmd = cmd + " --define '_sourcedir " + d.getVar('ARCHIVER_OUTDIR') + "'"
704 cmdsrpm = cmd + " --define '_srcrpmdir " + d.getVar('ARCHIVER_RPMOUTDIR') + "'"
705 cmdsrpm = cmdsrpm + " -bs " + outspecfile
706 # Build the .src.rpm
707 d.setVar('SBUILDSPEC', cmdsrpm + "\n")
708 d.setVarFlag('SBUILDSPEC', 'func', '1')
709 bb.build.exec_func('SBUILDSPEC', d)
710 cmd = cmd + " -bb " + outspecfile
711
712 # rpm 4 creates various empty directories in _topdir, let's clean them up
713 cleanupcmd = "rm -rf %s/BUILDROOT %s/SOURCES %s/SPECS %s/SRPMS" % (workdir, workdir, workdir, workdir)
714
715 # Build the rpm package!
716 d.setVar('BUILDSPEC', cmd + "\n" + cleanupcmd + "\n")
717 d.setVarFlag('BUILDSPEC', 'func', '1')
718 bb.build.exec_func('BUILDSPEC', d)
719
720 if d.getVar('RPM_SIGN_PACKAGES') == '1':
721 bb.build.exec_func("sign_rpm", d)
722}
723
724python () {
725 if d.getVar('PACKAGES') != '':
726 deps = ' rpm-native:do_populate_sysroot virtual/fakeroot-native:do_populate_sysroot'
727 d.appendVarFlag('do_package_write_rpm', 'depends', deps)
728 d.setVarFlag('do_package_write_rpm', 'fakeroot', '1')
Patrick Williamsac13d5f2023-11-24 18:59:46 -0600729
730 # Needed to ensure PKG_xxx renaming of dependency packages works
731 d.setVarFlag('do_package_write_rpm', 'deptask', "do_packagedata")
732 d.setVarFlag('do_package_write_rpm', 'rdeptask', "do_packagedata")
Patrick Williams92b42cb2022-09-03 06:53:57 -0500733}
734
735SSTATETASKS += "do_package_write_rpm"
736do_package_write_rpm[sstate-inputdirs] = "${PKGWRITEDIRRPM}"
737do_package_write_rpm[sstate-outputdirs] = "${DEPLOY_DIR_RPM}"
738# Take a shared lock, we can write multiple packages at the same time...
739# but we need to stop the rootfs/solver from running while we do...
740do_package_write_rpm[sstate-lockfile-shared] += "${DEPLOY_DIR_RPM}/rpm.lock"
741
742python do_package_write_rpm_setscene () {
743 sstate_setscene(d)
744}
745addtask do_package_write_rpm_setscene
746
747python do_package_write_rpm () {
748 bb.build.exec_func("read_subpackage_metadata", d)
749 bb.build.exec_func("do_package_rpm", d)
750}
751
752do_package_write_rpm[dirs] = "${PKGWRITEDIRRPM}"
753do_package_write_rpm[cleandirs] = "${PKGWRITEDIRRPM}"
754do_package_write_rpm[depends] += "${@oe.utils.build_depends_string(d.getVar('PACKAGE_WRITE_DEPS'), 'do_populate_sysroot')}"
755addtask package_write_rpm after do_packagedata do_package do_deploy_source_date_epoch before do_build
756do_build[rdeptask] += "do_package_write_rpm"
757
758PACKAGEINDEXDEPS += "rpm-native:do_populate_sysroot"
759PACKAGEINDEXDEPS += "createrepo-c-native:do_populate_sysroot"