#*******************************************************************************
#* Usage:
#*
#*	ruby p4conf.rb  --apidir=<path to api> \
#*			--apilibdir=<path to p4 libs> \
#*			--apiincludedir=<path to p4 headers> \
#*			--apiver=<Perforce API version> \
#*                      --gccver=[23]
#*
#*      --apidir=xx is equivalent to --apilibdir=xx --apiincludedir=xx
#*
#*	--gccver defaults to 3 if not specified.
#*
#*
#*******************************************************************************
require 'mkmf'
require 'rbconfig'
require "getoptlong"

#*******************************************************************************
# C++ programs should be linked with g++ not with gcc. mkmf doesn't get that
# right, so we have to hack it a little here. This particularly affects
# gcc 3.x users as older gcc versions didn't mind too much.
#*******************************************************************************

if ( CONFIG[ 'LDSHARED' ] =~ /gcc/ )
    CONFIG[ 'LDSHARED' ].gsub!( /gcc/, "g++" )
end

if ( CONFIG[ "CC" ] =~ /gcc/ )
    CONFIG[ "CXX" ] = CONFIG[ "CC" ].sub(/gcc/,"g++") unless CONFIG.has_key?( "CXX" )
end

#*******************************************************************************
# Prompt the user for something
#*******************************************************************************
def prompt( str, testproc )
    while true
	print( str + ": " )
	val = $stdin.gets.chomp
	break if ( testproc.call( val ) )
    end
    return val
end


#*******************************************************************************
# Set platform specific compile options
#*******************************************************************************
def set_platform_cflags
    $CFLAGS += "-DOS_#{$p4osname} "

    if ( $p4osname == "NT" )
      $CFLAGS += "/DCASE_INSENSITIVE "
      $CFLAGS += "/MT "
    end

    if ( $p4osname == "SOLARIS" )
	osver = `uname -r`
	osver.gsub!( /5\./, "2" )
	if ( osver == "25" )
	    $CFLAGS += "-DSOLARIS25 "
	else
	    $CFLAGS += "-DSOLARIS#{osver} "
	end

	# Solaris 2.7 and later require -Dconst_char="const char"
	if ( osver.to_i >= 27 )
	    $CFLAGS += "-Dconst_char='const char'"
	end
	$CFLAGS += "-Dsolaris "
    end

    if( $p4osname == "DARWIN" )
	$CFLAGS += "-DCASE_INSENSITIVE "
    end
end

#*******************************************************************************
# Set platform specific build options
#*******************************************************************************
def set_platform_opts
    if ( $p4osname == "CYGWIN" && $p4gccver == 2 )
	CONFIG[ "CC" ] = "gcc-2"
	CONFIG[ "CXX" ] = "g++-2"
	CONFIG[ "LDSHARED" ].gsub!( /g\+\+/, "g++-2" ) 
    elsif ( $p4osname == "DARWIN" && $p4gccver == 2 )
	CONFIG[ "CC" ] = "gcc2"
	CONFIG[ "CXX" ] = "g++2"
	CONFIG[ "LDSHARED" ].sub!( /^cc/, "g++2" ) 
    end
end


#*******************************************************************************
# Use platform libs. Call *before* adding the API libs to preserve linking 
# order.
#*******************************************************************************
def use_platform_libs
    if ( $p4osname == "SOLARIS" )
	osver = `uname -r`
	osver.gsub!( /5\./, "2" )
	if ( osver == "25" )
	    $LDFLAGS += "/usr/ucblib/libucb.a "
	end
	use_lib( "nsl" )
	use_lib( "socket" )
    elsif ( $p4osname == "FREEBSD" )
	use_lib( "gcc" )
    elsif ( $p4osname == "NT" )
	use_lib( "advapi32" )
	use_lib( "wsock32" )
	use_lib( "kernel32" )
	use_lib( "oldnames" )
	use_lib( "libcmt" )
    elsif( $p4osname == "CYGWIN" )
        # Clear out the bogus libs on cygwin.
        CONFIG[ "LIBS" ] = ""
    end
end


#*******************************************************************************
# Set directory for the Perforce API libraries
#*******************************************************************************
def set_apilibdir( path )
    $LIBPATH = $LIBPATH | [ path ]
end

#*******************************************************************************
# Set directory for the Perforce API header files
#*******************************************************************************
def set_apiincludedir( path )
    $CFLAGS += " -I#{path} "
end

#*******************************************************************************
# function for generating a correctly formatted #define string
#*******************************************************************************
def define( macro, value )
    %Q{#define #{macro}\t"#{value}"}
end

#*******************************************************************************
#* Write the extconf.h file 
#*******************************************************************************
def write_extconf_h
    File.open( "extconf.h", "w" ) do
	|ch|
	ch.puts( define( "P4APIVER", $apiver ) )
	ch.puts( define( "P4OSNAME", $p4osname ) )
	ch.puts( define( "P4CFLAGS", $CFLAGS ) )
	ch.puts( define( "P4LIBPATH",$LIBPATH.join( ":" ) ) )
	ch.puts( define( "P4LIBS",   $libs ) )
    end
end

#*******************************************************************************
# Add a Perforce API library making sure it's properly named on NT
#*******************************************************************************
def api_lib( lib )
    rlib = lib
    rlib = "lib" + lib if ( $p4osname == "NT" )
    use_lib( rlib )
end
 
#*******************************************************************************
# Add a library to the link command bypassing lame have_library/find_library
# which utterly suck for C++ libs. 
# NOTE: Add libs in *reverse* order.
#*******************************************************************************
def use_lib( lib ) 
    $libs = append_library( $libs, lib )
end

#*******************************************************************************
# Function to write the makefile itself. This is mostly a wrapper around
# mkmf's create_makefile(), but due to some deficiencies in mkmf w.r.t C++
# sources, we occasionally have to edit the makefile
#*******************************************************************************
def write_makefile( name )
    create_makefile( name )
    if ( CONFIG[ "CC" ] =~ /^gcc/ )
	# Then we need to add a definition of CXX to the makefile
	# or the build will fail on some platforms (Cygwin at least).
	File.unlink( "Makefile.tmp" ) if File.exists?( "Makefile.tmp" )
	File.rename( "Makefile", "Makefile.tmp" )
	inmf = File.open( "Makefile.tmp" )
	outmf = File.open( "Makefile", "w+" )
	seen = false
	inmf.each do
	    |line|
	    if ( !seen && line =~ /^CC = / )
		outmf.puts( "CXX = " + CONFIG[ "CXX" ] )
		seen = true
	    end
	    outmf.puts( line )
	end
	inmf.close
	outmf.close
	File.unlink( "Makefile.tmp" )
    end


end

#*******************************************************************************
# Start of main functionality
#*******************************************************************************

opts = GetoptLong.new(
		[ "--apidir",	"-d", 	GetoptLong::REQUIRED_ARGUMENT ],
		[ "--apilibdir", 	GetoptLong::REQUIRED_ARGUMENT ],
		[ "--apiincludedir", 	GetoptLong::REQUIRED_ARGUMENT ],
		[ "--apiver",	"-v",	GetoptLong::REQUIRED_ARGUMENT ],
		[ "--gccver",		GetoptLong::REQUIRED_ARGUMENT ]
		)
$apilibdir = nil
$apiincludedir = nil
$apiver = nil
$p4gccver = 3

opts.each do
    |opt,arg|
    if ( opt == "--apidir" )
	$apiincludedir = $apilibdir = File.expand_path( arg ) 
    elsif( opt == "--apilibdir" )
	$apilibdir = File.expand_path( arg )
    elsif( opt == "--apiincludedir" )
	$apiincludedir = File.expand_path( arg )
    elsif( opt == "--apiver" )
	$apiver = arg 
    elsif( opt == "--gccver" )
	$p4gccver = arg[0,1].to_i 
    end
end

puts( "" )
puts( "Starting to configure P4/Ruby for building" )
puts( "" )

#
# If the user has not supplied the api directories or apiver we prompt for them
#
dirproc = Proc.new {
	    |dir|
	    dir = File.expand_path( dir )
	    if ( ! File.directory?( dir ) )
		puts( "No such directory. Try again" )
		false
	    else
		true
	    end
	}

if ( ! $apilibdir )
    $apilibdir = prompt( "Where are the Perforce API Libraries?", dirproc )
    $apilibdir = File.expand_path( $apilibdir )
end

if ( ! $apiincludedir )
    $apiincludedir = prompt( "Where are the Perforce API Headers?", dirproc )
    $apiincludedir = File.expand_path( $apiincludedir )
end

if ( ! $apiver )

    verproc = Proc.new {
		|ver|
		if ( ver =~ /^\d{4}\.\d/ )
		    true 
		else
		    puts( "Invalid version number. Should be like '2002.1'" )
		    false
		end
	    }

    $apiver = prompt( "Enter Perforce API Version", verproc )
end

#
# Work out the target OS, to guess the right defines to pass to the 
# Perforce API. Set this globally so we can refer to it elsewhere.
#

$p4osname = RUBY_PLATFORM.dup
$p4osname.gsub!( /.*-(.*)(-.*)?/, '\1' )
$p4osname.gsub!( /(\d+\.?)*/, "" )
$p4osname.upcase!
$p4osname.gsub!( /MSWIN/, "NT" )

set_apilibdir( $apilibdir )
set_apiincludedir( $apiincludedir )
set_platform_opts()
set_platform_cflags()
use_platform_libs()

# Perforce API libraries - in reverse order 
api_lib( "supp" )
api_lib( "rpc" )
api_lib( "client" )

# 
# OK, now we know what we're going to do, let's tell the user
#
puts <<EOS

P4/Ruby Configuration Summary
-----------------------------

Using Perforce API Version: #{$apiver}
API headers and libs from : #{$apidir}
Using OS name             : #{$p4osname}
CFLAGS                    : #{$CFLAGS}
LIBPATH                   : #{$LIBPATH.join(":")}
libs                      : #{$libs}

EOS

puts( "Creating extconf.h" )
write_extconf_h
write_makefile( "P4" )
