def update_spec(spec_file, commit_hash, archive_name, packager, email, reader): ''' Update the release tag and changelog of the specified spec file to work with the specified commit_hash. ''' LOG.debug('Update spec file: %s', spec_file) release = '%s%s%s' % (date.today().strftime('%Y%m%d'), reader.short, commit_hash) output = [] version = None rpm.spec(spec_file) with open(spec_file) as stream: for row in stream: row = row.rstrip() if row.startswith('Version:'): version = row.split('Version:')[1].strip() if row.startswith('Release:'): if commit_hash in row: raise DgrocException('Spec already up to date') LOG.debug('Release line before: %s', row) rel_num = row.split('ase:')[1].strip().split('%{?dist')[0] rel_list = rel_num.split('.') if reader.short in rel_list[-1]: rel_list = rel_list[:-1] if rel_list[-1].isdigit(): rel_list[-1] = str(int(rel_list[-1]) + 1) rel_num = '.'.join(rel_list) LOG.debug('Release number: %s', rel_num) row = 'Release: %s.%s%%{?dist}' % (rel_num, release) LOG.debug('Release line after: %s', row) if row.startswith('Source0:'): row = 'Source0: %s' % (archive_name) LOG.debug('Source0 line after: %s', row) if row.startswith('%changelog'): output.append(row) output.append( rpm.expandMacro( '* %s %s <%s> - %s-%s.%s' % (date.today().strftime('%a %b %d %Y'), packager, email, version, rel_num, release))) output.append('- Update to %s: %s' % (reader.short, commit_hash)) row = '' output.append(row) with open(spec_file, 'w') as stream: for row in output: stream.write(row + '\n') LOG.info('Spec file updated: %s', spec_file)
def main(self): """ The main function.""" parser = setup_parser() args = parser.parse_args() if not args.test: bzurl = 'https://bugzilla.redhat.com' else: bzurl = 'https://partner-bugzilla.redhat.com' self.bzclient = RHBugzilla3(url="%s/xmlrpc.cgi" % bzurl) self.srpmfile = os.path.expanduser(args.srpmfile) self.specfile = os.path.expanduser(args.specfile) self.spec = rpm.spec(self.specfile) if not args.no_build: (output_build, returncode) = self.do_scratch_build(target=args.koji_target) if returncode != 0: raise FedoraCreateReviewError( 'Something happened while trying to build this package on koji: \n %s' % output_build) self.info['summary'] = self.retrieve_summary() self.info['description'] = self.retrieve_description() self.info['name'] = self.retrieve_name() self.find_existing_reviews() (output_upload, returncode) = self.upload_files() if returncode != 0: raise FedoraCreateReviewError( 'Something happened while uploading the files:\n %s' % output_upload) self.fill_urls() bug = self.create_review_request(args.rename_request) if not args.no_build: self.add_comment_build(output_build, bug) print 'Review created at: %s/show_bug.cgi?id=%s' % (bzurl, bug.id) print bug
def _update_data(self): """ Function updates data from given SPEC file :return: """ # Load rpm information try: self.spc = rpm.spec(self.path) except ValueError: raise RebaseHelperError("Problem with parsing SPEC file '%s'" % self.path) self.sources = self._get_spec_sources_list(self.spc) self.prep_section = self.spc.prep # HEADER of SPEC file self.hdr = self.spc.sourceHeader self.rpm_sections = self._split_sections() # determine the extra_version logger.debug("Updating the extra version") _, self.extra_version, separator = SpecFile.extract_version_from_archive_name( self.get_archive(), self._get_raw_source_string(0)) self.set_extra_version_separator(separator) self.patches = self._get_initial_patches_list() self.macros = MacroHelper.dump() # TODO: don't call this at all in SPEC file methods if self.download: self.download_remote_sources()
def main(self): """ The main function.""" parser = setup_parser() args = parser.parse_args() if not args.test: bzurl = 'https://bugzilla.redhat.com' else: bzurl = 'https://partner-bugzilla.redhat.com' self.bzclient = RHBugzilla3(url="%s/xmlrpc.cgi" % bzurl) self.srpmfile = os.path.expanduser(args.srpmfile) self.specfile = os.path.expanduser(args.specfile) self.spec = rpm.spec(self.specfile) if not args.no_build: (output_build, returncode) = self.do_scratch_build( target=args.koji_target) if returncode != 0: raise FedoraCreateReviewError( 'Something happened while trying to build this package on koji: \n %s' % output_build) self.info['summary'] = self.retrieve_summary() self.info['description'] = self.retrieve_description() self.info['name'] = self.retrieve_name() self.find_existing_reviews() (output_upload, returncode) = self.upload_files() if returncode != 0: raise FedoraCreateReviewError( 'Something happened while uploading the files:\n %s' % output_upload) self.fill_urls() bug = self.create_review_request(args.rename_request) if not args.no_build: self.add_comment_build(output_build, bug) print 'Review created at: %s/show_bug.cgi?id=%s' % (bzurl, bug.id) print bug
def get_source0_url_from_rpmspec(rpmspec): """ Parse given rpm spec and return (source0's url, source0). It may throw ValuError("can't parse specfile"), etc. :param rpmspec: Path to the RPM SPEC file :return: (URL_of_source0, source0_basename) """ spec = rpm.spec(rpmspec) # looks rpmSourceFlags_e in <rpm/rpmspec.h>: is_source = lambda t: t[-1] == 1 << 0 src0 = [s for s in spec.sources if is_source(s)][0][0] assert src0, "SOURCE0 should not be empty!" # 1. Try SOURCE0: if re.match(r"^(ftp|http|https)://", src0): logging.debug("URL=" + src0) url_src0 = (src0, os.path.basename(src0)) else: # 2. Try URL + basename(src0): base_url = spec.sourceHeader["URL"] assert base_url, "URL should not be empty!" url_src0 = (os.path.join(base_url, src0), src0) logging.debug("URL=%s, src0=%s" % url_src0) return url_src0
def _update_data(self): """ Function updates data from given SPEC file :return: """ # Load rpm information try: self.spc = rpm.spec(self.path) except ValueError: raise RebaseHelperError("Problem with parsing SPEC file '%s'" % self.path) self.prep_section = self.spc.prep # HEADER of SPEC file self.hdr = self.spc.sourceHeader # All source file mentioned in SPEC file Source[0-9]* self.rpm_sections = self._split_sections() # determine the extra_version logger.debug("Updating the extra version") self.sources, self.tar_sources = self._get_initial_sources_list() _, self.extra_version, separator = SpecFile.extract_version_from_archive_name( self.get_archive(), self._get_raw_source_string(0)) self.set_extra_version_separator(separator) self.patches = self._get_initial_patches_list() self.macros = MacroHelper.dump()
def _copy_startup_scripts(self, spec_filename): common_init_content = utils.load_template("packaging", "common.init")[1] for src in rpm.spec(spec_filename).sources: script = sh.basename(src[0]) if not (script.endswith(".init")): continue target_filename = sh.joinpths(self.rpm_sources_dir, script) if sh.isfile(target_filename): continue bin_name = utils.strip_prefix_suffix(script, "openstack-", ".init") if bin_name == "quantum-server": daemon_args = ("'--config-file=/etc/quantum/plugin.ini" " --config-file=/etc/quantum/quantum.conf'") elif bin_name == "quantum-l3-agent": daemon_args = ("'--config-file=/etc/quantum/l3_agent.ini" " --config-file=/etc/quantum/quantum.conf'") elif bin_name == "quantum-dhcp-agent": daemon_args = ("'--config-file=/etc/quantum/dhcp_agent.ini" " --config-file=/etc/quantum/quantum.conf'") else: daemon_args = "" params = { "bin": bin_name, "package": bin_name.split("-", 1)[0], "daemon_args": daemon_args, } sh.write_file(target_filename, utils.expand_template(common_init_content, params))
def parse_spec(spec_file): """Simple wrapper around rpm.spec that catches errors printed to stdout :param spec_file: spec file name :returns: rpm.spec object instance :raises: ValueError in case parsing failed """ with NamedTemporaryFile(mode="w+") as tmplog: # rpm will print errors to stdout if logfile is not set rpm.setLogFile(tmplog) try: rpm.spec(spec_file) except ValueError as exc: # re-raise errors with rpm output appended to message raise ValueError(str(exc) + open(tmplog.name, 'r').read())
def build_rpm(self): package_name = "".join( random.choice(string.ascii_lowercase) for _ in range(12)) # Fill template spec_data = self.DEFAULT_SPEC_DATA spec_data["name"] = package_name spec_data["files"] = f"/{package_name}.bin" # Create spec rendered = self.template.render(**spec_data) tmp_spec_path = os.path.join(self.tmp_dir, "SPECS", f"{package_name}.spec") with open(tmp_spec_path, "w") as tmp_spec: tmp_spec.write(rendered) # Create build dirs build_root = rpm.expandMacro("%{buildroot}") if not os.path.exists(build_root): os.makedirs(build_root) # Writing binary file tmp_bin_path = os.path.join(build_root, f"{package_name}.bin") with open(tmp_bin_path, "wb") as tmp_bin: tmp_bin.write(os.urandom(1024 * 1024)) # size in bytes # Generating rpm build_amount = rpm.RPMBUILD_PACKAGEBINARY | rpm.RPMBUILD_RMBUILD | rpm.RPMBUILD_CLEAN sp = rpm.spec(specfile=tmp_spec_path) sp._doBuild(ts=self.ts, buildAmount=build_amount)
def prepare_spec(spec, version, release, prefix, patches): """Modify spec according our needs. :param str spec: Path to RPM spec :param str version: Version :param str release: Release :param str prefix: Archive prefix :param rgo.git.PatchesAction patches: What to do with patches :return: Patches RPM spec :rtype: str """ import rpm rpmspec = rpm.spec(spec) # TODO: currently we don't support multiple sources if len([x for _, _, x in rpmspec.sources if x == rpm.RPMBUILD_ISSOURCE]) > 1: raise NotImplementedError with open(spec, "r") as specfile: _spec = specfile.readlines() out = [] patch_tag_re = re.compile(r"^Patch\d+:") source_tag_re = re.compile(r"^Source(\d*):") patch_apply_re = re.compile(r"^%patch\d+") for line in _spec: line = line.rstrip() if line.startswith("Version:"): line = "Version: {!s}".format(version) elif line.startswith("Release:"): line = "Release: {!s}%{{?dist}}".format(release) elif line.startswith("Patch"): match = patch_tag_re.match(line) if match and patches == PatchesAction.drop: continue elif line.startswith("Source"): match = source_tag_re.match(line) if match: archive = "{!s}.tar.xz".format(prefix) if not match.group(1): line = "Source: {!s}".format(archive) else: line = "Source{:d}: {!s}".format(int(match.group(1)), archive) elif line.startswith(("%setup", "%autosetup")): line = "{!s} -n {!s}".format(line, prefix) elif line.startswith("%patch"): match = patch_apply_re.match(line) if match and patches == PatchesAction.drop: continue elif line.startswith("%autopatch"): if patches == PatchesAction.drop: continue elif line == "%changelog": # Wipe out changelog break out.append(line) return "\n".join(out)
def update_spec(spec_file, commit_hash, archive_name, packager, email, reader): ''' Update the release tag and changelog of the specified spec file to work with the specified commit_hash. ''' LOG.debug('Update spec file: %s', spec_file) release = '%s%s%s' % (date.today().strftime('%Y%m%d'), reader.short, commit_hash) output = [] version = None rpm.spec(spec_file) with open(spec_file) as stream: for row in stream: row = row.rstrip() if row.startswith('Version:'): version = row.split('Version:')[1].strip() if row.startswith('Release:'): if commit_hash in row: raise DgrocException('Spec already up to date') LOG.debug('Release line before: %s', row) rel_num = row.split('ase:')[1].strip().split('%{?dist')[0] rel_list = rel_num.split('.') if reader.short in rel_list[-1]: rel_list = rel_list[:-1] if rel_list[-1].isdigit(): rel_list[-1] = str(int(rel_list[-1])+1) rel_num = '.'.join(rel_list) LOG.debug('Release number: %s', rel_num) row = 'Release: %s.%s%%{?dist}' % (rel_num, release) LOG.debug('Release line after: %s', row) if row.startswith('Source0:'): row = 'Source0: %s' % (archive_name) LOG.debug('Source0 line after: %s', row) if row.startswith('%changelog'): output.append(row) output.append(rpm.expandMacro('* %s %s <%s> - %s-%s.%s' % ( date.today().strftime('%a %b %d %Y'), packager, email, version, rel_num, release) )) output.append('- Update to %s: %s' % (reader.short, commit_hash)) row = '' output.append(row) with open(spec_file, 'w') as stream: for row in output: stream.write(row + '\n') LOG.info('Spec file updated: %s', spec_file)
def parse_spec(cls, path, flags=None): with open(path, 'rb') as orig: with tempfile.NamedTemporaryFile() as tmp: # remove BuildArch to workaround rpm bug tmp.write(b''.join(l for l in orig.readlines() if not l.startswith(b'BuildArch'))) tmp.flush() capturer = None try: with ConsoleHelper.Capturer(stderr=True) as capturer: result = rpm.spec(tmp.name, flags) if flags is not None else rpm.spec(tmp.name) except ValueError as e: output = capturer.stderr.strip().split('\n') if capturer else [] if len(output) == 1: output = output[0] raise RebaseHelperError('Failed to parse SPEC file{0}'.format( ': ' + str(output) if output else '')) from e return result
def rpmspec(self): if not self._rpmspec: rpm.addMacro('_sourcedir', os.path.dirname(os.path.realpath(self.fn))) try: self._rpmspec = rpm.spec(self.fn) except ValueError, e: raise Exception("Error parsing spec: {0}".format(e))
def parse_spec(cls, path, flags=None): with open(path, 'rb') as orig: with tempfile.NamedTemporaryFile() as tmp: # remove BuildArch to workaround rpm bug tmp.write(b''.join([ l for l in orig.readlines() if not l.startswith(b'BuildArch') ])) tmp.flush() with ConsoleHelper.Capturer(stderr=True) as capturer: result = rpm.spec( tmp.name, flags) if flags is not None else rpm.spec( tmp.name) for line in capturer.stderr.split('\n'): if line: logger.verbose('rpm: %s', line) return result
def _spec_deps(self, spec_fn): try: spec = rpm.spec(spec_fn) except ValueError as e: msg = _("Failed to open: '%s', not a valid spec file.") % spec_fn raise dnf.exceptions.Error(msg) for dep in rpm.ds(spec.sourceHeader, 'requires'): reldep_str = self._rpm_dep2reldep_str(dep) self.base.install(reldep_str)
def load_rpmspec(self): if not RPM_AVAILABLE: raise exception.RpmModuleNotAvailable() rpm.addMacro('_sourcedir', os.path.dirname(os.path.realpath(self.fn))) try: self._rpmspec = rpm.spec(self.fn) except ValueError as e: raise exception.SpecFileParseError(spec_fn=self.fn, error=e.args[0])
def __init__(self, filename): # # Workaround for https://github.com/rpm-software-management/rpm/pull/1067 # See also http://lists.rpm.org/pipermail/rpm-list/2020-February/002012.html # rpm.reloadConfig() self._spec = rpm.spec(filename) self._filename = filename
def rpmspec(self): if not self._rpmspec: if not RPM_AVAILABLE: raise exception.RpmModuleNotAvailable() rpm.addMacro("_sourcedir", os.path.dirname(os.path.realpath(self.fn))) try: self._rpmspec = rpm.spec(self.fn) except ValueError, e: raise exception.SpecFileParseError(spec_fn=self.fn, error=e.args[0])
def get_package_info(path_to_spec): spec = rpm.spec(path_to_spec) hdr = spec.sourceHeader # need to process spec for requires # http://lists.rpm.org/pipermail/rpm-list/2011-September/000992.html ret = {} for field in ["name", "version", "release", "license", "summary", "url", "changelogtext"]: ret[field] = hdr[field] return ret
def installSpecDeps(self, spec_file): try: spec=rpm.spec(spec_file).sourceHeader.dsFromHeader() self.uid_manager.becomeUser(0, 0) for i in range(len(spec)): requirement_name = spec[i][2:] self.buildroot.pkg_manager.install(requirement_name, check=True) finally: self.uid_manager.restorePrivs()
def __init__(self, path, sources_location='', download=True): self.path = path self.download = download self.sources_location = sources_location # Read the content of the whole SPEC file self._read_spec_content() # Load rpm information self.spc = rpm.spec(self.path) self.patches = self._get_initial_patches_list() self._update_data()
def get_version_from_spec(spec_file): """ Return the version from the spec :param spec_file: The path to a spec file :type spec_file: str :return: Version field :rtype: str """ # Get the dep name & version spec = rpm.spec(spec_file) return spec.sourceHeader[rpm.RPMTAG_VERSION]
def _update_data(self): """ Function updates data from given SPEC file :return: """ # Load rpm information self.spc = rpm.spec(self.path) self.prep_section = rpm.spec(self.path).prep # HEADER of SPEC file self.hdr = self.spc.sourceHeader # All source file mentioned in SPEC file Source[0-9]* self.rpm_sections = self._split_sections() # determine the extra_version logger.debug("Updating the extra version") self.sources, self.tar_sources = self._get_initial_sources_list() self.extra_version = SpecFile.extract_version_from_archive_name(self.get_archive(), self._get_raw_source_string(0))[1] self.patches = self._get_initial_patches_list()
def installSpecDeps(self, spec_file): try: # pylint: disable=no-member spec = rpm.spec(spec_file).sourceHeader.dsFromHeader() self.uid_manager.becomeUser(0, 0) for i in range(len(spec)): # pylint: disable=consider-using-enumerate requirement_name = spec[i][2:] self.buildroot.pkg_manager.install(requirement_name, check=True) finally: self.uid_manager.restorePrivs()
def __init__(self, path, sources_location='', download=True): self.path = path self.download = download self.sources_location = sources_location # Read the content of the whole SPEC file rpm.addMacro("_sourcedir", self.sources_location) self._read_spec_content() # Load rpm information self.spc = rpm.spec(self.path) self.patches = self._get_initial_patches_list() self.set_extra_version_separator('') self._update_data()
def _spec_deps(self, spec_fn): try: spec = rpm.spec(spec_fn) except ValueError: msg = _("Failed to open: '%s', not a valid spec file.") % spec_fn raise dnf.exceptions.Error(msg) for dep in rpm.ds(spec.sourceHeader, 'requires'): reldep_str = self._rpm_dep2reldep_str(dep) try: self.base.install(reldep_str) except dnf.exceptions.MarkingError: msg = _("No matching package to install: '%s'") % reldep_str raise dnf.exceptions.Error(msg)
def _spec_deps(self, spec_fn): try: spec = rpm.spec(spec_fn) except ValueError: msg = _("Failed to open: '%s', not a valid spec file.") % spec_fn raise dnf.exceptions.Error(msg) done = True for dep in rpm.ds(spec.sourceHeader, 'requires'): reldep_str = self._rpm_dep2reldep_str(dep) done &= self._install(reldep_str) if not done: err = _("Not all dependencies satisfied") raise dnf.exceptions.Error(err)
def parse_spec(self): version = 0 if not os.path.exists(self.spec_filename): print_fail("No spec file") return False # open spec file try: spec = rpm.spec(self.spec_filename) self.version = spec.sourceHeader["version"] except ValueError as e: print_fail("Can't parse spec file") return False return True
def get_release_from_spec(spec_file): """ Return the release from a spec file :param spec_file: The path to a spec file :type spec_file: str :return: Release field without the dist macro :rtype: str """ # Get the dep name & version spec = rpm.spec(spec_file) release = spec.sourceHeader[rpm.RPMTAG_RELEASE] # split the dist from the end of the nvr release = release[:release.rfind('.')] return release
def get_package_nvr_from_spec(spec_file): """ Return a list of the NVR required for a given spec file :param spec_file: The path to a spec file :type spec_file: str :return: list of nevra that should be built for that spec file :rtype: str """ # Get the dep name & version spec = rpm.spec(spec_file) package_nvr = spec.sourceHeader[rpm.RPMTAG_NVR] # split the dist from the end of the nvr package_nvr = package_nvr[:package_nvr.rfind('.')] return package_nvr
def get_obs_version(self, project, package="erlang", filename=None): if filename is None: filename = "{}.spec".format(package) with tempfile.NamedTemporaryFile() as temp_file: target_filename = temp_file.name core.get_source_file(conf.config['apiurl'], project, package, filename, target_filename) s = rpm.spec(target_filename) obs_version = s.sourceHeader[rpm.RPMTAG_VERSION].decode("ascii") major, *_ = obs_version.split(".") return obs_version, major
def getSRPMHead(spec): """ Get the begining of an srpm file name based on a rpm spec :type spec: str :param spec: Spec file to parse """ spec_obj = rpm.spec(spec) spec_headers = spec_obj.packages[0].header head = "{0}-{1}".format( spec_headers.format("%{name}"), spec_headers.format("%{version}") ) return head
def _copy_startup_scripts(self, spec_filename): common_init_content = utils.load_template("packaging", "common.init")[1] for src in rpm.spec(spec_filename).sources: script = sh.basename(src[0]) if not (script.endswith(".init")): continue target_filename = sh.joinpths(self.rpm_sources_dir, script) if sh.isfile(target_filename): continue bin_name = utils.strip_prefix_suffix(script, "openstack-", ".init") params = { "bin": bin_name, "package": bin_name.split("-", 1)[0], } sh.write_file(target_filename, utils.expand_template(common_init_content, params))
def get_pkg_tag(tag, path=os.path.curdir, subpkg=None): topdir = getpkgtopdir(path) speclist = glob.glob(os.path.join(topdir, "SPECS", "*.spec")) if not speclist: raise Error("no spec files found") specfile = speclist[0] pkg = rpm.spec(specfile) if subpkg is None: header = pkg.sourceHeader elif isinstance(subpkg, int): header = pkg.packages(subpkg) else: raise Error("Subpkg must be the index number of a package,"\ "or None for source package") if isinstance(header[tag], bytes): return header[tag].decode("utf8") else: return header[tag]
def parse_spec(spec_file, keep_config=False): """Simple wrapper around rpm.spec that catches errors printed to stdout :param spec_file: spec file name :param keep_config: If set to True, does not call rpm.reloadConfig() This can be used to preserve the internal rpm state with any custom macros or definitions. :returns: rpm.spec object instance :raises: ValueError in case parsing failed """ if not keep_config: rpm.reloadConfig() with NamedTemporaryFile(mode="w+") as tmplog: # rpm will print errors to stdout if logfile is not set rpm.setLogFile(tmplog) try: spec = rpm.spec(spec_file) except ValueError as exc: # re-raise errors with rpm output appended to message raise ValueError(str(exc) + open(tmplog.name, 'r').read()) return spec
def _make_srpm(self, tmpdir, distgit, patches_action): """ Build SRPM. :param str tmpdir: Dir where to create the new srpm :param str distgit: Distgit repository if specified used for Specfile and pathes :param PatchesAction patches_action: What to do with patches :return: Path to the built srpm :rtype: str """ workdir = os.path.join(tmpdir, self.name) os.mkdir(workdir) spec_name = distgit.spec_path or "{!s}.spec".format(self.name) # extract spec file from specified branch and save it in temporary location original_spec_path = os.path.join(workdir, "original.spec") with open(original_spec_path, "w") as f_spec: subprocess.run(["git", "cat-file", "-p", "{}:{}".format(distgit.ref, spec_name)], cwd=distgit.cwd, check=True, stdout=f_spec) original_rpmspec = rpm.spec(original_spec_path) spec_name = original_rpmspec.sourceHeader["Name"] if isinstance(spec_name, bytes): spec_name = spec_name.decode("utf-8") spec_version = original_rpmspec.sourceHeader["Version"] if isinstance(spec_version, bytes): spec_version = spec_version.decode("utf-8") final_spec_path = os.path.join(workdir, "{!s}.spec".format(spec_name)) # PREPARE SPECFILE AND ARCHIVE if self.git: version, release = self.git.describe(self.name, spec_version=spec_version, version_from=self.version_from) # Prepare nvr nvr = "{!s}-{!s}-{!s}".format(self.name, version, release) # Prepare archive from git (compressed tarball) archive = os.path.join(workdir, "{!s}.tar.xz".format(nvr)) LOGGER.debug("Preparing archive %r", archive) proc = subprocess.run(["git", "archive", "--prefix={!s}/".format(nvr), "--format=tar", self.git.ref], cwd=self.git.cwd, check=True, stdout=subprocess.PIPE) with open(archive, "w") as f_archive: subprocess.run(["xz", "-z", "--threads=0", "-"], check=True, input=proc.stdout, stdout=f_archive) # Prepare new spec with open(final_spec_path, "w") as specfile: prepared = "{!s}\n{!s}".format( utils.prepare_spec(original_spec_path, version, release, nvr, patches_action), utils.generate_changelog(self.git.timestamp, version, release, self.git.get_rpm_changelog())) specfile.write(prepared) else: # If no git specified use distgit for both specfile and source code. # We could simply copy specifile like this: # shutil.copy2(original_spec_path, final_spec_path) # but getting the source code (tarball) is not so simple. raise NotImplementedError("Getting a tarball from distgit is not implemented") # PREPARE SPECFILE PATCHES if patches_action == PatchesAction.keep: _sources = [] for src, _, src_type in rpm.spec(final_spec_path).sources: if src_type == rpm.RPMBUILD_ISPATCH: _sources.append(src) if _sources and not distgit: raise NotImplementedError("Patches are applied in upstream") else: # Copy sources/patches from distgit for source in _sources: shutil.copy2(os.path.join(distgit.cwd, distgit.patches_dir, source), os.path.join(workdir, source)) # RPMBUILD SRPM try: result = subprocess.run(["rpmbuild", "-bs", final_spec_path, "--nodeps", "--define", "_topdir {!s}".format(workdir), "--define", "_sourcedir {!s}".format(workdir)], check=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as err: LOGGER.critical("Failed to build (no)source RPM:\n%s", err.output) raise else: LOGGER.debug(result.stdout) srpms_dir = os.path.join(workdir, "SRPMS") srpms = [f for f in os.listdir(srpms_dir) if SRPM_RE.search(f)] assert len(srpms) == 1, "We expect 1 .(no)src.rpm, but we found: {!r}".format(srpms) srpm_path = os.path.join(srpms_dir, srpms[0]) # CLEAN UP WORKDIR name_prefix = "" if hasattr(distgit, 'chroots'): name_prefix = "-".join(distgit.chroots) + "-" final_moved_out_srpm = os.path.join(tmpdir, name_prefix + srpms[0]) shutil.move(srpm_path, final_moved_out_srpm) LOGGER.info("Built (no)source RPM: %r from %r", final_moved_out_srpm, distgit.ref_info()) shutil.rmtree(workdir) return final_moved_out_srpm
import sys import rpm import optparse import subprocess parser = optparse.OptionParser() parser.add_option('-d', '--directory', help='Target directory', default='/tmp/sources') options, args = parser.parse_args() if not os.path.isdir(options.directory): os.makedirs(options.directory) for specfile in args: try: spec = rpm.spec(specfile) except ValueError as e: print(str(e)) sources = spec.sources for source in sources: filename_or_url = source[0] if '://' not in filename_or_url: continue command = ('wget', '-q', '-P', options.directory, '-c', filename_or_url) print(' '.join(command)) subprocess.call(command)
def parse_spec(specFile, cacheFile='.repocache.json'): '''Parse the Spec file contents. Args: specFile: A string of spec file path. Returns: Return the list contains the Spec file name and content. If the file is not found, it returns false. ''' if os.path.exists(cacheFile): if 'repocache' not in globals().keys(): echo('green', 'info:', ' Load cache from {} file.'.format(cacheFile)) with open(cacheFile, 'r') as f: global repocache repocache = json.loads(f.read()) return specFile, repocache[specFile] items = lambda t, c: re.findall('%s:\s+(.*)' % t, c) split_str = lambda l: [re.split('[\s,=|>=|<=]+', i) for i in l] flat = lambda L: sum(map(flat, L), []) if isinstance(L, list) else [L] remove_ver = lambda l: [i for i in l if not re.match('^[0-9]', i)] decode = lambda v: v.decode() if v else v if os.path.exists(specFile) and specFile.endswith('.spec'): rpm_info = {} subpkgs, reqpkgs = [], [] spec = rpm.spec(specFile) hdr = spec.sourceHeader reqlist = [decode(i) for i in hdr[rpm.RPMTAG_REQUIRES]] content = getoutput('/bin/rpmspec -P {}'.format(specFile)) content = content[:content.index('%changelog')] # subpackages name = decode(hdr[rpm.RPMTAG_NAME]) subpkgs.append(name) if re.search('%package', content): for i in re.findall('%package\s*(.+)', content): if i.startswith('-n'): subpkgs.append(re.match('-n\s*(.*)', i).group(1)) else: subpkgs.append('{}-{}'.format(name, i)) provpkgs = remove_ver(flat(split_str(items('Provides', content)))) + subpkgs # parse buildrequires for i in reqlist: if re.match('.*\((.*)\)', i): reqpkgs.append(query_package(i)[0].name) else: reqpkgs.append(i) rpm_info = { "name": decode(hdr[rpm.RPMTAG_NAME]), "epoch": hdr[rpm.RPMTAG_EPOCHNUM], "version": decode(hdr[rpm.RPMTAG_VERSION]), "release": decode(hdr[rpm.RPMTAG_RELEASE]), "vendor": decode(hdr[rpm.RPMTAG_VENDOR]), "summary": decode(hdr[rpm.RPMTAG_SUMMARY]), "packager": decode(hdr[rpm.RPMTAG_PACKAGER]), "group": decode(hdr[rpm.RPMTAG_GROUP]), "license": decode(hdr[rpm.RPMTAG_LICENSE]), "url": decode(hdr[rpm.RPMTAG_URL]), "description": decode(hdr[rpm.RPMTAG_DESCRIPTION]), "sources": spec.sources, "patchs": [decode(i) for i in hdr[rpm.RPMTAG_PATCH]], "build_archs": [decode(i) for i in hdr[rpm.RPMTAG_BUILDARCHS]], "exclusive_archs": [decode(i) for i in hdr[rpm.RPMTAG_EXCLUSIVEARCH]], #"build_requires": [i.DNEVR()[2:] for i in rpm.ds(hdr, 'requires')], "build_requires": sorted(list(set(reqpkgs))), "requires": remove_ver(flat(split_str(items('\nRequires', content)))), "recommends": remove_ver(flat(split_str(items('Recommends', content)))), "supplements": [decode(i) for i in hdr[rpm.RPMTAG_SUPPLEMENTS]], "suggests": [decode(i) for i in hdr[rpm.RPMTAG_SUGGESTS]], "enhances": [decode(i) for i in hdr[rpm.RPMTAG_ENHANCES]], "provides": sorted(list(set(provpkgs))), "obsoletes": remove_ver(flat(split_str(items('Obsoletes', content)))), "conflicts": remove_ver(flat(split_str(items('Conflicts', content)))) } return specFile, rpm_info return False
def getSpecSummary(spec): return rpm.spec(spec).packages[0].headers.format("%{summary}")
def scanspec2(specfile, specdir): spec = os.path.join(specdir, specfile) try: parsedspec = rpm.spec(spec) except Exception, e: return None
#!/usr/bin/python import rpm spec = rpm.spec("rdma-core.spec") print '%s' % (getattr(spec, "build"),)
""" A simple script downloading sources from specfiles """ import os import sys import rpm import optparse import subprocess parser = optparse.OptionParser() parser.add_option('-d', '--directory', help='Target directory', default='/tmp/sources') options, args = parser.parse_args() if not os.path.isdir(options.directory): os.makedirs(options.directory) for specfile in args: try: spec = rpm.spec(specfile) except ValueError as e: print(str(e)) sources = spec.sources for source in sources: filename_or_url = source[0] if '://' not in filename_or_url: continue command = ('wget', '-q', '-P', options.directory, '-c', filename_or_url) print(' '.join(command)) subprocess.call(command)
def _get_rpmspec(self, spec_path): with silence(sys.stderr): spec = rpm.spec(spec_path) return spec
def make_srpm(self, cwd): """ Build SRPM. :param str cwd: Working directory (for spec, sources) """ assert self.cloned import rpm if self.distgit: spec = os.path.join(self.distgit.cwd, "{!s}.spec".format(self.name)) patches = self.distgit.patches else: spec = os.path.join(self.git.cwd, self.git.spec_path or "{!s}.spec".format(self.name)) patches = PatchesAction.keep rpmspec = rpm.spec(spec) _spec_path = os.path.join(cwd, "{!s}.spec".format(rpmspec.sourceHeader["Name"].decode("utf-8"))) if self.git: # Get version and release version, release = self.git.describe(self.name) # If spec is located in upstream it's possible that version can be changed # which means we also should align it here if not self.distgit: _version = rpmspec.sourceHeader["Version"].decode("utf-8") LOGGER.debug("Version in upstream spec file: %r", _version) if rpm.labelCompare((None, _version, None), (None, version, None)) == 1: # Version in spec > than from git tags LOGGER.debug("Setting version from upstream spec file") version = _version # FIXME: Add prefix 0. to indicate that it was not released yet # There are no reliable way to get in which commit version was set # except iterating over commits release = "0.{!s}".format(release) # Prepare archive if release == "1": # tagged version, no need to add useless numbers nvr = "{!s}-{!s}".format(self.name, version) else: nvr = "{!s}-{!s}-{!s}".format(self.name, version, release) archive = os.path.join(cwd, "{!s}.tar.xz".format(nvr)) LOGGER.debug("Preparing archive %r", archive) proc = subprocess.run(["git", "archive", "--prefix={!s}/".format(nvr), "--format=tar", self.git.ref], cwd=self.git.cwd, check=True, stdout=subprocess.PIPE) with open(archive, "w") as f_archive: subprocess.run(["xz", "-z", "--threads=0", "-"], check=True, input=proc.stdout, stdout=f_archive) # Prepare new spec with open(_spec_path, "w") as specfile: prepared = "{!s}\n{!s}".format( utils.prepare_spec(spec, version, release, nvr, patches), utils.generate_changelog(self.git.timestamp, version, release)) specfile.write(prepared) else: # Just copy spec from distgit shutil.copy2(spec, _spec_path) spec = _spec_path _sources = [] for src, _, src_type in rpm.spec(spec).sources: if src_type == rpm.RPMBUILD_ISPATCH: if patches == PatchesAction.keep: _sources.append(src) elif src_type == rpm.RPMBUILD_ISSOURCE: # src in fact is url, but we need filename. We don't want to get # Content-Disposition from HTTP headers, because link could be not # available anymore or broken. src = os.path.basename(src) if self.git and src == os.path.basename(archive): # Skip sources which are just built continue _sources.append(src) if _sources and not self.distgit: raise NotImplementedError("Patches/Sources are applied in upstream") # Copy sources/patches from distgit for source in _sources: shutil.copy2(os.path.join(self.distgit.cwd, source), os.path.join(cwd, source)) # Build .(no)src.rpm try: result = subprocess.run(["rpmbuild", "-bs", _spec_path, "--nodeps", "--define", "_topdir {!s}".format(cwd), "--define", "_sourcedir {!s}".format(cwd)], check=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as err: LOGGER.critical("Failed to build (no)source RPM:\n%s", err.output) raise else: LOGGER.debug(result.stdout) srpms_dir = os.path.join(cwd, "SRPMS") srpms = [f for f in os.listdir(srpms_dir) if SRPM_RE.search(f)] assert len(srpms) == 1, "We expect 1 .(no)src.rpm, but we found: {!r}".format(srpms) srpm = os.path.join(srpms_dir, srpms[0]) LOGGER.info("Built (no)source RPM: %r", srpm) return srpm
def getSpecName(spec): return rpm.spec(spec).packages[0].headers.format("%{name}")
def parseSpec(self, specfile): return rpm.spec(specfile)
def parse_spec(specFile, cacheFile='.repocache.json'): '''Parse the Spec file contents. Args: specFile: A string of spec file path. Returns: Return the list contains the Spec file name and content. If the file is not found, it returns false. ''' if os.path.exists(cacheFile): if 'repocache' not in globals().keys(): echo('green', 'info:', ' Load cache from {} file.'.format(cacheFile)) with open(cacheFile, 'r') as f: global repocache repocache = json.loads(f.read()) return specFile, repocache[specFile] items = lambda t, c: re.findall('%s:\s+(.*)'%t, c) split_str = lambda l: [re.split('[\s,=|>=|<=]+', i) for i in l] flat = lambda L: sum(map(flat, L), []) if isinstance(L, list) else [L] remove_ver = lambda l: [i for i in l if not re.match('^[0-9]', i)] decode = lambda v: v.decode() if v else v if os.path.exists(specFile) and specFile.endswith('.spec'): rpm_info = {} subpkgs, reqpkgs = [], [] spec = rpm.spec(specFile) hdr = spec.sourceHeader reqlist = [decode(i) for i in hdr[rpm.RPMTAG_REQUIRES]] content = getoutput('/bin/rpmspec -P {}'.format(specFile)) content = content[:content.index('%changelog')] # subpackages name = decode(hdr[rpm.RPMTAG_NAME]) subpkgs.append(name) if re.search('%package', content): for i in re.findall('%package\s*(.+)', content): if i.startswith('-n'): subpkgs.append(re.match('-n\s*(.*)', i).group(1)) else: subpkgs.append('{}-{}'.format(name, i)) provpkgs = remove_ver(flat(split_str(items('Provides', content)))) + subpkgs # parse buildrequires for i in reqlist: if re.match('.*\((.*)\)', i): if len(query_package(i)) > 0: reqpkgs.append(query_package(i)[0].name) else: reqpkgs.append(i) rpm_info = { "name": decode(hdr[rpm.RPMTAG_NAME]), "epoch": hdr[rpm.RPMTAG_EPOCHNUM], "version": decode(hdr[rpm.RPMTAG_VERSION]), "release": decode(hdr[rpm.RPMTAG_RELEASE]), "vendor": decode(hdr[rpm.RPMTAG_VENDOR]), "summary": decode(hdr[rpm.RPMTAG_SUMMARY]), "packager": decode(hdr[rpm.RPMTAG_PACKAGER]), "group": decode(hdr[rpm.RPMTAG_GROUP]), "license": decode(hdr[rpm.RPMTAG_LICENSE]), "url": decode(hdr[rpm.RPMTAG_URL]), "description": decode(hdr[rpm.RPMTAG_DESCRIPTION]), "sources": spec.sources, "patchs": [decode(i) for i in hdr[rpm.RPMTAG_PATCH]], "build_archs": [decode(i) for i in hdr[rpm.RPMTAG_BUILDARCHS]], "exclusive_archs": [decode(i) for i in hdr[rpm.RPMTAG_EXCLUSIVEARCH]], #"build_requires": [i.DNEVR()[2:] for i in rpm.ds(hdr, 'requires')], "build_requires": sorted(list(set(reqpkgs))), "requires": remove_ver(flat(split_str(items('\nRequires', content)))), "recommends": remove_ver(flat(split_str(items('Recommends', content)))), "supplements": [decode(i) for i in hdr[rpm.RPMTAG_SUPPLEMENTS]], "suggests": [decode(i) for i in hdr[rpm.RPMTAG_SUGGESTS]], "enhances": [decode(i) for i in hdr[rpm.RPMTAG_ENHANCES]], "provides": sorted(list(set(provpkgs))), "obsoletes": remove_ver(flat(split_str(items('Obsoletes', content)))), "conflicts": remove_ver(flat(split_str(items('Conflicts', content)))) } return specFile, rpm_info return False
def make_srpm(self, tmpdir): """ Build SRPM. :param str tmpdir: Temporary directory to work in """ workdir = os.path.join(tmpdir, self.name) os.mkdir(workdir) assert self.cloned import rpm if self.distgit: spec_git = self.distgit spec_name = "{!s}.spec".format(self.name) patches = self.distgit.patches else: spec_git = self.git spec_name = self.git.spec_path or "{!s}.spec".format(self.name) patches = PatchesAction.keep # extract spec file from specified branch and save it in temporary location spec = os.path.join(workdir, "original.spec") with open(spec, "w") as f_spec: subprocess.run(["git", "cat-file", "-p", "{}:{}".format(spec_git.ref, spec_name)], cwd=spec_git.cwd, check=True, stdout=f_spec) rpmspec = rpm.spec(spec) _name = rpmspec.sourceHeader["Name"] if isinstance(_name, bytes): _name = _name.decode("utf-8") _spec_path = os.path.join(workdir, "{!s}.spec".format(_name)) if self.git: # Get version and release version, release = self.git.describe(self.name) # If spec is located in upstream it's possible that version can be changed # which means we also should align it here if not self.distgit: _version = rpmspec.sourceHeader["Version"] if isinstance(_version, bytes): _version = _version.decode("utf-8") LOGGER.debug("Version in upstream spec file: %r", _version) if rpm.labelCompare((None, _version, None), (None, version, None)) == 1: # Version in spec > than from git tags LOGGER.debug("Setting version from upstream spec file") version = _version # FIXME: Add prefix 0. to indicate that it was not released yet # There are no reliable way to get in which commit version was set # except iterating over commits release = "0.{!s}".format(release) # Prepare archive if release == "1": # tagged version, no need to add useless numbers nvr = "{!s}-{!s}".format(self.name, version) else: nvr = "{!s}-{!s}-{!s}".format(self.name, version, release) archive = os.path.join(workdir, "{!s}.tar.xz".format(nvr)) LOGGER.debug("Preparing archive %r", archive) proc = subprocess.run(["git", "archive", "--prefix={!s}/".format(nvr), "--format=tar", self.git.ref], cwd=self.git.cwd, check=True, stdout=subprocess.PIPE) with open(archive, "w") as f_archive: subprocess.run(["xz", "-z", "--threads=0", "-"], check=True, input=proc.stdout, stdout=f_archive) # Prepare new spec with open(_spec_path, "w") as specfile: prepared = "{!s}\n{!s}".format( utils.prepare_spec(spec, version, release, nvr, patches), utils.generate_changelog(self.git.timestamp, version, release)) specfile.write(prepared) else: # Just copy spec from distgit shutil.copy2(spec, _spec_path) spec = _spec_path _sources = [] for src, _, src_type in rpm.spec(spec).sources: if src_type == rpm.RPMBUILD_ISPATCH: if patches == PatchesAction.keep: _sources.append(src) elif src_type == rpm.RPMBUILD_ISSOURCE: # src in fact is url, but we need filename. We don't want to get # Content-Disposition from HTTP headers, because link could be not # available anymore or broken. src = os.path.basename(src) if self.git and src == os.path.basename(archive): # Skip sources which are just built continue _sources.append(src) if _sources and not self.distgit: raise NotImplementedError("Patches/Sources are applied in upstream") # Copy sources/patches from distgit for source in _sources: shutil.copy2(os.path.join(self.distgit.cwd, source), os.path.join(workdir, source)) # Build .(no)src.rpm try: result = subprocess.run(["rpmbuild", "-bs", _spec_path, "--nodeps", "--define", "_topdir {!s}".format(workdir), "--define", "_sourcedir {!s}".format(workdir)], check=True, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except subprocess.CalledProcessError as err: LOGGER.critical("Failed to build (no)source RPM:\n%s", err.output) raise else: LOGGER.debug(result.stdout) srpms_dir = os.path.join(workdir, "SRPMS") srpms = [f for f in os.listdir(srpms_dir) if SRPM_RE.search(f)] assert len(srpms) == 1, "We expect 1 .(no)src.rpm, but we found: {!r}".format(srpms) self.srpm = os.path.join(tmpdir, srpms[0]) shutil.move(os.path.join(srpms_dir, srpms[0]), self.srpm) shutil.rmtree(workdir) LOGGER.info("Built (no)source RPM: %r from %r", self.srpm, self.git.ref_info()) return self.srpm
error = True continue macros.append(words) if error: sys.exit(1) for name in specnames: # (re)load rpm config for target if set if reloadworks and opts.target: rpm.reloadConfig(opts.target) for macro in macros: rpm.addMacro(*macro) try: spec = rpm.spec(name) except ValueError: self.logger.error("Bad spec: %s" % name) continue # reset default rpm config after each parse to avoid side-effects if reloadworks: rpm.reloadConfig() buildreqs = [] for d in rpm.ds(spec.sourceHeader, 'requires'): buildreqs.append(d.DNEVR()[2:]) self.logger.info('Getting requirements for %s' % name) self.install_deps(buildreqs, opts)
def main(): parser = parser = argparse.ArgumentParser( description='rediff patches to avoid fuzzy hunks') parser.add_argument('spec', type=str, help='spec file name') parser.add_argument('-p', '--patches', type=str, help='comma separated list of patch numbers to rediff') parser.add_argument( '-s', '--skip-patches', type=str, help='comma separated list of patch numbers to skip rediff') parser.add_argument('-v', '--verbose', help='increase output verbosity', action='store_true') args = parser.parse_args() logging.basicConfig(level=logging.INFO) rpm.setVerbosity(rpm.RPMLOG_ERR) if args.verbose: logging.basicConfig(level=logging.DEBUG) rpm.setVerbosity(rpm.RPMLOG_DEBUG) if args.patches: args.patches = [int(x) for x in args.patches.split(',')] if args.skip_patches: args.skip_patches = [int(x) for x in args.skip_patches.split(',')] specfile = args.spec appsourcedir = os.path.dirname(os.path.abspath(specfile)) try: tempdir = tempfile.TemporaryDirectory(dir="/dev/shm") except FileNotFoundError as e: tempdir = tempfile.TemporaryDirectory(dir="/tmp") topdir = tempdir.name builddir = os.path.join(topdir, 'BUILD') rpm.addMacro("_builddir", builddir) r = rpm.spec(specfile) patches = {} for (name, nr, flags) in r.sources: if flags & RPMBUILD_ISPATCH: patches[nr] = name applied_patches = collections.OrderedDict() re_patch = re.compile(r'^%patch(?P<patch_number>\d+)\w*(?P<patch_args>.*)') for line in r.parsed.split('\n'): m = re_patch.match(line) if not m: continue patch_nr = int(m.group('patch_number')) patch_args = m.group('patch_args') applied_patches[patch_nr] = patch_args appbuilddir = rpm.expandMacro("%{_builddir}/%{?buildsubdir}") for patch_nr in applied_patches.keys(): if args.patches and patch_nr not in args.patches: continue if args.skip_patches and patch_nr in args.skip_patches: continue patch_name = patches[patch_nr] logging.info("*** patch %d: %s" % (patch_nr, patch_name)) tempspec = prepare_spec(r, patch_nr, before=True) unpack(tempspec.name, appsourcedir, builddir) tempspec.close() os.rename(appbuilddir, appbuilddir + ".org") tempspec = prepare_spec(r, patch_nr, before=False) unpack(tempspec.name, appsourcedir, builddir) tempspec.close() patch_comment = patch_comment_get(patch_name) diff( appbuilddir + ".org", appbuilddir, builddir, patch_comment, os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff"))) diffstat(os.path.join(topdir, os.path.join(appsourcedir, patch_name))) diffstat( os.path.join(topdir, os.path.join(appsourcedir, patch_name + ".rediff"))) shutil.rmtree(builddir) tempdir.cleanup()