Source

ruby-pg / Rakefile.cross

Full commit
#!/usr/bin/env rake

require 'uri'
require 'tempfile'
require 'rbconfig'
require 'rake/clean'
require 'rake/extensiontask'
require 'rake/extensioncompiler'

MISCDIR = BASEDIR + 'misc'

NUM_CPUS = if File.exist?('/proc/cpuinfo')
	File.read('/proc/cpuinfo').scan('processor').length
elsif RUBY_PLATFORM.include?( 'darwin' )
	`system_profiler SPHardwareDataType | grep 'Cores' | awk '{print $5}'`.chomp
else
	1
end

# Cross-compilation constants
OPENSSL_VERSION            = ENV['OPENSSL_VERSION'] || '1.0.1c'
POSTGRESQL_VERSION         = ENV['POSTGRESQL_VERSION'] || '9.1.4'

COMPILE_HOME               = Pathname( "./build" ).expand_path
STATIC_SOURCESDIR          = COMPILE_HOME + 'sources'
STATIC_BUILDDIR            = COMPILE_HOME + 'builds'

# Static OpenSSL build vars
STATIC_OPENSSL_BUILDDIR    = STATIC_BUILDDIR + "openssl-#{OPENSSL_VERSION}"

OPENSSL_SOURCE_URI         =
	URI( "http://www.openssl.org/source/openssl-#{OPENSSL_VERSION}.tar.gz" )
OPENSSL_TARBALL            = STATIC_SOURCESDIR + File.basename( OPENSSL_SOURCE_URI.path )
OPENSSL_MAKEFILE           = STATIC_OPENSSL_BUILDDIR + 'Makefile'

LIBSSLEAY32                = STATIC_OPENSSL_BUILDDIR + 'libssleay32.a'
LIBEAY32                   = STATIC_OPENSSL_BUILDDIR + 'libeay32.a'

OPENSSL_PATCHES            = Rake::FileList[ (MISCDIR + "openssl-#{OPENSSL_VERSION}.*.patch").to_s ]

# Static PostgreSQL build vars
STATIC_POSTGRESQL_BUILDDIR = STATIC_BUILDDIR + "postgresql-#{POSTGRESQL_VERSION}"
POSTGRESQL_SOURCE_URI      = begin
	uristring = "http://ftp.postgresql.org/pub/source/v%s/postgresql-%s.tar.bz2" %
		[ POSTGRESQL_VERSION, POSTGRESQL_VERSION ]
	URI( uristring )
end
POSTGRESQL_TARBALL         = STATIC_SOURCESDIR + File.basename( POSTGRESQL_SOURCE_URI.path )

STATIC_POSTGRESQL_SRCDIR   = STATIC_POSTGRESQL_BUILDDIR + 'src'
STATIC_POSTGRESQL_LIBDIR   = STATIC_POSTGRESQL_SRCDIR + 'interfaces/libpq'
STATIC_POSTGRESQL_INCDIR   = STATIC_POSTGRESQL_SRCDIR + 'include'

POSTGRESQL_GLOBAL_MAKEFILE = STATIC_POSTGRESQL_SRCDIR + 'Makefile.global'
POSTGRESQL_SHLIB_MAKEFILE  = STATIC_POSTGRESQL_SRCDIR + 'Makefile.shlib'
POSTGRESQL_SHLIB_MF_ORIG   = STATIC_POSTGRESQL_SRCDIR + 'Makefile.shlib.orig'
POSTGRESQL_LIB             = STATIC_POSTGRESQL_LIBDIR + 'libpq.a'
POSTGRESQL_PATCHES         = Rake::FileList[ (MISCDIR + "postgresql-#{POSTGRESQL_VERSION}.*.patch").to_s ]

CROSS_PREFIX = begin
	Rake::ExtensionCompiler.mingw_host
rescue => err
	$stderr.puts "Cross-compilation disabled -- %s" % [ err.message ]
	'unknown'
end


# clean intermediate files and folders
CLEAN.include( STATIC_BUILDDIR.to_s )


ENV['RUBY_CC_VERSION'] ||= '1.8.7:1.9.3'

def download(url, save_to)
	part = save_to+".part"
	sh "wget #{url.to_s.inspect} -O #{part.inspect} || curl #{url.to_s.inspect} -o #{part.inspect}"
	FileUtils.mv part, save_to
end

def run(*args)
	sh *args
end

#####################################################################
### C R O S S - C O M P I L A T I O N - T A S K S
#####################################################################


directory STATIC_SOURCESDIR.to_s

#
# Static OpenSSL build tasks
#
directory STATIC_OPENSSL_BUILDDIR.to_s

# openssl source file should be stored there
file OPENSSL_TARBALL => STATIC_SOURCESDIR do |t|
	download( OPENSSL_SOURCE_URI, t.name )
end

# Extract the openssl builds
file STATIC_OPENSSL_BUILDDIR => OPENSSL_TARBALL do |t|
	puts "extracting %s to %s" % [ OPENSSL_TARBALL, STATIC_OPENSSL_BUILDDIR.parent ]
	STATIC_OPENSSL_BUILDDIR.mkpath
	run 'tar', '-xzf', OPENSSL_TARBALL.to_s, '-C', STATIC_OPENSSL_BUILDDIR.parent.to_s
	OPENSSL_MAKEFILE.unlink if OPENSSL_MAKEFILE.exist?

	OPENSSL_PATCHES.each do |patchfile|
		puts "  applying patch #{patchfile}..."
		run 'patch', '-Np1', '-d', STATIC_OPENSSL_BUILDDIR.to_s,
		'-i', File.expand_path( patchfile, BASEDIR )
	end
end

CMD_PRELUDE = [
	'env',
	"CC=#{CROSS_PREFIX}-gcc",
	"CFLAGS=-DDSO_WIN32",
	"AR=#{CROSS_PREFIX}-ar",
	"RANLIB=#{CROSS_PREFIX}-ranlib"
]


# generate the makefile in a clean build location
file OPENSSL_MAKEFILE => STATIC_OPENSSL_BUILDDIR do |t|
	Dir.chdir( STATIC_OPENSSL_BUILDDIR ) do
		cmd = CMD_PRELUDE.dup
		cmd << "./Configure" << 'mingw'

		run( *cmd )
	end
end

desc "compile static openssl libraries"
task :openssl_libs => [ LIBSSLEAY32, LIBEAY32 ]

task :compile_static_openssl => OPENSSL_MAKEFILE do |t|
	Dir.chdir( STATIC_OPENSSL_BUILDDIR ) do
		cmd = CMD_PRELUDE.dup
		cmd << 'make' << "-j#{NUM_CPUS}" << 'build_libs'

		run( *cmd )
	end
end

desc "compile static #{LIBEAY32}"
file LIBEAY32 => :compile_static_openssl do |t|
	FileUtils.cp( STATIC_OPENSSL_BUILDDIR + 'libcrypto.a', LIBEAY32.to_s )
end

desc "compile static #{LIBSSLEAY32}"
file LIBSSLEAY32 => :compile_static_openssl do |t|
	FileUtils.cp( STATIC_OPENSSL_BUILDDIR + 'libssl.a', LIBSSLEAY32.to_s )
end



#
# Static PostgreSQL build tasks
#
directory STATIC_POSTGRESQL_BUILDDIR.to_s


# postgresql source file should be stored there
file POSTGRESQL_TARBALL => STATIC_SOURCESDIR do |t|
	download( POSTGRESQL_SOURCE_URI, t.name )
end

# Extract the postgresql sources
file STATIC_POSTGRESQL_BUILDDIR => POSTGRESQL_TARBALL do |t|
	puts "extracting %s to %s" % [ POSTGRESQL_TARBALL, STATIC_POSTGRESQL_BUILDDIR.parent ]
	STATIC_POSTGRESQL_BUILDDIR.mkpath
	run 'tar', '-xjf', POSTGRESQL_TARBALL.to_s, '-C', STATIC_POSTGRESQL_BUILDDIR.parent.to_s
	mv POSTGRESQL_SHLIB_MAKEFILE, POSTGRESQL_SHLIB_MF_ORIG
	
	POSTGRESQL_PATCHES.each do |patchfile|
		puts "  applying patch #{patchfile}..."
		run 'patch', '-Np1', '-d', STATIC_POSTGRESQL_BUILDDIR.to_s,
		'-i', File.expand_path( patchfile, BASEDIR )
	end
end

# generate the makefile in a clean build location
file POSTGRESQL_GLOBAL_MAKEFILE => [ STATIC_POSTGRESQL_BUILDDIR, :openssl_libs ] do |t|
	options = [
		'--target=i386-mingw32',
		"--host=#{Rake::ExtensionCompiler.mingw_host}",
		'--with-openssl',
		'--without-zlib',
		'--disable-shared',
	]

	Dir.chdir( STATIC_POSTGRESQL_BUILDDIR ) do
		configure_path = STATIC_POSTGRESQL_BUILDDIR + 'configure'
		cmd = [ configure_path.to_s, *options ]
		cmd << "CFLAGS=-L#{STATIC_OPENSSL_BUILDDIR}"
		cmd << "LDFLAGS=-L#{STATIC_OPENSSL_BUILDDIR}"
		cmd << "LDFLAGS_SL=-L#{STATIC_OPENSSL_BUILDDIR}"
		cmd << "LIBS=-lwsock32 -lws2_32 -lgdi32"
		cmd << "CPPFLAGS=-I#{STATIC_OPENSSL_BUILDDIR}/include"

		run( *cmd )
	end
end


# patch the Makefile.shlib -- depend on the build dir so it's only
# rewritten if the tarball is re-extracted.
file POSTGRESQL_SHLIB_MAKEFILE => POSTGRESQL_SHLIB_MF_ORIG do |t|
	tf = Tempfile.new( POSTGRESQL_SHLIB_MAKEFILE.basename.to_s )
	POSTGRESQL_SHLIB_MF_ORIG.open( File::RDONLY ) do |ifh|
		ifh.each_line do |line|
			tf.print( line.sub(/^(\s*haslibarule\s*=\s*yes)/, "# \\1 ") )
		end
	end
	tf.close

	FileUtils.mv( tf.path, t.name, :verbose => $puts )
end


# make libpq.a
task POSTGRESQL_LIB => [ POSTGRESQL_GLOBAL_MAKEFILE, POSTGRESQL_SHLIB_MAKEFILE ] do |t|
	Dir.chdir( POSTGRESQL_LIB.dirname ) do
		sh 'make', "-j#{NUM_CPUS}", POSTGRESQL_LIB.basename.to_s, 'PORTNAME=win32'
	end
end


#desc 'compile static libpg.a'
task :static_libpq => POSTGRESQL_LIB

desc 'cross compile pg for win32'
task :cross do
	ENV['CROSS_COMPILING'] = 'yes'
end
task :cross => [ :mingw32, :static_libpq ]

task :mingw32 do
	# Use Rake::ExtensionCompiler helpers to find the proper host
	unless Rake::ExtensionCompiler.mingw_host then
		warn "You need to install mingw32 cross compile functionality to be able to continue."
		warn "Please refer to your distribution/package manager documentation about installation."
		fail
	end
end