#!/usr/bin/perl -w

#
# configure
# part of Equivalence, version 0.2.x
#
# Copyright (c) 2005-2007 Operational Dynamics Consulting Pty Ltd and Others
# 
# The code in this file, and the library it is a part of, are made available
# to you by the authors under the terms of the "GNU General Public Licence,
# version 2". See the LICENCE file for the terms governing usage and
# redistribution.
#

#
# THIS IS A PROTOTYPE. Although functional enough, this has grown to the point
# of being a somewhat embarassing and monolithic piece of code. Patches are
# gladly accepted to this file to fix bugs and improve prerequisite detection,
# but be aware that a new and refactored version is in progress.  With any
# luck, it will be easier to extend and suitable for use by other projets. See
# http://research.operationaldynamics.com/projects/equivalence/ In the mean
# time, see README for instructions on how to use this one.
#

use strict;

use File::Basename;

#
# Configure prototype for building. We:
#
# - determine the operating system
#
# - set known defaults that correspond to that OS
#
# - for items where we have multiple possibilities, work through the
#   possibilities until we find one
#
# - for items where there are options to choose from (notably which java
#   compiler and VM we're using), we select a sensible default unless
#   instructed otherwise on the command line.
#

my $os;
my $quiet;

# There's nothing worse than having an old config file, getting half way
# through this, having it break, and then being able to build, but getting
# errors because configure really didn't finish. We do leave .config.tmp 
# in place on error to facilitate troubleshooting.

`rm -f .config`;
`rm -f Hello.java Hello.class`;


# --------------------------------------------------------------------
# Utility and checking functions
# --------------------------------------------------------------------

#
# Very simply: if the msg does not contain a newline, then it is assumed
# to introduce a statement, so we print it and left pad it with spaces.
# If the text does contain a newline, then probably it is concluding a 
# statment (ok, failed, whatever) but not necessarily - just print the thing
# without any padding.
#
sub output {
	my $str = shift;
	if ($str =~ /\n/) {
		print $str unless $quiet;
	} else {
		printf "%-35s", $str unless $quiet;
	}
}

sub which {
	my $program = shift;
	my $path = $ENV{'PATH'};
	my @path_dirs = split /:+/, $path;
	foreach my $dir (@path_dirs) {
		if (-f "$dir/$program") {
			return "$dir/$program";
		}
	}
	return $program;
}

sub bail {
	my $status = shift || "failed";
	my $msg = shift || "";

	# assuming that we're in an incomplete line 
	output "$status\n\n";


	print "$msg\n\n" if $msg;
	print "Failed to complete configuration.\n";
	exit(1);
}

# The files (jars) to check for should be listed in order of preference, as the
# first one found will be the one selected.  For example, if you want version 3
# but version 2 will do, list them in that order.  Typically, this means that
# you list newer libraries first, on the presumption that you'd rather use the
# newer one than the older one; when adding upgrades put them above the
# already present entries.

sub check_prereq (\@$$@) {
	my ($jararrayref, $item, $package, @files) = @_;

	output " - ".$item;

	my $str;
	my $tries = "";
	my $found = "";

	foreach my $file ( @files ) {
		if ( -f "$file" ) {
			$found = $file;
			last;
		}
		$tries .= ($tries ? ", or" : "" ) . "\n\t".  basename($file) . "\t(looked in ".dirname($file).")";
	}

	if ( ! "$found" ) {
		$str = "In order to build prototype, you need\n".$tries;
		$str .= "\n\nwhich is part of the $item Java library.\n";
		$str .= "On a ".ucfirst($os)." system, you should be able to get this requirement by doing:\n\n";
		$str .= "     # ";

		if ($os eq "gentoo") {
			$str .= "emerge";
		} elsif ($os eq "debian") {
			$str .= "apt-get install";
		} elsif ($os eq "fedora") {
			$str .= "yum install";
		} elsif ($os eq "solaris") {
			$str .= "pkgadd";
		} else {
			$str .= "[FIXME fetch and install command for this OS]"; 
		}
		$str .= " $package";

		bail "not found!", $str;
	}
	print "found\n";

	push (@$jararrayref, $found);
}

# if we return without setting the variable pointed at by scalarref, its being
# empty will be used later to indicate that this compiler wasn't present /
# usable.
#
# The "not present" check is somewhat spurious given the input in many cases
# is the result of a `which` call.

sub check_compiler (\$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	# appealing to my sense of economy, we only print something out if 
	# it's there - that way we can list lots of options to check without
	# cluttering things endlessly.
	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	# Ok, so inline code is lame, but it's so small, and only one,
	# and, avoids having a file in tests/ that will be picked up later
	# as neededing compiling.
	if (! -f "Hello.java") {
		open HELLO, ">Hello.java";
		print HELLO <<HERE ;
public class Hello {
	public static void main(String[] args) {
		System.out.println("Hello");
	}
}
HERE
		close HELLO;
	}

	`$program $args Hello.java >/dev/null 2>&1`;
	if ($? != 0) {
		output "doesn't work\n";
		$$scalarref = "";
		return
	}

	output "works\n";
	$$scalarref = "$program $args";
}

#
# Check that a jar program somehwere works.
#

sub check_jar (\$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	# appealing to my sense of economy, we only print something out if 
	# it's there - that way we can list lots of options to check without
	# cluttering things endlessly.
	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	`$program cf Hello.jar $args Hello.class >/dev/null 2>&1`;
	if (($? != 0) || (! -f "Hello.jar")) {
		output "doesn't work\n";
		$$scalarref = "";
		return
	}

	# TODO validate the result

	output "works\n";
	$$scalarref = "$program $args";
}

#
# Check that a javadoc program somehwere works.
#

sub check_javadoc (\$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	# appealing to my sense of economy, we only print something out if 
	# it's there - that way we can list lots of options to check without
	# cluttering things endlessly.
	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	# TODO validate the result

	output "found\n";
	$$scalarref = "$program $args";
}


sub check_runtime (\$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	my $output = `$program -version 2>&1 | grep '^java version'`;
	if (!(($output =~ /^java version \"1\.4\.2/i) ||
	      ($output =~ /^java version \"1\.5\./i)  ||
	      ($output =~ /^java version \"1\.6\./i))) {
		output "not >= 1.4.2\n";
		$$scalarref = "";
		return
	}

	$output = `$program $args Hello 2>/dev/null`;
	chomp $output;

	if (($? != 0) || ($output ne "Hello")) {
		output "doesn't work\n";
		$$scalarref = "";
		return
	}

	output "works\n";
	$$scalarref = "$program $args";
}


# test gcj building and executing native code.

sub check_native (\$$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];
	my $rpath = $_[4];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	# appealing to my sense of economy, we only print something out if 
	# it's there - that way we can list lots of options to check without
	# cluttering things endlessly.
	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	# Hello.java should already available.

	if ( ! -f "Hello.java") {
		bail "internal error","where did Hello.java go?";
	}

	# compile
	`$program $args -o Hello.o -c Hello.java >/dev/null 2>&1`;
	if ($? != 0) {
		output "compiling doesn't work\n";
		$$scalarref = "";
		return
	}

	if ( ! -f "Hello.o") {
		bail "internal error","why isn't Hello.o present?";
	}

	# link. Note that an argument suitable for -Wl,-rpath=<gcj_base/lib> must
	# be passed in as the last argument.
	`$program $args -Wl,-rpath=$rpath -o Hello --main=Hello Hello.o >/dev/null 2>&1`;
	if ($? != 0) {
		output "linking doesn't work\n";
		$$scalarref = "";
		return
	}

	# run
	my $output = `./Hello 2>/dev/null`;
	chomp $output;

	if (($? != 0) || ($output ne "Hello")) {
		output "executable doesn't work\n";
		$$scalarref = "";
		return
	}

	output "works\n";
	$$scalarref = "$program $args";
}

sub check_jni_header_generator(\$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	# appealing to my sense of economy, we only print something out if 
	# it's there - that way we can list lots of options to check without
	# cluttering things endlessly.
	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	# FIXME do a test!

	output "found\n";
	$$scalarref = "$program $args";
}


# test for C compiler

sub check_CC (\$$$$) {
	my $scalarref = $_[0];
	my $item = $_[1];
	my $program = $_[2];
	my $args = $_[3];

	chomp $program;
	if ( ! -f "$program") {
		$$scalarref = "";
		return;
	}

	# appealing to my sense of economy, we only print something out if 
	# it's there - that way we can list lots of options to check without
	# cluttering things endlessly.
	output " - ".$item;

	if ( ! -x "$program") {
		output "found but not executable\n";
		$$scalarref = "";
		return;
	}

	# Hello.java should already available.

		open HELLO, ">Hello.c";
		print HELLO <<HERE ;
#include <stdio.h>
int main(int argc, char **argv) {
	printf("Hello");
	return 0;
}
HERE
		close HELLO;

	# compile
	`$program $args -o Hello.o -c Hello.c >/dev/null 2>&1`;
	if ($? != 0) {
		output "compiling doesn't work\n";
		$$scalarref = "";
		return
	}

	if ( ! -f "Hello.o") {
		bail "internal error","why isn't Hello.o present?";
	}

	# link
	`$program $args -o Hello Hello.o >/dev/null 2>&1`;
	if ($? != 0) {
		output "linking doesn't work\n";
		$$scalarref = "";
		return
	}

	# run
	my $output = `./Hello 2>/dev/null`;
	chomp $output;

	if (($? != 0) || ($output ne "Hello")) {
		output "executable doesn't work\n";
		$$scalarref = "";
		return
	}

	output "works\n";
	$$scalarref = "$program $args";
}

# --------------------------------------------------------------------
# Process command line arguments for overrides
# --------------------------------------------------------------------

my $compiler;
my $runtime;
my $jdk_home;
my $gcj_home;
my $javagnome_home = "/opt/local/java-gnome";
my $jamvm_bin;
my $cacao_bin;

foreach my $arg (@ARGV) {
	my ($key, $value) = split /=/, "$arg";

	if ($key eq "quiet") {
		$quiet = 1;
	} elsif ($key =~ /^runtime/) {
		$runtime="$value";
	} elsif ($key =~ /^compiler/) {
		$compiler="$value";
	} elsif ($key =~ /^jdk/) {
		$jdk_home="$value";
	} elsif ($key =~ /^gcj/) {
		$gcj_home="$value";
	} elsif ($key =~ /^cacao/) {
		$cacao_bin="$value";
	} elsif ($key =~ /^jamvm/) {
		$jamvm_bin="$value";
	} elsif ($key =~ /^java-gnome/) {
		$javagnome_home="$value";
	}
}


# check jdk_home and gcj_home overrides. compiler and runtime are checked 
# later (at the end) against choices that have been validated.

if ($jdk_home) {
	$jdk_home =~ s/\/$//;
	if (! -x "$jdk_home/bin/javac") {
		bail "bad override", "jdk_home specified doesn't seem to be a Java Development Kit home directory!";
	}
}

if ($gcj_home) {
	$gcj_home =~ s/\/$//;
	if (! -x "$gcj_home/bin/gcj") {
		bail "bad override", "gcj_home specified doesn't seem to be a GCJ install!";
	}
}


# --------------------------------------------------------------------
# Determine Operating System
# --------------------------------------------------------------------

output "\n";

open CONFIG, ">.config.tmp";
print CONFIG <<HERE ;
# This is an automatically generated Makefile fragment which is used to
# configure prototype for building. Do not edit (your changes will be overwritten
# next time ./configure is run), do not commit to repository. Anyone packaging
# prototype on any operating system: please do not override this file by patching
# it! Figure out what the problem is, and let us know so we can improve the
# ./configure perl script which generates it.

HERE

output "equivalence, v0.2\n";
output "...configuring Java projects to build and run on Linux & Unix\n";
output "\n";

output "Identify operating system:";

if (( -f "/etc/gentoo-release" ) || ( -f "/etc/make.conf" )) {
	output "Gentoo\n";
	$os = "gentoo";
} elsif ( -f "/etc/debian_version") {
	output "Debian\n";
	# and Ubuntu
	$os = "debian";
} elsif ( -f "/etc/fedora-release" ) {
	output "Fedora Core";
	$os = "fedora";
} elsif ( -f "/etc/release" ) {
	if (`grep Solaris /etc/release`) {
		output "Solaris";
		$os = "solaris";
	}
}

if ($os) {
	print CONFIG "OS=$os\n";
} else {
	bail "unknown!", <<HERE ;
What we really need you to do is to look into this configure program, and
tell us what to add. Based on the examples of what is specified for other
distributions, you can probably quickly figure out what the appropriate
settings are for your platform. 

Letting us know what changes you had to make here (ie, whatever actions
resulted in a .config that allows you to build, test, and run prototype) we can
help others with your operating system take advantage of this program.
HERE
}

output "\n";


# --------------------------------------------------------------------
# Specifiy locations of dependencies, by operating system, and 
# verify pre-requsites are present.
# --------------------------------------------------------------------

my @javagnome_jars;
my $javagnome_lib_path;
my $jni_path;

output "Check for required jar files:\n";


# ADVICE TO PEOPLE EXTENDING THIS SECTION FOR THEIR OWN OPERATING SYSTEM:
# You might as well list things in such an order that you tell the builder
# the package whose dependencies will bring the rest of the pre-requisites 
# in along the way...

if ($os eq "gentoo") {

# NEW	check_prereq(@javagnome_jars,
# NEW		"glib Java bindings",
# NEW		"glib-java",
# NEW		"$javagnome_home/share/java/glib-4.0.jar",
# NEW		"/usr/share/glib-java-4.0/lib/glib-4.0.jar");
# NEW	check_prereq(@javagnome_jars,
# NEW		"GTK+ Java bindings",
# NEW		"gtk-java",
# NEW		"$javagnome_home/share/java/gtk-4.0.jar",
# NEW		"/usr/share/gtk-java-4.0/lib/gtk-4.0.jar");


	if (-f $javagnome_home) {
		$javagnome_lib_path = "$javagnome_home/lib";
		$jni_path = "$javagnome_lib_path:/usr/lib";
	} else {
		$javagnome_lib_path = "/usr/lib";
		$jni_path = "/usr/lib";
	}

} elsif ($os eq "debian") {

# NEW	check_prereq(@javagnome_jars,
# NEW		"glib Java bindings",
# NEW		"libgtk-java",
# NEW		"$javagnome_home/share/java/glib-4.0.jar",
# NEW		"/usr/share/java/glib-4.0.jar");
# NEW	check_prereq(@javagnome_jars,
# NEW		"GTK+ Java bindings",
# NEW		"libgtk-java",
# NEW		"$javagnome_home/share/java/gtk-4.0.jar",
# NEW		"/usr/share/java/gtk-4.0.jar");


	if (-f $javagnome_home) {
		$javagnome_lib_path = "$javagnome_home/lib";
		$jni_path = "$javagnome_lib_path:/usr/lib:/usr/lib/jni";
	} else {
		$javagnome_lib_path = "/usr/lib";
		$jni_path = "/usr/lib:/usr/lib/jni";
	}
	
} elsif ($os eq "fedora") {

# NEW	check_prereq(@javagnome_jars,
# NEW		"glib Java bindings",
# NEW		"glib-java",
# NEW		"$javagnome_home/share/java/glib-4.0.jar",
# NEW		"/usr/share/java/glib-4.0.jar");
# NEW	check_prereq(@javagnome_jars,
# NEW		"GTK+ Java bindings",
# NEW		"gtk-java",
# NEW		"$javagnome_home/share/java/gtk-4.0.jar",
# NEW		"/usr/share/java/gtk-4.0.jar");


	if ( -f $javagnome_home) {
		$javagnome_lib_path = "$javagnome_home/lib";
		$jni_path = "$javagnome_lib_path:/usr/lib";
	} else {
		$javagnome_lib_path = "/usr/lib";
		$jni_path = "/usr/lib";
	}
	
} elsif ($os eq "solaris") {

# NEW	check_prereq(@javagnome_jars,
# NEW		"glib Java bindings",
# NEW		"SUNWglib-java",
# NEW		"$javagnome_home/share/java/glib-4.0.jar",
# NEW		"/usr/share/lib/java/glib-4.0.jar");
# NEW	check_prereq(@javagnome_jars,
# NEW		"GTK+ Java bindings",
# NEW		"SUNWlibgtk-java",
# NEW		"$javagnome_home/share/java/gtk-4.0.jar",
# NEW		"/usr/share/lib/java/gtk-4.0.jar");


	if ( -f $javagnome_home) {
		$javagnome_lib_path = "$javagnome_home/lib";
		$jni_path = "$javagnome_lib_path:/usr/lib";
	} else {
		$javagnome_lib_path = "/usr/lib";
		$jni_path = "/usr/lib";
	}
	
} else {
	bail "failed!", "This OS not configured with defaults!\nTHIS IS AN INTERNAL ERROR, PLEASE FILE A BUG.";
}


# --------------------------------------------------------------------
# Record jar locations
# --------------------------------------------------------------------

print CONFIG <<HERE ;

# The lists of jars are colon separated, suitable for being
# concatonated into a CLASSPATH

HERE

if ($javagnome_home eq "{java-gnome_home}") {
	$javagnome_home = "/usr";
}


print CONFIG "JAVAGNOME_HOME=$javagnome_home\n";
print CONFIG "JAVAGNOME_JARS=".join(":",@javagnome_jars)."\n";
print CONFIG "JAVAGNOME_LIB_PATH=$javagnome_lib_path\n";

print CONFIG <<HERE ;

# the JNI_PATH is what is passed as LD_LIBRARY_PATH when running
# java Virtual Machines so that JNI shared libraries are picked up 

HERE
print CONFIG "JNI_PATH=$jni_path\n";

output "\n";

# --------------------------------------------------------------------
# Check compilers: locations, necessary arguments, and that they work
# --------------------------------------------------------------------

output "Check Java compilers:\n";

# compilers we will check for:
my $javac;
my $jikes;
my $gcjC;	# The moniker $gcjC referes to `gcj -C`
my $kaffec;

# tools we check at same time (not switchable)
my $javah;
my $jar;
my $javadoc;

if ($os eq "gentoo") {

	if ( ! -x "/usr/bin/java-config") {
		bail "", "INTERNAL ERROR couldn't find java-config";
	}
	# this is getting rediculous
	my $java_home;
	if ($jdk_home) {
		$java_home = "$jdk_home";
	} else {
		$java_home = `java-config -O`;
		chomp $java_home;
	}

	# check jikes. Since Jikes isn't a JRE of its own, it needs to be
	# told about where to find the Java system classes. (java.lang, etc)
	# we make an educated guess until java-config can tell us properly.
	my $rt = "$java_home"."/jre/lib/rt.jar";
	check_compiler($jikes, "IBM jikes", which("jikes"), "-bootclasspath $rt -nowarn -source 1.4 -target 1.4 +T=4 +Z0") if ( -f "$rt");
	
	# check javac (the one specified by Gentoo's java-config tool)
	# The $vendor business is just some precision prettiness for the
	# display.
	my $javac_candidate;
	my $javah_candidate;
	my $jar_candidate;
	my $javadoc_candidate;
	my $vendor;

	if ($jdk_home) {
		$javac_candidate = "$jdk_home/bin/javac";
		$javah_candidate = "$jdk_home/bin/javah";
		$jar_candidate = "$jdk_home/bin/jar";
		$javadoc_candidate = "$jdk_home/bin/javadoc";
		$vendor = "Specified";
	} else {
		$javac_candidate = `java-config --javac`;

		$javah_candidate = `java-config -O`;
		chomp $javah_candidate;
		$javadoc_candidate = $javah_candidate;

		$javah_candidate .= "/bin/javah";
		$javadoc_candidate .= "/bin/javadoc";

		$jar_candidate = `java-config --jar`;
		$vendor = "System";
	}

	if ($javac_candidate =~ /sun/i) {
		$vendor = "Sun";
	} elsif ($javac_candidate =~ /blackdown/i) {
		$vendor = "Blackdown";
	} elsif ($javac_candidate =~ /ibm/i) {
		$vendor = "IBM";
	}
	check_compiler($javac, "$vendor javac", $javac_candidate, "-source 1.4 -target 1.4");

	# check gcj. -C means generate .class files, not .o files (which are
	# for linking into native executables.

	my $gcj_candidate;

	if ($gcj_home) {
		$gcj_candidate = "$gcj_home/bin/gcj";
	} else {
		$gcj_candidate = which("gcj");
	}
	check_compiler($gcjC, "GNU gcj -C (bytecode mode)", $gcj_candidate, "-C");

	# check tools
	check_jni_header_generator($javah, "$vendor javah", $javah_candidate, "-jni");
	check_jar($jar, "$vendor jar", $jar_candidate, "");
	check_javadoc($javadoc, "$vendor javadoc", $javadoc_candidate, "");

} elsif ($os eq "debian") {
	# we can do much better than this, especially for java/javac.
	# Do we access the alternatives system, or just go with known
	# paths, or...? `which` is lame

	# check jikes. Since Jikes isn't a JRE of its own, it needs to be told
	# about where to find the Java system classes. (java.lang, etc).
	check_compiler($jikes, "IBM jikes", "/usr/bin/jikes", "-bootclasspath /usr/share/classpath/glibj.zip -source 1.4 -target 1.4 +T=4 +Z0");

	# check for a proper "real" JDK's javac as installed (and maybe
	# selected in the alternatives system) by the user. In other words,
	# javac -> /opt/sun-jdk-1.4.2.02/bin/javac, not javac -> kaffec.
	my $javac_candidate;
	my $javah_candidate;
	my $jar_candidate;
	my $javadoc_candidate;
	my $vendor;

	if ($jdk_home) {
		$javac_candidate = "$jdk_home/bin/javac";
		$javah_candidate = "$jdk_home/bin/javah";
		$jar_candidate = "$jdk_home/bin/jar";
		$javadoc_candidate = "$jdk_home/bin/javadoc";
		$vendor = "Specified";
	} else {
		$javac_candidate = which("javac");
		$javah_candidate = "";
		$jar_candidate = "";
		$javadoc_candidate = "";
		$vendor = "System";
	}
	if ($javac_candidate =~ /sun/i) {
		$vendor = "Sun";
	} elsif ($javac_candidate =~ /blackdown/i) {
		$vendor = "Blackdown";
	} elsif ($javac_candidate =~ /ibm/i) {
		$vendor = "IBM";
	}
	check_compiler($javac, "$vendor javac", $javac_candidate, "-source 1.4 -target 1.4");

	# check gcj. The moniker $gcjC referes to `gcj -C`. See HACKING.
	my $gcj_candidate;
	if ($gcj_home) {
		$gcj_candidate = "$gcj_home/bin/gcj";
	} else {
		$gcj_candidate = which("gcj");
	}
	check_compiler($gcjC, "GNU gcj -C (bytecode mode)", $gcj_candidate, "-C -g");

	# check for kaffe's compiler
	check_compiler($kaffec, "Kaffe javac", "/usr/lib/kaffe/bin/javac", "");

	# check for JDK tools. To suit Debian prejudices, use GNU tools if found.
	check_jni_header_generator($javah, "$vendor javah", $javah_candidate, "-jni");
	check_jni_header_generator($javah, "GNU gcjh", which("gcjh"), "-jni") unless $javah;
	check_jni_header_generator($javah, "System javah", which("javah"), "-jni") unless $javah;

	check_jar($jar, "$vendor jar", $jar_candidate, "");
	check_jar($jar, "GNU fastjar", which("fastjar"), "") unless $jar;
	check_jar($jar, "System jar", which("jar"), "") unless $jar;

	check_javadoc($javadoc, "$vendor javadoc", $javadoc_candidate, "");
	check_javadoc($javadoc, "GNU gjdoc", which("gjdoc"), "");
	check_javadoc($javadoc, "System javadoc", which("javadoc"), "") unless $javadoc;

} elsif ($os eq "fedora") {
	# we can do much better than this, especially for java/javac.
	# Should we just go with known paths, or...? `which` is so lame

	# check for jikes. Presumably -bootclasspath will need to be set
	# like on Gentoo and Debian; to what?
	check_compiler($jikes, "IBM jikes", "/usr/bin/jikes", "-source 1.4 -target 1.4 +T=4 +Z0");

	my $javac_candidate;
	my $javah_candidate;
	my $jar_candidate;
	my $javadoc_candidate;
	my $vendor;
	if ($jdk_home) {
		$javac_candidate = "$jdk_home/bin/javac";
		$javah_candidate = "$jdk_home/bin/javah";
		$jar_candidate = "$jdk_home/bin/jar";
		$javadoc_candidate = "$jdk_home/bin/javadoc";
		$vendor = "Specified";
	} else {
		$javac_candidate = "/usr/lib/jvm/java-ibm/bin/javac";
		$javah_candidate = "/usr/lib/jvm/java-ibm/bin/javah";
		$jar_candidate = "/usr/lib/jvm/java-ibm/bin/jar";
		$javadoc_candidate = "/usr/lib/jvm/java-ibm/bin/javadoc";
		$vendor = "IBM";
	}
	check_compiler($javac, "$vendor javac", $javac_candidate, "-source 1.4 -target 1.4");

	# check for gcj
	my $gcj_candidate;
	if ($gcj_home) {
		$gcj_candidate = "$gcj_home/bin/gcj";
	} else {
		$gcj_candidate = which("gcj");
	}
	check_compiler($gcjC, "GNU gcj -C (bytecode mode)", $gcj_candidate, "-C");

	# check for kaffe's compiler
	check_compiler($kaffec, "Kaffe javac", which("kaffec"), "");

	check_jni_header_generator($javah, "$vendor javah", $javah_candidate, "-jni");
	check_jar($jar, "$vendor jar", $jar_candidate, "");
	check_javadoc($javadoc, "$vendor javadoc", $javadoc_candidate, "");

} elsif ($os eq "solaris") {
	check_compiler($jikes, "IBM jikes", "/usr/bin/jikes", "-bootclasspath /usr/java/jre/lib/rt.jar -source 1.4 -target 1.4 +T=4 +Z0");

	my $javac_candidate;
	my $javah_candidate;
	my $jar_candidate;
	my $javadoc_candidate;
	my $vendor;
	if ($jdk_home) {
		$javac_candidate = "$jdk_home/bin/javac";
		$javah_candidate = "$jdk_home/bin/javah";
		$jar_candidate = "$jdk_home/bin/jar";
		$javadoc_candidate = "$jdk_home/bin/javadoc";
		$vendor = "Specified";
	} else {
		$javac_candidate = "/usr/java/bin/javac";
		$javah_candidate = "/usr/java/bin/javah";
		$jar_candidate = "/usr/java/bin/jar";
		$javadoc_candidate = "/usr/java/bin/javadoc";
		$vendor = "Sun";
	}
	check_compiler($javac, "$vendor javac", $javac_candidate, "-source 1.4 -target 1.4");

	# check for gcj
	my $gcj_candidate;
	if ($gcj_home) {
		$gcj_candidate = "$gcj_home/bin/gcj";
	} else {
		$gcj_candidate = which("gcj");
	}
	check_compiler($gcjC, "GNU gcj -C (bytecode mode)", $gcj_candidate, "-C -g");

	# check for kaffe's compiler
	check_compiler($kaffec, "Kaffe javac", which("kaffec"), "");

	check_jni_header_generator($javah, "$vendor javah", $javah_candidate, "-jni");
	check_jar($jar, "$vendor jar", $jar_candidate, "");
	check_javadoc($javadoc, "$vendor javadoc", $javadoc_candidate, "");

} else {
	bail "failed!", "This OS not configured with a workable Java compiler checks!\nTHIS IS AN INTERNAL ERROR, PLEASE FILE A BUG.";
}

output "\n";

# --------------------------------------------------------------------
# Check runtimes
# --------------------------------------------------------------------

output "Check Java virtual machines:\n";

# runtimes we will check for:
my $java;
my $gij;
my $kaffe;
my $cacao;
my $jamvm;

if ($os eq "gentoo") {
	# check java (the one specified by Gentoo's java-config tool)
	# Is there any actual scenario where the javac would be from one 
	# vendor's JDK and the java from anothers JRE? I can't imagine, but
	# do the $vendor check again. It's only cosmetic in any event.
	my $java_candidate;
	my $vendor;

	if ($jdk_home) {
		$java_candidate = "$jdk_home/bin/java";
	} else {
		$java_candidate = `java-config --java`;
	}

	if ($java_candidate =~ /sun/i) {
		$vendor = "Sun";
	} elsif ($java_candidate =~ /blackdown/i) {
		$vendor = "Blackdown";
	} elsif ($java_candidate =~ /ibm/i) {
		$vendor = "IBM";
	} else {
		$vendor = "System";
	}
	check_runtime($java, "$vendor java VM", $java_candidate, "-client");

	# check gij (the bytecode interpreter from the GCJ project)
	my $gij_candidate;

	if ($gcj_home) {
		$gij_candidate = "$gcj_home/bin/gij";
	} else {
		$gij_candidate = which("gij");
	}
	check_runtime($gij, "GNU gij", $gij_candidate, "");

	# check kaffe
	check_runtime($kaffe, "kaffe VM", which("kaffe"), "");

	# check jamvm (an elegant bytecode interpreter used by many in the
	# CLASSPATH project to test new releases)
	my $jamvm_candidate;
	if ($jamvm_bin) {
		$jamvm_candidate = "$jamvm_bin";
	} else {
		$jamvm_candidate = "/usr/bin/jamvm";
	}
	check_runtime($jamvm, "JamVM VM", $jamvm_candidate, "");

	my $cacao_candidate;
	if ($cacao_bin) {
		$cacao_candidate = "$cacao_bin";
	} else {
		$cacao_candidate = "/usr/bin/cacao";
	}
	check_runtime($cacao, "CACAO VM", $cacao_candidate, "");

} elsif ($os eq "debian") {
	# check for a proper JDK/JRE java Virtual Machine (presumably either
	# blackdown, or the real thing from Sun or IBM, as installed by the
	# user).  NOTE that this does *NOT* mean Sable VM or kaffe (so, if the
	# Debian alternatives system can say that's what's providing
	# java-runtime, then we need to take advantage of that. This is for a
	# real JRE only, ie java -> /opt/sun-jdk-1.4.2.02/bin/java, not for
	# java -> kaffe.
	my $java_candidate;
	my $vendor;

	if ($jdk_home) {
		$java_candidate = "$jdk_home/bin/java";
		$vendor = "Specified";
	} else {
		$java_candidate = which("java");
		$vendor = "System";
	}
	check_runtime($java, "$vendor java VM", $java_candidate, "-client");

	# check gij (the bytecode interpreter from the GCJ project). In Debian
	# it is in package gij-3.4 and so named in /usr/bin.
	my $gij_candidate;
	if ($gcj_home) {
		$gij_candidate = "$gcj_home/bin/gij";
	} else {
		$gij_candidate = which("gij-4.0");
	}
	check_runtime($gij, "GNU gij", $gij_candidate, "");

	# check kaffe. Don't take it personally, but kaffe is not meant as a
	# robust production ready VM.  It's a research tool (so described on
	# their home page) but given the progress in GNU classpath lately it
	# *may* work, so we do check for it  - we just don't pick it by
	# preference.
	check_runtime($kaffe, "Kaffe VM", "/usr/lib/kaffe/bin/java", "");

	# check jamvm (an elegant bytecode interpreter used by many in the
	# CLASSPATH project to test new releases)
	my $jamvm_candidate;
	if ($jamvm_bin) {
		$jamvm_candidate = "$jamvm_bin";
	} else {
		$jamvm_candidate = "/usr/bin/jamvm";
	}
	check_runtime($jamvm, "JamVM VM", $jamvm_candidate, "");

	my $cacao_candidate;
	if ($cacao_bin) {
		$cacao_candidate = "$cacao_bin";
	} else {
		$cacao_candidate = "/usr/bin/cacao";
	}
	check_runtime($cacao, "CACAO VM", $cacao_candidate, "");

} elsif ($os eq "fedora") {
	# check for a proper JDK/JRE java Virtual Machine. Red Hat is using
	# the alternatives system symlinks to select JVMs, and then *again*
	# to select versions. All the symlinks end up back in /usr/lib/jvm with
	# predictable names, which makes this workable.
	my $java_candidate;
	my $vendor;
	if ($jdk_home) {
		$java_candidate = "$jdk_home/bin/java";
		$vendor = "Specified";
	} else {
		$java_candidate = "/usr/lib/jvm/jre-ibm/bin/java";
		$vendor = "IBM";
	}
	check_runtime($java, "$vendor java VM", $java_candidate, "-client");

	# check gij (the bytecode interpreter from the GCJ project)
	my $gij_candidate;
	if ($gcj_home) {
		$gij_candidate = "$gcj_home/bin/gij";
	} else {
		$gij_candidate = which("gij");
	}
	check_runtime($gij, "GNU gij", $gij_candidate, "");

	# check kaffe. See the comment about Kaffe above in the Debian block.
	check_runtime($kaffe, "Kaffe VM", which("kaffe"), "");

	# check jamvm (an elegant bytecode interpreter used by many in the
	# CLASSPATH project to test new releases)
	my $jamvm_candidate;
	if ($jamvm_bin) {
		$jamvm_candidate = "$jamvm_bin";
	} else {
		$jamvm_candidate = "/usr/bin/jamvm";
	}
	check_runtime($jamvm, "JamVM VM", $jamvm_candidate, "");

	my $cacao_candidate;
	if ($cacao_bin) {
		$cacao_candidate = "$cacao_bin";
	} else {
		$cacao_candidate = "/usr/bin/cacao";
	}
	check_runtime($cacao, "CACAO VM", $cacao_candidate, "");
} elsif ($os eq "solaris") {
	# check for a JDK/JRE java Virtual Machine, allowing an alternate to be set
	# (no reason to disable that)
	my $java_candidate;
	if ($jdk_home) {
		$java_candidate = "$jdk_home/bin/java";
	} else {
		$java_candidate = "/usr/java/bin/java";
	}

	check_runtime($java, "Sun java VM", $java_candidate, "-client");

	# check gij (the bytecode interpreter from the GCJ project)
	my $gij_candidate;

	if ($gcj_home) {
		$gij_candidate = "$gcj_home/bin/gij";
	} else {
		$gij_candidate = which("gij");
	}
	check_runtime($gij, "GNU gij", $gij_candidate, "");

	# check kaffe
	check_runtime($kaffe, "kaffe VM", which("kaffe"), "");

	# check jamvm (an elegant bytecode interpreter used by many in the
	# CLASSPATH project to test new releases)
	my $jamvm_candidate;
	if ($jamvm_bin) {
		$jamvm_candidate = "$jamvm_bin";
	} else {
		$jamvm_candidate = "/usr/bin/jamvm";
	}
	check_runtime($jamvm, "JamVM VM", $jamvm_candidate, "");

	my $cacao_candidate;
	if ($cacao_bin) {
		$cacao_candidate = "$cacao_bin";
	} else {
		$cacao_candidate = "/usr/bin/cacao";
	}
	check_runtime($cacao, "CACAO VM", $cacao_candidate, "");

} else {
	bail "failed!", "This OS not configured with appropriate Java VM checks!\nTHIS IS AN INTERNAL ERROR, PLEASE FILE A BUG.";
}

output "\n";


# --------------------------------------------------------------------
# Check for GCJ native Java compiler and for a C compiler
# --------------------------------------------------------------------

output "Check native compiler:\n";

# these are initialized as, unlike javac which is populated with *something*,
# even if gcj is not found we still need a variable for it.
my $gcj;
my $gcj_lib_path;

if ($os) {
	my $gcj_candidate;

	if ($gcj_home) {
		$gcj_candidate = "$gcj_home/bin/gcj";
		$gcj_lib_path = "$gcj_home/lib";
	} else {
		$gcj_candidate = which("gcj");
		# this is pretty rough
		$gcj_lib_path = "$gcj_candidate";
		$gcj_lib_path =~ s/bin\/gcj$/lib/;
	}

	check_native($gcj, "GNU gcj", $gcj_candidate, "", $gcj_lib_path);
}

my $cc;

if ($os eq "solaris") {
	check_CC($cc, "Sun cc", which("cc"), "");
} elsif ($os) {
	my $gcc_candidate;

	if ($gcj_home) {
		$gcc_candidate = "$gcj_home/bin/gcc";
	} else {
		$gcc_candidate = which("gcc");
	}

	check_CC($cc, "GNU gcc", $gcc_candidate, "");
}

output "\n";

# --------------------------------------------------------------------
# Choose between java compilers and VMs, reviewing overrides
# --------------------------------------------------------------------

# if gij isn't sufficient version, then knock out gcj
if (!$gij) {
	if ($gcj) {
		output "Can't use GCJ, insufficiently recent version\n\n";
	}
	undef $gcjC;
	undef $gcj;
	undef $gij;
}

print CONFIG <<HERE ;

# the JAVAC variable contains the path to the java source compiler, the JAVA
# variable contains the path to the java runtime virtual machine. In both
# cases, the _CMD variable is for the terse output when make commands are 
# running.

HERE

output "Select compiler:";

if ($compiler) {
	# if overridden, check override...
	if ($compiler eq "javac") {
		bail "bad override", "javac specified but not detected as a workable compiler." unless $javac;
	} elsif ($compiler eq "jikes") {
		bail "bad override", "jikes specified but not detected as a workable compiler." unless $jikes;
	} elsif ($compiler eq "gcj") {
		bail "bad override", "gcj (-C) specified but gcj not detected as a workable compiler." unless $gcjC;
	} else {
		bail "bad override", <<HERE ;
You specified compiler=$compiler on the command line, but that's not an option.
Valid choices are javac, jikes, or gcj - but of course that compiler must be
installed (and detected!) in order to be able to specifiy it.
HERE
	}

} else {
	# otherwise, pick a compiler.
	if ($jikes) {
		$compiler = "jikes";
	} elsif ($javac) {
		$compiler = "javac";
	} elsif ($gcjC) {
		$compiler = "gcj";
	} else {
		bail "failed", "No java compiler was detected.";
	}
}

if ($compiler eq "javac") {
	print CONFIG "JAVAC=$javac\n";
	print CONFIG "JAVAC_CMD=JAVAC    \n";
} elsif ($compiler eq "jikes") {
	print CONFIG "JAVAC=$jikes\n";
	print CONFIG "JAVAC_CMD=JIKES    \n";
} elsif ($compiler eq "gcj") {
	print CONFIG "JAVAC=$gcjC\n";
	print CONFIG "JAVAC_CMD=GCJ [-C] \n";
} else {
	bail "failed", "INTERNAL ERROR no compiler selected.";
}


print CONFIG <<HERE ;

# the JAVAH, JAR, and JAVADOC variables simply contains a usable jar and javah
# executable, respectively, while the JAVAH_CMD, JAR_CMD, and JAVADOC_CMD
# variables is for display purposes, matching the pattern above.

HERE


if ($javah) {
	print CONFIG "JAVAH=$javah\n";
	print CONFIG "JAVAH_CMD=JAVAH    \n";
} else {
	bail "failed", "No JNI header generator detected.";
}

if ($jar) {
	print CONFIG "JAR=$jar\n";
	print CONFIG "JAR_CMD=JAR      \n";
} else {
	bail "failed", "No Java archive tool detected.";
}
if ($javadoc) {
	print CONFIG "JAVADOC=$javadoc\n";
	print CONFIG "JAVADOC_CMD=JAVADOC  \n";
} else {
	bail "failed", "No JavaDoc tool detected.";
}

output "$compiler\n";


output "Select runtime:";

# Note that java is favoured over gij only because the error messages
# are better! (Ok, and, frankly, the compliance is obviously better
# if its a real Java VM). The Free ones are getting there...

if ($runtime) {
	# if overridden, check override...
	if ($runtime eq "java")  {
		bail "bad override", "java specified but not detected." unless $java;
	} elsif ($runtime eq "gij") {
		bail "bad override", "gij specified but not detected." unless $gij;
	} elsif ($runtime eq "kaffe") {
		bail "bad override", "kaffe specified but not detected." unless $kaffe;
	} elsif ($runtime eq "cacao") {
		bail "bad override", "cacao specified but not detected." unless $cacao;
	} elsif ($runtime eq "jamvm") {
		bail "bad override", "jamvm specified but not detected." unless $jamvm;
	} else {
		bail "bad override", <<HERE ;
You specified runtime=$runtime on the command line, but that's not an option.
Valid choices are java, gij, or kaffe - but of course that virtual machine
must be installed (and detected!) before you can specifiy it.
HERE
	}

} else {
	if ($java) {
		$runtime = "java";
	} elsif ($cacao) {
		$runtime = "cacao";
	} elsif ($jamvm) {
		$runtime = "jamvm";
	} elsif ($gij) {
		$runtime = "gij";
	} elsif ($kaffe) {
		$runtime = "kaffe";
	} else {
		bail "failed", "No usable Java runtime environment was detected.";
	}
}

if ($runtime eq "java") {
	print CONFIG "JAVA=$java\n";
	print CONFIG "JAVA_CMD=JAVA     \n";
} elsif ($runtime eq "gij") {
	print CONFIG "JAVA=$gij\n";
	print CONFIG "JAVA_CMD=GIJ      \n";
} elsif ($runtime eq "kaffe") {
	print CONFIG "JAVA=$kaffe\n";
	print CONFIG "JAVA_CMD=KAFFE    \n";
} elsif ($runtime eq "cacao") {
	print CONFIG "JAVA=$cacao\n";
	print CONFIG "JAVA_CMD=CACAO    \n";
} elsif ($runtime eq "jamvm") {
	print CONFIG "JAVA=$jamvm\n";
	print CONFIG "JAVA_CMD=JAMVM    \n";
} else {
	bail "failed", "INTERNAL ERROR no virtual machine selected";
}
output "$runtime\n";

# --------------------------------------------------------------------
# Output native build information
# --------------------------------------------------------------------
print CONFIG <<HERE ;

# if present, the GCJ variable contains the path to the GCC native Java
# builder. GCJ_LIB_PATH contains an argument suitable for use with -Wl,-rpath
# so that the linker can find the relevent libgcj.so class library.

HERE

if ($gcj) {
	print CONFIG "GCJ=$gcj -g -fPIC\n";
	print CONFIG "GCJ_CMD=GCJ      \n";
	print CONFIG "GCJ_LINK_CMD=LINK     \n";
	print CONFIG "GCJ_LIB_PATH=$gcj_lib_path\n";
}

print CONFIG <<HERE ;

# the path to the C compiler

HERE

if ($cc =~ /gcc/) {
	use File::Basename;

	my $jni_include = "";
	my $java_home = dirname($java);
	$java_home =~ s/\/bin//;

	if (-f "$java_home"."/include/jni.h") {
		$jni_include = "-I$java_home/include -I$java_home/include/linux";
	} elsif (-f "/usr/include/jni.h") {
		# good, but that's default search path - no -I required.
	} else {
		bail "failed", "Can't locate the JNI header file";
	}

	my $ccache = which("ccache");
	if ( -x $ccache) {
		print CONFIG "CCACHE=$ccache\n";
	}
	
	print CONFIG "CC=$cc -g -Wall -fPIC $jni_include -Wno-int-to-pointer-cast -Wno-pointer-to-int-cast\n";
	print CONFIG "CC_CMD=GCC      \n";
	print CONFIG "LINK=$cc -g -Wall -fPIC\n";
	print CONFIG "LINK_CMD=LINK     \n";
} elsif (($cc) && ($os eq "solaris")) {
	print CONFIG "CC=$cc -Kpic -I/usr/java/include -I/usr/java/include/solaris\n";
	print CONFIG "CC_CMD=CC       \n";
	print CONFIG "LINK=$cc -G -zdefs -Kpic -lc\n";
	print CONFIG "LINK_CMD=LINK     \n";
} else {
	bail "failed", "C compiler not detected";
}

`rm -f Hello.java Hello.class Hello.o Hello Hello.c Hello.jar`;

# --------------------------------------------------------------------
# Get version constant
# --------------------------------------------------------------------

my $apiversion;
my $version;

open SOURCE, "src/java/org/gnome/gtk/Gtk.java";
while (<SOURCE>) {
	chomp;
	if (/.* APIVERSION = \"([\d\.]+)\";/) {
		$apiversion = $1;
	}
	if (/.* VERSION = \"([\d\.]+)\";/) {
		$version = $1;
	}
}
close SOURCE;

if (!$apiversion) {
	bail("","Couldn't find the API version");
}
if (!$version) {
	bail("","Couldn't find the release version");
}

print CONFIG <<HERE ;

# finally, we extract the version strings from the source code, 
# for use in naming the target library files.

HERE

print CONFIG "APIVERSION=$apiversion\n";
print CONFIG "VERSION=$version\n";

# --------------------------------------------------------------------
# Done! Create .config file
# --------------------------------------------------------------------

output "Write .config file:";
close CONFIG;
system "mv .config.tmp .config";

output "ok\n";
output "\n";

