Complete Guide to Building RPM Packages from Source on Kylin OS V11
Introduction
Many administrators prefer deploying services using RPM packages on Kylin Advanced Server Operating System V11 due to their simplicity and efficiency. However, the default base and update repositories in KylinOS V11 often lack specific versions of RPM packages required for particular projects. In such scenarios, creating custom RPM packages becomes necessary to achieve package deployment flexibility.
This comprehensive guide walks through the complete process of building RPM packages from source code on KylinOS V11, using CRI-O version 1.35.0 as a practical demonstration. The workflow closely mirrors the V10 process, utilizing rpmbuild as the primary build tool.
Preparation Phase
Downloading CRI-O Source Code
Begin by obtaining the CRI-O source code from the official GitHub repository:
wget https://github.com/cri-o/cri-o/archive/refs/tags/v1.35.0.zipThis specific version serves as our example, but the process applies to any source package requiring RPM compilation.
Installing the Compilation Environment
Golang Installation
Since CRI-O is written in Go and version 1.35.0 requires Go 1.25.0, which isn't available in V11's yum repositories, we must install Go via binary distribution.
Step 1: Download the Go Binary Package
Select the appropriate architecture-specific package:
wget https://go.dev/dl/go1.25.0.linux-amd64.tar.gzStep 2: Extract to System Directory
tar -C /usr/local -xzf go1.25.0.linux-amd64.tar.gzStep 3: Configure Environment Variables
Edit your bash configuration file:
vim ~/.bashrcAdd the following environment variable configurations:
# Go installation path (default)
export GOROOT=/usr/local/go
# Add executables to system PATH
export PATH=$PATH:$GOROOT/bin
# Workspace directory (for source code, compiled binaries, dependencies)
export GOPATH=$HOME/go
# Module cache path
export GOCACHE=$HOME/.cache/go-build
# Domestic module proxy (essential for fast dependency downloads in China)
export GOPROXY=https://goproxy.cn,direct
# Enable Go Modules (enabled by default in 1.16+)
export GO111MODULE=onStep 4: Apply Configuration Changes
source ~/.bashrcStep 5: Create Workspace Directories
cd $HOME/go
mkdir src bin pkgDirectory purposes:
src/: Source code storagebin/: Compiled executablespkg/: Dependency package cache
Step 6: Verify Installation
go version
go envThese commands confirm proper Go environment configuration.
Installing CRI-O Compilation Dependencies
Install the required system libraries and development tools:
yum install -y \
containers-common \
git \
make \
glib2-devel \
glibc-devel \
glibc-static \
crunInstall Go dependencies:
go get github.com/cpuguy83/go-md2manInstall additional required libraries:
yum install -y \
libassuan \
libassuan-devel \
libgpg-error \
libseccomp-devel \
libselinux \
pkgconf-pkg-config \
gpgme-develInstalling RPM Build Environment
Install the RPM building tools:
yum install -y rpm-build rpmdevtools brp-chrpathThese packages provide the essential infrastructure for RPM package creation.
Building Process
Creating Standard rpmbuild Directory Structure
Execute the following command as a regular user (root privileges not required):
rpmdev-setuptreeThis command generates the standard RPM build directory structure in your home directory:
~/rpmbuild/
├── BUILD # Temporary compilation directory
├── BUILDROOT # Installation root directory
├── RPMS # Final generated RPM packages (primary output)
├── SOURCES # Source code packages placed here
├── SPECS # Compilation script spec files placed here
└── SRPMS # Source RPM packagesUnderstanding each directory's purpose proves crucial for successful package building.
Preparing the Build Directory
Step 1: Place Source Code in SOURCES
Move the CRI-O source package to the SOURCES directory:
mv cri-o-1.35.0.zip ~/rpmbuild/SOURCES/Step 2: Create the Spec File
Create the cri-o.spec file in the SPECS directory:
vim ~/rpmbuild/SPECS/cri-o.specThe complete specification file configuration appears below:
# Disable automatic debuginfo package generation
%global debug_package %{nil}
%define _enable_debug_packages 0
Name: cri-o
Version: 1.35.0
Release: 1%{?dist}
Summary: Kubernetes CRI implementation for OCI
License: ASL 2.0
URL: https://cri-o.io/
# Note: Package name must match source package name in SOURCES directory
Source0: https://github.com/cri-o/cri-o/cri-o-%{version}.zip
# Build dependencies (required on compilation machine)
BuildRequires: golang >= 1.25.0
BuildRequires: make
BuildRequires: gcc
# Runtime dependencies (required on installation machine)
Requires: conmon >= 2.1
Requires: runc
Requires: container-selinux
%description
CRI-O is an implementation of the Kubernetes CRI (Container Runtime Interface)
to enable using OCI (Open Container Initiative) compatible runtime.
%prep
%setup -q
%build
# Compile CRI-O (official compilation command)
make
%install
# 1. Official installation command (critical - automatically installs all files)
make install DESTDIR=%{buildroot}
# 2. Create standard systemd directories (paths recognized by host system)
mkdir -p %{buildroot}/usr/lib/systemd/system
# 3. Move service files from default path to standard system path
mv %{buildroot}/usr/local/lib/systemd/system/crio.service %{buildroot}/usr/lib/systemd/system/
mv %{buildroot}/usr/local/lib/systemd/system/crio-wipe.service %{buildroot}/usr/lib/systemd/system/
# 4. Delete empty directories from default path (mandatory - prevents "unpacked files" errors)
rm -rf %{buildroot}/usr/local/lib/systemd
%files
# ===================== Binary Files =====================
/usr/local/bin/crio
/usr/local/bin/pinns
# ===================== systemd Services =====================
/usr/lib/systemd/system/crio.service
/usr/lib/systemd/system/crio-wipe.service
# ===================== Configuration Files =====================
%config(noreplace) /etc/crio/crio.conf
%dir /etc/crio/crio.conf.d
%config(noreplace) /etc/crictl.yaml
# ===================== Auxiliary Files =====================
/usr/local/share/oci-umount/oci-umount.d/crio-umount.conf
/usr/local/share/containers/oci/hooks.d
# ===================== Manual Pages =====================
/usr/local/share/man/man5/crio.conf.5*
/usr/local/share/man/man5/crio.conf.d.5*
/usr/local/share/man/man8/crio.8*
# ===================== Command Completion (bash/fish/zsh) =====================
/usr/local/share/bash-completion/completions/crio
/usr/local/share/fish/completions/crio.fish
/usr/local/share/zsh/site-functions/_crio
%changelog
* Wed Apr 08 2026 Packager - 1.35.0-1
- Initial build for cri-o 1.35.0Understanding Spec File Sections
Header Section: Defines package metadata including name, version, release number, summary, license, and URL.
Source Declaration: Specifies the source code location. The %{version} variable automatically substitutes the Version field value.
BuildRequires: Lists dependencies needed during compilation. These must exist on the build machine.
Requires: Lists runtime dependencies. These must exist on machines where the package installs.
%description: Provides a detailed package description visible in package managers.
%prep: Prepares the build environment. The %setup -q command extracts the source archive quietly.
%build: Contains compilation commands. For CRI-O, the official make command suffices.
%install: Handles installation into the build root directory. Critical steps include:
- Running the official installation command with
DESTDIR=%{buildroot} - Creating standard systemd directories
- Moving service files to system-recognized paths
- Cleaning up empty directories to prevent packaging errors
%files: Lists all files to include in the final package. The %config(noreplace) directive marks configuration files that shouldn't be overwritten during upgrades.
%changelog: Documents package version history and changes.
Building the RPM Package
Execute the build command:
cd ~/rpmbuild/SPECS
rpmbuild -ba --nodeps cri-o.specThe --nodeps flag proves essential in this scenario. Since our Go environment was installed via binary rather than RPM, omitting this parameter would cause rpmbuild to fail dependency checks, believing Go isn't installed.
Build Output
Upon successful completion, rpmbuild generates the following structure:
.
├── BUILD
├── BUILDROOT
├── RPMS
│ └── x86_64
│ └── cri-o-1.35.0-1.ky11.x86_64.rpm
├── SOURCES
│ └── cri-o-1.35.0.zip
├── SPECS
│ └── cri-o.spec
└── SRPMS
└── cri-o-1.35.0-1.ky11.src.rpmThe final RPM package resides in RPMS/x86_64/, while the source RPM package locates in SRPMS/.
Installation and Testing
Package Installation
Install the generated RPM package:
yum -y install ./cri-o-1.35.0-1.ky11.x86_64.rpmThe yum command handles dependency resolution automatically.
Service Startup
Start the CRI-O service:
systemctl start crio.serviceEnable the service for automatic startup on boot:
systemctl enable crio.serviceFunctional Testing
Test image pulling using crictl:
crictl pull swr.cn-east-3.myhuaweicloud.com/itho/docker.io/busybox:latest-amd64Verify the image downloaded successfully:
crictl imagesExpected output:
IMAGE TAG
swr.cn-east-3.myhuaweicloud.com/itho/docker.io/busybox latest-amd64If both the image pull and service status function correctly, the RPM package build and installation succeeded.
Troubleshooting Common Issues
Dependency Resolution Failures
If rpmbuild reports missing dependencies despite installation:
- Verify all BuildRequires packages are installed
- Check Go environment variables are properly configured
- Ensure
--nodepsflag is used when appropriate
File Packaging Errors
"Unpacked files" errors typically indicate:
- Files installed to unexpected locations during
%install - Missing entries in the
%filessection - Empty directories not properly cleaned up
Review the build log carefully to identify specific problematic paths.
Service Startup Failures
If the service fails to start:
- Check systemd service file paths match system expectations
- Verify all runtime dependencies are installed
- Review journal logs:
journalctl -u crio.service - Ensure configuration files contain valid settings
Build Environment Issues
For consistent build failures:
- Clean the build environment:
rm -rf ~/rpmbuild/BUILD/* - Verify source archive integrity
- Check available disk space
- Review spec file syntax for errors
Advanced Considerations
Spec File Optimization
For production deployments, consider:
- Adding version detection logic for dynamic dependency versions
- Implementing conditional compilation for different architectures
- Including comprehensive documentation in the package
- Adding post-install and post-uninstall scripts for cleanup
Automation Possibilities
The entire build process lends itself well to automation:
- CI/CD pipeline integration for automatic package generation
- Version detection and spec file generation scripts
- Automated testing of generated packages
- Repository management for package distribution
Future enhancements might include AI-assisted spec file generation, though current attempts reveal challenges with AI-generated spec files often containing outdated fields and instructions. However, combining AI generation with automated error recognition and correction shows promise for streamlining the process.
Conclusion
The complete RPM package building workflow on KylinOS V11 involves straightforward steps:
- Preparation: Download source code and install compilation environment
- Setup: Create standard rpmbuild directory structure
- Configuration: Write comprehensive spec file
- Build: Execute rpmbuild with appropriate flags
- Testing: Install and verify package functionality
While the process appears simple, spec file writing often requires iterative debugging to resolve build errors. The key lies in understanding each section's purpose and carefully managing file paths during the installation phase.
Mastering RPM package building provides significant advantages for system administrators managing KylinOS deployments, enabling custom package creation for applications unavailable in standard repositories or requiring specific versions not provided by default sources.
The skills developed through this process transfer to other RPM-based distributions, making this knowledge valuable across the broader Linux ecosystem.