class FPM::Package::RPM
RPM
Package type.
Build RPMs without having to waste hours reading Maximum-RPM. Well, in case you want to read it, here: www.rpm.org/max-rpm/
The following attributes are supported:
-
:rpm_rpmbuild_define - an array of definitions to give to rpmbuild. These are used, verbatim, each as: –define ITEM
Constants
- COMPRESSION_MAP
- DIGEST_ALGORITHM_MAP
Public Instance Methods
Handle any architecture naming conversions. For example, debian calls amd64 what redhat calls x86_64, this method fixes those types of things.
# File lib/fpm/package/rpm.rb, line 236 def architecture case @architecture when nil return %x{uname -m}.chomp # default to current arch when "amd64" # debian and redhat disagree on architecture names return "x86_64" when "native" return %x{uname -m}.chomp # 'native' is current arch when "all" # Translate fpm "all" arch to what it means in RPM. return "noarch" else return @architecture end end
# File lib/fpm/package/rpm.rb, line 564 def build_sub_dir return "BUILD" #return File.join("BUILD", prefix) end
See FPM::Package#converted_from
# File lib/fpm/package/rpm.rb, line 258 def converted_from(origin) if origin == FPM::Package::Gem fixed_deps = [] self.dependencies.collect do |dep| # Gem dependency operator "~>" is not compatible with rpm. Translate any found. fixed_deps = fixed_deps + expand_pessimistic_constraints(dep) end self.dependencies = fixed_deps # Convert 'rubygem-foo' provides values to 'rubygem(foo)' # since that's what most rpm packagers seem to do. self.provides = self.provides.collect do |provides| # Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present # and return it in rubygem_prefix(gem_name) form if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(provides) "#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}" else provides end end if !self.attributes[:rpm_verbatim_gem_dependencies?] self.dependencies = self.dependencies.collect do |dependency| # Tries to match rubygem_prefix [1], gem_name [2] and version [3] if present # and return it in rubygem_prefix(gem_name) form if name=/^(#{attributes[:gem_package_name_prefix]})-([^\s]+)\s*(.*)$/.match(dependency) "#{name[1]}(#{name[2]})#{name[3] ? " #{name[3]}" : ""}" else dependency end end end end # Convert != dependency as Conflict =, as rpm doesn't understand != self.dependencies = self.dependencies.select do |dep| name, op, version = dep.split(/\s+/) dep_ok = true if op == '!=' self.conflicts << "#{name} = #{version}" dep_ok = false end dep_ok end # if --ignore-iteration-in-dependencies is true convert foo = X, to # foo >= X , foo < X+1 if self.attributes[:rpm_ignore_iteration_in_dependencies?] self.dependencies = self.dependencies.collect do |dep| name, op, version = dep.split(/\s+/) if op == '=' nextversion = version.split('.').collect { |v| v.to_i } nextversion[-1] += 1 nextversion = nextversion.join(".") logger.warn("Converting dependency #{dep} to #{name} >= #{version}, #{name} < #{nextversion}") ["#{name} >= #{version}", "#{name} < #{nextversion}"] else dep end end.flatten end setscript = proc do |scriptname| script_path = self.attributes[scriptname] # Skip scripts not set next if script_path.nil? if !File.exist?(script_path) logger.error("No such file (for #{scriptname.to_s}): #{script_path.inspect}") script_errors << script_path end # Load the script into memory. scripts[scriptname] = File.read(script_path) end setscript.call(:rpm_verifyscript) setscript.call(:rpm_posttrans) setscript.call(:rpm_pretrans) end
# File lib/fpm/package/rpm.rb, line 622 def digest_algorithm return DIGEST_ALGORITHM_MAP[attributes[:rpm_digest]] end
The default epoch value must be nil, see #381
# File lib/fpm/package/rpm.rb, line 588 def epoch return @epoch if @epoch.is_a?(Numeric) if @epoch.nil? or @epoch.empty? return nil end return @epoch end
# File lib/fpm/package/rpm.rb, line 354 def input(path) rpm = ::RPM::File.new(path) tags = {} rpm.header.tags.each do |tag| tags[tag.tag] = tag.value end self.architecture = tags[:arch] self.category = tags[:group] self.description = tags[:description] self.epoch = (tags[:epoch] || [nil]).first # for some reason epoch is an array self.iteration = tags[:release] self.license = tags[:license] self.maintainer = maintainer self.name = tags[:name] self.url = tags[:url] self.vendor = tags[:vendor] self.version = tags[:version] self.scripts[:before_install] = tags[:prein] self.scripts[:after_install] = tags[:postin] self.scripts[:before_remove] = tags[:preun] self.scripts[:after_remove] = tags[:postun] self.scripts[:rpm_verifyscript] = tags[:verifyscript] self.scripts[:rpm_posttrans] = tags[:posttrans] self.scripts[:rpm_pretrans] = tags[:pretrans] # TODO(sissel): prefix these scripts above with a shebang line if there isn't one? # Also taking into account the value of tags[preinprog] etc, something like: # #!#{tags[:preinprog]} # #{tags[prein]} if !tags[:triggerindex].nil? val = tags[:triggerindex].zip(tags[:triggername],tags[:triggerflags],tags[:triggerversion]).group_by{ |x| x[0]} val = val.collect do |order,data| new_data = data.collect { |x| [ x[1], rpm.operator(x[2]), x[3]].join(" ").strip}.join(", ") [order, rpm_get_trigger_type(data[0][2]), new_data] end val.each do |order, attr,data| self.attributes[attr] = [] if self.attributes[attr].nil? scriptprog = (tags[:triggerscriptprog][order] == '/bin/sh') ? "" : "-p #{tags[:triggerscriptprog][order]}" self.attributes[attr] << [data,tags[:triggerscripts][order],scriptprog] end end if !attributes[:no_auto_depends?] self.dependencies += rpm.requires.collect do |name, operator, version| [name, operator, version].join(" ") end end self.conflicts += rpm.conflicts.collect do |name, operator, version| [name, operator, version].join(" ") end self.provides += rpm.provides.collect do |name, operator, version| [name, operator, version].join(" ") end #input.replaces += replaces self.config_files += rpm.config_files # rpms support '%dir' things for specifying empty directories to package, # but the rpm header itself doesn't actually record this information. # so there's no 'directories' to copy, so don't try to merge in the # 'directories' feature. # TODO(sissel): If you want this feature, we'll have to find scan # the extracted rpm for empty directories. I'll wait until someone asks for # this feature #self.directories += rpm.directories # Extract to the staging directory rpm.extract(staging_path) end
This method ensures a default value for iteration if none is provided.
# File lib/fpm/package/rpm.rb, line 253 def iteration return @iteration ? @iteration : 1 end
# File lib/fpm/package/rpm.rb, line 432 def output(output_path) output_check(output_path) %w(BUILD RPMS SRPMS SOURCES SPECS).each { |d| FileUtils.mkdir_p(build_path(d)) } args = ["rpmbuild", "-bb"] if %x{uname -m}.chomp != self.architecture rpm_target = self.architecture end # issue #309 if !attributes[:rpm_os].nil? rpm_target = "#{architecture}-unknown-#{attributes[:rpm_os]}" end # issue #707 if !rpm_target.nil? args += ["--target", rpm_target] end # set the rpm dist tag args += ["--define", "dist .#{attributes[:rpm_dist]}"] if attributes[:rpm_dist] args += [ "--define", "buildroot #{build_path}/BUILD", "--define", "_topdir #{build_path}", "--define", "_sourcedir #{build_path}", "--define", "_rpmdir #{build_path}/RPMS", "--define", "_tmppath #{attributes[:workdir]}" ] args += ["--sign"] if attributes[:rpm_sign?] if attributes[:rpm_auto_add_directories?] fs_dirs_list = File.join(template_dir, "rpm", "filesystem_list") fs_dirs = File.readlines(fs_dirs_list).reject { |x| x =~ /^\s*#/}.map { |x| x.chomp } fs_dirs.concat((attributes[:auto_add_exclude_directories] or [])) Find.find(staging_path) do |path| next if path == staging_path if File.directory? path and !File.symlink? path add_path = path.gsub(/^#{staging_path}/,'') self.directories << add_path if not fs_dirs.include? add_path end end else self.directories = self.directories.map { |x| self.prefixed_path(x) } alldirs = [] self.directories.each do |path| Find.find(File.join(staging_path, path)) do |subpath| if File.directory? subpath and !File.symlink? subpath alldirs << subpath.gsub(/^#{staging_path}/, '') end end end self.directories = alldirs end # include external config files (attributes[:config_files] or []).each do |conf| dest_conf = File.join(staging_path, conf) if File.exist?(dest_conf) logger.debug("Using --config-file from staging area", :path => conf) elsif File.exist?(conf) logger.info("Copying --config-file from local path", :path => conf) FileUtils.mkdir_p(File.dirname(dest_conf)) FileUtils.cp_r conf, dest_conf else logger.error("Failed to find given --config-file", :path => conf) raise "Could not find config file '#{conf}' in staging area or on host. This can happen if you specify `--config-file '#{conf}'` but this file does not exist in the source package and also does not exist in filesystem." end end # scan all conf file paths for files and add them allconfigs = [] self.config_files.each do |path| cfg_path = File.join(staging_path, path) raise "Config file path #{cfg_path} does not exist" unless File.exist?(cfg_path) Find.find(cfg_path) do |p| allconfigs << p.gsub("#{staging_path}/", '') if File.file? p end end allconfigs.sort!.uniq! self.config_files = allconfigs.map { |x| File.join("/", x) } # add init script if present (attributes[:rpm_init_list] or []).each do |init| name = File.basename(init, ".init") dest_init = File.join(staging_path, "etc/init.d/#{name}") FileUtils.mkdir_p(File.dirname(dest_init)) FileUtils.cp init, dest_init File.chmod(0755, dest_init) end (attributes[:rpm_rpmbuild_define] or []).each do |define| args += ["--define", define] end # copy all files from staging to BUILD dir # [#1538] Be sure to preserve the original timestamps. Find.find(staging_path) do |path| src = path.gsub(/^#{staging_path}/, '') dst = File.join(build_path, build_sub_dir, src) copy_entry(path, dst, preserve=true) end rpmspec = template("rpm.erb").result(binding) specfile = File.join(build_path("SPECS"), "#{name}.spec") File.write(specfile, rpmspec) edit_file(specfile) if attributes[:edit?] args << specfile logger.info("Running rpmbuild", :args => args) safesystem(*args) ::Dir["#{build_path}/RPMS/**/*.rpm"].each do |rpmpath| # This should only output one rpm, should we verify this? FileUtils.cp(rpmpath, output_path) end end
# File lib/fpm/package/rpm.rb, line 613 def payload_compression if attributes[:rpm_compression] == 'none' # when 'none' ignore any compression level and return w0.gzdio return COMPRESSION_MAP[attributes[:rpm_compression]] else return "w#{attributes[:rpm_compression_level]}" + COMPRESSION_MAP[attributes[:rpm_compression]] end end
# File lib/fpm/package/rpm.rb, line 556 def prefix if attributes[:prefix] and attributes[:prefix] != '/' return attributes[:prefix].chomp('/') else return "/" end end
# File lib/fpm/package/rpm.rb, line 428 def prefixed_path(path) Pathname.new(path).absolute?() ? path : File.join(self.prefix, path) end
# File lib/fpm/package/rpm.rb, line 569 def summary if !attributes[:rpm_summary] return @description.split("\n").find { |line| !line.strip.empty? } || "_" end return attributes[:rpm_summary] end
FPM::Package#to_s
# File lib/fpm/package/rpm.rb, line 602 def to_s(format=nil) if format.nil? format = if attributes[:rpm_dist] "NAME-VERSION-ITERATION.DIST.ARCH.EXTENSION" else "NAME-VERSION-ITERATION.ARCH.EXTENSION" end end return super(format.gsub("DIST", to_s_dist)) end
# File lib/fpm/package/rpm.rb, line 577 def version if @version.kind_of?(String) and @version.include?("-") logger.warn("Package version '#{@version}' includes dashes, converting" \ " to underscores") @version = @version.gsub(/-/, "_") end return @version end
Private Instance Methods
# File lib/fpm/package/rpm.rb, line 204 def rpm_file_entry(file) original_file = file file = rpm_fix_name(file) if !attributes[:rpm_use_file_permissions?] if attrs[file].nil? return file else return sprintf("%%attr(%s) %s\n", attrs[file], file) end end return sprintf("%%attr(%s) %s\n", attrs[file], file) unless attrs[file].nil? # Stat the original filename in the relative staging path ::Dir.chdir(staging_path) do stat = File.lstat(original_file.gsub(/\"/, '').sub(/^\//,'')) # rpm_user and rpm_group attribute should override file ownership # otherwise use the current file user/group by name. user = attributes[:rpm_user] || Etc.getpwuid(stat.uid).name group = attributes[:rpm_group] || Etc.getgrgid(stat.gid).name mode = stat.mode return sprintf("%%attr(%o, %s, %s) %s\n", mode & 4095 , user, group, file) end end
Fix path name Replace [ with [[] to make rpm not use globs Replace * with [*] to make rpm not use globs Replace ? with [?] to make rpm not use globs Replace % with [%] to make rpm not expand macros
# File lib/fpm/package/rpm.rb, line 192 def rpm_fix_name(name) name = name.gsub(/(\ |\[|\]|\*|\?|\%|\$)/, { ' ' => '?', '%' => '[%]', '$' => '[$]', '?' => '[?]', '*' => '[*]', '[' => '[\[]', ']' => '[\]]' }) end
# File lib/fpm/package/rpm.rb, line 336 def rpm_get_trigger_type(flag) if (flag & (1 << 25)) == (1 << 25) # RPMSENSE_TRIGGERPREIN = (1 << 25), /*!< %triggerprein dependency. */ :rpm_trigger_before_install elsif (flag & (1 << 16)) == (1 << 16) # RPMSENSE_TRIGGERIN = (1 << 16), /*!< %triggerin dependency. */ :rpm_trigger_after_install elsif (flag & (1 << 17)) == (1 << 17) # RPMSENSE_TRIGGERUN = (1 << 17), /*!< %triggerun dependency. */ :rpm_trigger_before_uninstall elsif (flag & (1 << 18)) == (1 << 18) # RPMSENSE_TRIGGERPOSTUN = (1 << 18), /*!< %triggerpostun dependency. */ :rpm_trigger_after_target_uninstall else @logger.fatal("I don't know about this triggerflag ('#{flag}')") end end
# File lib/fpm/package/rpm.rb, line 598 def to_s_dist; attributes[:rpm_dist] ? "#{attributes[:rpm_dist]}" : "DIST"; end