summaryrefslogtreecommitdiff
path: root/lib/rubygems/doc_manager.rb
blob: e1c8e03b9111bcca47d594209f27ae2718e0ae02 (plain)
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
#--
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
# All rights reserved.
# See LICENSE.txt for permissions.
#++

require 'fileutils'
require 'rubygems'

##
# The documentation manager generates RDoc and RI for RubyGems.

class Gem::DocManager

  include Gem::UserInteraction

  @configured_args = []

  def self.configured_args
    @configured_args ||= []
  end

  def self.configured_args=(args)
    case args
    when Array
      @configured_args = args
    when String
      @configured_args = args.split
    end
  end

  ##
  # Load RDoc from a gem if it is available, otherwise from Ruby's stdlib

  def self.load_rdoc
    begin
      gem 'rdoc'
    rescue Gem::LoadError
      # use built-in RDoc
    end

    begin
      require 'rdoc/rdoc'

      @rdoc_version = if defined? RDoc::VERSION then
                        Gem::Version.new RDoc::VERSION
                      else
                        Gem::Version.new '1.0.1' # HACK parsing is hard
                      end

    rescue LoadError => e
      raise Gem::DocumentError,
          "ERROR: RDoc documentation generator not installed: #{e}"
    end
  end

  def self.rdoc_version
    @rdoc_version
  end

  ##
  # Updates the RI cache for RDoc 2 if it is installed

  def self.update_ri_cache
    load_rdoc rescue return

    return unless defined? RDoc::VERSION # RDoc 1 does not have VERSION

    require 'rdoc/ri/driver'

    options = {
      :use_cache => true,
      :use_system => true,
      :use_site => true,
      :use_home => true,
      :use_gems => true,
      :formatter => RDoc::RI::Formatter,
    }

    driver = RDoc::RI::Driver.new(options).class_cache
  end

  ##
  # Create a document manager for +spec+. +rdoc_args+ contains arguments for
  # RDoc (template etc.) as a String.

  def initialize(spec, rdoc_args="")
    @spec = spec
    @doc_dir = File.join(spec.installation_path, "doc", spec.full_name)
    @rdoc_args = rdoc_args.nil? ? [] : rdoc_args.split
  end

  ##
  # Is the RDoc documentation installed?

  def rdoc_installed?
    File.exist?(File.join(@doc_dir, "rdoc"))
  end

  ##
  # Generate the RI documents for this gem spec.
  #
  # Note that if both RI and RDoc documents are generated from the same
  # process, the RI docs should be done first (a likely bug in RDoc will cause
  # RI docs generation to fail if run after RDoc).

  def generate_ri
    setup_rdoc
    install_ri # RDoc bug, ri goes first

    FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
  end

  ##
  # Generate the RDoc documents for this gem spec.
  #
  # Note that if both RI and RDoc documents are generated from the same
  # process, the RI docs should be done first (a likely bug in RDoc will cause
  # RI docs generation to fail if run after RDoc).

  def generate_rdoc
    setup_rdoc
    install_rdoc

    FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)
  end

  ##
  # Generate and install RDoc into the documentation directory

  def install_rdoc
    rdoc_dir = File.join @doc_dir, 'rdoc'

    FileUtils.rm_rf rdoc_dir

    say "Installing RDoc documentation for #{@spec.full_name}..."
    run_rdoc '--op', rdoc_dir
  end

  ##
  # Generate and install RI into the documentation directory

  def install_ri
    ri_dir = File.join @doc_dir, 'ri'

    FileUtils.rm_rf ri_dir

    say "Installing ri documentation for #{@spec.full_name}..."
    run_rdoc '--ri', '--op', ri_dir
  end

  ##
  # Run RDoc with +args+, which is an ARGV style argument list

  def run_rdoc(*args)
    args << @spec.rdoc_options
    args << self.class.configured_args
    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

    if self.class.rdoc_version >= Gem::Version.new('2.4.0') then
      args.delete '--inline-source'
      args.delete '--promiscuous'
      args.delete '-p'
      args.delete '--one-file'
      # HACK more
    end

    r = RDoc::RDoc.new

    old_pwd = Dir.pwd
    Dir.chdir(@spec.full_gem_path)
    begin
      r.document args
    rescue Errno::EACCES => e
      dirname = File.dirname e.message.split("-")[1].strip
      raise Gem::FilePermissionError.new(dirname)
    rescue RuntimeError => ex
      alert_error "While generating documentation for #{@spec.full_name}"
      ui.errs.puts "... MESSAGE:   #{ex}"
      ui.errs.puts "... RDOC args: #{args.join(' ')}"
      ui.errs.puts "\t#{ex.backtrace.join "\n\t"}" if
      Gem.configuration.backtrace
      ui.errs.puts "(continuing with the rest of the installation)"
    ensure
      Dir.chdir(old_pwd)
    end
  end

  def setup_rdoc
    if File.exist?(@doc_dir) && !File.writable?(@doc_dir) then
      raise Gem::FilePermissionError.new(@doc_dir)
    end

    FileUtils.mkdir_p @doc_dir unless File.exist?(@doc_dir)

    self.class.load_rdoc
  end

  ##
  # Remove RDoc and RI documentation

  def uninstall_doc
    raise Gem::FilePermissionError.new(@spec.installation_path) unless
    File.writable? @spec.installation_path

    original_name = [
      @spec.name, @spec.version, @spec.original_platform].join '-'

    doc_dir = File.join @spec.installation_path, 'doc', @spec.full_name
    unless File.directory? doc_dir then
      doc_dir = File.join @spec.installation_path, 'doc', original_name
    end

    FileUtils.rm_rf doc_dir

    ri_dir = File.join @spec.installation_path, 'ri', @spec.full_name

    unless File.directory? ri_dir then
      ri_dir = File.join @spec.installation_path, 'ri', original_name
    end

    FileUtils.rm_rf ri_dir
  end

end