Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A library for parsing and manipulating RPM spec files

License

NotificationsYou must be signed in to change notification settings

packit/specfile

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Python library for parsing and manipulating RPM spec files. Main focus is on modifying existing spec files, any change should result in a minimal diff.

Motivation

Originally,rebase-helper provided an API for spec file modifications that was also used bypackit. The goal of this project is to make the interface more general and convenient to use by not only packit but also by other Python projects that need to interact with RPM spec files.

Important terms used in this library

Section

Section is a spec file section, it has a well-defined name that starts with% character and that can optionally be followed by arguments.

In this library, the starting% of section name is omitted for convenience.

There is a special section internally called%package, often also referred to as preamble, and it represents the content of the spec file that precedes the first named section (usually%description). This section contains the main package metadata (tags). Metadata of subpackages are defined in subsequent%package sections, that are not anonymous and are always followed by arguments specifying the name of the subpackage (e.g.%package doc or%package -n completely-different-subpackage-name).

Tag

Tag represents a single item of metadata of a package. It has a well-defined name and a value. Tags are defined in%package sections.

For the purposes of this library, a tag can have associated comments. These are consecutive comment lines directly above the tag definition in a spec file.

Source

Source is a source file or a downstream patch defined by aSource/Patch tag or by an entry in%sourcelist/%patchlist section.

Source can be local, specified by a filename, or remote, specified by a URL. Local sources should be located in a directory referred to assourcedir. Remote sources should be downloaded to this directory.

Sources defined by tags can be explicitly numbered, e.g.Source0 orPatch999, otherwise implicit numbering takes place and source numbers are auto-assigned in a sequential manner.

Prep macros

Prep macros are macros that often appear in (and only in, they don't make sense anywhere else)%prep section.

4 such macros are recognized by this library,%setup,%patch,%autosetup and%autopatch. A typical spec file uses either%autosetup or a combination of%setup and%patch or%autopatch.

Documentation

Full documentation generated from code.

Examples and use cases

The following examples should cover use cases required bypackit.

Instantiating

fromspecfileimportSpecfile# using an absolute pathspecfile=Specfile('/tmp/test.spec')# using a relative path and a different sourcedirspecfile=Specfile('test.spec',sourcedir='/tmp/sources')

Reloading

# if the spec file happens to be modified externally, it can be reloadedspecfile.reload()

Saving changes

# no autosavespecfile=Specfile('test.spec')...# saving explicitly when neededspecfile.save()# enabling autosave, changes are saved immediately after any modificationspecfile=Specfile('test.spec',autosave=True)# as a context manager, saving is performed at context exitwithSpecfile('test.spec')asspecfile:    ...

Defining and undefining macros

# override macros loaded from system macro filesspecfile=Specfile('test.spec',macros=[('fedora','38'), ('dist','.fc38')])# undefine a system macro (in case it's defined)specfile=Specfile('test.spec',macros=[('rhel',None)])

Low-level manipulation

withspecfile.sections()assections:# replacing the content of a sectionsections.prep= ['%autosetup -p1']# removing a sectiondelsections.changelog# swapping two sectionssections[1],sections[2]=sections[2],sections[1]# accessing a section with argumentsprint(sections.get('package devel'))# inserting a line into a sectionsections.build.insert(0,'export VERBOSE=1')# copying a section from one specfile to anotherwithspecfile1.sections()assections1,withspecfile2.sections()assections2:sections2.changelog[:]=sections1.changelog

Mid-level manipulation - tags, changelog and prep

# accessing tags in preamblewithspecfile.tags()astags:# name of the first tagprint(tags[0].name)# raw value of the first tagprint(tags[0].value)# expanded value of the first tagprint(tags[0].expanded_value)# comments associated with the first tagprint(tags[0].comments)# value of a tag by nameprint(tags.url)tags.url='https://example.com'# accessing tags in subpackageswithspecfile.tags('package devel')astags:print(tags.requires)# working with changelogwithspecfile.changelog()aschangelog:# most recent changelog entryprint(changelog[-1])# making changeschangelog[1].content.append('- another line')# removing the oldest entrydelchangelog[0]# working with macros in %prep section, supports %setup, %patch, %autosetup and %autopatchfromspecfile.prepimportAutosetupMacrowithspecfile.prep()asprep:# name of the first macroprint(prep.macros[0].name)# checking if %autosetup is being usedprint('%autosetup'inprep)print(AutosetupMacroinprep)# changing macro optionsprep.autosetup.options.n='%{srcname}-%{version}'# adding a new %patch macroprep.add_patch_macro(28,p=1,b='.test')# removing an existing %patch macro by namedelprep.patch0# this works for both '%patch0' and '%patch -P0'prep.remove_patch_macro(0)

High-level manipulation

Version and release

# getting version and releaseprint(specfile.version)print(specfile.release)# setting version and releasespecfile.version='2.1'specfile.release='3'# setting both at the same time (release defaults to 1)specfile.set_version_and_release('2.1',release='3')# setting version while trying to preserve macrosspecfile.set_version_and_release('2.1',preserve_macros=True)

Bumping release

To bump release and add a new changelog entry, you could use the following code:

fromspecfileimportSpecfilewithSpecfile("example.spec")asspec:spec.release=str(int(spec.expanded_release)+1)spec.add_changelog_entry("- Bumped release for test purposes")

Changelog

# adding a new entry, author is automatically determined# (using the same heuristics that rpmdev-packager uses) if possible# this function already honors autochangelogspecfile.add_changelog_entry('- New upstream release 2.1')# adding a new entry, specifying author and timestamp explicitlyspecfile.add_changelog_entry('- New upstream release 2.1',author='Nikola Forró',email='nforro@redhat.com',timestamp=datetime.date(2021,11,20),)ifspecfile.has_autochangelog:# do something

Sources and patches

withspecfile.sources()assources:# expanded location of the first sourceprint(sources[0].expanded_location)# adding a sourcesources.append('tests.tar.gz')withspecfile.patches()aspatches:# modifying location of the first patchpatches[0].location='downstream.patch'# removing comments associated with the last patchpatches[-1].comments.clear()# adding and removing patchespatches.append('another.patch')delpatches[2]# inserting a patch with a specific numberpatches.insert_numbered(999,'final.patch')# adding a single patchspecfile.add_patch('necessary.patch',comment='a human-friendly comment to the patch')

Other attributes

print(specfile.name)print(specfile.license)print(specfile.summary)specfile.url='https://example.com'

Note that if you want to access multiple tag values, it may be noticeably faster to do it using thetags context manager:

# same as above, but roughly 4x times faster (parsing/saving happens only once)withspecfile.tags()astags:print(tags.name.value)print(tags.license.value)print(tags.summary.value)tags.url.value='https://example.com'

Read-only access

If you don't need write access, you can use thecontent property of context managers and avoid thewith statement:

# no changes done to the tags object will be savedtags=specfile.tags().contentprint(tags.version.expanded_value)print(tags.release.expanded_value)# number of sourcesprint(len(specfile.sources().content))

Validity

Macro definitions, tags,%sourcelist/%patchlist entries and sources/patches have avalid attribute. An entity is considered valid if it isn't present in a false branch of any condition.

Consider the following in a spec file:

%if 0%{?fedora}>= 36Recommends: %{name}-selinux%endif

Provided there are no otherRecommends tags, the following would printTrue orFalse depending on the value of the%fedora macro:

withspecfile.tags()astags:print(tags.recommends.valid)

You can define macros or redefine/undefine system macros using themacros argument of the constructor or by modifying themacros attribute of aSpecfile instance.

The same applies to%ifarch/%ifos statements:

%ifarch %{java_arches}BuildRequires: java-devel%endif

Provided there are no otherBuildRequires tags, the following would printTrue in case the current platform was part of%java_arches:

withspecfile.tags()astags:print(tags.buildrequires.valid)

To override this, you would have to redefine the%_target_cpu system macro (or%_target_os in case of%ifos).

Videos

Here is a demo showcasing theSpecfile.update_tag() method and its use cases:

Demo of Specfile.update_tag() functionality


[8]ページ先頭

©2009-2025 Movatter.jp