1 # Copyright 1999-2019 Gentoo Authors
2 # Distributed under the terms of the GNU General Public License v2
4 # @ECLASS: ruby-fakegem.eclass
6 # Ruby herd <ruby@gentoo.org>
8 # Author: Diego E. Pettenò <flameeyes@gentoo.org>
9 # Author: Alex Legler <a3li@gentoo.org>
10 # Author: Hans de Graaff <graaff@gentoo.org>
11 # @SUPPORTED_EAPIS: 4 5 6 7
12 # @BLURB: An eclass for installing Ruby packages to behave like RubyGems.
14 # This eclass allows to install arbitrary Ruby libraries (including Gems),
15 # providing integration into the RubyGems system even for "regular" packages.
19 # @ECLASS-VARIABLE: RUBY_FAKEGEM_NAME
21 # Sets the Gem name for the generated fake gemspec.
22 # This variable MUST be set before inheriting the eclass.
23 RUBY_FAKEGEM_NAME="${RUBY_FAKEGEM_NAME:-${PN}}"
25 # @ECLASS-VARIABLE: RUBY_FAKEGEM_VERSION
27 # Sets the Gem version for the generated fake gemspec.
28 # This variable MUST be set before inheriting the eclass.
29 RUBY_FAKEGEM_VERSION="${RUBY_FAKEGEM_VERSION:-${PV/_pre/.pre}}"
31 # @ECLASS-VARIABLE: RUBY_FAKEGEM_TASK_DOC
33 # Specify the rake(1) task to run to generate documentation.
34 RUBY_FAKEGEM_TASK_DOC="${RUBY_FAKEGEM_TASK_DOC-rdoc}"
36 # @ECLASS-VARIABLE: RUBY_FAKEGEM_RECIPE_TEST
38 # Specify one of the default testing function for ruby-fakegem:
39 # - rake (default; see also RUBY_FAKEGEM_TASK_TEST)
40 # - rspec (calls ruby-ng_rspec, adds dev-ruby/rspec:2 to the dependencies)
41 # - rspec3 (calls ruby-ng_rspec, adds dev-ruby/rspec:3 to the dependencies)
42 # - cucumber (calls ruby-ng_cucumber, adds dev-util/cucumber to the
45 RUBY_FAKEGEM_RECIPE_TEST="${RUBY_FAKEGEM_RECIPE_TEST-rake}"
47 # @ECLASS-VARIABLE: RUBY_FAKEGEM_TASK_TEST
49 # Specify the rake(1) task used for executing tests. Only valid
50 # if RUBY_FAKEGEM_RECIPE_TEST is set to "rake" (the default).
51 RUBY_FAKEGEM_TASK_TEST="${RUBY_FAKEGEM_TASK_TEST-test}"
53 # @ECLASS-VARIABLE: RUBY_FAKEGEM_RECIPE_DOC
55 # Specify one of the default API doc building function for ruby-fakegem:
56 # - rake (default; see also RUBY_FAKEGEM_TASK_DOC)
57 # - rdoc (calls `rdoc-2`, adds dev-ruby/rdoc to the dependencies);
58 # - yard (calls `yard`, adds dev-ruby/yard to the dependencies);
62 RUBY_FAKEGEM_RECIPE_DOC="${RUBY_FAKEGEM_RECIPE_DOC-rake}"
65 RUBY_FAKEGEM_RECIPE_DOC="${RUBY_FAKEGEM_RECIPE_DOC-rdoc}"
69 # @ECLASS-VARIABLE: RUBY_FAKEGEM_DOCDIR
72 # Specify the directory under which the documentation is built;
73 # if empty no documentation will be installed automatically.
74 # Note: if RUBY_FAKEGEM_RECIPE_DOC is set to `rdoc`, this variable is
77 # @ECLASS-VARIABLE: RUBY_FAKEGEM_EXTRADOC
80 # Extra documentation to install (readme, changelogs, …).
82 # @ECLASS-VARIABLE: RUBY_FAKEGEM_DOC_SOURCES
84 # Allow settings defined sources to scan for documentation.
85 # This only applies if RUBY_FAKEGEM_DOC_TASK is set to `rdoc`.
86 RUBY_FAKEGEM_DOC_SOURCES="${RUBY_FAKEGEM_DOC_SOURCES-lib}"
88 # @ECLASS-VARIABLE: RUBY_FAKEGEM_BINWRAP
90 # Binaries to wrap around (relative to the RUBY_FAKEGEM_BINDIR directory)
91 RUBY_FAKEGEM_BINWRAP="${RUBY_FAKEGEM_BINWRAP-*}"
93 # @ECLASS-VARIABLE: RUBY_FAKEGEM_BINDIR
95 # Path that contains binaries to be binwrapped. Equivalent to the
96 # gemspec bindir option.
97 RUBY_FAKEGEM_BINDIR="${RUBY_FAKEGEM_BINDIR-bin}"
99 # @ECLASS-VARIABLE: RUBY_FAKEGEM_REQUIRE_PATHS
102 # Extra require paths (beside lib) to add to the specification
104 # @ECLASS-VARIABLE: RUBY_FAKEGEM_GEMSPEC
107 # Filename of .gemspec file to install instead of generating a generic one.
109 # @ECLASS-VARIABLE: RUBY_FAKEGEM_EXTRAINSTALL
112 # List of files and directories relative to the top directory that also
113 # get installed. Some gems provide extra files such as version information,
114 # Rails generators, or data that needs to be installed as well.
118 die "Unsupported EAPI=${EAPI} (too old) for ruby-fakegem.eclass" ;;
122 die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
127 RUBY_FAKEGEM_SUFFIX="${RUBY_FAKEGEM_SUFFIX:-}"
130 [[ ${RUBY_FAKEGEM_TASK_DOC} == "" ]] && RUBY_FAKEGEM_RECIPE_DOC="none"
132 case ${RUBY_FAKEGEM_RECIPE_DOC} in
135 ruby_add_bdepend "doc? ( dev-ruby/rake )"
136 RUBY_FAKEGEM_DOCDIR="doc"
140 ruby_add_bdepend "doc? ( dev-ruby/rdoc )"
141 RUBY_FAKEGEM_DOCDIR="doc"
145 ruby_add_bdepend "doc? ( dev-ruby/yard )"
146 RUBY_FAKEGEM_DOCDIR="doc"
149 [[ -n ${RUBY_FAKEGEM_DOCDIR} ]] && IUSE+=" doc"
153 [[ ${RUBY_FAKEGEM_TASK_TEST} == "" ]] && RUBY_FAKEGEM_RECIPE_TEST="none"
155 case ${RUBY_FAKEGEM_RECIPE_TEST} in
158 RESTRICT+=" !test? ( test )"
159 ruby_add_bdepend "test? ( dev-ruby/rake )"
163 RESTRICT+=" !test? ( test )"
164 # Also require a new enough rspec-core version that installs the
166 ruby_add_bdepend "test? ( dev-ruby/rspec:2 >=dev-ruby/rspec-core-2.14.8-r2 )"
170 RESTRICT+=" !test? ( test )"
171 ruby_add_bdepend "test? ( dev-ruby/rspec:3 )"
175 RESTRICT+=" !test? ( test )"
176 ruby_add_bdepend "test? ( dev-util/cucumber )"
179 RUBY_FAKEGEM_RECIPE_TEST="none"
183 SRC_URI="https://rubygems.org/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}${RUBY_FAKEGEM_SUFFIX:+-${RUBY_FAKEGEM_SUFFIX}}.gem"
185 ruby_add_bdepend virtual/rubygems
186 ruby_add_rdepend virtual/rubygems
191 ruby_add_depend virtual/rubygems
195 # @FUNCTION: ruby_fakegem_gemsdir
196 # @RETURN: Returns the gem data directory
198 # This function returns the gems data directory for the ruby
199 # implementation in question.
200 ruby_fakegem_gemsdir() {
201 local _gemsitedir=$(ruby_rbconfig_value 'sitelibdir')
202 _gemsitedir=${_gemsitedir//site_ruby/gems}
203 _gemsitedir=${_gemsitedir#${EPREFIX}}
205 [[ -z ${_gemsitedir} ]] && {
206 eerror "Unable to find the gems dir"
207 die "Unable to find the gems dir"
210 echo "${_gemsitedir}"
213 # @FUNCTION: ruby_fakegem_doins
214 # @USAGE: <file> [file...]
216 # Installs the specified file(s) into the gems directory.
217 ruby_fakegem_doins() {
219 insinto $(ruby_fakegem_gemsdir)/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}
221 ) || die "failed $0 $@"
224 # @FUNCTION: ruby_fakegem_newins
225 # @USAGE: <file> <newname>
227 # Installs the specified file into the gems directory using the provided filename.
228 ruby_fakegem_newins() {
230 # Since newins does not accept full paths but just basenames
231 # for the target file, we want to extend it here.
232 local newdirname=/$(dirname "$2")
233 [[ ${newdirname} == "/." ]] && newdirname=
235 local newbasename=$(basename "$2")
237 insinto $(ruby_fakegem_gemsdir)/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}${newdirname}
238 newins "$1" ${newbasename}
239 ) || die "failed $0 $@"
242 # @FUNCTION: ruby_fakegem_install_gemspec
244 # Install a .gemspec file for this package. Either use the file indicated
245 # by the RUBY_FAKEGEM_GEMSPEC variable, or generate one using
246 # ruby_fakegem_genspec.
247 ruby_fakegem_install_gemspec() {
248 local gemspec="${T}"/${RUBY_FAKEGEM_NAME}-${_ruby_implementation}
251 if [[ ${RUBY_FAKEGEM_GEMSPEC} != "" ]]; then
252 ruby_fakegem_gemspec_gemspec ${RUBY_FAKEGEM_GEMSPEC} ${gemspec}
254 local metadata="${WORKDIR}"/${_ruby_implementation}/metadata
256 if [[ -e ${metadata} ]]; then
257 ruby_fakegem_metadata_gemspec ${metadata} ${gemspec}
259 ruby_fakegem_genspec ${gemspec}
262 ) || die "Unable to generate gemspec file."
264 insinto $(ruby_fakegem_gemsdir)/specifications
265 newins ${gemspec} ${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}.gemspec || die "Unable to install gemspec file."
268 # @FUNCTION: ruby_fakegem_gemspec_gemspec
269 # @USAGE: <gemspec-input> <gemspec-output>
271 # Generates an installable version of the specification indicated by
272 # RUBY_FAKEGEM_GEMSPEC. This file is eval'ed to produce a final specification
273 # in a way similar to packaging the gemspec file.
274 ruby_fakegem_gemspec_gemspec() {
275 ${RUBY} -e "puts eval(File::open('$1').read).to_ruby" > $2
278 # @FUNCTION: ruby_fakegem_metadata_gemspec
279 # @USAGE: <gemspec-metadata> <gemspec-output>
281 # Generates an installable version of the specification indicated by
282 # the metadata distributed by the gem itself. This is similar to how
283 # rubygems creates an installation from a .gem file.
284 ruby_fakegem_metadata_gemspec() {
285 ${RUBY} -r yaml -e "puts Gem::Specification.from_yaml(File::open('$1', :encoding => 'UTF-8').read).to_ruby" > $2
288 # @FUNCTION: ruby_fakegem_genspec
289 # @USAGE: <output-gemspec>
291 # Generates a gemspec for the package and places it into the "specifications"
292 # directory of RubyGems.
293 # If the metadata normally distributed with a gem is present then that is
294 # used to generate the gemspec file.
296 # As a fallback we can generate our own version.
297 # In the gemspec, the following values are set: name, version, summary,
298 # homepage, and require_paths=["lib"].
299 # See RUBY_FAKEGEM_NAME and RUBY_FAKEGEM_VERSION for setting name and version.
300 # See RUBY_FAKEGEM_REQUIRE_PATHS for setting extra require paths.
301 ruby_fakegem_genspec() {
305 eqawarn "Generating generic fallback gemspec *without* dependencies"
306 eqawarn "This will only work when there are no runtime dependencies"
307 eqawarn "Set RUBY_FAKEGEM_GEMSPEC to generate a proper specifications file"
311 local required_paths="'lib'"
312 for path in ${RUBY_FAKEGEM_REQUIRE_PATHS}; do
313 required_paths="${required_paths}, '${path}'"
316 # We use the _ruby_implementation variable to avoid having stray
317 # copies with different implementations; while for now we're using
318 # the same exact content, we might have differences in the future,
319 # so better taking this into consideration.
320 local quoted_description=${DESCRIPTION//\"/\\\"}
322 # generated by ruby-fakegem.eclass
323 Gem::Specification.new do |s|
324 s.name = "${RUBY_FAKEGEM_NAME}"
325 s.version = "${RUBY_FAKEGEM_VERSION}"
326 s.summary = "${quoted_description}"
327 s.homepage = "${HOMEPAGE}"
328 s.require_paths = [${required_paths}]
333 # @FUNCTION: ruby_fakegem_binwrapper
334 # @USAGE: <command> [path] [content]
336 # Creates a new binary wrapper for a command installed by the RubyGem.
337 # path defaults to /usr/bin/$command content is optional and can be used
338 # to inject additional ruby code into the wrapper. This may be useful to
339 # e.g. force a specific version using the gem command.
340 ruby_fakegem_binwrapper() {
343 local newbinary=${2:-/usr/bin/$gembinary}
345 local relativegembinary=${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}/${RUBY_FAKEGEM_BINDIR}/${gembinary}
346 local binpath=$(dirname $newbinary)
347 [[ ${binpath} = . ]] && binpath=/usr/bin
349 # Try to find out whether the package is going to install for
350 # one or multiple implementations; if we're installing for a
351 # *single* implementation, no need to use “/usr/bin/env ruby”
352 # in the shebang, and we can actually avoid errors when
353 # calling the script by default.
355 for implementation in $(_ruby_get_all_impls); do
356 # ignore non-enabled implementations
357 use ruby_targets_${implementation} || continue
358 if [ -z $rubycmd ]; then
359 # if no other implementation was set before, set it.
360 rubycmd="$(ruby_implementation_command ${implementation})"
362 # if another implementation already arrived, then make
363 # it generic and break out of the loop. This ensures
364 # that we do at most two iterations.
365 rubycmd="/usr/bin/env ruby"
370 cat - > "${T}"/gembin-wrapper-${gembinary} <<EOF
372 # This is a simplified version of the RubyGems wrapper
374 # Generated by ruby-fakegem.eclass
379 load Gem::default_path[-1] + "/gems/${relativegembinary}"
383 exeinto ${binpath:-/usr/bin}
384 newexe "${T}"/gembin-wrapper-${gembinary} $(basename $newbinary)
385 ) || die "Unable to create fakegem wrapper"
388 # @FUNCTION: all_fakegem_compile
390 # Build documentation for the package if indicated by the doc USE flag
391 # and if there is a documetation task defined.
392 all_fakegem_compile() {
393 if [[ -n ${RUBY_FAKEGEM_DOCDIR} ]] && use doc; then
394 case ${RUBY_FAKEGEM_RECIPE_DOC} in
396 rake ${RUBY_FAKEGEM_TASK_DOC} || die "failed to (re)build documentation"
399 rdoc ${RUBY_FAKEGEM_DOC_SOURCES} || die "failed to (re)build documentation"
400 rm -f doc/js/*.gz || die "failed to remove duplicated compressed javascript files"
403 yard doc ${RUBY_FAKEGEM_DOC_SOURCES} || die "failed to (re)build documentation"
409 # @FUNCTION: all_ruby_unpack
411 # Unpack the source archive, including support for unpacking gems.
413 # Special support for extracting .gem files; the file need to be
414 # extracted twice and the mtime from the archive _has_ to be
415 # ignored (it's always set to epoch 0).
416 for archive in ${A}; do
419 # Make sure that we're not running unpack for more than
420 # one .gem file, since we won't support that at all.
421 [[ -d "${S}" ]] && die "Unable to unpack ${archive}, ${S} exists"
423 ebegin "Unpacking .gem file..."
424 tar -mxf "${DISTDIR}"/${archive} || die
427 ebegin "Uncompressing metadata"
428 gunzip metadata.gz || die
432 pushd "${S}" &>/dev/null || die
434 ebegin "Unpacking data.tar.gz"
435 tar -mxf "${my_WORKDIR}"/data.tar.gz || die
438 popd &>/dev/null || die
441 # We apply the patches with RUBY_PATCHES directly from DISTDIR,
442 # as the WORKDIR variable changes value between the global-scope
443 # and the time all_ruby_unpack/_prepare are called. Since we can
444 # simply decompress them when applying, this is much easier to
446 einfo "Keeping ${archive} as-is"
455 # @FUNCTION: all_ruby_compile
457 # Compile the package.
462 # @FUNCTION: each_fakegem_test
464 # Run tests for the package for each ruby target if the test task is defined.
465 each_fakegem_test() {
466 case ${RUBY_FAKEGEM_RECIPE_TEST} in
468 ${RUBY} -S rake ${RUBY_FAKEGEM_TASK_TEST} || die "tests failed"
471 RSPEC_VERSION=2 ruby-ng_rspec
474 RSPEC_VERSION=3 ruby-ng_rspec
480 ewarn "each_fakegem_test called, but \${RUBY_FAKEGEM_RECIPE_TEST} is 'none'"
485 if [[ ${RUBY_FAKEGEM_RECIPE_TEST} != none ]]; then
486 # @FUNCTION: each_ruby_test
488 # Run the tests for this package.
494 # @FUNCTION: each_fakegem_install
496 # Install the package for each ruby target.
497 each_fakegem_install() {
498 ruby_fakegem_install_gemspec
500 local _gemlibdirs="${RUBY_FAKEGEM_EXTRAINSTALL}"
501 for directory in "${RUBY_FAKEGEM_BINDIR}" lib; do
502 [[ -d ${directory} ]] && _gemlibdirs="${_gemlibdirs} ${directory}"
505 [[ -n ${_gemlibdirs} ]] && \
506 ruby_fakegem_doins -r ${_gemlibdirs}
509 # @FUNCTION: each_ruby_install
511 # Install the package for each target.
512 each_ruby_install() {
516 # @FUNCTION: all_fakegem_install
518 # Install files common to all ruby targets.
519 all_fakegem_install() {
520 if [[ -n ${RUBY_FAKEGEM_DOCDIR} ]] && use doc; then
521 for dir in ${RUBY_FAKEGEM_DOCDIR}; do
522 [[ -d ${dir} ]] || continue
524 pushd ${dir} &>/dev/null || die
525 dodoc -r * || die "failed to install documentation"
526 popd &>/dev/null || die
530 if [[ -n ${RUBY_FAKEGEM_EXTRADOC} ]]; then
531 dodoc ${RUBY_FAKEGEM_EXTRADOC} || die "failed to install further documentation"
534 # binary wrappers; we assume that all the implementations get the
535 # same binaries, or something is wrong anyway, so...
536 if [[ -n ${RUBY_FAKEGEM_BINWRAP} ]]; then
537 local bindir=$(find "${D}" -type d -path "*/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}/${RUBY_FAKEGEM_BINDIR}" -print -quit)
539 if [[ -d "${bindir}" ]]; then
540 pushd "${bindir}" &>/dev/null || die
541 for binary in ${RUBY_FAKEGEM_BINWRAP}; do
542 ruby_fakegem_binwrapper "${binary}"
544 popd &>/dev/null || die
549 # @FUNCTION: all_ruby_install
551 # Install files common to all ruby targets.