Creating FreeBSD ports for ruby gems involves creating a pkg-plist that lists all files and directories for a given port. This kind of sucks for gems that have a lot of rdoc documentation (like ActiveSupport). There are a couple of automatic solutions like pkg_trackinst but none is perfect.
While updating the ports for Rails 1.2.1 I had enough and created a small Ruby script to help with the generation of pkg-plists. Updating a Rubygem port now looks like this:
# cd /usr/ports/devel/rubygem-activesupport # vim Makefile (update PORTVERSION) # make makesum
At first update the port version and create the new checksum. Now install the new version (without updating the pkg-plist, so will will get a lot of errors during deinstall).
# make install
Now run the script to generate the new pkg-plist out of the installed gem:
# /tmp/gen_plist.rb activesupport-1.4.0 > pkg-plist
The script assembles a pkg-plist of of all files and directories under /gems and /doc. Any other files like the rails executable for the Rails gem must be added by hand as they will not be installed under /usr/local/lib/ruby/gems/1.8/.
What’s left to do now is to cleanup manually the installation and test the pkg-plist through creating a package and deinstalling the port.
The small script looks like this:
#! /usr/bin/env ruby
package_name = ARGV[0]
raise ArgumentError if (package_name.nil? || package_name == '')
prefix = "/usr/local"
gem_lib_dir = "#{prefix}/lib/ruby/gems/1.8/gems/#{package_name}"
gem_doc_dir = "#{prefix}/lib/ruby/gems/1.8/doc/#{package_name}"
pkg_plist = ["%%GEM_CACHE%%", "%%GEM_SPEC%%"]
# add lib
lib_files = `find #{gem_lib_dir} -type f`.split("\n")
lib_dirs = `find #{gem_lib_dir} -type d`.split("\n").reverse
lib_dirs = lib_dirs.collect{|d| "@dirrm #{d}"}
# add doc
doc_files = `find #{gem_doc_dir} -type f`.split("\n")
doc_dirs = `find #{gem_doc_dir} -type d`.split("\n").reverse
doc_dirs = doc_dirs.collect{|d| "@dirrm #{d}"}
# assemble
pkg_plist += lib_files + doc_files + lib_dirs + doc_dirs
# change PLIST_SUBs
pkg_plist.each do |line|
line.gsub!(gem_doc_dir, "%%GEM_DOC_DIR%%")
line.gsub!(gem_lib_dir, "%%GEM_LIB_DIR%%")
end
# print out pkg-plist
pkg_plist.each do |line|
puts line
end

x-generate-plist: (${PORTSDIR}/Tools/scripts/plist -d -m ${MTREE_FILE} ${PREFIX} \ | ${SED} -E \ 's,.*share/nls/.+$$,,g \ ;s,.*etc/rc.d/.+$$,,g \ ;s,^${GEM_CACHE}$$,%%GEM_CACHE%%,g \ ;s,${GEM_DOC_DIR}(/.+)?$$,%%GEM_DOC_DIR%%\1,g \ ;s,${GEM_LIB_DIR}(/.+)?$$,%%GEM_LIB_DIR%%\1,g \ ;s,^${GEM_SPEC}$$,%%GEM_SPEC%%,g \ ;s,^${GEMS_BASE_DIR}/(.+)$$,\1,g \ ;s,^@dirrm (${SPEC_DIR}|${GEMS_DIR}|lib/ruby).*$$,,g \ ' | ${TR} -s '\n') > temp-pkg-plistI used this a couple of times when I submitted some PRs. Provided that the x-generate-plist target makes it into ruby-gems/Makefile.common, I guess the second part could be easily scripted. What do you think?