Permalink
Fetching contributors…
Cannot retrieve contributors at this time
523 lines (428 sloc) 14.5 KB
#
# This is the Rakefile for Rubygame. It's used for
# compiling, packaging, installing generating the
# documentation, and running specs.
#
# The version number for Rubygame.
RUBYGAME_VERSION = [2,4,0]
#
# The following terms (the MIT license) apply to this Rakefile:
#
# Copyright (c) 2008 John Croisant
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
require 'rubygems'
Gem::manage_gems
require 'rake'
require 'rake/gempackagetask'
require 'rake/rdoctask'
require "rbconfig"
include Config
require 'English'
class ShellCommandError < RuntimeError
end
# Execute the block (which is supposed to perform a shell command),
# then raise ShellCommandError if the command failed.
def try_shell( &block )
result = yield
unless $CHILD_STATUS.exitstatus == 0
raise ShellCommandError, "Command failed. Aborting."
end
return result
end
def try_sdl_config( flag )
begin
if $options[:"sdl-config"]
return try_shell { `sdl-config #{flag}`.chomp }
else
return String.new
end
rescue ShellCommandError
warn "WARNING: 'sdl-config' failed."
warn "Continuing anyway, but compile may fail."
return String.new
end
end
# Get a variable from ENV or CONFIG, with ENV having precedence.
# Returns "" if the variable didn't exist at all.
def from_env_or_config(string)
([ENV[string], CONFIG[string]] - ["", nil])[0] or ""
end
OBJEXT = from_env_or_config("OBJEXT")
DLEXT = from_env_or_config("DLEXT")
gem_spec = Gem::Specification.new do |s|
s.name = "rubygame"
s.version = RUBYGAME_VERSION.join(".")
s.author = "John Croisant"
s.email = "jacius@gmail.com"
s.homepage = "http://rubygame.org/"
s.summary = "Clean and powerful library for game programming"
s.has_rdoc = true
s.files = FileList.new do |fl|
fl.include("{lib,ext,samples,doc}/**/*")
fl.exclude(/svn/)
#fl.exclude(/\.#{OBJEXT}/)
end
s.require_paths = ["lib", "lib/rubygame/", "ext/rubygame/"]
s.extensions = ["Rakefile"]
s.extra_rdoc_files = FileList.new do |fl|
fl.include "doc/*.rdoc"
fl.include "README", "LICENSE", "CREDITS", "ROADMAP", "NEWS"
end
end
task :binary do
gem_spec.platform = Gem::Platform::CURRENT
end
Rake::GemPackageTask.new(gem_spec) do |pkg|
pkg.need_tar_bz2 = true
end
Rake::RDocTask.new do |rd|
rd.main = "README"
rd.title = "Rubygame #{RUBYGAME_VERSION.join(".")} Docs"
rd.rdoc_files.include("ext/rubygame/*.c",
"lib/rubygame/**/*.rb",
"doc/*.rdoc",
"README",
"LICENSE",
"CREDITS",
"ROADMAP",
"NEWS")
end
task :default => [:build]
desc "Compile all of the extensions"
task :build
require 'rake/clean'
task(:clean) { puts "Cleaning out temporary generated files" }
task(:clobber) { puts "Cleaning out final generated files" }
##############################
## BUILD OPTIONS ##
##############################
$options = {
:"sdl-gfx" => true,
:"sdl-image" => true,
:"sdl-ttf" => true,
:"sdl-mixer" => true,
:opengl => true,
:"sdl-config" => true,
:universal => false,
:debug => false,
:verbose => false,
:sitearchdir => CONFIG["sitearchdir"],
:sitelibdir => CONFIG["sitelibdir"]
}
# Default behavior for win32 is to skip sdl_config,
# since it's usually not available. It can still be
# enabled through the options, though.
if PLATFORM =~ /win32/
$options[:"sdl-config"] = false
end
# Define tasks to enable and disable bool options.
#
# rake option
# rake no-option
#
# task_name:: the task name to use
# option_name:: the option name (if different from
# the task name)
# desc:: a longer description, to fill
# "Enable ________.", if different
# from the task name.
#
def bool_option( task_name, option_name=nil, desc=nil )
option_name = task_name unless option_name
option_name = option_name.intern if option_name.kind_of? String
desc = task_name.to_s unless desc
notask_name = "no-#{task_name.to_s}".intern
is_true = $options[option_name]
desc "Enable #{desc} #{'(default)' if is_true}"
task(task_name) { $options[option_name] = true }
desc "Disable #{desc} #{'(default)' unless is_true}"
task(notask_name) { $options[option_name] = false }
end
# Gather a string option from an environment variable:
#
# rake option="the value of the option"
#
# task_name:: the task name to use
# option_name:: the option name (if different from
# the task name)
#
def string_option( task_name, option_name=nil )
option_name = task_name unless option_name
option_name = option_name.intern if option_name.kind_of? String
$options[option_name] = ENV["#{task_name}"] if ENV["#{task_name}"]
end
bool_option :"sdl-gfx", nil, "SDL_gfx support"
bool_option :"sdl-image", nil, "SDL_image support"
bool_option :"sdl-mixer", nil, "SDL_mixer support"
bool_option :"sdl-ttf", nil, "SDL_ttf support"
bool_option :"sdl-config", nil, "guess compiler flags for SDL"
bool_option :opengl, nil, "OpenGL support"
bool_option :debug, nil, "compile with debug symbols"
bool_option :verbose, nil, "show compiler commands"
bool_option :universal, nil, "universal binary (MacOS X Intel)"
string_option "RUBYARCHDIR", :sitearchdir
string_option :sitearchdir
string_option "RUBYLIBDIR", :sitelibdir
string_option :sitelibdir
CFLAGS = [from_env_or_config("CFLAGS"),
try_sdl_config("--cflags"),
"-I. -I#{CONFIG['topdir']}",
"-DRUBYGAME_MAJOR_VERSION=#{RUBYGAME_VERSION[0]}",
"-DRUBYGAME_MINOR_VERSION=#{RUBYGAME_VERSION[1]}",
"-DRUBYGAME_PATCHLEVEL=#{RUBYGAME_VERSION[2]}"
].join(" ")
LINK_FLAGS = [from_env_or_config("LIBRUBYARG_SHARED"),
from_env_or_config("LDFLAGS"),
try_sdl_config("--libs")].join(" ")
DEFAULT_EXTDIR = File.join('ext','rubygame','')
class ExtensionModule
@@libflag = " -l%s " # compiler flag for giving linked libraries
attr_accessor :dynlib, :objs, :libs, :cflags, :lflags, :directory
def initialize(&block)
@directory = DEFAULT_EXTDIR
@dynlib = ""
@objs = []
@libs = []
@lflags = ""
yield self if block_given?
end
def add_lib( lib )
@lflags << @@libflag%lib
end
def add_header( header )
#CFLAGS << " -DHAVE_#{header.upcase.gsub('.','_')} "
end
def create_all_tasks()
create_obj_task
create_dl_task
CLEAN.include("#{@directory}/*.#{OBJEXT}")
CLOBBER.include("#{@directory}/*.#{DLEXT}")
end
# Create a file task for each dynamic library (.so) we want to generate.
#
# The file task invokes another task which does the actual compiling, and
# has the true prerequisites.
#
# This is done so that the prerequisites don't have to be compiled when
# the final product already exists (such as in the precompiled win32 gem).
#
def create_dl_task
dynlib_full = File.join( @directory, "#{dynlib}.#{DLEXT}" )
objs_full = @objs.collect { |obj|
File.join( @directory, "#{obj}.#{OBJEXT}" )
}
desc "Compile the #{@dynlib} extension"
file dynlib_full => objs_full do |task|
link_command = "#{from_env_or_config('LDSHARED')} #{LINK_FLAGS} #{@lflags} -o #{dynlib_full} #{task.prerequisites.join(' ')}"
# If link command includes i386 arch, and we're not allowing universal
if( /-arch i386/ === link_command and not $options[:universal] )
# Strip "-arch ppc" to prevent building a universal binary.
link_command.gsub!("-arch ppc","")
end
if( $options[:verbose] )
try_shell { sh link_command }
else
puts "Linking compiled files to create #{File.basename(@directory)}/#{File.basename(dynlib_full)}"
try_shell { `#{link_command}` }
end
end
task :build => [dynlib_full] # Add this as a prereq of the build
task :install_ext => [dynlib_full] # ...and install_ext tasks
end
def create_obj_task
# A rule for object files (".o" on linux).
# This won't work for rake < 0.7.2, because the proc returns an Array.
# If it raises an exception, we'll try a more compatible way.
rule(/#{@directory}.+\.#{OBJEXT}$/ =>
[
# Generate dependencies for this .o file
proc do |objfile|
source = objfile.sub(".#{OBJEXT}", ".c") # the .c file
[source] + depends_headers( source ) # Array of .c + .h dependencies
end
])\
do |t|
compile_command = "#{from_env_or_config('CC')} -c #{CFLAGS} #{"-g " if $options[:debug]} #{t.source} -o #{t.name}"
# If compile command includes i386 arch, and we're not allowing universal
if( /-arch i386/ === compile_command and not $options[:universal] )
# Strip "-arch ppc" to prevent building a universal binary.
compile_command.gsub!("-arch ppc","")
end
if( $options[:verbose] )
try_shell { sh compile_command }
else
puts "Compiling #{File.basename(@directory)}/#{File.basename(t.source)}"
try_shell { `#{compile_command}` }
end
end
rescue
# Generate a .o rule for each .c file in the directory.
FileList.new("#{@directory}*.c").each do |source|
object = source.sub(".c", ".#{OBJEXT}")
file object => ([source] + depends_headers( source )) do |t|
compile_command = "#{CONFIG['CC']} -c #{CFLAGS} #{"-g " if $options[:debug]} #{source} -o #{t.name}"
if( $options[:verbose] )
try_shell { sh compile_command }
else
puts "Compiling #{File.basename(@directory)}/#{File.basename(source)}"
try_shell { `#{compile_command}` }
end
end
end
end
# Extracts the names of all the headers that the C file depends on.
def depends_headers( filename )
return [] # workaround for a bug
depends = []
File.open(filename, "r") do |file|
file.each_line do |line|
if /#include\s+"(\w+\.h)"/ =~ line
depends << @directory+$1
end
end
end
return depends
end
end
rubygame_core = ExtensionModule.new do |core|
core.dynlib = 'rubygame_core'
core.objs = ['rubygame_main',
'rubygame_shared',
'rubygame_event',
'rubygame_event2',
'rubygame_gl',
'rubygame_joystick',
'rubygame_screen',
'rubygame_surface',
'rubygame_time',
]
core.create_all_tasks()
end
# TODO: We should check if the libraries exist?
rubygame_gfx = ExtensionModule.new do |gfx|
gfx.dynlib = 'rubygame_gfx'
gfx.objs = ['rubygame_shared', 'rubygame_gfx']
gfx.add_lib( 'SDL_gfx' )
gfx.add_header( 'SDL_gfxPrimitives.h')
gfx.add_header( 'SDL_rotozoom.h' )
gfx.create_all_tasks() if $options[:"sdl-gfx"]
end
rubygame_image = ExtensionModule.new do |image|
image.dynlib = 'rubygame_image'
image.objs = ['rubygame_shared', 'rubygame_image']
image.add_lib('SDL_image')
image.add_header('SDL_image.h')
image.create_all_tasks() if $options[:"sdl-image"]
end
rubygame_mixer = ExtensionModule.new do |mixer|
mixer.dynlib = 'rubygame_mixer'
mixer.objs = ['rubygame_shared', 'rubygame_mixer', 'rubygame_sound', 'rubygame_music']
mixer.add_lib('SDL_mixer')
mixer.add_header('SDL_mixer.h')
mixer.create_all_tasks() if $options[:"sdl-mixer"]
end
rubygame_ttf = ExtensionModule.new do |ttf|
ttf.dynlib = 'rubygame_ttf'
ttf.add_lib('SDL_ttf')
ttf.objs = ['rubygame_shared', 'rubygame_ttf']
ttf.add_header('SDL_ttf.h')
ttf.create_all_tasks() if $options[:"sdl-ttf"]
end
if $options[:opengl]
CFLAGS << " -DHAVE_OPENGL "
end
desc "(Called when installing via Rubygems)"
task :extension => [:fix_filenames, :build]
task :fix_filenames do
unless DLEXT == 'so'
Rake::Task[:install_ext].prerequisites.each do |prereq|
prereq = prereq.ext('so')
if File.exist? prereq
mv prereq, prereq.ext(DLEXT)
end
end
end
end
desc "Install just the extensions"
task :install_ext do |task|
puts "Installing extensions to #{$options[:sitearchdir]}"
mkdir_p $options[:sitearchdir]
cp task.prerequisites.to_a, $options[:sitearchdir]
end
desc "Install just the library"
task :install_lib do |task|
puts "Installing library to #{$options[:sitelibdir]}"
files = FileList.new do |fl|
fl.include("lib/**/*.rb")
fl.exclude(/svn/)
end
files.each do |f|
dir = File.join($options[:sitelibdir], File.dirname(f).sub('lib',''), "")
mkdir_p dir
cp f, dir
end
end
desc "Install both the extensions and the library"
task :install => [:install_ext, :install_lib]
#########
# SPECS #
#########
begin
require 'spec/rake/spectask'
desc "Run all specs"
Spec::Rake::SpecTask.new do |t|
t.spec_files = FileList['spec/*_spec.rb']
end
namespace :spec do
desc "Run all specs"
Spec::Rake::SpecTask.new(:all) do |t|
t.spec_files = FileList['spec/*_spec.rb']
end
desc "Run spec/[name]_spec.rb (e.g. 'color')"
task :name do
puts( "This is just a stand-in spec.",
"Run rake spec:[name] where [name] is e.g. 'color', 'music'." )
end
end
rule(/spec:.+/) do |t|
name = t.name.gsub("spec:","")
path = File.join( File.dirname(__FILE__),'spec','%s_spec.rb'%name )
if File.exist? path
Spec::Rake::SpecTask.new(name) do |t|
t.spec_files = [path]
end
puts "\nRunning spec/%s_spec.rb"%name
Rake::Task[name].invoke
else
puts "File does not exist: %s"%path
end
end
rescue LoadError
error = "ERROR: RSpec is not installed?"
task :spec do
puts error
end
rule( /spec:.*/ ) do
puts error
end
end