mini_portile 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/History.txt +3 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.rdoc +147 -0
  4. data/Rakefile +44 -0
  5. data/lib/mini_portile.rb +256 -0
  6. metadata +82 -0
@@ -0,0 +1,3 @@
1
+ === 0.1.0 / 2011-03-08
2
+
3
+ * Initial release. Welcome to this world!
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Luis Lavena.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,147 @@
1
+ = MiniPortile
2
+
3
+ * {Source Code}[https://github.com/luislavena/mini_portile]
4
+ * {Bug Reports}[https://github.com/luislavena/mini_portile/issues]
5
+
6
+ This project is a minimalistic, simplistic and stupid implementation of a port
7
+ /recipe system <b>for developers</b>.
8
+
9
+ == Another port system, srsly?
10
+
11
+ No, is not a general port system, is not aimed to take over apt, macports or
12
+ anything like that.
13
+
14
+ The rationale is simple.
15
+
16
+ You create a library A that uses B at runtime or compile time. Target audience
17
+ of your library might have different versions of B installed than yours.
18
+
19
+ You know, <em>Works on my machine</em> is not what you expect from one
20
+ developer to another.
21
+
22
+ Developers having problems report them back to you, and what you do then?
23
+ Compile B locally, replacing your existing installation of B or simply hacking
24
+ things around so nothing breaks.
25
+
26
+ All this, manually.
27
+
28
+ Computers are tools, are meant to help us, not the other way around.
29
+
30
+ What if I tell you the above scenario can be simplified with something like
31
+ this:
32
+
33
+ rake compile B_VERSION=1.2.3
34
+
35
+ And your library will use the version of B you specified. Done.
36
+
37
+ == You make it sound easy, where is the catch?
38
+
39
+ You got me, there is a catch. At this time (and highly likely will be always)
40
+ MiniPortile is only compatible with GCC compilers and autoconf/configure-based
41
+ projects.
42
+
43
+ It assumes the library you want to build contains a <tt>configure</tt> script,
44
+ which all the autoconf-based libraries do.
45
+
46
+ === How to use
47
+
48
+ Now that you know the catch, and you're still reading this, let me show you a
49
+ quick example:
50
+
51
+ require "mini_portile"
52
+ recipe = MiniPortile.new("libiconv", "1.13.1")
53
+ recipe.files = ["http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz"]
54
+ recipe.cook
55
+ recipe.activate
56
+
57
+ That's all. <tt>cook</tt> will download, extract, configure and compile the
58
+ library into a namespaced structure. <tt>activate</tt> ensures GCC find this
59
+ library and prefers it over a system-wide installation.
60
+
61
+ === Structure
62
+
63
+ At this time, if you haven't digged into the code yet, are wondering <em>what
64
+ is all that structure talk about?</em>.
65
+
66
+ MiniPortile follows the principle of <b>convention over configuration</b> and
67
+ established a folder structure where is going to place files and perform work.
68
+
69
+ Take the above example, and let's draw some picture:
70
+
71
+ mylib
72
+ |-- ports
73
+ | |-- archives
74
+ | | `-- libiconv-1.13.1.tar.gz
75
+ | `-- <platform>
76
+ | `-- libiconv
77
+ | `-- 1.13.1
78
+ | |-- bin
79
+ | |-- include
80
+ | `-- lib
81
+ `-- tmp
82
+ `-- <platform>
83
+ `-- ports
84
+
85
+ In above structure, <tt>platform</tt> refers to the architecture that represents
86
+ the operating system you're using (e.g. i686-linux, i386-mingw32, etc).
87
+
88
+ Inside this folder, MiniPortile will store the artifacts that result from the
89
+ compilation process. As you cans see, it versions out the library so you can
90
+ run multiple version combination without compromising these overlap each other.
91
+
92
+ <tt>archives</tt> is where downloaded source files are stored. It is recommended
93
+ you avoid trashing that folder so no further downloads will be required (save
94
+ bandwidth, save the world).
95
+
96
+ The <tt>tmp</tt> is where compilation is performed and can be safely discarded.
97
+
98
+ === How can I combine this with my compilation task?
99
+
100
+ In the simplified proposal, the idea is that using Rake, your <tt>compile</tt>
101
+ task depends on MiniPortile compilation and most important, activation.
102
+
103
+ Take the following as a simplification of how you can use MiniPortile with
104
+ Rake:
105
+
106
+ file ".libiconv.1.13.1.installed" do |f|
107
+ recipe = MiniPortile.new("libiconv", "1.13.1")
108
+ recipe.files = ["http://ftp.gnu.org/pub/gnu/libiconv/libiconv-1.13.1.tar.gz"]
109
+ recipe.cook
110
+ touch f.name
111
+ end
112
+
113
+ task :libiconv => [".libiconv.1.13.1.installed"] do
114
+ recipe = MiniPortile.new("libiconv", "1.13.1")
115
+ recipe.activate
116
+ end
117
+
118
+ task :compile => [:libiconv] do
119
+ # ...
120
+ end
121
+
122
+ This example will:
123
+
124
+ * Compile the library only once (using a timestamp file)
125
+ * Ensure compiled library gets activated every time
126
+ * Make compile task depend on compiled library activation
127
+
128
+ For your homework, you can make libiconv version be taken from <tt>ENV</tt>
129
+ variables.
130
+
131
+ === Supported scenarios
132
+
133
+ As mentioned before, MiniPortile requires a GCC compiler toolchain. This has
134
+ been tested against Ubuntu, OSX and even Windows (RubyInstaller with DevKit)
135
+
136
+ == Disclaimer
137
+
138
+ If you have any trouble, don't hesitate to contact the author. As always,
139
+ I'm not going to say <em>Use at your own risk</em> because I don't want this
140
+ library to be risky.
141
+
142
+ If you trip on something, I'll share the liability by repairing things
143
+ as quickly as I can. Your responsibility is to report the inadequacies.
144
+
145
+ == License
146
+
147
+ This library is licensed under MIT license. Please see LICENSE.txt for details.
@@ -0,0 +1,44 @@
1
+ require "rake/clean"
2
+ require "rubygems/package_task"
3
+
4
+ GEM_SPEC = Gem::Specification.new do |s|
5
+ # basic information
6
+ s.name = "mini_portile"
7
+ s.version = "0.1.0"
8
+ s.platform = Gem::Platform::RUBY
9
+
10
+ # description and details
11
+ s.summary = "Simplistic port-like solution for developers"
12
+ s.description = "Provide a standard and simplified way to build and package\nRuby extensions (C, Java) using Rake as glue."
13
+
14
+ # requirements
15
+ s.required_ruby_version = ">= 1.8.6"
16
+ s.required_rubygems_version = ">= 1.3.5"
17
+
18
+ # dependencies (add_dependency)
19
+ # development dependencies (add_development_dependency)
20
+
21
+ # components, files and paths
22
+ s.files = FileList["lib/**/*.rb", "Rakefile", "*.{rdoc,txt}"]
23
+
24
+ s.require_path = 'lib'
25
+
26
+ # documentation
27
+ s.has_rdoc = true
28
+ s.rdoc_options << '--main' << 'README.rdoc' << '--title' << 'MiniPortile -- Documentation'
29
+
30
+ s.extra_rdoc_files = %w(README.rdoc History.txt LICENSE.txt)
31
+
32
+ # project information
33
+ s.homepage = 'http://github.com/luislavena/mini_portile'
34
+ s.licenses = ['MIT']
35
+
36
+ # author and contributors
37
+ s.author = 'Luis Lavena'
38
+ s.email = 'luislavena@gmail.com'
39
+ end
40
+
41
+ Gem::PackageTask.new(GEM_SPEC) do |pkg|
42
+ pkg.need_tar = false
43
+ pkg.need_zip = false
44
+ end
@@ -0,0 +1,256 @@
1
+ require 'rbconfig'
2
+ require 'net/http'
3
+ require 'fileutils'
4
+ require 'tempfile'
5
+
6
+ class MiniPortile
7
+ attr_reader :name, :version, :target
8
+ attr_accessor :host, :files, :logger, :config_options
9
+
10
+ def initialize(name, version)
11
+ @name = name
12
+ @version = version
13
+ @target = 'ports'
14
+ @files = []
15
+ @logger = STDOUT
16
+ @config_options = []
17
+
18
+ @host = RbConfig::CONFIG['arch']
19
+ end
20
+
21
+ def download
22
+ @files.each do |url|
23
+ filename = File.basename(url)
24
+ download_file(url, File.join(archives_path, filename))
25
+ end
26
+ end
27
+
28
+ def extract
29
+ @files.each do |url|
30
+ filename = File.basename(url)
31
+ extract_file(File.join(archives_path, filename), tmp_path)
32
+ end
33
+ end
34
+
35
+ def configure
36
+ return if configured?
37
+
38
+ prefix = File.expand_path(port_path)
39
+ options = [
40
+ "--disable-shared", # disable generation of shared object
41
+ "--enable-static", # build static library
42
+ "--host=#{@host}", # build for specific target (host)
43
+ "--prefix=#{prefix}" # installation target
44
+ ].concat(@config_options).join(' ')
45
+
46
+ execute('configure', %Q(sh configure #{options}))
47
+ end
48
+
49
+ def compile
50
+ execute('compile', 'make')
51
+ end
52
+
53
+ def install
54
+ return if installed?
55
+ execute('install', %Q(make install))
56
+ end
57
+
58
+ def downloaded?
59
+ missing = @files.detect do |url|
60
+ filename = File.basename(url)
61
+ !File.exist?(File.join(archives_path, filename))
62
+ end
63
+
64
+ missing ? false : true
65
+ end
66
+
67
+ def configured?
68
+ configure = File.join(work_path, 'configure')
69
+ makefile = File.join(work_path, 'Makefile')
70
+
71
+ newer?(makefile, configure)
72
+ end
73
+
74
+ def installed?
75
+ makefile = File.join(work_path, 'Makefile')
76
+ target_dir = Dir.glob("#{port_path}/*").find { |d| File.directory?(d) }
77
+
78
+ newer?(target_dir, makefile)
79
+ end
80
+
81
+ def cook
82
+ download unless downloaded?
83
+ extract
84
+ configure unless configured?
85
+ compile
86
+ install unless installed?
87
+
88
+ return true
89
+ end
90
+
91
+ def activate
92
+ vars = {
93
+ 'PATH' => File.join(port_path, 'bin'),
94
+ 'CPATH' => File.join(port_path, 'include'),
95
+ 'LIBRARY_PATH' => File.join(port_path, 'lib')
96
+ }.reject { |env, path| !File.directory?(path) }
97
+
98
+ output "Activating #{@name} #{@version} (from #{port_path})..."
99
+ vars.each do |var, path|
100
+ full_path = File.expand_path(path)
101
+
102
+ # turn into a valid Windows path (if required)
103
+ full_path.gsub!(File::SEPARATOR, File::ALT_SEPARATOR) if File::ALT_SEPARATOR
104
+
105
+ # save current variable value
106
+ old_value = ENV[var] || ''
107
+
108
+ unless old_value.include?(full_path)
109
+ ENV[var] = "#{full_path}#{File::PATH_SEPARATOR}#{old_value}"
110
+ end
111
+ end
112
+ end
113
+
114
+ private
115
+
116
+ def tmp_path
117
+ @tmp_path ||= "tmp/#{@host}/ports/#{@name}/#{@version}"
118
+ end
119
+
120
+ def port_path
121
+ @port_path ||= "#{@target}/#{@host}/#{@name}/#{@version}"
122
+ end
123
+
124
+ def archives_path
125
+ @archives_path ||= "#{@target}/archives"
126
+ end
127
+
128
+ def work_path
129
+ @work_path ||= begin
130
+ Dir.glob("#{tmp_path}/*").find { |d| File.directory?(d) }
131
+ end
132
+ end
133
+
134
+ def log_file(action)
135
+ File.join(tmp_path, "#{action}.log")
136
+ end
137
+
138
+ def extract_file(file, target)
139
+ filename = File.basename(file)
140
+ FileUtils.mkdir_p target
141
+
142
+ message "Extracting #{filename} into #{target}... "
143
+ result = `tar xf #{file} -C #{target}`
144
+ if $?.success?
145
+ output "OK"
146
+ else
147
+ output "ERROR"
148
+ output result
149
+ raise "Failed to complete extract task"
150
+ end
151
+ end
152
+
153
+ def execute(action, command)
154
+ log = log_file(action)
155
+ log_out = File.expand_path(log)
156
+ redirected = command << " >#{log_out} 2>&1"
157
+
158
+ Dir.chdir work_path do
159
+ message "Running '#{action}' for #{@name} #{@version}... "
160
+ system redirected
161
+ if $?.success?
162
+ output "OK"
163
+ return true
164
+ else
165
+ output "ERROR, review '#{log}' to see what happened."
166
+ raise "Failed to complete #{action} task"
167
+ end
168
+ end
169
+ end
170
+
171
+ def newer?(target, checkpoint)
172
+ if (target && File.exist?(target)) && (checkpoint && File.exist?(checkpoint))
173
+ File.mtime(target) > File.mtime(checkpoint)
174
+ else
175
+ false
176
+ end
177
+ end
178
+
179
+ # print out a message with the logger
180
+ def message(text)
181
+ @logger.print text
182
+ @logger.flush
183
+ end
184
+
185
+ # print out a message using the logger but return to a new line
186
+ def output(text = "")
187
+ @logger.puts text
188
+ @logger.flush
189
+ end
190
+
191
+ # Slighly modified from RubyInstaller uri_ext, Rubinius configure
192
+ # and adaptations of Wayne's RailsInstaller
193
+ def download_file(url, full_path, count = 3)
194
+ return if File.exist?(full_path)
195
+ filename = File.basename(full_path)
196
+
197
+ begin
198
+
199
+ if ENV['http_proxy']
200
+ protocol, userinfo, host, port = URI::split(ENV['http_proxy'])
201
+ proxy_user, proxy_pass = userinfo.split(/:/) if userinfo
202
+ http = Net::HTTP::Proxy(host, port, proxy_user, proxy_pass)
203
+ else
204
+ http = Net::HTTP
205
+ end
206
+
207
+ message "Downloading #{filename} "
208
+ http.get_response(URI.parse(url)) do |response|
209
+ case response
210
+ when Net::HTTPNotFound
211
+ output "404 - Not Found"
212
+ return false
213
+
214
+ when Net::HTTPClientError
215
+ output "Error: Client Error: #{response.inspect}"
216
+ return false
217
+
218
+ when Net::HTTPRedirection
219
+ raise "Too many redirections for the original URL, halting." if count <= 0
220
+ url = response["location"]
221
+ return download_file(url, full_path, count - 1)
222
+
223
+ when Net::HTTPOK
224
+ temp_file = Tempfile.new("download-#{filename}")
225
+ temp_file.binmode
226
+
227
+ size = 0
228
+ progress = 0
229
+ total = response.header["Content-Length"].to_i
230
+
231
+ response.read_body do |chunk|
232
+ temp_file << chunk
233
+ size += chunk.size
234
+ new_progress = (size * 100) / total
235
+ unless new_progress == progress
236
+ message "\rDownloading %s (%3d%%) " % [filename, new_progress]
237
+ end
238
+ progress = new_progress
239
+ end
240
+
241
+ output
242
+
243
+ temp_file.close
244
+ File.unlink full_path if File.exists?(full_path)
245
+ FileUtils.mkdir_p File.dirname(full_path)
246
+ FileUtils.mv temp_file.path, full_path, :force => true
247
+ end
248
+ end
249
+
250
+ rescue Exception => e
251
+ File.unlink full_path if File.exists?(full_path)
252
+ output "ERROR: #{e.message}"
253
+ raise "Failed to complete download task"
254
+ end
255
+ end
256
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_portile
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Luis Lavena
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-03-07 00:00:00 -03:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: |-
23
+ Provide a standard and simplified way to build and package
24
+ Ruby extensions (C, Java) using Rake as glue.
25
+ email: luislavena@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README.rdoc
32
+ - History.txt
33
+ - LICENSE.txt
34
+ files:
35
+ - lib/mini_portile.rb
36
+ - Rakefile
37
+ - README.rdoc
38
+ - History.txt
39
+ - LICENSE.txt
40
+ has_rdoc: true
41
+ homepage: http://github.com/luislavena/mini_portile
42
+ licenses:
43
+ - MIT
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --main
47
+ - README.rdoc
48
+ - --title
49
+ - MiniPortile -- Documentation
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 59
58
+ segments:
59
+ - 1
60
+ - 8
61
+ - 6
62
+ version: 1.8.6
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ hash: 17
69
+ segments:
70
+ - 1
71
+ - 3
72
+ - 5
73
+ version: 1.3.5
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.5.2
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Simplistic port-like solution for developers
81
+ test_files: []
82
+