#******************************************************************************* # Usage: # # ruby p4conf.rb --apidir= # #******************************************************************************* require 'mkmf' require 'rbconfig' require "getoptlong" require "fileutils" class ApiVersion def initialize( major, minor = nil ) if( major.kind_of?( String ) && ! minor ) if( major =~ /(\d+)\.(\d+)/ ) major = $1 minor = $2 else raise( "Bad API version: #{major}" ) end end @major = major.to_i @minor = minor.to_i @type = nil end def set_type( type ) if( type.kind_of?( String ) ) @type = type end end attr_reader :major, :minor, :type include Comparable def to_s if( @type and not @type.empty? ) "#{major}.#{minor}.#{@type.upcase}" else "#{major}.#{minor}" end end def to_i major << 8 | minor end def <=>( other ) hi = @major <=> other.major lo = @minor <=> other.minor return hi == 0 ? lo : hi end 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 #******************************************************************************* # Find out as much info about the platform as we can. Ideally we want to # determine the OS name, version and CPU architecture so we can decide # which compiler flags should be used for the Perforce API for that # platform. #******************************************************************************* def probe_platform # # Work out the target OS, Version, and Platform # $p4osplat = CONFIG[ 'arch' ].split( /-/ )[ 0 ].upcase $p4osname = CONFIG[ 'arch' ].split( /-/ )[ 1 ].upcase $p4osname = $p4osname.gsub( /MSWIN32(_\d+)?/, "NT" ) $p4osname = $p4osname.split( "-" ).shift $p4osver = "" # Now work out the OS version if( $p4osname == "NT" ) $p4osver = "" elsif( $p4osname =~ /FREEBSD([0-9]+)/ ) $p4osname = "FREEBSD" $p4osver = $1 elsif( $p4osname =~ /DARWIN([0-9]+)/ ) $p4osname = "DARWIN" $p4osver = $1 elsif( $p4osname =~ /AIX(5)\.(\d)/ ) # AIX osname looks like "aix5.3.0.0" $p4osname = "AIX" $p4osver = $1 + $2 elsif( $p4osname =~ /SOLARIS2\.(\d+)/ ) $p4osname = "SOLARIS" $p4osver = $1 else # Try running a 'uname -r' to work out the version begin $p4osver=`uname -r`.chomp ver_re = /^(\d+)\.(\d+)/ md = ver_re.match( $p4osver ) if( md ) maj = md[1].to_i min = md[2].to_i $p4osver = maj.to_s + min.to_s end rescue # Nothing - if it failed, it failed. end end # On Mac OSX, fix the build to 64 bit arch $p4osplat = "X86_64" if( $p4osname == "DARWIN" ) # Translate Ruby's arch names into Perforce's. Mostly the same so # only the exceptions are handled here. if( $p4osplat =~ /^I.86$/ ) $p4osplat = "X86" elsif( $p4osplat =~ /^AMD64$/ ) $p4osplat = "X86_64" elsif( $p4osplat == "POWERPC" ) $p4osplat = "PPC" end end def uname_platform plat = "UNKNOWN" begin plat = `uname -p` plat = plat.chomp.upcase rescue # Nothing - if it failed, it failed. end plat end #******************************************************************************* # Allow the user to override our guesses as to the platform and provide # an explicit API build version. #******************************************************************************* def override_platform( plat ) archs = %w{ x86 amd64 ppc sparc arm axp } # Build the regular expression we're going to use, first as a string plat_re = '(\D+)(\d+)?(' plat_re += archs.join( '|' ) plat_re += ')?' # Now convert to a regexp plat_re = Regexp.new( plat_re ) if( md = plat_re.match( plat ) ) $p4osname = md[ 1 ].upcase $p4osver = md[ 2 ] || "" $p4osplat = ( md[ 3 ] || "" ).upcase end end #******************************************************************************* # Set platform specific compile options #******************************************************************************* def set_platform_cflags $CFLAGS += "-DOS_#{$p4osname} " $CFLAGS += "-DOS_#{$p4osname}#{$p4osver} " $CFLAGS += "-DOS_#{$p4osname}#{$p4osver}#{$p4osplat} " if( $p4osname == "NT" ) $CFLAGS += "/DCASE_INSENSITIVE " end if( $p4osname == "MINGW32") $CFLAGS += "-DOS_NT -DCASE_INSENSITIVE " end if( $p4osname == "SOLARIS" ) $CFLAGS += "-Dsolaris " end if( $p4osname == "DARWIN" ) # Only build for 64 bit if we have more than one arch defined in CFLAGS $CFLAGS.slice!( "-arch i386" ) $CFLAGS.slice!( "-arch ppc" ); $CFLAGS += "-DCASE_INSENSITIVE " end end #******************************************************************************* # Set platform specific build options #******************************************************************************* def set_platform_opts #*************************************************************************** # 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. Since mkmf is # pretty convoluted it seems the safest way to ensure that g++ is used is # to update the LDSHARED and CC values directly #*************************************************************************** # Expand any embedded variables (like '$(CC)') CONFIG[ 'CC' ] = Config::CONFIG[ 'CC' ] CONFIG[ 'LDSHARED' ] = Config::CONFIG[ 'LDSHARED' ] # Now ensure we're using g++ instead of gcc CONFIG[ 'CC' ].sub!( /gcc/, "g++" ) CONFIG[ 'LDSHARED' ].sub!( /gcc/, "g++" ) CONFIG[ "CXX" ] = CONFIG[ "CC" ] unless CONFIG.has_key?( "CXX" ) # O/S specific oddities 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" ) elsif ( $p4osname == "DARWIN" ) CONFIG[ "LDSHARED" ] = CONFIG[ "CXX" ] + " -bundle" elsif ( $p4osname == "FREEBSD" || $p4osname == "LINUX" ) # FreeBSD 6 and some Linuxes use 'cc' for linking by default. The # gcc detection patterns above won't catch that, so for these # platforms, we specifically convert cc to c++. CONFIG[ "LDSHARED" ].sub!( /^cc/, "c++" ) elsif( $p4osname == "MINGW32" ) # When building with MinGW we need to statically link libgcc CONFIG[ "LDSHARED" ] = CONFIG[ "LDSHARED" ] + " -static-libgcc -static-libstdc++" 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 == "NT" ) use_lib( "advapi32" ) use_lib( "wsock32" ) use_lib( "kernel32" ) use_lib( "oldnames" ) elsif( $p4osname == "CYGWIN" ) # Clear out the bogus libs on cygwin. CONFIG[ "LIBS" ] = "" elsif( $p4osname == "DARWIN" && $p4osver.to_i >= 8 ) # Only build for 64 bit if we have more than one arch defined in CFLAGS $LDFLAGS.slice!( "-arch i386" ) $LDFLAGS.slice!( "-arch ppc" ); $LDFLAGS += " -framework Carbon" end end #******************************************************************************* # Set directory for the Perforce API #******************************************************************************* def set_apidir( path, sslpath ) libdir = path + "/lib" libdir = [ File.directory?( libdir ) ? libdir : path ] libdir.push( sslpath ) unless( sslpath.nil? or sslpath.empty? ) hdrdir = path + "/include/p4" hdrdir = File.directory?( hdrdir ) ? hdrdir : path # This bit's real ugly... Ruby's mkmf.rb seems to have a very strange idea # about link order. It puts the default library paths ahead of thos suppled # by the user at runtime. When library names clash that doesn't help # anyone. So, we mess with DEFLIBPATH here, as well as LIBPATH in the hope # that a belt-and-braces approach will do the trick on all platforms. # Sadly, DEFLIBPATH doesn't always exist so we have to test for it. $DEFLIBPATH = [ libdir ] | $DEFLIBPATH if $DEFLIBPATH $LIBPATH = [ libdir ] | $LIBPATH $DEFLIBPATH.flatten! $LIBPATH.flatten! $CFLAGS += " -I#{hdrdir} " end #******************************************************************************* # Get the version of the Perforce API in a directory #******************************************************************************* def get_api_version( dir ) # # 2007.2 and later APIs put the Version file in the 'sample' # subdirectory. Look there if we can't find it in the API root # ver_file = dir + "/Version" unless File.exists?( ver_file ) ver_file = dir + "/sample/Version" return nil unless File.exists?( ver_file ) end re = Regexp.new( '^RELEASE = (\d+)\s+(\d+)\s+(\w*\S*)\s*;' ) rp = Regexp.new( '^PATCHLEVEL = (\d+)' ) apiver = nil File.open( ver_file, "r" ) do |f| f.each_line do |line| if md = re.match( line ) apiver = ApiVersion.new( md[ 1 ], md[ 2 ] ) apiver.set_type( md[ 3 ] ) elsif md = rp.match( line ) $apipatch = md[ 1 ] end end end puts( "Found #{apiver} Perforce API in #{dir}" ) return apiver end #******************************************************************************* # Function to check that the version of the API used is digestible #******************************************************************************* def check_api_version( ver ) return true if ver == $TARGET_API_VERSION if( ver != $TARGET_API_VERSION ) puts <