luislavena-rake-compiler 0.1.1

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/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 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.
data/README.rdoc ADDED
@@ -0,0 +1,191 @@
1
+ = rake-compiler
2
+
3
+ rake-compiler aims to help Gem developers while dealing with Ruby C
4
+ extensions, simplifiying the code and reducing the duplication.
5
+
6
+ It follows *convention over configuration* and set an standarized
7
+ structure to build and package C extensions in your gems.
8
+
9
+ This is the result of expriences dealing with several Gems that required
10
+ native extensions across platforms and different user configurations
11
+ where details like portability and clarity of code were lacking.
12
+
13
+
14
+ == An Overview
15
+
16
+ Let's summarize what rake-compiler provides:
17
+
18
+ * No custom rake tasks required. Less code duplication and errors.
19
+
20
+ * Painlessly build extensions on different platforms (Linux, OSX and Windows).
21
+
22
+ * Allow multiple extensions be compiled inside the same gem.
23
+
24
+ * Mimics RubyGems installation process, so helps as test environment.
25
+
26
+
27
+ == I'm sold! show me how to use it! (Installation)
28
+
29
+ Usage of rake-compiler is pretty much straight forward.
30
+
31
+ First, you need to install the gem:
32
+
33
+ $ gem install rake-compiler
34
+
35
+ Since this package is in constant evolution, you could try installing it
36
+ from GitHub:
37
+
38
+ $ gem install luislavena-rake-compiler --source http://gems.github.com
39
+
40
+ The development gem is usually in pretty good shape actually.
41
+
42
+
43
+ == Now what? (Usage)
44
+
45
+ Now that you have the gem installed, let's give your project some structure.
46
+
47
+
48
+ === Structure
49
+
50
+ Let's say we want to compile an extension called 'hello_world', so we should
51
+ organize the code and folders that will help rake-compiler do it's job:
52
+
53
+ |-- ext
54
+ | `-- hello_world
55
+ | |-- extconf.rb
56
+ | `-- hello_world.c
57
+ |-- lib
58
+ `-- Rakefile
59
+
60
+ TIP: Having a consistent folder structure will help developers and newcomers
61
+ to find code and also contribute back to your project more easily.
62
+
63
+
64
+ === Adding the code
65
+
66
+ So now it's time to introduce the code to compile our extension:
67
+
68
+ # File: Rakefile
69
+
70
+ require 'rake/extensiontask'
71
+
72
+ Rake::ExtensionTask.new('hello_world')
73
+
74
+ Ok, that's it. No other line of code.
75
+
76
+
77
+ === Compile process
78
+
79
+ Those *two* lines of code automatically added the needed rake tasks to build
80
+ the hello_world extension:
81
+
82
+ $ rake -T
83
+ (in /home/user/my_extesion)
84
+ rake compile # Compile the extension(s)
85
+ rake compile:hello_world # Compile just the hello_world extension
86
+
87
+ Simply calling <tt>compile</tt>:
88
+
89
+ $ rake compile
90
+
91
+ Will do all the compile process for us, putting the result extension inside
92
+ <tt>lib</tt> directory.
93
+
94
+ NOTE: Please be aware that building C extensions requires the proper
95
+ development environment for your Platform, which includes libraries, headers
96
+ and build tools. Check your distro / vendor documentation on how to install it.
97
+
98
+ === Generate native gems
99
+
100
+ A common usage scenario of rake-compiler is generate native gems that bundles
101
+ your extensions.
102
+
103
+ This got over-simplified with <tt>Rake::ExtensionTask</tt>:
104
+
105
+ # somewhere in your Rakefile, define your gem spec
106
+ spec = Gem::Specification.new do |s|
107
+ s.name = "my_gem"
108
+ s.platform = Gem::Platform::RUBY
109
+ s.extensions = FileList["ext/**/extconf.rb"]
110
+ end
111
+
112
+ # add your default gem packing task
113
+ Rake::GemPackageTask.new(spec) do |pkg|
114
+ end
115
+
116
+ # feed your ExtensionTask with your spec
117
+ Rake::ExtensionTask.new('hello_world', spec)
118
+
119
+ Now, as usual, you can build your pure-ruby gem (standard output):
120
+
121
+ $ rake gem
122
+ (in /projects/oss/my_gem.git)
123
+ mkdir -p pkg
124
+ Successfully built RubyGem
125
+ Name: my_gem
126
+ Version: 0.1.0
127
+ File: my_gem-0.1.0.gem
128
+ mv my_gem-0.1.0.gem pkg/my_gem-0.1.0.gem
129
+
130
+ Plus, you have the functionality to build native versions of the gem:
131
+
132
+ # rake native gem
133
+ (... compilation output ...)
134
+ mkdir -p pkg
135
+ Successfully built RubyGem
136
+ Name: my_gem
137
+ Version: 0.1.0
138
+ File: my_gem-0.1.0.gem
139
+ mv my_gem-0.1.0.gem pkg/my_gem-0.1.0.gem
140
+ Successfully built RubyGem
141
+ Name: my_gem
142
+ Version: 0.1.0
143
+ File: my_gem-0.1.0-x86-mingw32.gem
144
+ mv my_gem-0.1.0-x86-mingw32.gem pkg/my_gem-0.1.0-x86-mingw32.gem
145
+
146
+ You get two gems for the price of one.
147
+
148
+ === What about breaking the standards? (Customization)
149
+
150
+ In case you want to bend the convention established, rake-compiler let you
151
+ personalize several settings for <tt>Rake::ExtensionTask</tt>:
152
+
153
+ Rake::ExtensionTask.new do |ext|
154
+ ext.name = 'hello_world' # indicate the name of the extension.
155
+ ext.ext_dir = 'ext/weird_world' # search for 'hello_world' inside it.
156
+ ext.lib_dir = 'lib/my_lib' # put binaries into this folder.
157
+ ext.config_script = 'custom_extconf.rb' # use instead of 'extconf.rb' default
158
+ ext.tmp_dir = 'tmp' # temporary folder used during compilation.
159
+ ext.source_pattern = "*.{c,cpp}" # monitor file changes to allow simple rebuild.
160
+ ext.additional_options << '--with-foo' # supply addition configure options to config script-
161
+ ext.gem_spec = spec # optional indicate which gem specification
162
+ # will be used to based on.
163
+ end
164
+
165
+
166
+ == Future
167
+
168
+ rake-compiler is a work in progress and we will appreciate feedback during the
169
+ development of it! (and contributions too!)
170
+
171
+ You can find more information about rake-compiler:
172
+
173
+ Blog: http://blog.mmediasys.com
174
+ GitHub: http://github.com/luislavena/rake-compiler
175
+
176
+
177
+ === Some of the features already underworks
178
+
179
+ * <tt>Rake::JavaJarTask</tt> to generate <tt>jar</tt> packages and gems for JRuby.
180
+
181
+ $ rake java gem
182
+
183
+
184
+ == Disclaimer
185
+
186
+ If you have any trouble, don't hesitate to contact the author. As always,
187
+ I'm not going to say "Use at your own risk" because I don't want this library
188
+ to be risky.
189
+
190
+ If you trip on something, I'll share the liability by repairing things
191
+ as quickly as I can. Your responsibility is to report the inadequacies.
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ #--
2
+ # Copyright (c) 2008 Luis Lavena
3
+ #
4
+ # This source code is released under the MIT License.
5
+ # See LICENSE file for details
6
+ #++
7
+
8
+ #
9
+ # NOTE: Keep this file clean.
10
+ # Add your customizations inside tasks directory.
11
+ # Thank You.
12
+ #
13
+
14
+ begin
15
+ require 'rake'
16
+ rescue LoadError
17
+ require 'rubygems'
18
+ gem 'rake', '~> 0.8.3.1'
19
+ require 'rake'
20
+ end
21
+
22
+ # load rakefile extensions (tasks)
23
+ Dir['tasks/*.rake'].each { |f| import f }
data/bin/rake-compiler ADDED
@@ -0,0 +1,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # Copyright (c) 2008 Luis Lavena
5
+ #
6
+ # This source code is released under the MIT License.
7
+ # See LICENSE file for details
8
+ #++
9
+
10
+ begin
11
+ require 'rake'
12
+ rescue LoadError
13
+ require 'rubygems'
14
+ require 'rake'
15
+ end
16
+
17
+ # Initialize 'rake-compiler' application
18
+ Rake.application.init('rake-compiler')
19
+
20
+ # Load the already cooked tasks ;-)
21
+ load File.join(File.dirname(__FILE__), %w{.. tasks bin cross-ruby.rake})
22
+
23
+ # delegate control to Rake
24
+ Rake.application.top_level
data/cucumber.yml ADDED
@@ -0,0 +1 @@
1
+ autotest: --format progress features
@@ -0,0 +1,71 @@
1
+ Feature: Compile C code into Ruby extensions.
2
+
3
+ In order to automate compilation process.
4
+ As a Gem developer.
5
+ I want rake tasks compile source code for me.
6
+
7
+ Scenario: compile single extension
8
+ Given a safe project directory
9
+ And a extension named 'extension_one'
10
+ And 'tmp' folder is deleted
11
+ When rake task 'compile' is invoked
12
+ Then rake task 'compile' succeeded
13
+ And binary extension 'extension_one' do exist in 'lib'
14
+ And 'tmp' folder is created
15
+
16
+ Scenario: not recompile unmodified extension
17
+ Given a safe project directory
18
+ And a extension named 'extension_one'
19
+ And I've already successfully executed rake task 'compile'
20
+ And not changed any file since
21
+ When rake task 'compile' is invoked
22
+ Then rake task 'compile' succeeded
23
+ And output of rake task 'compile' do not contain /extension_one/
24
+
25
+ Scenario: recompile extension when source is modified
26
+ Given a safe project directory
27
+ And a extension named 'extension_one'
28
+ And I've already successfully executed rake task 'compile'
29
+ When touching 'source.c' file of extension 'extension_one'
30
+ And rake task 'compile' is invoked
31
+ Then rake task 'compile' succeeded
32
+ And output of rake task 'compile' contains /extension_one/
33
+
34
+ Scenario: compile multiple extensions
35
+ Given a safe project directory
36
+ And a extension named 'extension_one'
37
+ And a extension named 'extension_two'
38
+ And 'tmp' folder is deleted
39
+ When rake task 'compile' is invoked
40
+ Then rake task 'compile' succeeded
41
+ And binary extension 'extension_one' do exist in 'lib'
42
+ And binary extension 'extension_two' do exist in 'lib'
43
+ And 'tmp' folder is created
44
+
45
+ Scenario: compile one extension instead of all present
46
+ Given a safe project directory
47
+ And a extension named 'extension_one'
48
+ And a extension named 'extension_two'
49
+ When rake task 'compile:extension_one' is invoked
50
+ Then rake task 'compile:extension_one' succeeded
51
+ And output of rake task 'compile:extension_one' do not contain /extension_two/
52
+ And binary extension 'extension_one' do exist in 'lib'
53
+ And binary extension 'extension_two' do not exist in 'lib'
54
+
55
+ Scenario: removing temporary files
56
+ Given a safe project directory
57
+ And a extension named 'extension_one'
58
+ And I've already successfully executed rake task 'compile'
59
+ When rake task 'clean' is invoked
60
+ Then rake task 'clean' succeeded
61
+ And binary extension 'extension_one' do exist in 'lib'
62
+ And no left over from 'extension_one' remains in 'tmp'
63
+
64
+ Scenario: clobbering binary and temporary files
65
+ Given a safe project directory
66
+ And a extension named 'extension_one'
67
+ And I've already successfully executed rake task 'compile'
68
+ When rake task 'clobber' is invoked
69
+ Then rake task 'clobber' succeeded
70
+ And binary extension 'extension_one' do not exist in 'lib'
71
+ And 'tmp' folder do not exist
@@ -0,0 +1,40 @@
1
+ Feature: Distribute native extension with gems
2
+
3
+ In order to avoid compiler toolchain requirement during installation
4
+ As a Gem developer.
5
+ I want rake tasks generate platform specific gems for me
6
+
7
+ Scenario: generate pure ruby gem
8
+ Given a safe project directory
9
+ And a gem named 'my_project'
10
+ And a extension named 'extension_one'
11
+ And I've already successfully executed rake task 'compile'
12
+ And 'pkg' folder is deleted
13
+ When rake task 'gem' is invoked
14
+ Then rake task 'gem' succeeded
15
+ And 'pkg' folder is created
16
+ And ruby gem for 'my_project' version '0.1.0' do exist in 'pkg'
17
+
18
+ Scenario: generate native gem
19
+ Given a safe project directory
20
+ And a gem named 'my_project'
21
+ And a extension named 'extension_one'
22
+ And I've already successfully executed rake task 'compile'
23
+ And 'pkg' folder is deleted
24
+ When rake task 'native gem' is invoked
25
+ Then rake task 'native gem' succeeded
26
+ And 'pkg' folder is created
27
+ And ruby gem for 'my_project' version '0.1.0' do exist in 'pkg'
28
+ And binary gem for 'my_project' version '0.1.0' do exist in 'pkg'
29
+
30
+ Scenario: generate forced native gem
31
+ Given a safe project directory
32
+ And a gem named 'my_project'
33
+ And a extension 'extension_one' with forced platform 'universal-unknown'
34
+ And I've already successfully executed rake task 'compile'
35
+ And 'pkg' folder is deleted
36
+ When rake task 'native gem' is invoked
37
+ Then rake task 'native gem' succeeded
38
+ And 'pkg' folder is created
39
+ And ruby gem for 'my_project' version '0.1.0' do exist in 'pkg'
40
+ And a gem for 'my_project' version '0.1.0' platform 'universal-unknown' do exist in 'pkg'
@@ -0,0 +1,27 @@
1
+ Given /^a extension named '(.*)'$/ do |extension_name|
2
+ generate_extension_task_for extension_name
3
+ generate_source_code_for extension_name
4
+ end
5
+
6
+ Given /^a extension '(.*)' with forced platform '(.*)'$/ do |extension_name, forced_platform|
7
+ generate_extension_task_for extension_name, forced_platform
8
+ generate_source_code_for extension_name
9
+ end
10
+
11
+ Given /^not changed any file since$/ do
12
+ # don't do anything, that's the purpose of this step!
13
+ end
14
+
15
+ When /^touching '(.*)' file of extension '(.*)'$/ do |file, extension_name|
16
+ Kernel.sleep 1
17
+ FileUtils.touch "ext/#{extension_name}/#{file}"
18
+ end
19
+
20
+ Then /^binary extension '(.*)' (do|do not) exist in '(.*)'$/ do |extension_name, condition, folder|
21
+ ext_for_platform = File.join(folder, "#{extension_name}.#{RbConfig::CONFIG['DLEXT']}")
22
+ if condition == 'do'
23
+ File.exist?(ext_for_platform).should be_true
24
+ else
25
+ File.exist?(ext_for_platform).should be_false
26
+ end
27
+ end
@@ -0,0 +1,30 @@
1
+ Given %r{^I've already successfully executed rake task '(.*)'$} do |task_name|
2
+ emptyness = `rake #{task_name} 2>&1`
3
+ unless $?.success?
4
+ warn emptyness
5
+ raise "rake failed with #{$?.exitstatus}"
6
+ end
7
+ end
8
+
9
+ When /^rake task '(.*)' is invoked$/ do |task_name|
10
+ @output ||= {}
11
+ @result ||= {}
12
+ @output[task_name] = `rake #{task_name} 2>&1`
13
+ @result[task_name] = $?.success?
14
+ end
15
+
16
+ Then /^rake task '(.*)' succeeded$/ do |task_name|
17
+ if @result.nil? || !@result.include?(task_name) then
18
+ raise "The task #{task_name} should be invoked first."
19
+ else
20
+ @result[task_name].should be_true
21
+ end
22
+ end
23
+
24
+ Then /^output of rake task '(.*)' (contains|do not contain) \/(.*)\/$/ do |task_name, condition, regex|
25
+ if condition == 'contains' then
26
+ @output[task_name].should match(%r(#{regex}))
27
+ else
28
+ @output[task_name].should_not match(%r(#{regex}))
29
+ end
30
+ end
@@ -0,0 +1,32 @@
1
+ Given /^a safe project directory$/ do
2
+ # step back to ROOT
3
+ Dir.chdir ROOT_PATH
4
+ tmp_name = "project.#{Process.pid}"
5
+ @safe_dir = File.join(ROOT_PATH, 'tmp', tmp_name)
6
+ FileUtils.rm_rf @safe_dir
7
+ FileUtils.mkdir_p @safe_dir
8
+ Dir.chdir @safe_dir
9
+
10
+ generate_scaffold_structure
11
+ end
12
+
13
+ Given /^'(.*)' folder (exist|is deleted)$/ do |folder, condition|
14
+ case condition
15
+ when 'exist'
16
+ raise "Folder #{folder} do not exist" unless File.exist?(folder) && File.directory?(folder)
17
+ when 'is deleted'
18
+ FileUtils.rm_rf folder
19
+ end
20
+ end
21
+
22
+ Then /^'(.*)' folder is created$/ do |folder|
23
+ File.directory?(folder).should be_true
24
+ end
25
+
26
+ Then /^'(.*)' folder do not exist$/ do |folder|
27
+ File.directory?(folder).should_not be_true
28
+ end
29
+
30
+ Then /^no left over from '(.*)' remains in '(.*)'$/ do |name, folder|
31
+ Dir.glob("#{folder}/**/#{name}").should be_empty
32
+ end