Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/setuptools/_distutils/command/bdist_rpm.py @ 0:4f3585e2f14b draft default tip
"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author | shellac |
---|---|
date | Mon, 22 Mar 2021 18:12:50 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4f3585e2f14b |
---|---|
1 """distutils.command.bdist_rpm | |
2 | |
3 Implements the Distutils 'bdist_rpm' command (create RPM source and binary | |
4 distributions).""" | |
5 | |
6 import subprocess, sys, os | |
7 from distutils.core import Command | |
8 from distutils.debug import DEBUG | |
9 from distutils.file_util import write_file | |
10 from distutils.errors import * | |
11 from distutils.sysconfig import get_python_version | |
12 from distutils import log | |
13 | |
14 class bdist_rpm(Command): | |
15 | |
16 description = "create an RPM distribution" | |
17 | |
18 user_options = [ | |
19 ('bdist-base=', None, | |
20 "base directory for creating built distributions"), | |
21 ('rpm-base=', None, | |
22 "base directory for creating RPMs (defaults to \"rpm\" under " | |
23 "--bdist-base; must be specified for RPM 2)"), | |
24 ('dist-dir=', 'd', | |
25 "directory to put final RPM files in " | |
26 "(and .spec files if --spec-only)"), | |
27 ('python=', None, | |
28 "path to Python interpreter to hard-code in the .spec file " | |
29 "(default: \"python\")"), | |
30 ('fix-python', None, | |
31 "hard-code the exact path to the current Python interpreter in " | |
32 "the .spec file"), | |
33 ('spec-only', None, | |
34 "only regenerate spec file"), | |
35 ('source-only', None, | |
36 "only generate source RPM"), | |
37 ('binary-only', None, | |
38 "only generate binary RPM"), | |
39 ('use-bzip2', None, | |
40 "use bzip2 instead of gzip to create source distribution"), | |
41 | |
42 # More meta-data: too RPM-specific to put in the setup script, | |
43 # but needs to go in the .spec file -- so we make these options | |
44 # to "bdist_rpm". The idea is that packagers would put this | |
45 # info in setup.cfg, although they are of course free to | |
46 # supply it on the command line. | |
47 ('distribution-name=', None, | |
48 "name of the (Linux) distribution to which this " | |
49 "RPM applies (*not* the name of the module distribution!)"), | |
50 ('group=', None, | |
51 "package classification [default: \"Development/Libraries\"]"), | |
52 ('release=', None, | |
53 "RPM release number"), | |
54 ('serial=', None, | |
55 "RPM serial number"), | |
56 ('vendor=', None, | |
57 "RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") " | |
58 "[default: maintainer or author from setup script]"), | |
59 ('packager=', None, | |
60 "RPM packager (eg. \"Jane Doe <jane@example.net>\") " | |
61 "[default: vendor]"), | |
62 ('doc-files=', None, | |
63 "list of documentation files (space or comma-separated)"), | |
64 ('changelog=', None, | |
65 "RPM changelog"), | |
66 ('icon=', None, | |
67 "name of icon file"), | |
68 ('provides=', None, | |
69 "capabilities provided by this package"), | |
70 ('requires=', None, | |
71 "capabilities required by this package"), | |
72 ('conflicts=', None, | |
73 "capabilities which conflict with this package"), | |
74 ('build-requires=', None, | |
75 "capabilities required to build this package"), | |
76 ('obsoletes=', None, | |
77 "capabilities made obsolete by this package"), | |
78 ('no-autoreq', None, | |
79 "do not automatically calculate dependencies"), | |
80 | |
81 # Actions to take when building RPM | |
82 ('keep-temp', 'k', | |
83 "don't clean up RPM build directory"), | |
84 ('no-keep-temp', None, | |
85 "clean up RPM build directory [default]"), | |
86 ('use-rpm-opt-flags', None, | |
87 "compile with RPM_OPT_FLAGS when building from source RPM"), | |
88 ('no-rpm-opt-flags', None, | |
89 "do not pass any RPM CFLAGS to compiler"), | |
90 ('rpm3-mode', None, | |
91 "RPM 3 compatibility mode (default)"), | |
92 ('rpm2-mode', None, | |
93 "RPM 2 compatibility mode"), | |
94 | |
95 # Add the hooks necessary for specifying custom scripts | |
96 ('prep-script=', None, | |
97 "Specify a script for the PREP phase of RPM building"), | |
98 ('build-script=', None, | |
99 "Specify a script for the BUILD phase of RPM building"), | |
100 | |
101 ('pre-install=', None, | |
102 "Specify a script for the pre-INSTALL phase of RPM building"), | |
103 ('install-script=', None, | |
104 "Specify a script for the INSTALL phase of RPM building"), | |
105 ('post-install=', None, | |
106 "Specify a script for the post-INSTALL phase of RPM building"), | |
107 | |
108 ('pre-uninstall=', None, | |
109 "Specify a script for the pre-UNINSTALL phase of RPM building"), | |
110 ('post-uninstall=', None, | |
111 "Specify a script for the post-UNINSTALL phase of RPM building"), | |
112 | |
113 ('clean-script=', None, | |
114 "Specify a script for the CLEAN phase of RPM building"), | |
115 | |
116 ('verify-script=', None, | |
117 "Specify a script for the VERIFY phase of the RPM build"), | |
118 | |
119 # Allow a packager to explicitly force an architecture | |
120 ('force-arch=', None, | |
121 "Force an architecture onto the RPM build process"), | |
122 | |
123 ('quiet', 'q', | |
124 "Run the INSTALL phase of RPM building in quiet mode"), | |
125 ] | |
126 | |
127 boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode', | |
128 'no-autoreq', 'quiet'] | |
129 | |
130 negative_opt = {'no-keep-temp': 'keep-temp', | |
131 'no-rpm-opt-flags': 'use-rpm-opt-flags', | |
132 'rpm2-mode': 'rpm3-mode'} | |
133 | |
134 | |
135 def initialize_options(self): | |
136 self.bdist_base = None | |
137 self.rpm_base = None | |
138 self.dist_dir = None | |
139 self.python = None | |
140 self.fix_python = None | |
141 self.spec_only = None | |
142 self.binary_only = None | |
143 self.source_only = None | |
144 self.use_bzip2 = None | |
145 | |
146 self.distribution_name = None | |
147 self.group = None | |
148 self.release = None | |
149 self.serial = None | |
150 self.vendor = None | |
151 self.packager = None | |
152 self.doc_files = None | |
153 self.changelog = None | |
154 self.icon = None | |
155 | |
156 self.prep_script = None | |
157 self.build_script = None | |
158 self.install_script = None | |
159 self.clean_script = None | |
160 self.verify_script = None | |
161 self.pre_install = None | |
162 self.post_install = None | |
163 self.pre_uninstall = None | |
164 self.post_uninstall = None | |
165 self.prep = None | |
166 self.provides = None | |
167 self.requires = None | |
168 self.conflicts = None | |
169 self.build_requires = None | |
170 self.obsoletes = None | |
171 | |
172 self.keep_temp = 0 | |
173 self.use_rpm_opt_flags = 1 | |
174 self.rpm3_mode = 1 | |
175 self.no_autoreq = 0 | |
176 | |
177 self.force_arch = None | |
178 self.quiet = 0 | |
179 | |
180 def finalize_options(self): | |
181 self.set_undefined_options('bdist', ('bdist_base', 'bdist_base')) | |
182 if self.rpm_base is None: | |
183 if not self.rpm3_mode: | |
184 raise DistutilsOptionError( | |
185 "you must specify --rpm-base in RPM 2 mode") | |
186 self.rpm_base = os.path.join(self.bdist_base, "rpm") | |
187 | |
188 if self.python is None: | |
189 if self.fix_python: | |
190 self.python = sys.executable | |
191 else: | |
192 self.python = "python3" | |
193 elif self.fix_python: | |
194 raise DistutilsOptionError( | |
195 "--python and --fix-python are mutually exclusive options") | |
196 | |
197 if os.name != 'posix': | |
198 raise DistutilsPlatformError("don't know how to create RPM " | |
199 "distributions on platform %s" % os.name) | |
200 if self.binary_only and self.source_only: | |
201 raise DistutilsOptionError( | |
202 "cannot supply both '--source-only' and '--binary-only'") | |
203 | |
204 # don't pass CFLAGS to pure python distributions | |
205 if not self.distribution.has_ext_modules(): | |
206 self.use_rpm_opt_flags = 0 | |
207 | |
208 self.set_undefined_options('bdist', ('dist_dir', 'dist_dir')) | |
209 self.finalize_package_data() | |
210 | |
211 def finalize_package_data(self): | |
212 self.ensure_string('group', "Development/Libraries") | |
213 self.ensure_string('vendor', | |
214 "%s <%s>" % (self.distribution.get_contact(), | |
215 self.distribution.get_contact_email())) | |
216 self.ensure_string('packager') | |
217 self.ensure_string_list('doc_files') | |
218 if isinstance(self.doc_files, list): | |
219 for readme in ('README', 'README.txt'): | |
220 if os.path.exists(readme) and readme not in self.doc_files: | |
221 self.doc_files.append(readme) | |
222 | |
223 self.ensure_string('release', "1") | |
224 self.ensure_string('serial') # should it be an int? | |
225 | |
226 self.ensure_string('distribution_name') | |
227 | |
228 self.ensure_string('changelog') | |
229 # Format changelog correctly | |
230 self.changelog = self._format_changelog(self.changelog) | |
231 | |
232 self.ensure_filename('icon') | |
233 | |
234 self.ensure_filename('prep_script') | |
235 self.ensure_filename('build_script') | |
236 self.ensure_filename('install_script') | |
237 self.ensure_filename('clean_script') | |
238 self.ensure_filename('verify_script') | |
239 self.ensure_filename('pre_install') | |
240 self.ensure_filename('post_install') | |
241 self.ensure_filename('pre_uninstall') | |
242 self.ensure_filename('post_uninstall') | |
243 | |
244 # XXX don't forget we punted on summaries and descriptions -- they | |
245 # should be handled here eventually! | |
246 | |
247 # Now *this* is some meta-data that belongs in the setup script... | |
248 self.ensure_string_list('provides') | |
249 self.ensure_string_list('requires') | |
250 self.ensure_string_list('conflicts') | |
251 self.ensure_string_list('build_requires') | |
252 self.ensure_string_list('obsoletes') | |
253 | |
254 self.ensure_string('force_arch') | |
255 | |
256 def run(self): | |
257 if DEBUG: | |
258 print("before _get_package_data():") | |
259 print("vendor =", self.vendor) | |
260 print("packager =", self.packager) | |
261 print("doc_files =", self.doc_files) | |
262 print("changelog =", self.changelog) | |
263 | |
264 # make directories | |
265 if self.spec_only: | |
266 spec_dir = self.dist_dir | |
267 self.mkpath(spec_dir) | |
268 else: | |
269 rpm_dir = {} | |
270 for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): | |
271 rpm_dir[d] = os.path.join(self.rpm_base, d) | |
272 self.mkpath(rpm_dir[d]) | |
273 spec_dir = rpm_dir['SPECS'] | |
274 | |
275 # Spec file goes into 'dist_dir' if '--spec-only specified', | |
276 # build/rpm.<plat> otherwise. | |
277 spec_path = os.path.join(spec_dir, | |
278 "%s.spec" % self.distribution.get_name()) | |
279 self.execute(write_file, | |
280 (spec_path, | |
281 self._make_spec_file()), | |
282 "writing '%s'" % spec_path) | |
283 | |
284 if self.spec_only: # stop if requested | |
285 return | |
286 | |
287 # Make a source distribution and copy to SOURCES directory with | |
288 # optional icon. | |
289 saved_dist_files = self.distribution.dist_files[:] | |
290 sdist = self.reinitialize_command('sdist') | |
291 if self.use_bzip2: | |
292 sdist.formats = ['bztar'] | |
293 else: | |
294 sdist.formats = ['gztar'] | |
295 self.run_command('sdist') | |
296 self.distribution.dist_files = saved_dist_files | |
297 | |
298 source = sdist.get_archive_files()[0] | |
299 source_dir = rpm_dir['SOURCES'] | |
300 self.copy_file(source, source_dir) | |
301 | |
302 if self.icon: | |
303 if os.path.exists(self.icon): | |
304 self.copy_file(self.icon, source_dir) | |
305 else: | |
306 raise DistutilsFileError( | |
307 "icon file '%s' does not exist" % self.icon) | |
308 | |
309 # build package | |
310 log.info("building RPMs") | |
311 rpm_cmd = ['rpmbuild'] | |
312 | |
313 if self.source_only: # what kind of RPMs? | |
314 rpm_cmd.append('-bs') | |
315 elif self.binary_only: | |
316 rpm_cmd.append('-bb') | |
317 else: | |
318 rpm_cmd.append('-ba') | |
319 rpm_cmd.extend(['--define', '__python %s' % self.python]) | |
320 if self.rpm3_mode: | |
321 rpm_cmd.extend(['--define', | |
322 '_topdir %s' % os.path.abspath(self.rpm_base)]) | |
323 if not self.keep_temp: | |
324 rpm_cmd.append('--clean') | |
325 | |
326 if self.quiet: | |
327 rpm_cmd.append('--quiet') | |
328 | |
329 rpm_cmd.append(spec_path) | |
330 # Determine the binary rpm names that should be built out of this spec | |
331 # file | |
332 # Note that some of these may not be really built (if the file | |
333 # list is empty) | |
334 nvr_string = "%{name}-%{version}-%{release}" | |
335 src_rpm = nvr_string + ".src.rpm" | |
336 non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm" | |
337 q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % ( | |
338 src_rpm, non_src_rpm, spec_path) | |
339 | |
340 out = os.popen(q_cmd) | |
341 try: | |
342 binary_rpms = [] | |
343 source_rpm = None | |
344 while True: | |
345 line = out.readline() | |
346 if not line: | |
347 break | |
348 l = line.strip().split() | |
349 assert(len(l) == 2) | |
350 binary_rpms.append(l[1]) | |
351 # The source rpm is named after the first entry in the spec file | |
352 if source_rpm is None: | |
353 source_rpm = l[0] | |
354 | |
355 status = out.close() | |
356 if status: | |
357 raise DistutilsExecError("Failed to execute: %s" % repr(q_cmd)) | |
358 | |
359 finally: | |
360 out.close() | |
361 | |
362 self.spawn(rpm_cmd) | |
363 | |
364 if not self.dry_run: | |
365 if self.distribution.has_ext_modules(): | |
366 pyversion = get_python_version() | |
367 else: | |
368 pyversion = 'any' | |
369 | |
370 if not self.binary_only: | |
371 srpm = os.path.join(rpm_dir['SRPMS'], source_rpm) | |
372 assert(os.path.exists(srpm)) | |
373 self.move_file(srpm, self.dist_dir) | |
374 filename = os.path.join(self.dist_dir, source_rpm) | |
375 self.distribution.dist_files.append( | |
376 ('bdist_rpm', pyversion, filename)) | |
377 | |
378 if not self.source_only: | |
379 for rpm in binary_rpms: | |
380 rpm = os.path.join(rpm_dir['RPMS'], rpm) | |
381 if os.path.exists(rpm): | |
382 self.move_file(rpm, self.dist_dir) | |
383 filename = os.path.join(self.dist_dir, | |
384 os.path.basename(rpm)) | |
385 self.distribution.dist_files.append( | |
386 ('bdist_rpm', pyversion, filename)) | |
387 | |
388 def _dist_path(self, path): | |
389 return os.path.join(self.dist_dir, os.path.basename(path)) | |
390 | |
391 def _make_spec_file(self): | |
392 """Generate the text of an RPM spec file and return it as a | |
393 list of strings (one per line). | |
394 """ | |
395 # definitions and headers | |
396 spec_file = [ | |
397 '%define name ' + self.distribution.get_name(), | |
398 '%define version ' + self.distribution.get_version().replace('-','_'), | |
399 '%define unmangled_version ' + self.distribution.get_version(), | |
400 '%define release ' + self.release.replace('-','_'), | |
401 '', | |
402 'Summary: ' + self.distribution.get_description(), | |
403 ] | |
404 | |
405 # Workaround for #14443 which affects some RPM based systems such as | |
406 # RHEL6 (and probably derivatives) | |
407 vendor_hook = subprocess.getoutput('rpm --eval %{__os_install_post}') | |
408 # Generate a potential replacement value for __os_install_post (whilst | |
409 # normalizing the whitespace to simplify the test for whether the | |
410 # invocation of brp-python-bytecompile passes in __python): | |
411 vendor_hook = '\n'.join([' %s \\' % line.strip() | |
412 for line in vendor_hook.splitlines()]) | |
413 problem = "brp-python-bytecompile \\\n" | |
414 fixed = "brp-python-bytecompile %{__python} \\\n" | |
415 fixed_hook = vendor_hook.replace(problem, fixed) | |
416 if fixed_hook != vendor_hook: | |
417 spec_file.append('# Workaround for http://bugs.python.org/issue14443') | |
418 spec_file.append('%define __os_install_post ' + fixed_hook + '\n') | |
419 | |
420 # put locale summaries into spec file | |
421 # XXX not supported for now (hard to put a dictionary | |
422 # in a config file -- arg!) | |
423 #for locale in self.summaries.keys(): | |
424 # spec_file.append('Summary(%s): %s' % (locale, | |
425 # self.summaries[locale])) | |
426 | |
427 spec_file.extend([ | |
428 'Name: %{name}', | |
429 'Version: %{version}', | |
430 'Release: %{release}',]) | |
431 | |
432 # XXX yuck! this filename is available from the "sdist" command, | |
433 # but only after it has run: and we create the spec file before | |
434 # running "sdist", in case of --spec-only. | |
435 if self.use_bzip2: | |
436 spec_file.append('Source0: %{name}-%{unmangled_version}.tar.bz2') | |
437 else: | |
438 spec_file.append('Source0: %{name}-%{unmangled_version}.tar.gz') | |
439 | |
440 spec_file.extend([ | |
441 'License: ' + self.distribution.get_license(), | |
442 'Group: ' + self.group, | |
443 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', | |
444 'Prefix: %{_prefix}', ]) | |
445 | |
446 if not self.force_arch: | |
447 # noarch if no extension modules | |
448 if not self.distribution.has_ext_modules(): | |
449 spec_file.append('BuildArch: noarch') | |
450 else: | |
451 spec_file.append( 'BuildArch: %s' % self.force_arch ) | |
452 | |
453 for field in ('Vendor', | |
454 'Packager', | |
455 'Provides', | |
456 'Requires', | |
457 'Conflicts', | |
458 'Obsoletes', | |
459 ): | |
460 val = getattr(self, field.lower()) | |
461 if isinstance(val, list): | |
462 spec_file.append('%s: %s' % (field, ' '.join(val))) | |
463 elif val is not None: | |
464 spec_file.append('%s: %s' % (field, val)) | |
465 | |
466 | |
467 if self.distribution.get_url() != 'UNKNOWN': | |
468 spec_file.append('Url: ' + self.distribution.get_url()) | |
469 | |
470 if self.distribution_name: | |
471 spec_file.append('Distribution: ' + self.distribution_name) | |
472 | |
473 if self.build_requires: | |
474 spec_file.append('BuildRequires: ' + | |
475 ' '.join(self.build_requires)) | |
476 | |
477 if self.icon: | |
478 spec_file.append('Icon: ' + os.path.basename(self.icon)) | |
479 | |
480 if self.no_autoreq: | |
481 spec_file.append('AutoReq: 0') | |
482 | |
483 spec_file.extend([ | |
484 '', | |
485 '%description', | |
486 self.distribution.get_long_description() | |
487 ]) | |
488 | |
489 # put locale descriptions into spec file | |
490 # XXX again, suppressed because config file syntax doesn't | |
491 # easily support this ;-( | |
492 #for locale in self.descriptions.keys(): | |
493 # spec_file.extend([ | |
494 # '', | |
495 # '%description -l ' + locale, | |
496 # self.descriptions[locale], | |
497 # ]) | |
498 | |
499 # rpm scripts | |
500 # figure out default build script | |
501 def_setup_call = "%s %s" % (self.python,os.path.basename(sys.argv[0])) | |
502 def_build = "%s build" % def_setup_call | |
503 if self.use_rpm_opt_flags: | |
504 def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build | |
505 | |
506 # insert contents of files | |
507 | |
508 # XXX this is kind of misleading: user-supplied options are files | |
509 # that we open and interpolate into the spec file, but the defaults | |
510 # are just text that we drop in as-is. Hmmm. | |
511 | |
512 install_cmd = ('%s install -O1 --root=$RPM_BUILD_ROOT ' | |
513 '--record=INSTALLED_FILES') % def_setup_call | |
514 | |
515 script_options = [ | |
516 ('prep', 'prep_script', "%setup -n %{name}-%{unmangled_version}"), | |
517 ('build', 'build_script', def_build), | |
518 ('install', 'install_script', install_cmd), | |
519 ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"), | |
520 ('verifyscript', 'verify_script', None), | |
521 ('pre', 'pre_install', None), | |
522 ('post', 'post_install', None), | |
523 ('preun', 'pre_uninstall', None), | |
524 ('postun', 'post_uninstall', None), | |
525 ] | |
526 | |
527 for (rpm_opt, attr, default) in script_options: | |
528 # Insert contents of file referred to, if no file is referred to | |
529 # use 'default' as contents of script | |
530 val = getattr(self, attr) | |
531 if val or default: | |
532 spec_file.extend([ | |
533 '', | |
534 '%' + rpm_opt,]) | |
535 if val: | |
536 with open(val) as f: | |
537 spec_file.extend(f.read().split('\n')) | |
538 else: | |
539 spec_file.append(default) | |
540 | |
541 | |
542 # files section | |
543 spec_file.extend([ | |
544 '', | |
545 '%files -f INSTALLED_FILES', | |
546 '%defattr(-,root,root)', | |
547 ]) | |
548 | |
549 if self.doc_files: | |
550 spec_file.append('%doc ' + ' '.join(self.doc_files)) | |
551 | |
552 if self.changelog: | |
553 spec_file.extend([ | |
554 '', | |
555 '%changelog',]) | |
556 spec_file.extend(self.changelog) | |
557 | |
558 return spec_file | |
559 | |
560 def _format_changelog(self, changelog): | |
561 """Format the changelog correctly and convert it to a list of strings | |
562 """ | |
563 if not changelog: | |
564 return changelog | |
565 new_changelog = [] | |
566 for line in changelog.strip().split('\n'): | |
567 line = line.strip() | |
568 if line[0] == '*': | |
569 new_changelog.extend(['', line]) | |
570 elif line[0] == '-': | |
571 new_changelog.append(line) | |
572 else: | |
573 new_changelog.append(' ' + line) | |
574 | |
575 # strip trailing newline inserted by first changelog entry | |
576 if not new_changelog[0]: | |
577 del new_changelog[0] | |
578 | |
579 return new_changelog |