#******************************************************************************* # Usage: # # ruby p4conf.rb --apidir= # #******************************************************************************* require 'mkmf' require 'rbconfig' require "getoptlong" 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 end attr_reader :major, :minor include Comparable def to_s "#{major}.#{minor}" 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[ 'target_cpu' ].upcase $p4osname = CONFIG[ 'target_os' ].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` ver_re = /^(\d+)\.(\d+)/ if( md = ver_re.match( $p4osver ) ) 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 # Disambiguate the platform on Mac. We're not building a Universal binary here... if( $p4osname == "DARWIN" ) $p4osplat = uname_platform end # 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 == "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 " $CFLAGS += "/MT " end if( $p4osname == "SOLARIS" ) $CFLAGS += "-Dsolaris " end if( $p4osname == "DARWIN" ) $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" if( $p4osver =~ /^9/ ) # P4API is built with -fvisibility-inlines-hidden, so we need # to do the same. $CFLAGS += "-fvisibility-inlines-hidden " # Also, we only build for one architecture (no universal binaries), # so we need to strip out the other -arch flags from both $CFLAGS # and $LDFLAGS $CFLAGS.gsub!( /-arch [A-z0-9]+ ?/, "" ) $CFLAGS += "-arch i386 " if( $p4osplat == "X86" ) $CFLAGS += "-arch ppc " if( $p4osplat == "PPC" ) $LDFLAGS.gsub!( /-arch [A-z0-9]+ ?/, "" ) $LDFLAGS += "-arch i386 " if( $p4osplat == "X86" ) $LDFLAGS += "-arch ppc " if( $p4osplat == "PPC" ) end 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++" ) 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" ) use_lib( "libcmt" ) elsif( $p4osname == "CYGWIN" ) # Clear out the bogus libs on cygwin. CONFIG[ "LIBS" ] = "" elsif( $p4osname == "DARWIN" && $p4osver.to_i >= 8 ) $LDFLAGS += " -framework Carbon" end end #******************************************************************************* # Set directory for the Perforce API #******************************************************************************* def set_apidir( path ) libdir = path + "/lib" libdir = File.directory?( libdir ) ? libdir : path hdrdir = path + "/include/p4" hdrdir = File.directory?( hdrdir ) ? hdrdir : path $LIBPATH = [ libdir ] | $LIBPATH $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+)[\. ](\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 ] ) break 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 <" ) ch.puts( define( "P4APIVER_STRING", $apiver ) ) ch.puts( define( "P4APIVER_ID", $apiver.to_i, false ) ) ch.puts( define( "P4OSNAME", $p4osname ) ) ch.puts( define( "P4OSPLAT", $p4osplat ) ) ch.puts( define( "P4OSVER", $p4osver ) ) ch.puts( define( "P4CFLAGS", $CFLAGS.gsub( '"', '\"' ) ) ) ch.puts( define( "P4LIBPATH", $LIBPATH.join( ":" ) ) ) ch.puts( define( "P4LIBS", $libs ) ) ch.puts( define( "ID_OS", $p4osname + $p4osver + $p4osplat ) ) ch.puts( define( "ID_REL", $p4ruby_version.join( "." ) ) ) ch.puts( define( "ID_PATCH", $p4ruby_patchlevel, true ) ) ch.puts( define( "ID_Y", $p4ruby_date[ 0 ] ) ) ch.puts( define( "ID_M", $p4ruby_date[ 1 ] ) ) ch.puts( define( "ID_D", $p4ruby_date[ 2 ] ) ) ch.puts( define( "ID_API", $apiver ) ) ch.puts <