dep_walker 1.0.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.
@@ -0,0 +1,13 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'autotest/restart'
4
+
5
+ Autotest.add_hook :initialize do |at|
6
+ at.add_mapping(/.*\.c/) do |f, _|
7
+ at.files_matching(/test_.*rb$/)
8
+ end
9
+ end
10
+
11
+ Autotest.add_hook :run_command do |at|
12
+ system "rake clean compile"
13
+ end
File without changes
@@ -0,0 +1,6 @@
1
+ === 1.0.0 / 2011-04-13
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
@@ -0,0 +1,12 @@
1
+ .autotest
2
+ History.rdoc
3
+ Manifest.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/dep_walker
7
+ ext/dep_walker/dep_walker.c
8
+ ext/dep_walker/extconf.rb
9
+ ext/dep_walker/win.c
10
+ ext/dep_walker/win.h
11
+ lib/dep_walker.rb
12
+ test/test_dep_walker.rb
@@ -0,0 +1,88 @@
1
+ = dep_walker
2
+
3
+ * GitHub: http://github.com/bosko/dep_walker
4
+ * Issues: http://github.com/bosko/dep_walker/issues
5
+ * Blog: http://pragdevnotes.com
6
+
7
+ == DESCRIPTION:
8
+
9
+ The dep_walker is small utility gem that checks dependencies for native extensions
10
+ used by installed gems on Windows. If you are {RubyInstaller}[http://www.rubyinstaller.org]
11
+ user and have seen message box:
12
+
13
+ <em>"This application has failed to start because <name_of_dll>.dll was not found.
14
+ Re-installing the application may fix this problem"</em>
15
+
16
+ when you tried to use gem that has pre-built binariy extension, you've faced common
17
+ problem on Windows systems - missing dependency dll. Same error might occur even if
18
+ extension library was built during gem installation if all header files and libraries
19
+ are available to the build tools, but runtime dependencies are not present.
20
+
21
+ With dep_walker you can simply check all installed gems. Even more, if log is turned on,
22
+ gem will print out information where dependency is found on the system, so you can check
23
+ whether Ruby extension really uses correct version of required dll.
24
+
25
+ == FEATURES/PROBLEMS:
26
+
27
+ * None at the moment
28
+
29
+ == SYNOPSIS:
30
+
31
+ The dep_walker can check all installed gems:
32
+
33
+ >dep_walker -a
34
+
35
+ or just specific gem
36
+
37
+ >dep_walker -c nokogiri-1.4.4
38
+
39
+ For verbose output use -t switch
40
+
41
+ >dep_walker -c nokogiri-1.4.4 -t
42
+
43
+ Finally if you want colored output use --color option. In this case you must
44
+ have {ANSICON}[http://adoxa.110mb.com/ansicon/index.html] installed.
45
+
46
+ For full list of options use
47
+
48
+ >dep_walker -h
49
+
50
+ == REQUIREMENTS:
51
+
52
+ * rake
53
+ * rubygems
54
+ * hoe (development)
55
+ * rake-compiler (development)
56
+
57
+ == INSTALL:
58
+
59
+ >gem install dep_walker
60
+
61
+ == DEVELOPERS:
62
+
63
+ * Boško Ivanišević (bosko ivanisevic at gmail com)
64
+
65
+ == LICENSE:
66
+
67
+ (The MIT License)
68
+
69
+ Copyright (c) 2011 Boško Ivanišević
70
+
71
+ Permission is hereby granted, free of charge, to any person obtaining
72
+ a copy of this software and associated documentation files (the
73
+ 'Software'), to deal in the Software without restriction, including
74
+ without limitation the rights to use, copy, modify, merge, publish,
75
+ distribute, sublicense, and/or sell copies of the Software, and to
76
+ permit persons to whom the Software is furnished to do so, subject to
77
+ the following conditions:
78
+
79
+ The above copyright notice and this permission notice shall be
80
+ included in all copies or substantial portions of the Software.
81
+
82
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
83
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
84
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
85
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
86
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
87
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
88
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,22 @@
1
+ # -*- coding: utf-8 -*-
2
+ # -*- ruby -*-
3
+
4
+ require 'rubygems'
5
+ require 'hoe'
6
+ require 'rake/extensiontask'
7
+
8
+ Hoe.spec 'dep_walker' do
9
+ developer('Boško Ivanišević', 'bosko.ivanisevic@gmail.com')
10
+ self.url = %q{http://github.com/bosko/dep_walker}
11
+ self.readme_file = 'README.rdoc'
12
+ self.history_file = 'History.rdoc'
13
+ self.extra_rdoc_files = FileList['*.rdoc']
14
+ self.extra_dev_deps << ['rake-compiler', '>= 0']
15
+ self.spec_extras = { :extensions => ["ext/dep_walker/extconf.rb"] }
16
+
17
+ Rake::ExtensionTask.new('dep_walker', spec) do |ext|
18
+ ext.lib_dir = File.join('lib', 'dep_walker')
19
+ end
20
+ end
21
+
22
+ Rake::Task[:test].prerequisites << :compile
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ require "ostruct"
5
+ require "optparse"
6
+ require "dep_walker"
7
+
8
+ def print_result(deps)
9
+ if deps.empty?
10
+ print green("All dependencies found")
11
+ else
12
+ deps.each do |k,v|
13
+ print red("Missing dependencies:\n #{k}: ")
14
+ missing = []
15
+ v.each do |ext,dll|
16
+ missing << dll unless missing.include? dll
17
+ end
18
+ puts red(missing.join(","))
19
+ end
20
+ end
21
+ end
22
+
23
+ $options = OpenStruct.new
24
+
25
+ optparse = OptionParser.new do |opts|
26
+ opts.banner = "Usage: dep_walker [options]"
27
+ opts.separator ""
28
+ opts.separator "Options are ..."
29
+
30
+ opts.on('-c', '--check [GEM_NAME]', 'Check single gem') do |name|
31
+ $options.gem_name = name
32
+ end
33
+
34
+ opts.on('-i', '--gem-version [VERSION]', 'Gem version to be checked (optional)') do |version|
35
+ $options.gem_version = version
36
+ end
37
+
38
+ opts.on('-a', '--all', 'Check all installed gems (overrides -c)') do
39
+ $options.check_all = true
40
+ end
41
+
42
+ opts.on('-t', '--trace', 'Print messages while checkint is performed') do
43
+ $options.trace = true
44
+ end
45
+
46
+ opts.on('', '--color', 'Enable color in output') do
47
+ $options.color = true
48
+ end
49
+
50
+ opts.on('-v', '--version', 'Display program version') do
51
+ puts "DepWalker version #{DepWalker::VERSION}"
52
+ exit
53
+ end
54
+
55
+ opts.on('-h', '--help', 'Display this screen') do
56
+ puts opts
57
+ exit
58
+ end
59
+ end
60
+
61
+ begin
62
+ optparse.parse!
63
+ rescue OptionParser::ParseError => pe
64
+ puts pe.message
65
+ puts ''
66
+ puts optparse
67
+ exit
68
+ end
69
+
70
+ if $options.check_all
71
+ print_result(DepWalker.check_all)
72
+ elsif $options.gem_name
73
+ print_result(DepWalker.check $options.gem_name, $options.gem_version)
74
+ else
75
+ puts optparse
76
+ end
@@ -0,0 +1,18 @@
1
+ #include <ruby.h>
2
+ #include "win.h"
3
+
4
+ static VALUE raw_dependencies(VALUE self, VALUE path)
5
+ {
6
+ VALUE mDeps = rb_ary_new();
7
+ if (RSTRING_LEN(path) > 0)
8
+ {
9
+ DumpDllFromPath(mDeps, StringValuePtr(path));
10
+ }
11
+ return mDeps;
12
+ }
13
+
14
+ void Init_dep_walker()
15
+ {
16
+ VALUE mDepWalker = rb_define_module("DepWalker");
17
+ rb_define_singleton_method(mDepWalker, "raw_dependencies", raw_dependencies, 1);
18
+ }
@@ -0,0 +1,11 @@
1
+ require "mkmf"
2
+
3
+ unless find_header('imagehlp.h')
4
+ abort "header imagehlp.h is missing"
5
+ end
6
+
7
+ unless find_library('imagehlp', 'LoadImage')
8
+ abort "library imagehlp is missing"
9
+ end
10
+
11
+ create_makefile('dep_walker/dep_walker')
@@ -0,0 +1,64 @@
1
+ #include <windows.h>
2
+ #include <imagehlp.h>
3
+ #include <ruby.h>
4
+
5
+ IMAGE_SECTION_HEADER* GetEnclosingSectionHeader(unsigned long rva, IMAGE_NT_HEADERS32* pNTHeader)
6
+ {
7
+ IMAGE_SECTION_HEADER* section = IMAGE_FIRST_SECTION(pNTHeader);
8
+ unsigned int i;
9
+
10
+ for (i=0; i < pNTHeader->FileHeader.NumberOfSections; i++, section++)
11
+ {
12
+ unsigned long size = section->Misc.VirtualSize;
13
+ if (0 == size)
14
+ {
15
+ size = section->SizeOfRawData;
16
+ }
17
+
18
+ // Is the RVA within this section?
19
+ if ((rva >= section->VirtualAddress) &&
20
+ (rva < (section->VirtualAddress + size)))
21
+ {
22
+ return section;
23
+ }
24
+ }
25
+
26
+ return 0;
27
+ }
28
+
29
+ void* GetPtrFromRVA(unsigned long rva, IMAGE_NT_HEADERS32* pNTHeader, unsigned char* imageBase)
30
+ {
31
+ IMAGE_SECTION_HEADER* pSectionHdr;
32
+ int delta;
33
+
34
+ pSectionHdr = GetEnclosingSectionHeader(rva, pNTHeader);
35
+ if (!pSectionHdr)
36
+ return 0;
37
+
38
+ delta = (int)(pSectionHdr->VirtualAddress-pSectionHdr->PointerToRawData);
39
+ return (void*)(imageBase + rva - delta);
40
+ }
41
+
42
+ void DumpDllFromPath(VALUE mDeps, char* path)
43
+ {
44
+ LOADED_IMAGE* image=ImageLoad(path,0);
45
+
46
+ if (NULL != image && image->FileHeader->OptionalHeader.NumberOfRvaAndSizes>=2) {
47
+ IMAGE_IMPORT_DESCRIPTOR* importDesc=
48
+ (IMAGE_IMPORT_DESCRIPTOR*)GetPtrFromRVA(
49
+ image->FileHeader->OptionalHeader.DataDirectory[1].VirtualAddress,
50
+ image->FileHeader, image->MappedAddress);
51
+ while (1)
52
+ {
53
+ // See if we've reached an empty IMAGE_IMPORT_DESCRIPTOR
54
+ if ((importDesc->TimeDateStamp==0) && (importDesc->Name==0))
55
+ break;
56
+
57
+ rb_ary_push(mDeps, rb_str_new2((const char*)GetPtrFromRVA(importDesc->Name,
58
+ image->FileHeader,
59
+ image->MappedAddress)));
60
+ importDesc++;
61
+ }
62
+ }
63
+ ImageUnload(image);
64
+ }
@@ -0,0 +1,8 @@
1
+ #ifndef _WIN_H_
2
+ #define _WIN_H_
3
+
4
+ IMAGE_SECTION_HEADER* GetEnclosingSectionHeader(unsigned long rva, IMAGE_NT_HEADERS32* pNTHeader);
5
+ void* GetPtrFromRVA(unsigned long rva, IMAGE_NT_HEADERS32* pNTHeader, unsigned char* imageBase);
6
+ void DumpDllFromPath(VALUE mDeps, char* path);
7
+
8
+ #endif /* _WIN_H_ */
@@ -0,0 +1,164 @@
1
+ $LOAD_PATH.unshift File.expand_path('../', __FILE__)
2
+ require 'rubygems'
3
+ require "dep_walker/dep_walker"
4
+
5
+ ##
6
+ # Set the terminal's foreground color to green if
7
+ # colored output is set through options
8
+ ##
9
+ def green(string)
10
+ if $options.color
11
+ "\e[1;32m#{string}\e[0m"
12
+ else
13
+ string
14
+ end
15
+ end
16
+
17
+ ##
18
+ # Set the terminal's foreground color to red if
19
+ # colored output is set through options
20
+ ##
21
+ def red(string)
22
+ if $options.color
23
+ "\e[1;31m#{string}\e[0m"
24
+ else
25
+ string
26
+ end
27
+ end
28
+
29
+ ##
30
+ # Output message colored according to the message
31
+ # level (info - no color, success - green, error - red)
32
+ ##
33
+ def trace(message, level=TRACE_INFO)
34
+ return unless $options.trace
35
+
36
+ case level
37
+ when DepWalker::TRACE_INFO
38
+ puts message
39
+ when DepWalker::TRACE_SUCCESS
40
+ puts green(message)
41
+ when DepWalker::TRACE_ERROR
42
+ puts red(message)
43
+ end
44
+ end
45
+
46
+ module DepWalker
47
+ extend self
48
+
49
+ VERSION = '1.0.0'
50
+
51
+ ##
52
+ # Informational messages
53
+ ##
54
+ TRACE_INFO = 0
55
+
56
+ ##
57
+ # Succes messages
58
+ ##
59
+ TRACE_SUCCESS = 1
60
+
61
+ ##
62
+ # Error messages
63
+ #
64
+ TRACE_ERROR = 2
65
+
66
+ ##
67
+ # Checks dependencies for all installed gems. Returns
68
+ # hash of all unresolved dependencies with the full Gem
69
+ # name in keys and hash of extension libraries and
70
+ # dependencies that are not found in the system.
71
+ ##
72
+ def check_all
73
+ all_deps = {}
74
+ Gem::Specification.find_all.each do |spec|
75
+ gem_deps = check(spec)
76
+ all_deps[spec.full_name] = gem_deps unless gem_deps.empty?
77
+ end
78
+
79
+ all_deps
80
+ end
81
+
82
+ ##
83
+ # Checks dependencis for a single Gem. Returns hash of extension
84
+ # library used by Gem as a key and array of unresolved dependencies
85
+ # as a value.
86
+ ##
87
+ def check(name_or_spec, version=nil)
88
+ gem_deps = {}
89
+ spec = name_or_spec.is_a?(String) ? Gem::Specification.find_all_by_name(name_or_spec, version) : name_or_spec
90
+ spec.each do |s|
91
+ walk_deps(s).each do |k,v|
92
+ gem_deps[k] = v[:not_found] unless v[:not_found].empty?
93
+ end
94
+ end
95
+ gem_deps
96
+ end
97
+
98
+ ##
99
+ # Search dependency dlls for all extension libraries in the Gem.
100
+ # Search is performed across all directories in the system path
101
+ # and the extension library's directory. Returns hash with a
102
+ # full path to extension library as a key and hash with all
103
+ # dependencies found with the paths from which they will be loade
104
+ # and all dependencies that are not found.
105
+ ##
106
+ def walk_deps(gem_spec)
107
+ dep_tree = {}
108
+ if gem_spec.is_a? Gem::Specification
109
+ trace("Checking #{gem_spec.full_name}", TRACE_INFO)
110
+ dependencies(gem_spec).each do |k,v|
111
+ trace(" #{k}", TRACE_INFO)
112
+ found = {}
113
+ not_found = []
114
+ v.each do |shlib|
115
+ if found[shlib].nil? || not_found.include?(shlib)
116
+ shlib_dir = find_shlib_dir(File.dirname(k), shlib)
117
+ if shlib_dir
118
+ found[shlib] = shlib_dir
119
+ trace(" #{shlib} -> Found at #{shlib_dir}", TRACE_SUCCESS)
120
+ elsif !shlib.start_with?("msvcrt-ruby")
121
+ not_found << shlib
122
+ trace(" #{shlib} -> Not found", TRACE_ERROR)
123
+ end
124
+ end
125
+ end
126
+ dep_tree[k] = {:found => found, :not_found => not_found}
127
+ end
128
+ trace("="*40, TRACE_INFO)
129
+ end
130
+ dep_tree
131
+ end
132
+
133
+ ##
134
+ # Returns dependencies for all shared libraries found in the
135
+ # Gem's directories
136
+ ##
137
+ def dependencies(gem_spec)
138
+ deps = {}
139
+ Dir.glob(File.join(gem_spec.full_gem_path, "**/*")).select do |f|
140
+ f if [".so",".dll"].include? File.extname(f)
141
+ end.each do |ext|
142
+ deps[ext] = dll_dependencies(ext)
143
+ end
144
+ deps
145
+ end
146
+
147
+ ##
148
+ # Returns all dependencies for input shared library
149
+ ##
150
+ def dll_dependencies(dll_path = "")
151
+ return [] unless ['.dll', '.so'].include? File.extname dll_path
152
+
153
+ raw_dependencies(dll_path).map {|dep| dep.downcase}.uniq
154
+ end
155
+
156
+ private
157
+
158
+ def find_shlib_dir(init_path, shlib)
159
+ ENV["PATH"].split(File::PATH_SEPARATOR).unshift(init_path).each do |path|
160
+ return path if File.file?(File.join(path, shlib))
161
+ end
162
+ nil
163
+ end
164
+ end
@@ -0,0 +1,22 @@
1
+ require "test/unit"
2
+ require "dep_walker"
3
+
4
+ class TestDepWalker < Test::Unit::TestCase
5
+ def test_no_argument
6
+ deps = DepWalker.dll_dependencies
7
+ assert deps.is_a? Array
8
+ assert_equal 0, deps.length
9
+ end
10
+
11
+ def test_dep_walker
12
+ path = File.join(File.expand_path('../../lib/dep_walker', __FILE__), 'dep_walker.so')
13
+ deps = DepWalker.dll_dependencies path
14
+ assert_equal 4, deps.length
15
+ assert deps.include? 'msvcrt.dll'
16
+ assert deps.include? 'imagehlp.dll'
17
+ end
18
+
19
+ def test_wrong_extension
20
+ assert_equal 0, DepWalker.dll_dependencies("dummy.txt").length
21
+ end
22
+ end
metadata ADDED
@@ -0,0 +1,118 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dep_walker
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Boško Ivanišević
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-05-07 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake-compiler
16
+ requirement: &22551840 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *22551840
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ requirement: &22548516 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2.9.4
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *22548516
36
+ description: ! 'The dep_walker is small utility gem that checks dependencies for native
37
+ extensions
38
+
39
+ used by installed gems on Windows. If you are {RubyInstaller}[http://www.rubyinstaller.org]
40
+
41
+ user and have seen message box:
42
+
43
+
44
+ <em>"This application has failed to start because <name_of_dll>.dll was not found.
45
+
46
+ Re-installing the application may fix this problem"</em>
47
+
48
+
49
+ when you tried to use gem that has pre-built binariy extension, you''ve faced common
50
+
51
+ problem on Windows systems - missing dependency dll. Same error might occur even
52
+ if
53
+
54
+ extension library was built during gem installation if all header files and libraries
55
+
56
+ are available to the build tools, but runtime dependencies are not present.
57
+
58
+
59
+ With dep_walker you can simply check all installed gems. Even more, if log is turned
60
+ on,
61
+
62
+ gem will print out information where dependency is found on the system, so you can
63
+ check
64
+
65
+ whether Ruby extension really uses correct version of required dll.'
66
+ email:
67
+ - bosko.ivanisevic@gmail.com
68
+ executables:
69
+ - dep_walker
70
+ extensions:
71
+ - ext/dep_walker/extconf.rb
72
+ extra_rdoc_files:
73
+ - Manifest.txt
74
+ - History.rdoc
75
+ - README.rdoc
76
+ files:
77
+ - .autotest
78
+ - History.rdoc
79
+ - Manifest.txt
80
+ - README.rdoc
81
+ - Rakefile
82
+ - bin/dep_walker
83
+ - ext/dep_walker/dep_walker.c
84
+ - ext/dep_walker/extconf.rb
85
+ - ext/dep_walker/win.c
86
+ - ext/dep_walker/win.h
87
+ - lib/dep_walker.rb
88
+ - test/test_dep_walker.rb
89
+ - .gemtest
90
+ homepage: http://github.com/bosko/dep_walker
91
+ licenses: []
92
+ post_install_message:
93
+ rdoc_options:
94
+ - --main
95
+ - README.rdoc
96
+ require_paths:
97
+ - lib
98
+ required_ruby_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ requirements: []
111
+ rubyforge_project: dep_walker
112
+ rubygems_version: 1.8.1
113
+ signing_key:
114
+ specification_version: 3
115
+ summary: The dep_walker is small utility gem that checks dependencies for native extensions
116
+ used by installed gems on Windows
117
+ test_files:
118
+ - test/test_dep_walker.rb