Created
July 2, 2012 21:44
-
-
Save CameronCarroll/3035906 to your computer and use it in GitHub Desktop.
Rakefile to compile Ruby/C/Fortran extensions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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