openRuyi Packaging Specification
Foreword
The openRuyi project relies on a vast array of third-party software packages to construct a robust and maintainable distribution. To minimize package maintenance and review overhead and enhance repository consistency and predictability, this document establishes baseline consistency requirements and formatting conventions for openRuyi RPM Spec files (hereafter referred to as "Specs").
This specification focuses on the Spec as the target artifact itself—dictating the expected structure, tags, organization, naming conventions, and macro usage for any Spec submitted to the repository. A Spec adhering to this specification SHOULD be universally understandable, reviewable, and reusable.
This specification applies to all Specs for RPM packages published within the openRuyi repositories, encompassing both single-package and multi-subpackage Specs.
This specification comprises two types of documents:
-
Main Specification (this document): Defines the universal structure, mandatory tags, and overarching policies (utilizing "MUST/SHOULD/MUST NOT" constraints).
-
Supplementary Specifications (sub-documents): Provide granular constraints and design patterns for specific domains (e.g., naming, versioning, language ecosystems, scriptlets, patches). The main specification outlines essential "general rules," while intricate details are delegated to these supplementary documents.
In the event of a conflict between this document and a supplementary specification, the supplementary specification shall take precedence.
Terms and Definitions
Normative Language
This specification employs the following terms to denote requirement levels:
-
MUST: An absolute requirement; failure to comply means the Spec does not conform to this specification.
-
MUST NOT: An absolute prohibition; the presence of such elements renders the Spec non-compliant.
-
SHOULD: A highly recommended practice; expected to be followed unless a clear and justified exception exists.
-
MAY: An optional practice; adoption depends on the specific needs of the package.
Terminology
-
Spec: The RPM package description file (
.spec) defining the source, build instructions, installation steps, file lists, dependencies, and changelog. -
Tag: A key-value header field within a Spec, such as
NameorVersion. -
Section: A segment in a Spec prefixed with
%, such as%prep,%build,%install, or%files. -
Subpackage: Additional binary packages generated from a single Spec (e.g.,
-develpackages or language packs). -
RemoteAsset: An external resource accessed via a network URI.
-
BuildSystem: A declarative tag specifying the build system utilized by the Spec.
Overall Structural Requirements for Spec Files
SPDX Copyright and License Declarations
The top of every Spec file MUST feature SPDX-compliant copyright and license declarations in the following format (where SPDX-FileContributor is optional):
# SPDX-FileCopyrightText: (C) 2026 Institute of Software, Chinese Academy of Sciences (ISCAS)
# SPDX-FileCopyrightText: (C) 2026 openRuyi Project Contributors
# SPDX-FileContributor: Your Name <your.email@example.com>
#
# SPDX-License-Identifier: MulanPSL-2.0
Mandatory Tags and Sections
A Spec MUST include the following tags and sections, and they SHOULD appear in the specified order:
Name:
Version:
Release:
Summary:
License:
%description
%files
%changelog
A package MAY have an empty %files section when it intentionally produces an empty package, such as a dependency-only meta-package.
The absence of a conditional tag or section does not by itself make a Spec non-compliant.
Common conditional tags and sections include:
| Tag or section | When it is required |
|---|---|
URL | When the upstream project provides a meaningful homepage or public source repository |
VCS | When a usable upstream source repository exists and URL does not already point to it |
Source / SourceN / %sourcelist | When the package consumes source files or other source inputs |
BuildArch | When the package needs to override the default target architecture behavior |
BuildSystem | When the Spec uses the declarative build system mechanism |
Patch / %patchlist | When the package applies downstream or backported patches |
BuildOption | When a declarative build system needs additional stage-specific options |
BuildRequires | When the build needs dependencies that the openRuyi basic build environment does not guarantee |
Requires | When the package needs explicit runtime dependencies |
Provides / Conflicts / Obsoletes / Recommends / Supplements | When package relationships require them |
%package | When the Spec creates subpackages |
%prep / %build / %install / %check | When the package needs explicit actions in the corresponding stage |
When the following tags are present, they SHOULD appear in the following relative order:
Name:
Version:
Release:
Summary:
License:
URL:
VCS:
Source:
BuildArch:
BuildSystem:
Patch:
BuildOption:
BuildRequires:
Provides:
Conflicts:
Obsoletes:
Recommends:
Requires:
Supplements:
%description
The example above defines relative ordering, not a mandatory complete header. A Spec does not need to reserve a position for an omitted conditional tag. Source, Source0, Source1, and similar numbered forms belong to the same position. Equivalent source declarations such as %sourcelist also belong to the source declaration block.
The same relative ordering rules SHOULD apply to tags within each subpackage definition. A subpackage only needs tags that apply to that subpackage and does not need to repeat main-package tags.
Package-specific supplementary specifications MAY define additional tags or more specific ordering rules. When a supplementary specification defines such a rule, that rule takes precedence.
For tags that have no specific ordering rule, maintainers SHOULD keep related tags together and MAY order them alphabetically.
Blank lines MUST be used to separate sections.
Minimal Skeleton Example
The following example shows a valid minimal structure for a package that does not need upstream source input, a build system, build dependencies, or explicit runtime dependencies:
Name: <package-name>
Version: <version>
Release: %autorelease
Summary: <summary>
License: <SPDX-license-expression>
%description
<package-description>
%files
%changelog
%autochangelog
Formatting and readability
-
Specs MUST be strictly UTF-8 encoded.
-
Spec tags SHOULD be horizontally aligned (aligning tag names, colons, and values using spaces) to facilitate code review.
-
BuildRequiresandRequiresMUST adhere to a "one dependency per line" format. -
Descriptive text within a Spec (e.g.,
Summary, comments, and%description) SHOULD be written in American English, unless a package-specific supplementary specification dictates otherwise.
Core Tags
Name
-
NameMUST define the package's base name. -
Package names SHOULD be entirely lowercase and SHOULD prefer hyphens (
-) as word separators. Underscores (_) are strictly limited to exceptional cases explicitly permitted by supplementary specifications. -
Package names MUST NOT embed ABI information (e.g., SONAME major versions) or upstream major version numbers (e.g.,
libfoo2is prohibited). -
If the chosen package name diverges from the widely recognized upstream name, the Spec MAY provide an upstream alias via
Provides. The necessity of this depends on compatibility requirements.
For a comprehensive naming strategy (e.g., module packages, Perl/Python/font packaging), see the Naming Guidelines supplementary specification.
Version
Version MUST accurately reflect the upstream release version and SHOULD be normalized according to the following rules:
| Case | Normalization rule | Example (upstream → Spec) |
|---|---|---|
Contains only dots (.) | Retain the upstream version as-is | 1.5.7 → 1.5.7 |
Includes pre-release markers alpha/beta/rc | Convert to lowercase and prefix with ~ | 3.5.0-rc1 → 3.5.0~rc1 |
Contains hyphen (-) | Replace - with . | 7.1.1-44 → 7.1.1.44 |
Contains underscore (_) | Replace _ with . | 17_6 → 17.6 |
| Date-based version with dots | Retain the upstream version as-is | 2025.07 → 2025.07 |
| VCS commit-hash-based version | Format as 0+<vcs><YYYYMMDD>.<hash7> | ee5b7e3… → 0+git20250808.ee5b7e3 |
For version normalization and snapshot/pre-release rules, see the Version Numbers supplementary specification.
Release
-
ReleaseSHOULD utilize the%autoreleasemacro. -
ReleaseMUST NOT hardcode distribution-specific suffixes or override the predefined%{dist}macro. -
For a given
Version, the revision number defined in theReleaseMUST increment monotonically. -
Upon any update to the
Version, the revision number in theReleaseMUST be reset to1.
Epoch (optional)
Epoch is traditionally used to resolve ambiguous version sorting. Because it is practically irreversible, the use of Epoch SHOULD be strictly avoided. If Epoch is absolutely necessary, the Spec MUST include an adjacent comment detailing the rationale and guaranteeing that the subsequent versioning trajectory remains sustainable.
Summary
-
SummaryMUST provide a concise overview of the package's primary function. -
SummarySHOULD be written in straightforward English. -
SummaryMUST NOT terminate with a period (.).
License
-
LicenseMUST utilize standard SPDX License Identifiers or SPDX License Expressions. -
If multiple licenses apply, the expression MUST connect them using standard SPDX operators (e.g.,
AND/OR). -
If the upstream source includes license text files, the Spec MUST package them using the
%licensedirective within%files. If a subpackage operates under a different license than the main package, this specific license MUST be explicitly declared within that subpackage's definition.
For detailed licensing rules, see the Licenses supplementary specification.
URL
-
URLMUST point to the upstream project's official homepage if available. -
When the upstream project does not provide an official homepage but provides a public source repository, the Spec MUST set
URLto the source repository. -
When no meaningful upstream project URL exists, the Spec MAY omit
URL. Distribution-specific meta-packages, configuration packages, or similar packages may fall into this category. The Spec MUST include the following exact comment in place of theURLtag (the# URL:prefix MUST remain intact):
# URL: No URL link available
- The
URLtag MUST NOT dynamically construct its value using macros such as%{name}.
VCS
-
VCSSHOULD contain the source code repository URL to facilitate locating the upstream source. -
If the
URLtag already points to the source repository,VCSMAY be omitted. -
If no publicly accessible source repository exists, the Spec MUST include the following exact comment in place of the
VCStag (the# VCS:prefix MUST remain intact):
# VCS: No VCS link available
- For Git-hosted upstreams,
VCSSHOULD utilize a cloneable URL format, for instance:
VCS: git:https://git.example.org/project.git
Source
-
A Spec that consumes source files or other source inputs MUST declare those inputs using RPM source declarations such as
Source,SourceN, or%sourcelist, as appropriate. A package that does not consume any source input MAY omit source declarations entirely. -
A
Sourcethat represents upstream source code MUST identify where the upstream source can be obtained, or otherwise identify an equivalent reproducible source archive. -
A local source input MAY use a local file name instead of a URI. The Spec SHOULD include a nearby comment that explains the purpose or origin of a local source input when the file name does not make that information clear.
-
When a Spec contains one
Sourceentry, it MAY use eitherSourceorSource0. Both forms refer to source index0. -
When a Spec uses numbered
SourceNtags, source indexes SHOULD start at0and increase sequentially unless a package-specific reason requires otherwise. -
If the
URLtag value can serve as a valid prefix for the source link,SourceMAY leverage the%{url}macro. -
For any network-fetched
Source, a#!RemoteAssetcomment MUST immediately precede theSourcedeclaration. If multiple external sources exist, each MUST be individually annotated. -
For a
Sourcefetched over HTTP or HTTPS, the corresponding#!RemoteAssetannotation MUST include the SHA-256 checksum on the same line. For conveience, it can be generated automatically with remoteassetify. -
When creates an archive from a remote VCS source, the Spec MUST place
#!CreateArchivebetween the corresponding#!RemoteAssetannotation and theSourcedeclaration:
#!RemoteAsset: git+https://example.org/project.git#<revision>
#!CreateArchive
Source: <archive-name>.tar.gz
-
If the tarball filename is obscured or cannot be algorithmically inferred from the URL,
SourceSHOULD explicitly dictate the desired tarball name via a URL fragment (e.g.,#/name.tar.gz) to guarantee predictable local file naming. -
SourceIndexing Rules: -
The base index defaults to
0and increments by 1 for each subsequent source. -
If the Spec specifies only a single-source archive, the index MAY be omitted entirely.
Example:
#!RemoteAsset: sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Source0: https://example.org/example-%{version}.tar.gz
#!RemoteAsset: sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
Source1: https://example.org/example-%{version}-additional.tar.gz
A Spec MAY also use an RPM source-list mechanism when it better represents a set of local source inputs:
%sourcelist
file1.conf
file2.conf
file3.conf
For details regarding source URLs, see the Source Packages supplementary specification.
BuildArch (optional)
-
BuildArchexplicitly defines the target CPU architecture. -
BuildArchSHOULD be positioned between the finalSourcedeclaration and theBuildSystemtag. -
Setting
BuildArchtonoarchsignifies that the resulting package is entirely architecture-independent.
BuildSystem
-
A Spec that uses the RPM declarative build system mechanism MUST declare the corresponding build system with
BuildSystem. -
A Spec that does not use the declarative build system mechanism MAY omit
BuildSystem. Examples include source-less meta-packages, data-only packages, and packages that implement their required stages explicitly. -
A Spec MUST NOT add an empty
BuildSystemtag only to indicate that no declarative build system applies. -
When present, the
BuildSystemvalue MUST identify a supported declarative build system. The Declarative Build Systems supplementary specification defines the available build systems and their requirements.
The main packaging specification does not maintain a separate list of BuildSystem values.
When supplementary pre- or post-stage interventions are required, the Spec MAY deploy modifier tags. For example:
-
%prep -a: Appends custom instructions after the standard%prepmacro completes. -
%conf -p: Prepends custom instructions before the standardconfigurestage initiates.
For system-specific build patterns, see the Declarative Build Systems supplementary specification.
Patch and %patchlist (optional)
-
Every patch integrated into the Spec MUST be preceded by at least one comment line that elucidates its purpose or provides a link to the relevant upstream discussion. This requirement is waived only if the patch file's internal headers already contain exhaustive documentation.
-
Patch filenames MUST be prefixed with a four-digit sequence categorizing the patch type:
-
0001–0999: Upstream patches backported to the current release. -
1000–1999: Security (CVE) remediations or cross-release backports. -
2000–2999: openRuyi-specific architectural or downstream patches (not intended for upstreaming).
-
-
If a Spec accumulates more than 3 patches, it SHOULD group them using the
%patchlistdirective, which MUST be positioned directly above the%descriptionsection. -
Patch Ordering:
-
If the Spec utilizes
BuildOption, patches SHOULD be inserted betweenBuildSystemandBuildOption. -
If
BuildOptionis absent, patches SHOULD be located betweenBuildSystemandBuildRequires.
-
For the comprehensive patch strategy, see the Patches supplementary specification.
BuildOption (optional)
-
If specific build stages demand custom parameters, the Spec MAY leverage the
BuildOption(<stage>):tag. -
Formatting MUST include exactly two spaces separating
BuildOption(<stage>):from its corresponding argument. -
Multiple arguments MUST be spread across multiple lines (one argument per line).
-
When utilized,
BuildOptionSHOULD reside between theBuildSystemandBuildRequiresblocks. -
BuildOptionentries SHOULD be written in the same order as the RPM build process, for example:
BuildOption(conf): <configuration-option>
BuildOption(build): <build-option>
BuildOption(install): <installation-option>
BuildOption(check): <test-option>
BuildRequires
-
A Spec MUST ensure that all build-time dependencies are declared through static
BuildRequires, dynamic build dependency generation, or a combination of both. -
When a package uses dynamic build dependency generation, static
BuildRequiresMUST include dependencies required before dependency generation and any required build dependencies that the dynamic mechanism does not provide. -
When a package requires no additional build-time dependencies beyond the openRuyi basic build environment and dynamically generated dependencies, the Spec MAY omit static
BuildRequires. For example, for standard C/C++ applications, it is generally unnecessary to explicitly specify a compiler likegcc. -
static
BuildRequiresentries MUST follow the "one dependency per line" rule. -
The openRuyi basic build environment consists of packages that the build configuration guarantees before a package build begins. The preinstall image is the authoritative source for this set.
-
A Spec MUST NOT omit a required build dependency merely because the dependency happens to be installed in the current build root.
-
If a dependency is dynamically resolved via
pkg-config,BuildRequiresSHOULD utilize thepkgconfig(xxx)syntax rather than hardcoding thexxx-develpackage name.
For strategies on resolving dependencies, see the Using pkgconfig(xxx) supplementary specification.
Requires / Provides / Conflicts / Obsoletes / Recommends / Supplements (optional)
-
Requiresdictates runtime dependencies; these MUST also follow the "one dependency per line" rule. -
When a package does not need any explicit runtime dependency, the Spec MAY omit
Requires. -
The absence of an explicit
Requirestag does not exempt a package from having correct runtime dependencies. Maintainers MUST ensure that automatic and explicit dependency mechanisms together describe the package's runtime requirements correctly. -
A Spec MAY use
Provides,Recommends, andSupplementswhen the package needs to express the corresponding package relationship. -
During package renaming, logical splitting, or major migrations, the Spec MUST guarantee a seamless upgrade path using
ProvidesandObsoletes(see the Package Splitting supplementary specification). -
If strict incompatibilities exist,
ConflictsMAY be utilized. However, it SHOULD be applied with extreme caution to prevent creating unresolvable dependency graphs.
Section Requirements
%description
The %description section MUST provide a comprehensive, informative overview of the package's capabilities.
%prep / %build / %install / %check (as needed)
-
If custom build or installation procedures are necessary, the Spec MUST declare the relevant sections, or alternatively, represent these actions declaratively via
BuildSystemextensions. -
If upstream provides a test suite that can run within the build chroot, the Spec SHOULD invoke it in the
%checksection. If executing tests is technically infeasible, the Spec SHOULD document the technical blockers in a comment.
%files
Every binary package produced by a Spec MUST have a corresponding %files section. A Spec MAY omit the main package's %files section when it intentionally does not produce the main package.
A %files section MAY be empty when the corresponding binary package intentionally contains no files, such as a dependency-only meta-package.
The %files sections MUST follow these requirements:
-
Licensing documents MUST be tagged with
%license; standard documentation SHOULD be tagged with%doc. -
The
%filesmanifest MUST NOT duplicate file entries (exemptions apply only to highly specific, documented edge cases). -
Packages MUST NOT distribute
.la(libtool archive) artifacts. If the build process generates them, the Spec MUST forcefully remove them during the%installphase. -
Internationalization (i18n) and localization (l10n) assets MUST be processed using
%find_langwithin the%installsection; they MUST NOT be captured via brute-force globbing (e.g.,%{_datadir}/locale/*) within%files.
For recommended practices regarding language localization, see the Language Packs supplementary specification.
%changelog
The %changelog section MUST rely entirely on the %autochangelog macro; manual or handwritten changelog entries MUST NOT be used.
Subpackaging and Splitting Rules
When a single source archive yields multiple discrete binary packages, the Spec MUST adhere to these foundational principles:
-
Logical Functional Splitting: If a monolithic package bundles disparate functionalities, it MUST be structurally divided along major functional boundaries. It MUST NOT be hyper-fragmented into microscopic modules or plugins. Subpackage nomenclature MUST adopt the
%{name}-<feature>pattern. -
Segregation of Development Headers: Any C/C++ headers or development-centric files MUST be strictly isolated within a
%{name}-develsubpackage. -
Documentation Offloading: If bundled documentation is excessively large, it MUST be shipped in a dedicated
%{name}-docsubpackage. -
Shared Library Ownership: The distribution of shared objects (
.so) and their associated symlinks MUST strictly comply with the guidelines defined in the Package Splitting supplementary specification (specifically regarding runtime libraries, SONAME symlinks, and unversioned symlinks). -
Upgrade Continuity: If structural splitting alters the primary package name, a transparent upgrade path MUST be engineered via
ObsoletesandProvides.
For further nuances, refer to the Package Splitting and Naming Guidelines supplementary specifications.
Standard Path and Common Macros
Standard Path Macros
When referencing standard filesystem hierarchies within a Spec, native RPM path macros SHOULD be prioritized. Essential path macros include:
| Macro | Typical expansion | Meaning |
|---|---|---|
%{_lib} | lib64 | Architecture-dependent library directory name (example) |
%{_bindir} | %{_exec_prefix}/bin | Standard executable directory (typically /usr/bin) |
%{_docdir} | %{_datadir}/doc | Documentation directory (typically /usr/share/doc) |
%{_libdir} | %{_exec_prefix}/%{_lib} | Primary library directory (typically /usr/%{_lib}) |
%{_libexecdir} | %{_exec_prefix}/libexec | Executable directory for internal binaries (typically /usr/libexec) |
%{_datadir} | %{_datarootdir} | Architecture-independent shared data directory (typically /usr/share) |
%{_mandir} | %{_datarootdir}/man | Manual pages directory (typically /usr/share/man) |
%{_prefix} | /usr | Top-level installation prefix |
%{_sysconfdir} | /etc | System configuration directory |
%{_exec_prefix} | %{_prefix} | Executable path prefix |
%{_includedir} | %{_prefix}/include | C/C++ header directory (typically /usr/include) |
Bash Integration macros
| Macro | Meaning | Notes |
|---|---|---|
%{bash_completions_dir} | %{_datadir}/bash-completion/completions | Standard directory for Bash completion scripts |
Python Integration macros
| Macro | Meaning | Notes |
|---|---|---|
%{__python} | Path to the default Python interpreter | |
%{python_sitelib} | Installation path for architecture-independent Python pure modules | |
%{python_sitearch} | Installation path for architecture-dependent Python extensions |
Perl Integration macros
| Macro | Meaning | Notes |
|---|---|---|
%{__perl} | Path to the default Perl interpreter | |
%{perl_vendorlib} | Installation path for architecture-independent vendor Perl modules | |
%{perl_vendorarch} | Installation path for architecture-dependent vendor Perl modules |
RPM Scriptlets
When a Spec necessitates operational scriptlets (e.g., %pre %post %preun %postun %pretrans %posttrans):
-
Scriptlets MUST be strictly idempotent and accurately differentiate between initial installation, upgrade, and uninstallation events (typically evaluated via the transaction argument, e.g.,
$1). -
Scriptlets MUST guarantee a zero exit code (success); they MUST NOT trigger a transaction rollback due to the failure of trivial, non-critical operations.
-
If a scriptlet merely executes a standalone binary, it SHOULD utilise the
-pparameter to bypass unnecessary shell invocation overhead.
For comprehensive operational rules and templates, see the Scripts supplementary specification.
Conditional Builds
-
When designing optional compile-time feature toggles, the Spec SHOULD implement them via
%bcond. -
The Spec SHOULD strictly avoid the legacy
%bcond_withand%bcond_withoutmacros.
Example:
%bcond bootstrap 0
%bcond bootstrap 1
%if %{with bootstrap}
# ...
%endif
%if %{without bootstrap}
# ...
%endif
References
This specification builds upon and interacts with the following external standards:
-
Official RPM Packaging Guidelines, Spec syntax, and Macro Documentation
-
The SPDX License List and SPDX License Expression definitions