gem-compiler 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.
- data/README.md +176 -0
- data/lib/rubygems/commands/compile_command.rb +31 -0
- data/lib/rubygems/compiler.rb +91 -0
- data/lib/rubygems_plugin.rb +2 -0
- data/rakefile.rb +32 -0
- metadata +56 -0
data/README.md
ADDED
@@ -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
|
data/rakefile.rb
ADDED
@@ -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: []
|