gem-compiler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,176 @@
1
+ # gem-compiler
2
+
3
+ A RubyGems plugin that generates binary pre-compiled gems.
4
+
5
+ - [home](https://github.com/luislavena/gem-compiler)
6
+ - [bugs](https://github.com/luislavena/gem-compiler/issues)
7
+
8
+ ## Description
9
+
10
+ `gem-compiler` is a RubyGems plugin that helps generates binary pre-compiled
11
+ gems from already existing ones without altering the original gem source
12
+ code. It is aimed at the pre-compilation of Ruby C extensions.
13
+
14
+ It uses an *outside-in* approach and leverages on existing RubyGems code to
15
+ do it.
16
+
17
+ The result of the compilation is a gem built for your current platform,
18
+ skipping the need of a compiler toolchain when installing it.
19
+
20
+ ## Installation
21
+
22
+ To install gem-compiler you need to use RubyGems:
23
+
24
+ $ gem install gem-compiler
25
+
26
+ Which will fetch and install the plugin. After that the `compile` command
27
+ will be available through `gem`.
28
+
29
+ ## Features
30
+
31
+ gem-compiler is a one trick pony. It adds a single command `compile` to
32
+ RubyGems.
33
+
34
+ Using that command, you can generate a binary from an existing gem.
35
+
36
+ ## Usage
37
+
38
+ ### Fetching a gem
39
+
40
+ If you don't have the gem locally, you can use `fetch` to retrieve it first:
41
+
42
+ $ gem fetch yajl-ruby --platform=ruby
43
+ Fetching: yajl-ruby-1.1.0.gem (100%)
44
+ Downloaded yajl-ruby-1.1.0
45
+
46
+ Please note that I was specific about which gem to fetch. This will avoid
47
+ RubyGems attempt to download any existing pre-compiled gem for my current
48
+ platform.
49
+
50
+ ### Compiling a gem
51
+
52
+ You need to tell RubyGems the filename of the gem you want to compile:
53
+
54
+ $ gem compile yajl-ruby-1.1.0.gem
55
+
56
+ The above command will unpack, compile any existing extensions found and
57
+ generate a pre-compiled binary:
58
+
59
+ Unpacking gem: 'yajl-ruby-1.1.0' in temporary directory...
60
+ Building native extensions. This could take a while...
61
+ Successfully built RubyGem
62
+ Name: yajl-ruby
63
+ Version: 1.1.0
64
+ File: yajl-ruby-1.1.0-x86-mingw32.gem
65
+
66
+ You can now simply install the pre-compiled gem and it will not trigger any
67
+ build process:
68
+
69
+ C:\> gem install --local yajl-ruby-1.1.0-x86-mingw32.gem
70
+ Successfully installed yajl-ruby-1.1.0-x86-mingw32
71
+ 1 gem installed
72
+
73
+ ### Compiling from Rake
74
+
75
+ Most of the times, as gem developer, you would like to genrate both kind of
76
+ gems at once. For that purpose, you can add a task for Rake similar to the
77
+ one below:
78
+
79
+ ```ruby
80
+ desc "Generate a pre-compiled native gem"
81
+ task "gem:native" => ["gem"] do
82
+ sh "gem compile #{gem_file}"
83
+ end
84
+ ```
85
+
86
+ Of couse, that assusems you have a task `gem` that generates the gem needed
87
+ by this task.
88
+
89
+ ## Requirements
90
+
91
+ ### Ruby and RubyGems
92
+
93
+ It's assumed you have Ruby and RubyGems installed. gem-compiler requires
94
+ RubyGems 1.8.x to properly work.
95
+
96
+ If you don't have RubyGems 1.8.x, you can upgrade by running:
97
+
98
+ $ gem update --system
99
+
100
+ ### A compiler
101
+
102
+ In order to compile a gem, you need a compiler toolchain installed. Depending
103
+ on your Operating System you will have one already installed or will require
104
+ additional steps to do it. Check your OS documentation about getting the
105
+ right one.
106
+
107
+ ### If you're using Windows
108
+
109
+ For those using RubyInstaller-based builds, you will need to download the
110
+ DevKit from our [downloads page](http://rubyinstaller.org/downloads) and
111
+ follow the [installation instructions](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit).
112
+
113
+ To be sure your installation of Ruby is based on RubyInstaller, execute at
114
+ the command prompt:
115
+
116
+ C:\> ruby --version
117
+
118
+ And from the output:
119
+
120
+ tcs-ruby 1.9.3p196 (2012-04-21, TCS patched 2012-04-21) [i386-mingw32]
121
+
122
+ If you see `mingw32`, that means you're using a RubyInstaller build
123
+ (MinGW based).
124
+
125
+ ## Differences with rake-compiler
126
+
127
+ [rake-compiler](https://github.com/luislavena/rake-compiler) has provided to
128
+ Ruby library authors a *tool* for compiling extensions and generating binary
129
+ gems of their libraries.
130
+
131
+ You can consider rake-compiler's approach be an *inside-out* process. To do
132
+ its magic, it requires library authors to modify their source code, adjust
133
+ some structure and learn a series of commands.
134
+
135
+ While the ideal scenario is using a tool like rake-compiler that endorses
136
+ *convention over configuration*, is not humanly possible change all the
137
+ projects by snapping your fingers :wink:
138
+
139
+ ## What is missing
140
+
141
+ The following are the list of features I would like to implement at some
142
+ point:
143
+
144
+ * Cross compile gems to any platform that Ruby can run
145
+ (e.g. from Linux/OSX to Windows, x86 to x64, x86 Linux to ARM Linux, etc)
146
+
147
+ * Create multiple gems from the same build
148
+ (e.g. target both x86-mswin32-60 and x86-mingw32)
149
+
150
+ * Ability to build fat-binaries targeting both Ruby 1.8 and 1.9.x,
151
+ placing automatic stubs to handle extension loading.
152
+
153
+ ## License
154
+
155
+ (The MIT License)
156
+
157
+ Copyright (c) Luis Lavena
158
+
159
+ Permission is hereby granted, free of charge, to any person obtaining
160
+ a copy of this software and associated documentation files (the
161
+ 'Software'), to deal in the Software without restriction, including
162
+ without limitation the rights to use, copy, modify, merge, publish,
163
+ distribute, sublicense, and/or sell copies of the Software, and to
164
+ permit persons to whom the Software is furnished to do so, subject to
165
+ the following conditions:
166
+
167
+ The above copyright notice and this permission notice shall be
168
+ included in all copies or substantial portions of the Software.
169
+
170
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
171
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
172
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
173
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
174
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
175
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
176
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ require "rubygems/command"
2
+
3
+ class Gem::Commands::CompileCommand < Gem::Command
4
+ def initialize
5
+ super "compile", "Create binary pre-compiled gem",
6
+ :output => Dir.pwd
7
+ end
8
+
9
+ def arguments
10
+ "GEMFILE path to the gem file to compile"
11
+ end
12
+
13
+ def usage
14
+ "#{program_name} GEMFILE"
15
+ end
16
+
17
+ def execute
18
+ gemfile = options[:args].shift
19
+
20
+ # no gem, no binary
21
+ unless gemfile
22
+ raise Gem::CommandLineError,
23
+ "Please specify a gem file on the command line (e.g. #{program_name} foo-0.1.0.gem"
24
+ end
25
+
26
+ require "rubygems/compiler"
27
+
28
+ compiler = Gem::Compiler.new(gemfile, options[:output])
29
+ compiler.compile
30
+ end
31
+ end
@@ -0,0 +1,91 @@
1
+ require "rbconfig"
2
+ require "tmpdir"
3
+ require "rubygems/installer"
4
+ require "rubygems/builder"
5
+ require "fileutils"
6
+
7
+ class Gem::Compiler
8
+ include Gem::UserInteraction
9
+
10
+ # raise when there is a error
11
+ class CompilerError < Gem::InstallError; end
12
+
13
+ def initialize(gemfile, output_dir)
14
+ @gemfile = gemfile
15
+ @output_dir = output_dir
16
+ end
17
+
18
+ def compile
19
+ installer = Gem::Installer.new(@gemfile, :unpack => true)
20
+
21
+ # Hmm, gem already compiled?
22
+ if installer.spec.platform != Gem::Platform::RUBY
23
+ raise CompilerError,
24
+ "The gem file seems to be compiled already."
25
+ end
26
+
27
+ # Hmm, no extensions?
28
+ if installer.spec.extensions.empty?
29
+ raise CompilerError,
30
+ "There are no extensions to build on this gem file."
31
+ end
32
+
33
+ tmpdir = Dir.mktmpdir
34
+ basename = File.basename(@gemfile, '.gem')
35
+ target_dir = File.join(tmpdir, basename)
36
+
37
+ # unpack gem sources into target_dir
38
+ # We need the basename to keep the unpack happy
39
+ say "Unpacking gem: '#{basename}' in temporary directory..." if Gem.configuration.verbose
40
+ installer.unpack(target_dir)
41
+
42
+ # build extensions
43
+ installer.build_extensions
44
+
45
+ # determine build artifacts from require_paths
46
+ dlext = RbConfig::CONFIG["DLEXT"]
47
+ lib_dirs = installer.spec.require_paths.join(",")
48
+
49
+ artifacts = Dir.glob("#{target_dir}/{#{lib_dirs}}/**/*.#{dlext}")
50
+
51
+ # build a new gemspec from the original one
52
+ gemspec = installer.spec.dup
53
+
54
+ # add discovered artifacts
55
+ artifacts.each do |path|
56
+ # path needs to be relative to target_dir
57
+ file = path.gsub("#{target_dir}/", "")
58
+
59
+ say "Adding '#{file}' to gemspec" if Gem.configuration.really_verbose
60
+ gemspec.files.push file
61
+ end
62
+
63
+ # clear out extensions from gemspec
64
+ gemspec.extensions.clear
65
+
66
+ # adjust platform
67
+ gemspec.platform = Gem::Platform::CURRENT
68
+
69
+ # build new gem
70
+ output_gem = nil
71
+
72
+ Dir.chdir target_dir do
73
+ builder = Gem::Builder.new(gemspec)
74
+ output_gem = builder.build
75
+ end
76
+
77
+ unless output_gem
78
+ raise CompilerError,
79
+ "There was a problem building the gem."
80
+ end
81
+
82
+ # move the built gem to the original output directory
83
+ FileUtils.mv File.join(target_dir, output_gem), @output_dir
84
+
85
+ # cleanup
86
+ FileUtils.rm_rf tmpdir
87
+
88
+ # return the path of the gem
89
+ output_gem
90
+ end
91
+ end
@@ -0,0 +1,2 @@
1
+ require "rubygems/command_manager"
2
+ Gem::CommandManager.instance.register_command :compile
@@ -0,0 +1,32 @@
1
+ require "rubygems/package_task"
2
+
3
+ gemspec = Gem::Specification.new do |s|
4
+ # basic
5
+ s.name = "gem-compiler"
6
+ s.version = "0.1.0"
7
+ s.platform = Gem::Platform::RUBY
8
+
9
+ # description
10
+ s.summary = "A RubyGems plugin that generates binary pre-compiled gems."
11
+ s.description = <<-EOF
12
+ A RubyGems plugin that helps generates binary pre-compiled gems from
13
+ already existing ones without altering the original gem source code.
14
+ It is aimed at the pre-compilation of Ruby C extensions.
15
+ EOF
16
+
17
+ # project info
18
+ s.homepage = "https://github.com/luislavena/gem-compiler"
19
+ s.licenses = ["MIT"]
20
+ s.author = "Luis Lavena"
21
+ s.email = "luislavena@gmail.com"
22
+
23
+ # requirements
24
+ s.required_ruby_version = ">= 1.9.3"
25
+ s.required_rubygems_version = ">= 1.8.24"
26
+
27
+ # boring part
28
+ s.files = FileList["README.md", "rakefile.rb", "lib/**/*.rb"]
29
+ end
30
+
31
+ Gem::PackageTask.new(gemspec) do |pkg|
32
+ end
metadata ADDED
@@ -0,0 +1,56 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gem-compiler
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Luis Lavena
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-06 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! 'A RubyGems plugin that helps generates binary pre-compiled gems from
15
+
16
+ already existing ones without altering the original gem source code.
17
+
18
+ It is aimed at the pre-compilation of Ruby C extensions.
19
+
20
+ '
21
+ email: luislavena@gmail.com
22
+ executables: []
23
+ extensions: []
24
+ extra_rdoc_files: []
25
+ files:
26
+ - README.md
27
+ - rakefile.rb
28
+ - lib/rubygems/commands/compile_command.rb
29
+ - lib/rubygems/compiler.rb
30
+ - lib/rubygems_plugin.rb
31
+ homepage: https://github.com/luislavena/gem-compiler
32
+ licenses:
33
+ - MIT
34
+ post_install_message:
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 1.9.3
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ! '>='
48
+ - !ruby/object:Gem::Version
49
+ version: 1.8.24
50
+ requirements: []
51
+ rubyforge_project:
52
+ rubygems_version: 1.8.24
53
+ signing_key:
54
+ specification_version: 3
55
+ summary: A RubyGems plugin that generates binary pre-compiled gems.
56
+ test_files: []