cloc 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+