Revert "eclass: kill xfconf.eclass" (too early)
[gentoo.git] / eclass / ruby-fakegem.eclass
1 # Copyright 1999-2018 Gentoo Authors
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: ruby-fakegem.eclass
5 # @MAINTAINER:
6 # Ruby herd <ruby@gentoo.org>
7 # @AUTHOR:
8 # Author: Diego E. Pettenò <flameeyes@gentoo.org>
9 # Author: Alex Legler <a3li@gentoo.org>
10 # @SUPPORTED_EAPIS: 0 1 2 3 4 5 6
11 # @BLURB: An eclass for installing Ruby packages to behave like RubyGems.
12 # @DESCRIPTION:
13 # This eclass allows to install arbitrary Ruby libraries (including Gems),
14 # providing integration into the RubyGems system even for "regular" packages.
15
16 inherit ruby-ng
17
18 # @ECLASS-VARIABLE: RUBY_FAKEGEM_NAME
19 # @DESCRIPTION:
20 # Sets the Gem name for the generated fake gemspec.
21 # This variable MUST be set before inheriting the eclass.
22 RUBY_FAKEGEM_NAME="${RUBY_FAKEGEM_NAME:-${PN}}"
23
24 # @ECLASS-VARIABLE: RUBY_FAKEGEM_VERSION
25 # @DESCRIPTION:
26 # Sets the Gem version for the generated fake gemspec.
27 # This variable MUST be set before inheriting the eclass.
28 RUBY_FAKEGEM_VERSION="${RUBY_FAKEGEM_VERSION:-${PV/_pre/.pre}}"
29
30 # @ECLASS-VARIABLE: RUBY_FAKEGEM_TASK_DOC
31 # @DESCRIPTION:
32 # Specify the rake(1) task to run to generate documentation.
33 RUBY_FAKEGEM_TASK_DOC="${RUBY_FAKEGEM_TASK_DOC-rdoc}"
34
35 # @ECLASS-VARIABLE: RUBY_FAKEGEM_RECIPE_TEST
36 # @DESCRIPTION:
37 # Specify one of the default testing function for ruby-fakegem:
38 #  - rake (default; see also RUBY_FAKEGEM_TASK_TEST)
39 #  - rspec (calls ruby-ng_rspec, adds dev-ruby/rspec:2 to the dependencies)
40 #  - rspec3 (calls ruby-ng_rspec, adds dev-ruby/rspec:3 to the dependencies)
41 #  - cucumber (calls ruby-ng_cucumber, adds dev-util/cucumber to the
42 #    dependencies; does not work on JRuby).
43 #  - none
44 RUBY_FAKEGEM_RECIPE_TEST="${RUBY_FAKEGEM_RECIPE_TEST-rake}"
45
46 # @ECLASS-VARIABLE: RUBY_FAKEGEM_TASK_TEST
47 # @DESCRIPTION:
48 # Specify the rake(1) task used for executing tests. Only valid
49 # if RUBY_FAKEGEM_RECIPE_TEST is set to "rake" (the default).
50 RUBY_FAKEGEM_TASK_TEST="${RUBY_FAKEGEM_TASK_TEST-test}"
51
52 # @ECLASS-VARIABLE: RUBY_FAKEGEM_RECIPE_DOC
53 # @DESCRIPTION:
54 # Specify one of the default API doc building function for ruby-fakegem:
55 #  - rake (default; see also RUBY_FAKEGEM_TASK_DOC)
56 #  - rdoc (calls `rdoc-2`, adds dev-ruby/rdoc to the dependencies);
57 #  - yard (calls `yard`, adds dev-ruby/yard to the dependencies);
58 #  - none
59 RUBY_FAKEGEM_RECIPE_DOC="${RUBY_FAKEGEM_RECIPE_DOC-rake}"
60
61 # @ECLASS-VARIABLE: RUBY_FAKEGEM_DOCDIR
62 # @DEFAULT_UNSET
63 # @DESCRIPTION:
64 # Specify the directory under which the documentation is built;
65 # if empty no documentation will be installed automatically.
66 # Note: if RUBY_FAKEGEM_RECIPE_DOC is set to `rdoc`, this variable is
67 # hardwired to `doc`.
68
69 # @ECLASS-VARIABLE: RUBY_FAKEGEM_EXTRADOC
70 # @DEFAULT_UNSET
71 # @DESCRIPTION:
72 # Extra documentation to install (readme, changelogs, …).
73
74 # @ECLASS-VARIABLE: RUBY_FAKEGEM_DOC_SOURCES
75 # @DESCRIPTION:
76 # Allow settings defined sources to scan for documentation.
77 # This only applies if RUBY_FAKEGEM_DOC_TASK is set to `rdoc`.
78 RUBY_FAKEGEM_DOC_SOURCES="${RUBY_FAKEGEM_DOC_SOURCES-lib}"
79
80 # @ECLASS-VARIABLE: RUBY_FAKEGEM_BINWRAP
81 # @DESCRIPTION:
82 # Binaries to wrap around (relative to the RUBY_FAKEGEM_BINDIR directory)
83 RUBY_FAKEGEM_BINWRAP="${RUBY_FAKEGEM_BINWRAP-*}"
84
85 # @ECLASS-VARIABLE: RUBY_FAKEGEM_BINDIR
86 # @DESCRIPTION:
87 # Path that contains binaries to be binwrapped. Equivalent to the
88 # gemspec bindir option.
89 RUBY_FAKEGEM_BINDIR="${RUBY_FAKEGEM_BINDIR-bin}"
90
91 # @ECLASS-VARIABLE: RUBY_FAKEGEM_REQUIRE_PATHS
92 # @DEFAULT_UNSET
93 # @DESCRIPTION:
94 # Extra require paths (beside lib) to add to the specification
95
96 # @ECLASS-VARIABLE: RUBY_FAKEGEM_GEMSPEC
97 # @DEFAULT_UNSET
98 # @DESCRIPTION:
99 # Filename of .gemspec file to install instead of generating a generic one.
100
101 # @ECLASS-VARIABLE: RUBY_FAKEGEM_EXTRAINSTALL
102 # @DEFAULT_UNSET
103 # @DESCRIPTION:
104 # List of files and directories relative to the top directory that also
105 # get installed. Some gems provide extra files such as version information,
106 # Rails generators, or data that needs to be installed as well.
107
108 case "${EAPI:-0}" in
109                 0|1|2|3|4|5|6)
110                                 ;;
111                 *)
112                                 die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
113                                 ;;
114 esac
115
116
117 RUBY_FAKEGEM_SUFFIX="${RUBY_FAKEGEM_SUFFIX:-}"
118
119
120 [[ ${RUBY_FAKEGEM_TASK_DOC} == "" ]] && RUBY_FAKEGEM_RECIPE_DOC="none"
121
122 case ${RUBY_FAKEGEM_RECIPE_DOC} in
123         rake)
124                 IUSE+=" doc"
125                 ruby_add_bdepend "doc? ( dev-ruby/rake )"
126                 RUBY_FAKEGEM_DOCDIR="doc"
127                 ;;
128         rdoc)
129                 IUSE+=" doc"
130                 ruby_add_bdepend "doc? ( dev-ruby/rdoc )"
131                 RUBY_FAKEGEM_DOCDIR="doc"
132                 ;;
133         yard)
134                 IUSE+="doc"
135                 ruby_add_bdepend "doc? ( dev-ruby/yard )"
136                 RUBY_FAKEGEM_DOCDIR="doc"
137                 ;;
138         none)
139                 [[ -n ${RUBY_FAKEGEM_DOCDIR} ]] && IUSE+=" doc"
140                 ;;
141 esac
142
143 [[ ${RUBY_FAKEGEM_TASK_TEST} == "" ]] && RUBY_FAKEGEM_RECIPE_TEST="none"
144
145 case ${RUBY_FAKEGEM_RECIPE_TEST} in
146         rake)
147                 IUSE+=" test"
148                 ruby_add_bdepend "test? ( dev-ruby/rake )"
149                 ;;
150         rspec)
151                 IUSE+=" test"
152                 # Also require a new enough rspec-core version that installs the
153                 # rspec-2 wrapper.
154                 ruby_add_bdepend "test? ( dev-ruby/rspec:2 >=dev-ruby/rspec-core-2.14.8-r2 )"
155                 ;;
156         rspec3)
157                 IUSE+=" test"
158                 ruby_add_bdepend "test? ( dev-ruby/rspec:3 )"
159                 ;;
160         cucumber)
161                 IUSE+=" test"
162                 # Unfortunately as of August 2012, cucumber is not supported on
163                 # JRuby.  We work it around here to avoid repeating the same
164                 # code over and over again.
165                 USE_RUBY="${USE_RUBY/jruby/}" ruby_add_bdepend "test? ( dev-util/cucumber )"
166                 ;;
167         *)
168                 RUBY_FAKEGEM_RECIPE_TEST="none"
169                 ;;
170 esac
171
172 SRC_URI="mirror://rubygems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}${RUBY_FAKEGEM_SUFFIX:+-${RUBY_FAKEGEM_SUFFIX}}.gem"
173
174 ruby_add_bdepend virtual/rubygems
175 ruby_add_rdepend virtual/rubygems
176
177 # @FUNCTION: ruby_fakegem_gemsdir
178 # @RETURN: Returns the gem data directory
179 # @DESCRIPTION:
180 # This function returns the gems data directory for the ruby
181 # implementation in question.
182 ruby_fakegem_gemsdir() {
183         has "${EAPI}" 2 && ! use prefix && EPREFIX=
184
185         local _gemsitedir=$(ruby_rbconfig_value 'sitelibdir')
186         _gemsitedir=${_gemsitedir//site_ruby/gems}
187         _gemsitedir=${_gemsitedir#${EPREFIX}}
188
189         [[ -z ${_gemsitedir} ]] && {
190                 eerror "Unable to find the gems dir"
191                 die "Unable to find the gems dir"
192         }
193
194         echo "${_gemsitedir}"
195 }
196
197 # @FUNCTION: ruby_fakegem_doins
198 # @USAGE: file [file...]
199 # @DESCRIPTION:
200 # Installs the specified file(s) into the gems directory.
201 ruby_fakegem_doins() {
202         (
203                 insinto $(ruby_fakegem_gemsdir)/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}
204                 doins "$@"
205         ) || die "failed $0 $@"
206 }
207
208 # @FUNCTION: ruby_fakegem_newsins
209 # @USAGE: file filename
210 # @DESCRIPTION:
211 # Installs the specified file into the gems directory using the provided filename.
212 ruby_fakegem_newins() {
213         (
214                 # Since newins does not accept full paths but just basenames
215                 # for the target file, we want to extend it here.
216                 local newdirname=/$(dirname "$2")
217                 [[ ${newdirname} == "/." ]] && newdirname=
218
219                 local newbasename=$(basename "$2")
220
221                 insinto $(ruby_fakegem_gemsdir)/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}${newdirname}
222                 newins "$1" ${newbasename}
223         ) || die "failed $0 $@"
224 }
225
226 # @FUNCTION: ruby_fakegem_install_gemspec
227 # @DESCRIPTION:
228 # Install a .gemspec file for this package. Either use the file indicated
229 # by the RUBY_FAKEGEM_GEMSPEC variable, or generate one using
230 # ruby_fakegem_genspec.
231 ruby_fakegem_install_gemspec() {
232         local gemspec="${T}"/${RUBY_FAKEGEM_NAME}-${_ruby_implementation}
233
234         (
235                 if [[ ${RUBY_FAKEGEM_GEMSPEC} != "" ]]; then
236                         ruby_fakegem_gemspec_gemspec ${RUBY_FAKEGEM_GEMSPEC} ${gemspec}
237                 else
238                         local metadata="${WORKDIR}"/${_ruby_implementation}/metadata
239
240                         if [[ -e ${metadata} ]]; then
241                                 ruby_fakegem_metadata_gemspec ${metadata} ${gemspec}
242                         else
243                                 ruby_fakegem_genspec ${gemspec}
244                         fi
245                 fi
246         ) || die "Unable to generate gemspec file."
247
248         insinto $(ruby_fakegem_gemsdir)/specifications
249         newins ${gemspec} ${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}.gemspec || die "Unable to install gemspec file."
250 }
251
252 # @FUNCTION: ruby_fakegem_gemspec_gemspec
253 # @USAGE: gemspec-input gemspec-output
254 # @DESCRIPTION:
255 # Generates an installable version of the specification indicated by
256 # RUBY_FAKEGEM_GEMSPEC. This file is eval'ed to produce a final specification
257 # in a way similar to packaging the gemspec file.
258 ruby_fakegem_gemspec_gemspec() {
259         ${RUBY} -e "puts eval(File::open('$1').read).to_ruby" > $2
260 }
261
262 # @FUNCTION: ruby_fakegem_metadata_gemspec
263 # @USAGE: gemspec-metadata gemspec-output
264 # @DESCRIPTION:
265 # Generates an installable version of the specification indicated by
266 # the metadata distributed by the gem itself. This is similar to how
267 # rubygems creates an installation from a .gem file.
268 ruby_fakegem_metadata_gemspec() {
269         case ${RUBY} in
270                 *jruby)
271                         ${RUBY} -r yaml -e "puts Gem::Specification.from_yaml(File::open('$1').read).to_ruby" > $2
272                                 ;;
273                 *)
274                         ${RUBY} -r yaml -e "puts Gem::Specification.from_yaml(File::open('$1', :encoding => 'UTF-8').read).to_ruby" > $2
275                                 ;;
276         esac
277 }
278
279 # @FUNCTION: ruby_fakegem_genspec
280 # @USAGE: output-gemspec
281 # @DESCRIPTION:
282 # Generates a gemspec for the package and places it into the "specifications"
283 # directory of RubyGems.
284 # If the metadata normally distributed with a gem is present then that is
285 # used to generate the gemspec file.
286 #
287 # As a fallback we can generate our own version.
288 # In the gemspec, the following values are set: name, version, summary,
289 # homepage, and require_paths=["lib"].
290 # See RUBY_FAKEGEM_NAME and RUBY_FAKEGEM_VERSION for setting name and version.
291 # See RUBY_FAKEGEM_REQUIRE_PATHS for setting extra require paths.
292 ruby_fakegem_genspec() {
293         local required_paths="'lib'"
294         for path in ${RUBY_FAKEGEM_REQUIRE_PATHS}; do
295                 required_paths="${required_paths}, '${path}'"
296         done
297
298         # We use the _ruby_implementation variable to avoid having stray
299         # copies with different implementations; while for now we're using
300         # the same exact content, we might have differences in the future,
301         # so better taking this into consideration.
302         local quoted_description=${DESCRIPTION//\"/\\\"}
303         cat - > $1 <<EOF
304 # generated by ruby-fakegem.eclass
305 Gem::Specification.new do |s|
306   s.name = "${RUBY_FAKEGEM_NAME}"
307   s.version = "${RUBY_FAKEGEM_VERSION}"
308   s.summary = "${quoted_description}"
309   s.homepage = "${HOMEPAGE}"
310   s.require_paths = [${required_paths}]
311 end
312 EOF
313 }
314
315 # @FUNCTION: ruby_fakegem_binwrapper
316 # @USAGE: command [path] [content]
317 # @DESCRIPTION:
318 # Creates a new binary wrapper for a command installed by the RubyGem.
319 # path defaults to /usr/bin/$command content is optional and can be used
320 # to inject additional ruby code into the wrapper. This may be useful to
321 # e.g. force a specific version using the gem command.
322 ruby_fakegem_binwrapper() {
323         (
324                 local gembinary=$1
325                 local newbinary=${2:-/usr/bin/$gembinary}
326                 local content=$3
327                 local relativegembinary=${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}/${RUBY_FAKEGEM_BINDIR}/${gembinary}
328                 local binpath=$(dirname $newbinary)
329                 [[ ${binpath} = . ]] && binpath=/usr/bin
330
331                 # Try to find out whether the package is going to install for
332                 # one or multiple implementations; if we're installing for a
333                 # *single* implementation, no need to use “/usr/bin/env ruby”
334                 # in the shebang, and we can actually avoid errors when
335                 # calling the script by default (see for instance the
336                 # JRuby-specific commands).
337                 local rubycmd=
338                 for implementation in $(_ruby_get_all_impls); do
339                         # ignore non-enabled implementations
340                         use ruby_targets_${implementation} || continue
341                         if [ -z $rubycmd ]; then
342                                 # if no other implementation was set before, set it.
343                                 rubycmd="$(ruby_implementation_command ${implementation})"
344                         else
345                                 # if another implementation already arrived, then make
346                                 # it generic and break out of the loop. This ensures
347                                 # that we do at most two iterations.
348                                 rubycmd="/usr/bin/env ruby"
349                                 break
350                         fi
351                 done
352
353                 cat - > "${T}"/gembin-wrapper-${gembinary} <<EOF
354 #!${rubycmd}
355 # This is a simplified version of the RubyGems wrapper
356 #
357 # Generated by ruby-fakegem.eclass
358
359 require 'rubygems'
360
361 ${content}
362 load Gem::default_path[-1] + "/gems/${relativegembinary}"
363
364 EOF
365
366                 exeinto ${binpath:-/usr/bin}
367                 newexe "${T}"/gembin-wrapper-${gembinary} $(basename $newbinary)
368         ) || die "Unable to create fakegem wrapper"
369 }
370
371 # @FUNCTION: all_fakegem_compile
372 # @DESCRIPTION:
373 # Build documentation for the package if indicated by the doc USE flag
374 # and if there is a documetation task defined.
375 all_fakegem_compile() {
376         if [[ -n ${RUBY_FAKEGEM_DOCDIR} ]] && use doc; then
377                 case ${RUBY_FAKEGEM_RECIPE_DOC} in
378                         rake)
379                                 rake ${RUBY_FAKEGEM_TASK_DOC} || die "failed to (re)build documentation"
380                                 ;;
381                         rdoc)
382                                 rdoc ${RUBY_FAKEGEM_DOC_SOURCES} || die "failed to (re)build documentation"
383                                 rm -f doc/js/*.gz || die "failed to remove duplicated compressed javascript files"
384                                 ;;
385                         yard)
386                                 yard doc ${RUBY_FAKEGEM_DOC_SOURCES} || die "failed to (re)build documentation"
387                                 ;;
388                 esac
389         fi
390 }
391
392 # @FUNCTION: all_ruby_unpack
393 # @DESCRIPTION:
394 # Unpack the source archive, including support for unpacking gems.
395 all_ruby_unpack() {
396         # Special support for extracting .gem files; the file need to be
397         # extracted twice and the mtime from the archive _has_ to be
398         # ignored (it's always set to epoch 0).
399         for archive in ${A}; do
400                 case "${archive}" in
401                         *.gem)
402                                 # Make sure that we're not running unpack for more than
403                                 # one .gem file, since we won't support that at all.
404                                 [[ -d "${S}" ]] && die "Unable to unpack ${archive}, ${S} exists"
405
406                                 ebegin "Unpacking .gem file..."
407                                 tar -mxf "${DISTDIR}"/${archive} || die
408                                 eend $?
409
410                                 ebegin "Uncompressing metadata"
411                                 gunzip metadata.gz || die
412                                 eend $?
413
414                                 mkdir "${S}"
415                                 pushd "${S}" &>/dev/null || die
416
417                                 ebegin "Unpacking data.tar.gz"
418                                 tar -mxf "${my_WORKDIR}"/data.tar.gz || die
419                                 eend $?
420
421                                 popd &>/dev/null || die
422                                 ;;
423                         *.patch.bz2)
424                                 # We apply the patches with RUBY_PATCHES directly from DISTDIR,
425                                 # as the WORKDIR variable changes value between the global-scope
426                                 # and the time all_ruby_unpack/_prepare are called. Since we can
427                                 # simply decompress them when applying, this is much easier to
428                                 # deal with for us.
429                                 einfo "Keeping ${archive} as-is"
430                                 ;;
431                         *)
432                                 unpack ${archive}
433                                 ;;
434                 esac
435         done
436 }
437
438 # @FUNCTION: all_ruby_compile
439 # @DESCRIPTION:
440 # Compile the package.
441 all_ruby_compile() {
442         all_fakegem_compile
443 }
444
445 # @FUNCTION: each_fakegem_test
446 # @DESCRIPTION:
447 # Run tests for the package for each ruby target if the test task is defined.
448 each_fakegem_test() {
449         case ${RUBY_FAKEGEM_RECIPE_TEST} in
450                 rake)
451                         ${RUBY} -S rake ${RUBY_FAKEGEM_TASK_TEST} || die "tests failed"
452                         ;;
453                 rspec)
454                         RSPEC_VERSION=2 ruby-ng_rspec
455                         ;;
456                 rspec3)
457                         RSPEC_VERSION=3 ruby-ng_rspec
458                         ;;
459                 cucumber)
460                         ruby-ng_cucumber
461                         ;;
462                 none)
463                         ewarn "each_fakegem_test called, but \${RUBY_FAKEGEM_RECIPE_TEST} is 'none'"
464                         ;;
465         esac
466 }
467
468 if [[ ${RUBY_FAKEGEM_RECIPE_TEST} != none ]]; then
469                 # @FUNCTION: each_ruby_test
470                 # @DESCRIPTION:
471                 # Run the tests for this package.
472                 each_ruby_test() {
473                         each_fakegem_test
474                 }
475 fi
476
477 # @FUNCTION: each_fakegem_install
478 # @DESCRIPTION:
479 # Install the package for each ruby target.
480 each_fakegem_install() {
481         ruby_fakegem_install_gemspec
482
483         local _gemlibdirs="${RUBY_FAKEGEM_EXTRAINSTALL}"
484         for directory in "${RUBY_FAKEGEM_BINDIR}" lib; do
485                 [[ -d ${directory} ]] && _gemlibdirs="${_gemlibdirs} ${directory}"
486         done
487
488         [[ -n ${_gemlibdirs} ]] && \
489                 ruby_fakegem_doins -r ${_gemlibdirs}
490 }
491
492 # @FUNCTION: each_ruby_install
493 # @DESCRIPTION:
494 # Install the package for each target.
495 each_ruby_install() {
496         each_fakegem_install
497 }
498
499 # @FUNCTION: all_fakegem_install
500 # @DESCRIPTION:
501 # Install files common to all ruby targets.
502 all_fakegem_install() {
503         if [[ -n ${RUBY_FAKEGEM_DOCDIR} ]] && use doc; then
504                 for dir in ${RUBY_FAKEGEM_DOCDIR}; do
505                         [[ -d ${dir} ]] || continue
506
507                         pushd ${dir} &>/dev/null || die
508                         dodoc -r * || die "failed to install documentation"
509                         popd &>/dev/null || die
510                 done
511         fi
512
513         if [[ -n ${RUBY_FAKEGEM_EXTRADOC} ]]; then
514                 dodoc ${RUBY_FAKEGEM_EXTRADOC} || die "failed to install further documentation"
515         fi
516
517         # binary wrappers; we assume that all the implementations get the
518         # same binaries, or something is wrong anyway, so...
519         if [[ -n ${RUBY_FAKEGEM_BINWRAP} ]]; then
520                 local bindir=$(find "${D}" -type d -path "*/gems/${RUBY_FAKEGEM_NAME}-${RUBY_FAKEGEM_VERSION}/${RUBY_FAKEGEM_BINDIR}" -print -quit)
521
522                 if [[ -d "${bindir}" ]]; then
523                         pushd "${bindir}" &>/dev/null || die
524                         for binary in ${RUBY_FAKEGEM_BINWRAP}; do
525                                 ruby_fakegem_binwrapper "${binary}"
526                         done
527                         popd &>/dev/null || die
528                 fi
529         fi
530 }
531
532 # @FUNCTION: all_ruby_install
533 # @DESCRIPTION:
534 # Install files common to all ruby targets.
535 all_ruby_install() {
536         all_fakegem_install
537 }