How to compile Ruby Extension - ruby

I have a simple extension in ext/Q/flagvalue.c
My ext/Q/extconfig.rb looks like this:
require 'mkmf'
create_makefile('Q/flagvalue')
The task in Rakefile is set-up just so:
Rake::ExtensionTask.new("Q") do |extension|
extension.lib_dir = 'lib/Q'
end
when I rake build, i get the following output:
mkdir -p tmp/x86_64-linux/Q/1.9.3
cd tmp/x86_64-linux/Q/1.9.3
/usr/local/rvm/rubies/ruby-1.9.3-p286/bin/ruby -I. ../../../../ext/Q/extconf.rb
creating Makefile
cd -
cd tmp/x86_64-linux/Q/1.9.3
make
compiling ../../../../ext/Q/flagvalue.c
linking shared-object Q/flagvalue.so
cd -
install -c tmp/x86_64-linux/Q/1.9.3/Q.so lib/Q/Q.so
rake aborted!
No such file or directory - tmp/x86_64-linux/Q/1.9.3/Q.so
So it seems like the compiler compiles and links flagvalue.so and the installer tries to install non-existent Q.so… where does this error come from and what can I do about it?

Try this in your Rakefile:
Rake::ExtensionTask.new 'flagvalue' do |extension|
extension.ext_dir = 'ext/Q'
extension.lib_dir = 'lib/Q'
end
This does entail some duplication, as the Rake tasks doesn’t know what you specify as your target in extconf (i.e. it doesn’t know about the Q directory), so you have to specify again. This also means there won’t be a Q directory in the structure the the task creates under the tmp dir in your project, but that’s probably not a problem.

Ok, after some digging (and some vague guessing :-Δ) I found the solution:
I just got some code for my gemspec (from Writehack.com), that was so:
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
the problem with this method is that you'd have to have the just-to compile-binaries already in your repository. the correct way was to get the *.c-files from ext-directory and rename them to *.so like this:
s.executables = s.files.grep(%r{^ext/.*c$}).map{ |f| File.basename(f, '.c') + '.so'}
s.bindir = 'bin'
and also adding a bindir to Rakefile's ExtensionTask and make it get its files from spec:
spec = Gem::Specification.load('Q.gemspec')
spec.executables.each do |f|
Rake::ExtensionTask.new('Q', spec) do |ext|
ext.name = f.gsub(/\.so$/,'')
ext.tmp_dir = 'tmp'
ext.lib_dir = 'bin'
end
end
:-Δ

Related

Copy Folder Contents to Parent Directory in Rake (on Windows)

I have a set of files in a folder ../SomeFolder/AndAnother/dist
the dist folder contains a bunch of files and folders that I want to move up a level in a Rake task.
So
../SomeFolder/AndAnother/dist/subFolder/a.txt becomes ../SomeFolder/AndAnother/subFolder/a.txt
I can do this on linux by
task :lift_to_parent do
sh('mv', '../SomeFolder/AndAnother/dist/*', '../SomeFolder/AndAnother')
end
but this Rake task also runs on Windows and on that OS i get Errno::EACCES: Permission denied # unlink_internal
I'm hoping that FileUtils.mv will work on both linux and windows...
but if I
task :lift_to_parent do
FileUtils.mv '../SomeFolder/AndAnother/dist', '../SomeFolder/AndAnother', :force => true
end
I get ArgumentError: same file: ../SomeFolder/AndAnother/dist and ../SomeFolder/AndAnother/dist so I'm clearly missing something to allow FileUtils.mv to copy up a level (or going about this the wrong way)
So, how do I fix my FileUtils version or otherwise use a Rake task to copy a folder structure to its parent?
I've ended up doing this
task : lift_to_parent do
copied_by_jenkins = '../SomeFolder/AndAnother/dist'
copy_pattern = "#{copied_by_jenkins}/**/*"
target_directory = '../SomeFolder/AndAnother/Public'
next unless File.exists? copied_by_jenkins
FileList[copy_pattern].each do |file|
file_path = File.dirname(file).sub! copied_by_jenkins, ''
file_name = File.basename(file)
target_directory = File.join(target_directory, file_path)
destination = File.join(target_directory, file_name)
FileUtils.mkdir_p target_directory
FileUtils.copy_file(file, destination) unless File.directory? file
end
FileUtils.remove copied_by_jenkins
end
but that seems like a lot of the typing to achieve my goal

Add a Ruby command-line application to /usr/bin?

I have written a shell program in Ruby. Now I want to add it to my bin directory so that I can call the program by running $ my-rb-prog ....
First I tried to symlink my file into /usr/bin but then it said that it couldn't load the modules that I required.
On my second try, I tried to build a gem out of my project which worked fine, but I can still not access my shell program. After that I installed the gem. Here's what my gemspec looks like:
# -*- encoding: utf-8 -*-
$:.unshift(File.join(File.dirname(__FILE__), "/lib"))
require 'webcheck'
Gem::Specification.new do |s|
s.name = "webcheck"
s.version = WebCheck::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Victor Jonsson"]
s.email = ["kontakt#victorjonsson.se"]
s.homepage = "http://victorjonsson.se"
s.summary = %q{Check your website man!}
s.description = %q{Just check it!}
s.required_ruby_version = '>= 1.9.3'
s.add_dependency "httparty", "~> 0.12.0"
s.post_install_message = "Just check it!"
s.files = `git ls-files`.split("\n")
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
s.require_paths = ["lib"]
end
I thought that I would get access to my shell program, which is a Ruby file located in the bin directory inside my project, after that I had installed the gem but it clearly isn't that easy.
This is my first day of coding Ruby if you can't tell.
First, add a "she-bang" string as first line of your file. It will allow the shell to run the file. It should be:
#!/usr/bin/env ruby
Then give execution permissions to the file:
$ chmod +x your_file_name.rb
Now you can run your application:
./your_file_name.rb
Also you can add the path to the directory with this script to the PATH variable and run the application from anywhere you want.
# You may do this in ~/.bashrc file
PATH=$PATH:path/to/dir/with/script/
Don't forget to add #!/usr/bin/env ruby to the top of your Ruby script.
"Making a Ruby Script Executable" is a really good tutorial on making your executable available system wide, without the use of a gem.

Ruby - FileUtils.cp deletes file and fails

I am trying to copy a file in ruby using FileUtils#cp
Unfortunately, Ruby is deleting the file and then is unable to copy it because it is missing.
Is this a known bug or something I am doing wrong with the cp method.
src = "/var/tmp/myfile"
dest = "/usr/bin/myfile"
FileUtils.cp(src, dest)
It always complains that src file is missing but when I check it has been deleted. If I recreate the file and set permissions to 777 the file is present, after running the script it is gone and the copy fails
Place the following in a copy_myfile.rb, then run with: sudo ruby copy_myfile.rb
require 'fileutils'
src = "/var/tmp/myfile"
dest = "/usr/bin"
FileUtils.cp(src, dest)
It seems to work for me in Ruby 1.9.3:
my file permission: -rw-rw-r--
require 'fileutils'
=> true
irb(main):002:0> FileUtils.cp 'test.txt', 'text1.txt'
=> nil
The file does get copied.

Ruby 1.9.3 - Locate file local to ruby program regardless of where program is called from

I've been working on my first Ruby project, and in the process of trying to organize my files into different directories, I've run into trouble with having .rb files load non-ruby files (e.g. .txt files) local to themselves.
For example, suppose a project has the following structure:
myproject/
bin/
runner.rb
lib/
foo.rb
fooinfo.txt
test/
testfoo.rb
And the file contents are as follows:
runner.rb
require_relative '../lib/foo.rb'
foo.rb
File.open('./fooinfo.txt') do |file|
while line = file.gets
puts line
end
end
If I cd to lib and run foo.rb, it has no trouble finding fooinfo.txt in its own directory and printing its contents.
However, if I cd to bin and run runner.rb, I get
in `initialize': No such file or directory - ./fooinfo.txt (Errno::ENOENT)
I assume this is because File.open searches relative to whatever directory the top level program is run from.
Is there a way to ensure that foo.rb can find fooinfo.rb regardless of where it is run/required from (assuming that foo.rb and fooinfo.rb always maintain the same location relative to eachother)?
I'd like to be able to run foo.rb from bin/runner.rb, and a test file in test/, and have it be able to find fooinfo.txt in both cases.
Ideally, I'd like to have a solution that would work even if the entire myproject directory were moved.
Is there something like require_relative that can locate a non-ruby file?
Try using __FILE__ and File.dirname to build absolute paths. For example:
File.open(File.expand_path(File.dirname(__FILE__)) + './fooinfo.txt') do |file|
...
end
In this case, the simplest thing is to just change
File.open('./fooinfo.txt')
to
File.open('../lib/fooinfo.txt')
That will work from from any project subdirectory directly under your project root (including lib/).
The more robust solution, useful in larger projects, is to have a PROJECT_ROOT constant that you can use from anywhere. If you have lib/const.rb:
module Const
PROJECT_ROOT = File.expand_path("..", File.dirname(__FILE__))
end
Then (assuming you've requireed that file) you can use:
File.open(Const::PROJECT_ROOT + '/lib/fooinfo.txt')

Require all files in sub-directory

I have the following directory tree.
- app.rb
- folder/
- one/
- one.rb
- two/
- two.rb
I want to be able to load Ruby files in the folder/ directory, even the ones in the sub-directories. How would I do so?
Jekyll does something similar with its plugins. Something like this should do the trick:
Dir[File.join(".", "**/*.rb")].each do |f|
require f
end
With less code, but still working on Linux, OS X and Windows:
Dir['./**/*.rb'].each{ |f| require f }
The '.' is needed for Ruby 1.9.2 where the current directory is no longer part of the path.
Try this:
Dir.glob(File.join(".", "**", "*.rb")).each do |file|
require file
end
In my project this evaluates to ["./fixset.rb", "./spec/fixset_spec.rb", "./spec/spec_helper.rb"] which causes my specs to run twice. Therefore, here's a tweaked version:
Dir[File.join(".", "**/*.rb")].each { |f| require f unless f[/^\.\/spec\//]}
This'll safely ignore all *.rb files in ./spec/

Resources