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
|
# See README.
#
RDOC_VERSION = "1.0pr1"
rcs = '$Date$ $Revision$'.
gsub(/\$/, '').
sub(/Date: /, ': ').
sub(/ Revision: (\S+)/) { "(#$1)" }
VERSION_STRING = %{RDoc V} + RDOC_VERSION + rcs
require 'rdoc/parsers/parse_rb.rb'
require 'rdoc/parsers/parse_c.rb'
require 'rdoc/parsers/parse_f95.rb'
require 'rdoc/parsers/parse_simple.rb'
require 'rdoc/options'
require 'rdoc/diagram'
require 'find'
require 'ftools'
# We put rdoc stuff in the RDoc module to avoid namespace
# clutter.
#
# ToDo: This isn't universally true.
module RDoc
# Exception thrown by any rdoc error. Only the #message part is
# of use externally.
class RDocError < Exception
end
# Encapsulate the production of rdoc documentation. Basically
# you can use this as you would invoke rdoc from the command
# line:
#
# rdoc = RDoc::RDoc.new
# rdoc.document(args)
#
# where _args_ is an array of strings, each corresponding to
# an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
# for details.
class RDoc
##
# This is the list of output generators that we
# support
Generator = Struct.new(:file_name, :class_name, :key)
GENERATORS = {}
$:.collect {|d|
File::expand_path(d)
}.find_all {|d|
File::directory?("#{d}/rdoc/generators")
}.each {|dir|
Dir::entries("#{dir}/rdoc/generators").each {|gen|
next unless /(\w+)_generator.rb$/ =~ gen
type = $1
unless GENERATORS.has_key? type
GENERATORS[type] = Generator.new("rdoc/generators/#{gen}",
"#{type.upcase}Generator".intern,
type)
end
}
}
#######
private
#######
##
# Report an error message and exit
def error(msg)
raise RDocError.new(msg)
end
##
# Create an output dir if it doesn't exist. If it does
# exist, but doesn't contain the flag file <tt>created.rid</tt>
# then we refuse to use it, as we may clobber some
# manually generated documentation
def setup_output_dir(op_dir)
flag_file = File.join(op_dir, "created.rid")
if File.exist?(op_dir)
unless File.directory?(op_dir)
error "'#{op_dir}' exists, and is not a directory"
end
unless File.file?(flag_file)
error "\nDirectory #{op_dir} already exists, but it looks like it\n" +
"isn't an RDoc directory. Because RDoc doesn't want to risk\n" +
"destroying any of your existing files, you'll need to\n" +
"specify a different output directory name (using the\n" +
"--op <dir> option).\n\n"
end
else
File.makedirs(op_dir)
end
File.open(flag_file, "w") {|f| f.puts Time.now }
end
# Given a list of files and directories, create a list
# of all the Ruby files they contain.
def normalized_file_list(options, *relative_files)
file_list = []
relative_files.each do |rel_file_name|
case type = File.stat(rel_file_name).ftype
when "file"
file_list << rel_file_name
when "directory"
next if options.exclude && options.exclude =~ rel_file_name
Find.find(rel_file_name) do |fn|
next if options.exclude && options.exclude =~ fn
next unless ParserFactory.can_parse(fn)
next unless File.file?(fn)
file_list << fn.sub(%r{\./}, '')
end
else
raise RDocError.new("I can't deal with a #{type} #{rel_file_name}")
end
end
file_list
end
# Parse each file on the command line, recursively entering
# directories
def parse_files(options)
file_info = []
files = options.files
files = ["."] if files.empty?
file_list = normalized_file_list(options, *files)
file_list.each do |fn|
$stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
content = File.open(fn, "r") {|f| f.read}
top_level = TopLevel.new(fn)
parser = ParserFactory.parser_for(top_level, fn, content, options)
file_info << parser.scan
end
file_info
end
public
###################################################################
#
# Format up one or more files according to the given arguments.
# For simplicity, _argv_ is an array of strings, equivalent to the
# strings that would be passed on the command line. (This isn't a
# coincidence, as we _do_ pass in ARGV when running
# interactively). For a list of options, see rdoc/rdoc.rb. By
# default, output will be stored in a directory called +doc+ below
# the current directory, so make sure you're somewhere writable
# before invoking.
#
# Throws: RDocError on error
def document(argv)
TopLevel::reset
options = Options.instance
options.parse(argv, GENERATORS)
file_info = parse_files(options)
gen = options.generator
$stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet
require gen.file_name
gen_class = Generators.const_get(gen.class_name)
unless file_info.empty?
gen = gen_class.for(options)
pwd = Dir.pwd
unless options.all_one_file
setup_output_dir(options.op_dir)
Dir.chdir(options.op_dir)
end
begin
Diagram.new(file_info, options).draw if options.diagram
gen.generate(file_info)
ensure
Dir.chdir(pwd)
end
end
end
end
end
|