dep_walker 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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