From: Jim Weirich Date: 2005-01-21T21:50:28+09:00 Subject: Re: Rubygems, native ruby extensions (and their dependencies) --Boundary-00=_IuP8BEzZMugCyYk Content-Type: Multipart/Mixed; boundary="Boundary-00=_IuP8BEzZMugCyYk" --Boundary-00=_IuP8BEzZMugCyYk Content-Type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: 7bit Content-Disposition: inline On Friday 21 January 2005 03:00 am, Johan Nilsson wrote: > "Jim Weirich" wrote in message > news:200501190718.30178.jim@weirichhouse.org... > > > On Wednesday 19 January 2005 03:41 am, Johan Nilsson wrote: > >> - How should I handle native ruby extensions (building them at install > >> time > >> is not an option here). Part of the library is implemented natively > >> (Win32 > >> for now). > > > > I released my first binary gem yesterday. What I did: I compiled the gem > > software locally on my own machine. Then used the same gemspec I used > > for the source distribution with the following changes: > > > > (1) removed the extension > > From what? It might be obvious, but please treat me like a total beginner. Just to be clear, I had a gemspec for the project (x10-cm17a) that created a non-binary gem. I started with that gemspec and made the following changes.. (1) The gemspec has an extension field that is filled out if you have extensions to build. Because the extension is already built when you distribute an binary gem, you shouldn't set the extension field. (2) The "arch" directory is a directory that contains the .so files for a particular architecture. The directory is usually named something like i386-mswin32 or i686-linux. You can get the name of the arch directory from the ruby config, e.g. require 'rbconfig' ARCH = Config::CONFIG['arch'] The 'lib' field of the gemspec is a list if directories to be added to the load path when the gem is loaded. You need to add the arch directory to that list. The resulting line will look like this: s.require_paths << 'lib' << "lib/#{ARCH}" (3) The list of files to be included with the gem need to be updated to include the .so file. I added the following line to append the .so file to the list of files: s.files += ["lib/#{ARCH}/cm17a_api.so"] (4) Forget to mention this earlier, but I changed the gemspec platform from the default of 'RUBY' to ARCH (see the definition of ARCH in step 2 above. s.platform = ARCH I've included the modified Rakefile that I used to build the gemspec as an attachment. That will give the complete details. > > There is a requirements field in the gemspec that where you can list the > > dependencies. This is for comment purposes only, we don't do > > autodetection > > of non-gem dependencies. > > Could you expand on that a bit - do you mean that: > > - The user would him/herself manually have to install such dependencies > - It's not possible to submit them as part of the gem? Essentially, yes. -- -- Jim Weirich jim@weirichhouse.org http://onestepback.org ----------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas) --Boundary-00=_IuP8BEzZMugCyYk Content-Type: text/plain; charset="iso-8859-1"; name="Rakefile" Content-Transfer-Encoding: 7bit Content-Disposition: attachment; filename="Rakefile" # -*- ruby -*- require 'rake/clean' require 'rake/testtask' require 'rake/rdoctask' require 'rbconfig' begin require 'rubygems' require 'rake/gempackagetask' rescue Exception nil end ARCH = Config::CONFIG['arch'] SO_FILE = "lib/#{ARCH}/cm17a_api.so" MAKE = (ARCH =~ /win32/) ? 'nmake' : 'make' CLOBBER.include( SO_FILE, 'ext/cm17a_api/*.o', 'ext/cm17a_api/*.obj', 'ext/cm17a_api/*.def', 'ext/cm17a_api/*.exp', 'ext/cm17a_api/*.lib', 'ext/cm17a_api/*.pdb', 'ext/cm17a_api/*.so', 'ext/cm17a_api/Makefile', 'ext/cm17a_api/MANIFEST', 'lib/*-*', '**/*.log' ) desc "Default task (run tests)" task :default => :test desc "Run the unit tests" Rake::TestTask.new do |t| t.test_files = FileList['test/test_*.rb'] t.libs << "lib/#{ARCH}" end task :test => [SO_FILE] task :xtest => [SO_FILE] do test_files = FileList['test/test_*.rb'] ruby "-Ilib", "-Ilib/#{ARCH}", '-e0', *test_files.collect { |fn| "-r#{fn}" } end task :compile => [SO_FILE] directory File.dirname(SO_FILE) file SO_FILE => ["ext/cm17a_api/cm17a_api.so", File.dirname(SO_FILE)] do |t| cp t.prerequisites.first, SO_FILE end file "ext/cm17a_api/Makefile" => ["ext/cm17a_api/extconf.rb"] do Dir.chdir("ext/cm17a_api") do ruby "extconf.rb" end end CM17A_FILES = FileList[ 'ext/cm17a_api/*.c', 'ext/cm17a_api/*.h', 'ext/cm17a_api/extconf.rb', 'ext/cm17a_api/Makefile', 'ext/cm17a_api/MANIFEST', ] file "ext/cm17a_api/cm17a_api.so" => CM17A_FILES do Dir.chdir("ext/cm17a_api") do sh MAKE end end # RDoc Documentation ------------------------------------------------- # Choose template, prefer the jamis template if it is available def choose_rdoc_template libdir = Config::CONFIG['rubylibdir'] templates = Dir[File.join(libdir, 'rdoc/**/jamis.rb')] templates.empty? ? 'html': 'jamis' end rd = Rake::RDocTask.new("rdoc") { |rdoc| rdoc.rdoc_dir = 'html' rdoc.template = choose_rdoc_template rdoc.title = "Ruby X10 Software" rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README' rdoc.rdoc_files.include( 'lib/**/*.rb', 'doc/*.rdoc', 'ext/**/cm17a_api.c', 'README', 'MIT-LICENSE') rdoc.rdoc_files.exclude('lib/**/other.rb', 'lib/**/bottlerocket.rb') } # Packaging Tasks ---------------------------------------------------- file "ext/cm17a_api/MANIFEST" do |t| open(t.name, "w") do |f| f.puts Dir["ext/cm17a_api/*"].select { |fn| fn =~ /\.(h|c|rb)$/ }.collect { |fn| File.basename(fn) } end end PKG_VERSION = '1.0.1' PKG_FILES = FileList[ 'Rakefile', 'setup.rb', 'test/*.rb', 'lib/**/*.rb', 'ext/cm17a_api/MANIFEST', 'ext/cm17a_api/*.h', 'ext/cm17a_api/*.c', 'ext/cm17a_api/*.rb', ] if ! defined?(Gem) puts "Package Target requires RubyGEMs" else spec = Gem::Specification.new do |s| #### Basic information. s.name = 'x10-cm17a' s.version = PKG_VERSION s.summary = "Ruby based X10 CM17A Firecracker Controller" s.description = <<-EOF EOF s.files = PKG_FILES.to_a #### C code extensions. s.extensions << "ext/cm17a_api/extconf.rb" #### Load-time details: library and application (you will need one or both). s.require_paths << 'lib' << "lib/#{ARCH}" #### Documentation and testing. s.has_rdoc = true # s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a # s.rdoc_options << # '--title' << 'Rake -- Ruby Make' << # '--main' << 'README' << # '--line-numbers' #### Author and project details. s.author = "Jim Weirich" s.email = "jim@weirichhouse.org" s.homepage = "http://x10-cm17a.rubyforge.org" end binary_spec = spec.dup binary_spec.platform = ARCH binary_spec.files += ["lib/#{ARCH}/cm17a_api.so"] binary_spec.extensions = [] puts binary_spec.files.class Rake::GemPackageTask.new(binary_spec) do |pkg| pkg.need_zip = false pkg.need_tar = false end # Rake::GemPackageTask.new(binary_spec) do end end # Publishing Tasks --------------------------------------------------- task :publish_on_osb => ["pkg/cm17a-#{PKG_VERSION}.tgz", "pkg/cm17a-#{PKG_VERSION}.gem"] do |t| sh %{ssh umlcoop mkdir -p "htdocs/software/cm17a"} sh %{scp -rq #{t.prerequisites.join(' ')} umlcoop:htdocs/software/cm17a} end # Optional publish task for Rake require 'rake/contrib/sshpublisher' require 'rake/contrib/rubyforgepublisher' publisher = Rake::CompositePublisher.new publisher.add Rake::RubyForgePublisher.new('x10-cm17a', 'jimweirich') publisher.add Rake::SshFilePublisher.new( 'umlcoop', 'htdocs/software/x10-cm17a', '.', 'x10-cm17a.blurb') desc "Publish the Documentation to RubyForge." task :publish => [:rdoc] do publisher.upload end --Boundary-00=_IuP8BEzZMugCyYk-- --Boundary-00=_IuP8BEzZMugCyYk--