Skip to content

Instantly share code, notes, and snippets.

@nathania
Forked from CameronCarroll/RCF2.Rakefile.rb
Created August 4, 2012 21:09
Show Gist options
  • Save nathania/3259960 to your computer and use it in GitHub Desktop.
Save nathania/3259960 to your computer and use it in GitHub Desktop.
Rakefile to compile Ruby/C/Fortran extensions
# Ruby/C/Fortran Bridge Rakefile
# Cameron Carroll, July 2012
# Purpose: Rake build script to facilitate calling Fortran code from Ruby in order to outsource
# heavy lifting computation. This script will compile Fortran and C code in present directory
# but leaves implementation and linking up to the user.
#
# Notes: This script may require a little bit of configuration depending on the system.
# 1: The Ruby include flags are specifically for MY system. I haven't found a way to generate them,
# so you have to either tailor them by hand or generate a extconf.rb makefile, enable verbose mode,
# and copy the include flags from the compile command. Yeah, I know, I know...
# 2: Recursive directory search is NOT supported. This script will only build files in
# the present working directory.
# 3: Linking is left up to the user, although a suggested syntax for R/C/F is printed at the end.
#
# Usage:
# 1: Use 'rake' to invoke default task. (Simple build.)
# 2: Use 'rake clean' to delete output files.
# 3: Use 'rake rebuild' to clean and build files.
#
# References:
# 1: ruby/mkmf.rb (Thanks Ruby team.)
# 2: http://www.ruby-forum.com/topic/78239#126917 [Ruby2C2Fortran Pattern] (Thanks to Ron M.)
# 3: http://www.ats.ucla.edu/clusters/common/computing/c-fortran-interop.html [C/Fortran Interoperation]
#
# Written for/in tandem with: http://ckcarroll.herokuapp.com/log/ruby/ruby2c2fortran
#
# Released under WTFPL in July 2012, http://sam.zoy.org/wtfpl/
require 'rbconfig'
# Global Data:
PROGRAM_VERSION = '0.alpha'
CONFIG = RbConfig::CONFIG
# Default build:
task :default do
puts "Ruby/C/Fortran Bridge #{PROGRAM_VERSION}"
puts "----------------------------------------"
Rake::Task["build"].invoke
end
# Build task:
task :build do
puts ""
puts "Building Fortran files..."
puts "----------------------------------------"
Rake::Task["compile_fortran"].invoke
puts ""
puts "Building C files..."
puts "----------------------------------------"
Rake::Task["compile_c"].invoke
puts ""
puts "Finished build. Use 'gcc -shared -o [target].so [c_file].o [f_file].o -lgfortran -lruby -lc' to link up."
end
# Rebuild task: (Clean and build.)
task :rebuild do
puts "Ruby/C/Fortran Bridge #{PROGRAM_VERSION}"
puts "----------------------------------------"
Rake::Task["clean"].invoke
Rake::Task["build"].invoke
end
task :compile_fortran do
Fortran_Flags = '-c -fPIC'
Fortran_Compiler = `which gfortran`.chomp
unless $? == 0 # $? contains output from system call. (`which gfortran`)
"Couldn't find Fortran compiler (Checks for GFortran)"
end
unless Fortran_Compiler.empty?
Fortran_Files = Dir['*.{f08,F08,f03,F03}']
Fortran_Files.each do |file|
output = file[0..-5] + '.o'
if compile_output_exists?(output)
puts "Output file: #{output} already exists; Consider using 'rake rebuild'."
next
end
puts "Compiling Fortran file: #{file}"
`#{Fortran_Compiler} #{Fortran_Flags} #{file}` #System call to compiler
check_and_report_output(output)
end
end
puts "Didn't find any Fortran files to compile." if Fortran_Files.empty?
end
task :compile_c do
cflags = CONFIG['cflags']
# Edit the line below to reflect your own system.
iflags = "-I. -I/usr/local/include/ruby-1.9.1/x86_64-linux -I/usr/local/include/ruby-1.9.1/ruby/backward " \
"-I/usr/local/include/ruby-1.9.1 -I. -fPIC -O3"
C_Compiler = `which gcc`.chomp
unless $? == 0
"Couldn't find C compiler (Checks for GCC)"
end
unless C_Compiler.empty?
C_Files = Dir['*.{c, C}']
C_Files.each do |file|
output = file[0..-3] + '.o'
if compile_output_exists?(output)
puts "Output file: #{output} already exists; Consider using 'rake rebuild'."
next
end
puts "Compiling C file: #{file}"
`#{C_Compiler} #{cflags} #{iflags} -o #{output} -c #{file}` #System call to compiler
check_and_report_output(output)
end
end
puts "Didn't find any C files to compile." if C_Files.empty?
end
task :clean do
puts ""
puts "Cleaning up output files..."
puts "----------------------------------------"
so_files = Dir['*.so']
o_files = Dir['*.o']
out_files = Dir['*.out']
puts "Working directory is clean." if so_files.empty? && o_files.empty? && out_files.empty?
remove_files(so_files)
remove_files(o_files)
remove_files(out_files)
end
### Helper Methods: ###
def remove_files(file_list)
file_list.each do |file|
`rm -rf #{file}`
if compile_output_exists?(file)
puts "Failed to clean #{file} for some reason."
else
puts "Deleted file: #{file}."
end
end
end
# Used to determine whether the compilation output is already in pwd.
def compile_output_exists?(file)
if Dir["#{file}"].empty?
return false
else
return true
end
end
# DRYer method of printing success/failure messages.
def check_and_report_output(file)
if compile_output_exists?(file)
puts "Successfully compiled #{file}."
else
puts "Failed to compiled #{file}."
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment