root/bin/ohcount

Revision 80ebac282df611d962503aca576fe24957fa2861, 7.4 kB (checked in by Robin Luckey <robin@ohloh.net>, 2 months ago)

[FIX] When printing platforms to the console output, suppress the namespace prefixes

  • Property mode set to 100755
Line 
1 #!/usr/bin/env ruby
2 begin
3   # First, try to load our files from the source tree
4   $: << "#{File.dirname(__FILE__)}/../lib"
5   require 'ohcount'
6 rescue LoadError
7   # Failing that, try to load from a gem
8   require 'rubygems'
9   require 'ohcount'
10 end
11
12 class OhcountCommandLine
13   attr_accessor :paths
14
15   def initialize(args=[])
16     args = args.clone # Because shift is destructive
17     set_option(args.shift) while args.first =~ /^-/
18     assign_paths_and_files(args)
19   end
20
21   def assign_paths_and_files(args)
22     @files = []
23     @paths = []
24     args.each do |file_or_path|
25       if File.directory?(file_or_path)
26         @paths << file_or_path
27       else
28         @files << file_or_path
29       end
30     end
31     @paths = ['.'] if @files.empty? && @paths.empty?
32   end
33
34   def source_file_list
35     @source_file_list ||= Ohcount::SourceFileList.new(:paths => @paths, :files => @files)
36   end
37
38   def annotate
39     source_file_list.each_source_file do |s|
40       s.parse do |language, semantic, line|
41         puts "#{language}\t#{semantic}\t#{line}"
42       end
43     end
44   end
45
46   # Find all source code files
47   def detect
48     source_file_list.each_source_file do |s|
49       puts "#{s.polyglot}\t#{s.filename}"
50     end
51   end
52
53   # Licenses
54   def licenses
55     source_file_list.each_source_file do |s|
56       next unless s.licenses.any?
57       symbols = s.licenses.collect { |l| LicenseSniffer::LicenseMap.instance.map[l].symbol }.join(",")
58       puts "#{symbols}\t#{s.filename}"
59     end
60   end
61
62   def analyze_with_progress(what)
63     count = 0
64     source_file_list.analyze(what) do |s|
65       if count % 100 == 0
66         STDOUT.write('.')
67         STDOUT.flush
68       end
69       count+=1
70     end
71     puts "\n"
72   end
73
74   def write_gestalt
75     platforms = source_file_list.gestalt_facts.platforms
76     platforms = platforms.any? ? platforms.collect { |p| p.to_s.split('::').last }.join(", ") : 'None'
77     puts "Platforms detected: #{ platforms }"
78
79     tools = source_file_list.gestalt_facts.tools
80     tools = tools.any? ? tools.collect { |p| p.to_s.split('::').last }.join(", ") : 'None'
81     puts "Tools detected: #{ tools }"
82   end
83
84   # Gestalt
85   def gestalt
86     analyze_with_progress(:gestalt)
87     write_gestalt
88   end
89
90   def raw_entities
91     source_file_list.each_source_file do |s|
92       s.raw_entities do |language, entity, s, e|
93         puts "#{language}\t#{entity}\t#{s}\t#{e}"
94       end
95     end
96   end
97
98   def help
99     puts <<HELP
100 Usage: ohcount [option] [paths...]
101
102 Ohloh source code line counter command line tool.
103    http://www.ohloh.net/
104
105 [option] can be one of the following:
106    -a, --annotate
107    -d, --detect
108    -re
109    -h, --help
110    -s, --summary
111
112 -a, --annotate                  Show annotated source code
113
114    The contents of all source code files found within the given
115    paths will be emitted to stdout. Each line will be prefixed with
116    a tab-delimited language name and semantic categorization (code,
117    comment, or blank).
118
119 -d, --detect                    Find source code files
120
121    Recursively find all source code files within the given paths.
122    For each source code file found, the file name will be emitted to
123    stdout prefixed with a tab-delimited language name.
124
125 -h, --help                      Display this message
126
127 -g, --gestalt                   Project Properties
128
129    Inspects project contents to determine what platform(s) the project
130    runs on, as well as any detected tools/IDEs used to develop it.
131
132 -i, --individual                Count lines of code per file
133
134    Count lines in all source code files within the given paths, and
135    emit a report of the lines of code, comments, and blanks in each
136    language per file.
137
138 -l, --license
139
140    Displays detected licensing information contained in each source
141    code file.
142
143 -re
144
145    Prints raw entity information to the screen (mainly for debugging).
146
147 -s, --summary                   Count lines of code (default)
148
149    Count lines in all source code files within the given paths, and
150    emit a report of the total number of lines of code, comments,
151    and blanks in each language. This is the default action.
152
153 [paths] can refer to any number of individual files or directories.
154    Directories will be probed recursively. If no path is given,
155    the current directory will be used.
156
157 HELP
158   end
159
160   def individual
161     STDOUT.write "Examining #{source_file_list.size} file(s)"
162
163     puts
164     puts "Ohloh Line Count".center(76)
165     puts "Language             Code    Comment  Comment %      Blank      Total  File"
166     puts "--------------  ---------  ---------  ---------  ---------  ---------  -----------------------------------------------"
167
168     source_file_list.analyze(:language) do |s|
169       s.language_breakdowns.sort {|l1,l2| l2.name <=> l1.name}.each do |lb|
170         write_individual_row(s.filename, lb.name, lb.code_count, lb.comment_count, lb.blanks)
171       end
172     end
173   end
174
175   def write_individual_row(file, name, code, comment, blank)
176     printf("%-14s",name)
177     printf(" %10d",code)
178     printf(" %10d",comment)
179     if comment+code > 0
180       printf(" %9.1f%%", comment.to_f / (comment+code).to_f * 100.0)
181     else
182       printf("           ")
183     end
184     printf(" %10d",blank)
185     printf(" %10d",code+comment+blank)
186     printf("  %s\n", file)
187   end
188
189   def memoryleak
190     puts "Parsing one file repeatedly. Watch your RAM vanish...."
191     s = Ohcount::SourceCode.new(files.first, :filenames => files)
192     while true
193       s.parse {}
194     end
195   end
196
197   def summary
198     STDOUT.write "Examining #{source_file_list.size} file(s)"
199
200     analyze_with_progress(:*)
201
202     puts
203     puts "Ohloh Line Count Summary".center(76)
204     puts
205
206     puts "Language        Files       Code    Comment  Comment %      Blank      Total"
207     puts "--------------  -----  ---------  ---------  ---------  ---------  ---------"
208
209     language_facts = source_file_list.language_facts
210     language_facts.fact_map.values.sort { |a,b| b.code <=> a.code}.each do |lf|
211       write_summary_row(lf.language, lf.filecount, lf.code, lf.comments, lf.blanks)
212     end
213
214     puts "--------------  -----  ---------  ---------  ---------  ---------  ---------"
215     write_summary_row('Total', language_facts.filecount,
216                       language_facts.code,
217                       language_facts.comments,
218                       language_facts.blanks)
219
220
221     puts
222     write_gestalt
223   end
224
225   def write_summary_row(name, file_count, code, comment, blank)
226     printf("%-14s", name)
227     printf(" %6d", file_count)
228     printf(" %10d", code)
229     printf(" %10d", comment)
230     if comment+code > 0
231       printf(" %9.1f%%", comment.to_f / (comment+code).to_f * 100.0)
232     else
233       printf("       0.0%");
234     end
235     printf(" %10d", blank)
236     printf(" %10d\n", code+comment+blank)
237   end
238
239   def subcommand=(s)
240     if @subcommand
241       STDERR.puts "Error: Multiple commands specified."
242       exit 1
243     else
244       @subcommand=s
245     end
246   end
247
248   def subcommand
249     @subcommand
250   end
251
252   def set_option(option)
253     case option
254     when '-s', '--summary'
255       self.subcommand = :summary
256     when '-d', '--detect'
257       self.subcommand = :detect
258     when '-a', '--annotate'
259       self.subcommand = :annotate
260     when '-g', '--gestalt'
261       self.subcommand = :gestalt
262     when '-i', '--individual'
263       self.subcommand = :individual
264     when '-l', '--licenses'
265       self.subcommand = :licenses
266     when '-m', '--memoryleak'
267       self.subcommand = :memoryleak
268     when '-e', '--entities'
269       self.subcommand = :entities
270     when '-re'
271       self.subcommand = :raw_entities
272     #when /-n([\w_]+)/
273     # @entities ||= []
274     # @entities << $1.to_sym
275     # self.subcommand = :entities unless @subcommand
276     when '-?', '-h', '--help'
277       self.subcommand = :help
278     else
279       STDERR.puts "Type 'ohcount -?' for usage."
280       exit 1
281     end
282   end
283
284   def run!
285     self.subcommand ||= :summary
286     if self.respond_to?(self.subcommand)
287       self.send(self.subcommand)
288     else
289       STDERR.puts "Type 'ohcount -?' for usage."
290       exit 1
291     end
292   end
293 end
294
295 OhcountCommandLine.new(ARGV).run!
Note: See TracBrowser for help on using the browser.