cloc 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/cloc ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ # If we were installed as a gem we can just:
5
+ require 'cloc'
6
+ rescue LoadError
7
+ # No, we weren't installed as a gem. Let's try to make Ruby aware
8
+ # of our library folder.
9
+ lib = File.join(File.dirname(__FILE__), '..', 'lib')
10
+ $: << lib
11
+ require 'cloc'
12
+ end
13
+
14
+ require 'optparse'
15
+
16
+ SETTINGS = { :version => Cloc::Database.default_version }
17
+
18
+ def process_files
19
+ db = Cloc::Database.new SETTINGS[:version]
20
+ linker = Cloc::Linker.new
21
+ ARGV.each do |file|
22
+ if File.directory? file
23
+ Dir.glob(File.join(file, '**', '*.[cy]')).each do |file|
24
+ linker.process_file file
25
+ end
26
+ else
27
+ linker.process_file file
28
+ end
29
+ end
30
+ linker.resolve_non_static_functions
31
+ linker.remove_missing
32
+ db.merge linker.data
33
+ db.save
34
+ end
35
+
36
+ OptionParser.new do |opts|
37
+ opts.banner = 'Usage: cloc [options] files-or-folders ...'
38
+ opts.separator ''
39
+ opts.separator <<EOS
40
+ DESCRIPTION
41
+
42
+ 'cloc' is a program that locates Ruby methods in C source files and
43
+ records their location (filepath + line number) in ~/.cloc-VERSION
44
+
45
+ VERSIONS
46
+
47
+ Since you may have several different versions of Ruby's source code on
48
+ your system, cloc's data file has a "version" embedded in it. E.g.,
49
+ ~/.cloc-1.8.7 and ~/.cloc-1.9.1. It's up to you to decide on these version
50
+ strings (they have absolutely no meaning for cloc). You may specify the
51
+ version to work with with the '-v' switch.
52
+
53
+ Common options:
54
+ EOS
55
+ opts.on('-v', '--version VERSION', "Select the version (default: #{SETTINGS[:version]}).") do |v|
56
+ SETTINGS[:version] = v
57
+ end
58
+ opts.on('-l', '--list', 'List existing versions and exit.') do
59
+ p Cloc::Database.versions
60
+ exit
61
+ end
62
+
63
+ opts.separator ''
64
+ opts.separator 'Debugging options:'
65
+
66
+ opts.on('-p', '--pp', 'Print out the database contents and exit.') do
67
+ require 'pp'
68
+ pp Cloc::Database.new(SETTINGS[:version]).data
69
+ exit
70
+ end
71
+ opts.on('-w', '--warnings', "Show warnings (these are usually harmless).") do
72
+ Cloc.warn = true
73
+ end
74
+ opts.on('-b', 'Print out the process memory consumption (linux only).', 'and do some benchmarking.') do
75
+ system("ps -p #{$$} -o vsz,rss,cmd")
76
+ db = Cloc::Database.new(SETTINGS[:version])
77
+ system("ps -p #{$$} -o vsz,rss,cmd")
78
+ require 'benchmark'
79
+ Benchmark.bm do |x|
80
+ x.report { 10.times { db.load } }
81
+ end
82
+ exit
83
+ end
84
+ end.parse!
85
+
86
+ if ARGV.empty?
87
+ puts "No file(s) or folder(s) specified. Nothing to do. Exiting"
88
+ puts "Invoke with `--help' for help."
89
+ exit
90
+ else
91
+ process_files
92
+ end
data/lib/cloc.rb ADDED
@@ -0,0 +1,14 @@
1
+
2
+ module Cloc
3
+
4
+ class << self
5
+ # Whether to warn of various things (parsing/linking problems).
6
+ attr_accessor :warn
7
+ alias warn? warn
8
+ end
9
+
10
+ autoload :Database, 'cloc/database'
11
+ autoload :Linker, 'cloc/linker'
12
+ autoload :FileSource, 'cloc/filesource'
13
+
14
+ end
@@ -0,0 +1,63 @@
1
+
2
+ module Cloc
3
+
4
+ HOME = ENV['HOME'] ? File.expand_path('~') : Dir.pwd
5
+
6
+ class Database
7
+
8
+ # Find all existing versions.
9
+ def self.versions
10
+ @versions ||= Dir.entries(HOME).grep(/^.cloc-(.*)/) { $1 }.sort
11
+ end
12
+
13
+ # Pick a default version.
14
+ #
15
+ # We give preference to RUBY_VERSION.
16
+ def self.default_version
17
+ versions.include?(RUBY_VERSION) ? RUBY_VERSION : (versions.first || RUBY_VERSION)
18
+ end
19
+
20
+ def initialize(version)
21
+ @version = version
22
+ @table = {}
23
+ load if File.exist? path
24
+ end
25
+
26
+ def version; @version; end
27
+
28
+ def merge(hash)
29
+ @table.merge!(hash) do |key, oldval, newval|
30
+ oldval.merge(newval)
31
+ end
32
+ end
33
+
34
+ def data
35
+ @table
36
+ end
37
+
38
+ # Returns the source-code location where a method is defined.
39
+ #
40
+ # Returned value is either [ path_to_file, line_number ], or nil if
41
+ # the method isn't known.
42
+ def lookup(object_name, method_name)
43
+ if @table[object_name] and (loc = @table[object_name][method_name])
44
+ # Since the pathname is stored as a symbol, we convert
45
+ # it back to a string.
46
+ return [loc[0].to_s, loc[1]]
47
+ end
48
+ end
49
+
50
+ def path
51
+ File.join(HOME, ".cloc-#{@version}")
52
+ end
53
+
54
+ def save
55
+ File.open(path, 'w') { |f| Marshal.dump(@table, f) }
56
+ end
57
+
58
+ def load
59
+ @table = File.open(path) { |f| Marshal.load(f) }
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,115 @@
1
+
2
+ module Cloc
3
+
4
+ class NmNtFnd < Exception
5
+ end
6
+
7
+ class FileSource
8
+
9
+ def initialize(pathname)
10
+ @pathname = pathname
11
+ @c2r_cache = {}
12
+ end
13
+
14
+ def raw_source
15
+ @raw_source ||= IO.read(@pathname).gsub!(/\r?\n|\r/, "\n").freeze
16
+ end
17
+
18
+ # Returns the source after processing it a bit to make it easier
19
+ # for other parsing functions to not care about edge cases.
20
+ def source
21
+ return @source if defined? @source
22
+
23
+ @source = raw_source.dup
24
+ @source.gsub!('rb_define_global_function(', 'rb_define_module_function(rb_mKernel,')
25
+ @source.gsub!('define_filetest_function(', 'rb_define_singleton_method(rb_cFile,')
26
+
27
+ # Add any special cases here. As an example, here are some
28
+ # "macro expansions" required to process DataObjects extensions.
29
+ @source.gsub!('CONST_GET(rb_mKernel,', 'rb_path2class(')
30
+ @source.gsub!('SQLITE3_CLASS(', 'rb_define_class_under(mSqlite3,');
31
+ @source.gsub!('DRIVER_CLASS(', 'rb_define_class_under(mDOMysql,');
32
+
33
+ @source.freeze
34
+ end
35
+
36
+ # Converts a character offset to a line number.
37
+ def offs_to_line(offs)
38
+ # The following isn't as inefficient as it seems: slice() returns
39
+ # a string pointing inside the original string's allocated space.
40
+ return source.slice(0, offs).count("\n") + 1
41
+ end
42
+
43
+ # Returns a list of all the C functions that are callable from Ruby.
44
+ def cfunctions
45
+ @cfunction ||= begin
46
+ list = {}
47
+ source.scan( /\n (?:static\s+)? VALUE\s+ (\w+) \( /x ) do |func, |
48
+ list[func] = offs_to_line($~.begin(1))
49
+ end
50
+ list
51
+ end
52
+ end
53
+
54
+ # Returns the fully-qualified Ruby class/module name behind a C variable.
55
+ #
56
+ # Examples:
57
+ # "rb_mKernel" -> "Kernel"
58
+ # "cResolver" -> "YAML::Syck::Resolver"
59
+ def c2r(cvar)
60
+ return @c2r_cache[cvar] if @c2r_cache[cvar]
61
+ @c2r_cache[cvar] = _c2r(cvar)
62
+ end
63
+
64
+ def _c2r(cvar)
65
+ if source =~ /#{cvar} \s* = \s* rb_define_(?:class|module) \( \s* "([^"]+)" /x
66
+ return $1
67
+ end
68
+ if source =~ /#{cvar} \s* = \s* rb_define_(?:class|module)_under \( \s* (\w+) \s* , \s* "([^"]+)" /x
69
+ return c2r($1) + '::' + $2
70
+ end
71
+ if source =~ /#{cvar} \s* = \s* rb_path2class \( \s* "([^"]+)" /x
72
+ return $1
73
+ end
74
+ # As a last resort we use C naming conventions:
75
+ if cvar =~ /rb_[cm](.*)/
76
+ return $1
77
+ end
78
+ raise NmNtFnd
79
+ end
80
+
81
+ # Returns a list of all the method definitions.
82
+ def refs
83
+ errors = {}
84
+ list = []
85
+ source.scan( / (rb_define_method|rb_define_private_method|rb_define_singleton_method|rb_define_module_function)
86
+ \(
87
+ \s* (\w+) \s* ,
88
+ \s* "([^"]+)" \s* , # "
89
+ \s* (\w+) /x ) do |definer, cvar, rfunc, cfunc |
90
+ begin
91
+ klass = c2r(cvar)
92
+ is_singleton = (definer == 'rb_define_singleton_method' or definer == 'rb_define_module_function')
93
+ is_instance = (definer != 'rb_define_singleton_method')
94
+ if is_singleton
95
+ list << [klass + '--singleton', rfunc, cfunc]
96
+ end
97
+ if is_instance
98
+ list << [klass, rfunc, cfunc]
99
+ end
100
+ rescue NmNtFnd
101
+ errors[cvar] = true
102
+ end
103
+ end
104
+
105
+ if Cloc.warn?
106
+ errors.keys.each do |cvar|
107
+ puts "-- Couldn't find variable '#{cvar}'"
108
+ end
109
+ end
110
+
111
+ list
112
+ end
113
+ end
114
+
115
+ end
@@ -0,0 +1,72 @@
1
+
2
+ module Cloc
3
+
4
+ # Connects method declarations to location of c functions.
5
+ # The result is written to the database.
6
+ class Linker
7
+
8
+ def initialize
9
+ @table = {}
10
+ @non_static_functions = {}
11
+ end
12
+
13
+ def process_file(pathname)
14
+ puts "Processing #{pathname} ..."
15
+ # Since same pathnames appear many many times, we store them as
16
+ # symbols to conserve memory.
17
+ absolute_pathname = File.expand_path(pathname).to_sym
18
+
19
+ src = FileSource.new(pathname)
20
+ src.refs.each do |klass, rmethod, cfunc|
21
+ if @table[klass]
22
+ klass = @table[klass]
23
+ else
24
+ klass = @table[klass] = {}
25
+ end
26
+ klass[rmethod] = begin
27
+ if src.cfunctions[cfunc]
28
+ # Resolve static methods.
29
+ [ absolute_pathname, src.cfunctions[cfunc] ]
30
+ else
31
+ # Postpone to later.
32
+ cfunc
33
+ end
34
+ end
35
+ end
36
+
37
+ # Save non-static functions for later.
38
+ src.cfunctions.each_pair do |cfunc, lineno|
39
+ @non_static_functions[cfunc] = [ absolute_pathname, lineno ]
40
+ end
41
+ end
42
+
43
+ def resolve_non_static_functions
44
+ @table.each_pair do |klass, methods_table|
45
+ methods_table.each_pair do |method, location_or_cfunction|
46
+ if location_or_cfunction.is_a? String
47
+ if @non_static_functions[location_or_cfunction]
48
+ methods_table[method] = @non_static_functions[location_or_cfunction]
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
55
+ def remove_missing
56
+ @table.each_pair do |klass, methods_table|
57
+ methods_table.each_pair do |method, location_or_cfunction|
58
+ if location_or_cfunction.is_a? String
59
+ puts "-- Couldn't resolve #{klass}##{method}" if Cloc.warn?
60
+ methods_table.delete(method)
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ def data
67
+ @table
68
+ end
69
+
70
+ end
71
+
72
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ platform: ruby
6
+ authors:
7
+ - Mooffie
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-04-26 00:00:00 +03:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: mooffie@gmail.com
18
+ executables:
19
+ - cloc
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - bin/cloc
26
+ - lib/cloc.rb
27
+ - lib/cloc/database.rb
28
+ - lib/cloc/filesource.rb
29
+ - lib/cloc/linker.rb
30
+ has_rdoc: true
31
+ homepage: http://github.com/mooffie/cloc
32
+ licenses: []
33
+
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.8.0
44
+ version:
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ requirements: []
52
+
53
+ rubyforge_project:
54
+ rubygems_version: 1.3.5
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Locates Ruby methods in C source files.
58
+ test_files: []
59
+