mini_portile 0.1.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.
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
+