crystal-rpm
crystal-rpm
The RPM bindings for Crystal language based on ruby-rpm and ruby-rpm-ffi.
It supports RPM 4.8.0 or later.
Before use
RPM is licensed under GNU GPL. This means all libraries and applications which link to RPM must be licensed under GNU GPL. But, Crystal (and standard libraries) are licensed under Apache-2.0, which will be incompatible.
Actual COPYING
file in source tarball of RPM says that library part of RPM (i.e., librpm
) is licensed under LGPL 2.0 or later too, which can be compatible with MIT and Apache-2.0.
So currently, crystal-rpm is licensed under both GPLv2 or later and MIT (which should be compatible with LGPL 2.0 and Apache-2.0). We are thinking you do not have a chance to use this library under the conditions of GPLv2, but left to let us to keep in mind.
Installation
- Add the dependency to your
shard.yml
:
dependencies:
rpm:
github: lugia-kun/crystal-rpm
- Run
shards install
Usage
require "rpm"
The development files (pkg-config file) of RPM is required. Typically, it can be installed by yum install rpm-devel
on CentOS or Red Hat, dnf install rpm-devel
on Fedora, and zypper in rpm-devel
on openSUSE or SLES.
Inspect package
pkg = RPM::Package.open("foobar-1.0-0.x86_64.rpm")
puts pkg[RPM::Tag::Name] # => "foobar"
puts pkg[RPM::Tag::Arch] # => "x86_64"
puts pkg[RPM::Tag::Summary] # => (Content of Summary)
# and so on...
pkg.requires # => Array of Requires.
pkg.provides # => Array of Provides.
pkg.conflicts # => Array of Conflicts.
pkg.obsoletes # => Array of Obsolstes.
Install package
RPM.transaction do |ts|
path = "pkg_to_install-1.0-0.x86_64.rpm"
pkg = ts.read_package_file(path)
ts.install(pkg, path) # Add installation package. Package path is required.
ts.commit # Run Transaction
end
BTW, RPM::Package.open
allocates a Transaction
inside. So if you have an instance of Transaction
already, Transaction#read_package_file
reduces allocations.
Search installed packages
# with given name
RPM.transaction do |ts|
ts.db_iterator(RPM::DbiTag::Name, "package-name-to-find") do |iter|
iter.each do |pkg|
# Iterator over matching packages.
end
end
end
# with given regexp
RPM.transaction do |ts|
ts.db_iterator do |iter|
# Set condition
iter.regexp(RPM::DbiTag::Name, # <= Entry to search (here, Name)
RPM::MireMode::REGEX, # <= Default matching method
"simple.*") # <= Name to search
# Iterate over matching packages.
iter.each do |pkg|
puts pkg[RPM::Tag::Version].as(String) # => (Version of package "simple")
end
end
end
# Iterate over all installed packages
RPM.transaction do |ts|
ts.db_iterator do |iter|
iter.each do |pkg|
# ... iterates over all installed packages.
end
end
end
# Lookup package(s) which contains a specific file
RPM.transaction do |ts|
ts.db_iterator(RPM::DbiTag::BaseNames, "/path/to/lookup") do |iter|
iter.each do |pkg|
# ... iterates over packages contains "/path/to/lookup"
end
end
end
# NOTE: Using regexp with BaseNames, it will search packages which
# contain a file whose basename is the given name.
RPM.transaction do |ts|
ts.db_iterator do |iter|
iter.regexp(RPM::DbiTag::BaseNames, RPM::MireMode::STRCMP, "README")
iter.each do |pkg|
# ... iterates over packages which contain a file named "README"
end
end
end
Remove package
RPM.transaction do |ts|
ts.delete(pkg) # Add to removal package
ts.order
ts.clean
ts.commit # Run Transaction
end
Using Transaction without block
For installing and/or removing packages, the DB handle is bound to the transaction. So, DB must be closed via transaction.
ts = RPM::Transaction.new
begin
ts.install(...)
ts.delete(...)
ts.order
ts.clean
ts.commit
ensure
ts.close_db # Must close DB
end
ts.finalize # Not nesseary, but recommended.
Using DB iterator without block
For searching packages in DB, the DB handle is bound to the iterator. So, DB must be closed via finalizing the iterator.
ts = RPM::Transaction.new
begin
iter = ts.init_iterator(...)
begin
iter.each do |item|
# work with item
end
ensure
iter.finalize # Must finalize iterator.
end
end
ts.finalize
Install/Remove Problems
RPM.transation do |ts|
ts.install(...)
ts.order
ts.clean
ts.check
if (problems = ts.problems?)
problems.each do |problem|
puts problem.to_s # => Output install (typically dependency) problems.
end
end
end
Inspect Specfile
spec = RPM::Spec.open("foo.spec")
packages = spec.packages
packages[0][RPM::Tag::Name] # => (Name of the first package)
packages[1][RPM::Tag::Name] # => (Name of the second package)
# NOTE: The order is undefined.
spec.buildrequires # => Array of BuildRequires.
Build RPM
# Steps to be run.
#
# Here, %prep, %build, %install, %check, %clean and generates the source
# and binary packages.
#
amount = RPM::BuildFlags.flags(PREP, BUILD, INSTALL, CHECK, CLEAN,
PACKAGESOURCE, PACKAGEBINARY)
# Read specfile
spec = RPM::Spec.open("foo.spec")
spec.build(build_amount: amount)
Development
The definitions of structs are written by hand. Tests can check their size and member offsets if you have a C compiler (optional).
fakechroot (recommended), proot (untested), or root permission (i.e., sudo
) is required to run crystal spec
, since this spec uses chroot()
.
Contributing
- Fork it (https://github.com/lugia-kun/crystal-rpm/fork)
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Pull Request
Contributors
- Hajime Yoshimori - creator and maintainer
crystal-rpm
- 1
- 0
- 1
- 1
- 1
- over 3 years ago
- January 14, 2019
MIT License
Fri, 22 Nov 2024 13:42:40 GMT