1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
|
require 'rubygems'
require 'rubygems/user_interaction'
require 'fileutils'
begin
gem 'rdoc'
rescue Gem::LoadError
# swallow
else
# This will force any deps that 'rdoc' might have
# (such as json) that are ambiguous to be activated, which
# is important because we end up using Specification.reset
# and we don't want the warning it pops out.
Gem.finish_resolve
end
loaded_hook = false
begin
require 'rdoc/rubygems_hook'
loaded_hook = true
module Gem
RDoc = RDoc::RubygemsHook
end
rescue LoadError
end
##
# Gem::RDoc provides methods to generate RDoc and ri data for installed gems.
# It works for RDoc 1.0.1 (in Ruby 1.8) up to RDoc 3.6.
#
# This implementation is considered obsolete. The RDoc project is the
# appropriate location to find this functionality. This file provides the
# hooks to load RDoc generation code from the "rdoc" gem and a fallback in
# case the installed version of RDoc does not have them.
class Gem::RDoc # :nodoc: all
include Gem::UserInteraction
extend Gem::UserInteraction
@rdoc_version = nil
@specs = []
##
# Force installation of documentation?
attr_accessor :force
##
# Generate rdoc?
attr_accessor :generate_rdoc
##
# Generate ri data?
attr_accessor :generate_ri
class << self
##
# Loaded version of RDoc. Set by ::load_rdoc
attr_reader :rdoc_version
end
##
# Post installs hook that generates documentation for each specification in
# +specs+
def self.generation_hook installer, specs
start = Time.now
types = installer.document
generate_rdoc = types.include? 'rdoc'
generate_ri = types.include? 'ri'
specs.each do |spec|
new(spec, generate_rdoc, generate_ri).generate
end
return unless generate_rdoc or generate_ri
duration = (Time.now - start).to_i
names = specs.map(&:name).join ', '
say "Done installing documentation for #{names} after #{duration} seconds"
end
##
# Loads the RDoc generator
def self.load_rdoc
return if @rdoc_version
require 'rdoc/rdoc'
@rdoc_version = if ::RDoc.const_defined? :VERSION then
Gem::Version.new ::RDoc::VERSION
else
Gem::Version.new '1.0.1'
end
rescue LoadError => e
raise Gem::DocumentError, "RDoc is not installed: #{e}"
end
##
# Creates a new documentation generator for +spec+. RDoc and ri data
# generation can be enabled or disabled through +generate_rdoc+ and
# +generate_ri+ respectively.
#
# Only +generate_ri+ is enabled by default.
def initialize spec, generate_rdoc = true, generate_ri = true
@doc_dir = spec.doc_dir
@file_info = nil
@force = false
@rdoc = nil
@spec = spec
@generate_rdoc = generate_rdoc
@generate_ri = generate_ri
@rdoc_dir = spec.doc_dir 'rdoc'
@ri_dir = spec.doc_dir 'ri'
end
##
# Removes legacy rdoc arguments from +args+
#--
# TODO move to RDoc::Options
def delete_legacy_args args
args.delete '--inline-source'
args.delete '--promiscuous'
args.delete '-p'
args.delete '--one-file'
end
##
# Generates documentation using the named +generator+ ("darkfish" or "ri")
# and following the given +options+.
#
# Documentation will be generated into +destination+
def document generator, options, destination
generator_name = generator
options = options.dup
options.exclude ||= [] # TODO maybe move to RDoc::Options#finish
options.setup_generator generator
options.op_dir = destination
options.finish
generator = options.generator.new @rdoc.store, options
@rdoc.options = options
@rdoc.generator = generator
say "Installing #{generator_name} documentation for #{@spec.full_name}"
FileUtils.mkdir_p options.op_dir
Dir.chdir options.op_dir do
begin
@rdoc.class.current = @rdoc
@rdoc.generator.generate @file_info
ensure
@rdoc.class.current = nil
end
end
end
##
# Generates RDoc and ri data
def generate
return unless @generate_ri or @generate_rdoc
setup
options = nil
if Gem::Requirement.new('< 3').satisfied_by? self.class.rdoc_version then
generate_legacy
return
end
::RDoc::TopLevel.reset # TODO ::RDoc::RDoc.reset
::RDoc::Parser::C.reset
args = @spec.rdoc_options
args.concat @spec.source_paths
args.concat @spec.extra_rdoc_files
case config_args = Gem.configuration[:rdoc]
when String then
args = args.concat config_args.split
when Array then
args = args.concat config_args
end
delete_legacy_args args
Dir.chdir @spec.full_gem_path do
options = ::RDoc::Options.new
options.default_title = "#{@spec.full_name} Documentation"
options.parse args
end
options.quiet = !Gem.configuration.really_verbose
@rdoc = new_rdoc
@rdoc.options = options
say "Parsing documentation for #{@spec.full_name}"
Dir.chdir @spec.full_gem_path do
@file_info = @rdoc.parse_files options.files
end
document 'ri', options, @ri_dir if
@generate_ri and (@force or not File.exist? @ri_dir)
document 'darkfish', options, @rdoc_dir if
@generate_rdoc and (@force or not File.exist? @rdoc_dir)
end
##
# Generates RDoc and ri data for legacy RDoc versions. This method will not
# exist in future versions.
def generate_legacy
if @generate_rdoc then
FileUtils.rm_rf @rdoc_dir
say "Installing RDoc documentation for #{@spec.full_name}"
legacy_rdoc '--op', @rdoc_dir
end
if @generate_ri then
FileUtils.rm_rf @ri_dir
say "Installing ri documentation for #{@spec.full_name}"
legacy_rdoc '--ri', '--op', @ri_dir
end
end
##
# Generates RDoc using a legacy version of RDoc from the ARGV-like +args+.
# This method will not exist in future versions.
def legacy_rdoc *args
args << @spec.rdoc_options
args << '--quiet'
args << @spec.require_paths.clone
args << @spec.extra_rdoc_files
args << '--title' << "#{@spec.full_name} Documentation"
args = args.flatten.map do |arg| arg.to_s end
delete_legacy_args args if
Gem::Requirement.new('>= 2.4.0') =~ self.class.rdoc_version
r = new_rdoc
verbose { "rdoc #{args.join ' '}" }
Dir.chdir @spec.full_gem_path do
begin
r.document args
rescue Errno::EACCES => e
dirname = File.dirname e.message.split("-")[1].strip
raise Gem::FilePermissionError, dirname
rescue Interrupt => e
raise e
rescue Exception => ex
alert_error "While generating documentation for #{@spec.full_name}"
ui.errs.puts "... MESSAGE: #{ex}"
ui.errs.puts "... RDOC args: #{args.join(' ')}"
ui.backtrace ex
ui.errs.puts "(continuing with the rest of the installation)"
end
end
end
##
# #new_rdoc creates a new RDoc instance. This method is provided only to
# make testing easier.
def new_rdoc # :nodoc:
::RDoc::RDoc.new
end
##
# Is rdoc documentation installed?
def rdoc_installed?
File.exist? @rdoc_dir
end
##
# Removes generated RDoc and ri data
def remove
base_dir = @spec.base_dir
raise Gem::FilePermissionError, base_dir unless File.writable? base_dir
FileUtils.rm_rf @rdoc_dir
FileUtils.rm_rf @ri_dir
end
##
# Is ri data installed?
def ri_installed?
File.exist? @ri_dir
end
##
# Prepares the spec for documentation generation
def setup
self.class.load_rdoc
raise Gem::FilePermissionError, @doc_dir if
File.exist?(@doc_dir) and not File.writable?(@doc_dir)
FileUtils.mkdir_p @doc_dir unless File.exist? @doc_dir
end
end unless loaded_hook
Gem.done_installing(&Gem::RDoc.method(:generation_hook))
|