Home | History | Annotate | Line # | Download | only in gnulib-lib
      1 /* Compile a Java program.
      2    Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
      3    Written by Bruno Haible <haible (at) clisp.cons.org>, 2001.
      4 
      5    This program is free software; you can redistribute it and/or modify
      6    it under the terms of the GNU General Public License as published by
      7    the Free Software Foundation; either version 2, or (at your option)
      8    any later version.
      9 
     10    This program is distributed in the hope that it will be useful,
     11    but WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13    GNU General Public License for more details.
     14 
     15    You should have received a copy of the GNU General Public License
     16    along with this program; if not, write to the Free Software Foundation,
     17    Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
     18 
     19 #include <config.h>
     20 #include <alloca.h>
     21 
     22 /* Specification.  */
     23 #include "javacomp.h"
     24 
     25 #include <errno.h>
     26 #include <limits.h>
     27 #include <stdio.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 #include <sys/types.h>
     32 #include <sys/stat.h>
     33 
     34 #include "javaversion.h"
     35 #include "execute.h"
     36 #include "pipe.h"
     37 #include "wait-process.h"
     38 #include "classpath.h"
     39 #include "xsetenv.h"
     40 #include "sh-quote.h"
     41 #include "binary-io.h"
     42 #include "safe-read.h"
     43 #include "xalloc.h"
     44 #include "xallocsa.h"
     45 #include "getline.h"
     46 #include "pathname.h"
     47 #include "fwriteerror.h"
     48 #include "clean-temp.h"
     49 #include "error.h"
     50 #include "xvasprintf.h"
     51 #include "c-strstr.h"
     52 #include "gettext.h"
     53 
     54 #define _(str) gettext (str)
     55 
     56 
     57 /* Survey of Java compilers.
     58 
     59    A = does it work without CLASSPATH being set
     60    C = option to set CLASSPATH, other than setting it in the environment
     61    O = option for optimizing
     62    g = option for debugging
     63    T = test for presence
     64 
     65    Program  from        A  C               O  g  T
     66 
     67    $JAVAC   unknown     N  n/a            -O -g  true
     68    gcj -C   GCC 3.2     Y  --classpath=P  -O -g  gcj --version | sed -e 's,^[^0-9]*,,' -e 1q | sed -e '/^3\.[01]/d' | grep '^[3-9]' >/dev/null
     69    javac    JDK 1.1.8   Y  -classpath P   -O -g  javac 2>/dev/null; test $? = 1
     70    javac    JDK 1.3.0   Y  -classpath P   -O -g  javac 2>/dev/null; test $? -le 2
     71    jikes    Jikes 1.14  N  -classpath P   -O -g  jikes 2>/dev/null; test $? = 1
     72 
     73    All compilers support the option "-d DIRECTORY" for the base directory
     74    of the classes to be written.
     75 
     76    The CLASSPATH is a colon separated list of pathnames. (On Windows: a
     77    semicolon separated list of pathnames.)
     78 
     79    We try the Java compilers in the following order:
     80      1. getenv ("JAVAC"), because the user must be able to override our
     81 	preferences,
     82      2. "gcj -C", because it is a completely free compiler,
     83      3. "javac", because it is a standard compiler,
     84      4. "jikes", comes last because it has some deviating interpretation
     85 	of the Java Language Specification and because it requires a
     86 	CLASSPATH environment variable.
     87 
     88    We unset the JAVA_HOME environment variable, because a wrong setting of
     89    this variable can confuse the JDK's javac.
     90  */
     91 
     92 /* Return the default target_version.  */
     93 static const char *
     94 default_target_version (void)
     95 {
     96   /* Use a cache.  Assumes that the PATH environment variable doesn't change
     97      during the lifetime of the program.  */
     98   static const char *java_version_cache;
     99   if (java_version_cache == NULL)
    100     {
    101       /* Determine the version from the found JVM.  */
    102       java_version_cache = javaexec_version ();
    103       if (java_version_cache == NULL
    104 	  || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
    105 	       && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
    106 	       && java_version_cache[3] == '\0'))
    107 	java_version_cache = "1.1";
    108     }
    109   return java_version_cache;
    110 }
    111 
    112 /* ======================= Source version dependent ======================= */
    113 
    114 /* Convert a source version to an index.  */
    115 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
    116 static unsigned int
    117 source_version_index (const char *source_version)
    118 {
    119   if (source_version[0] == '1' && source_version[1] == '.'
    120       && (source_version[2] >= '3' && source_version[2] <= '5')
    121       && source_version[3] == '\0')
    122     return source_version[2] - '3';
    123   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
    124   return 0;
    125 }
    126 
    127 /* Return a snippet of code that should compile in the given source version.  */
    128 static const char *
    129 get_goodcode_snippet (const char *source_version)
    130 {
    131   if (strcmp (source_version, "1.3") == 0)
    132     return "class conftest {}\n";
    133   if (strcmp (source_version, "1.4") == 0)
    134     return "class conftest { static { assert(true); } }\n";
    135   if (strcmp (source_version, "1.5") == 0)
    136     return "class conftest<T> { T foo() { return null; } }\n";
    137   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
    138   return NULL;
    139 }
    140 
    141 /* Return a snippet of code that should fail to compile in the given source
    142    version, or NULL (standing for a snippet that would fail to compile with
    143    any compiler).  */
    144 static const char *
    145 get_failcode_snippet (const char *source_version)
    146 {
    147   if (strcmp (source_version, "1.3") == 0)
    148     return "class conftestfail { static { assert(true); } }\n";
    149   if (strcmp (source_version, "1.4") == 0)
    150     return "class conftestfail<T> { T foo() { return null; } }\n";
    151   if (strcmp (source_version, "1.5") == 0)
    152     return NULL;
    153   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
    154   return NULL;
    155 }
    156 
    157 /* ======================= Target version dependent ======================= */
    158 
    159 /* Convert a target version to an index.  */
    160 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
    161 static unsigned int
    162 target_version_index (const char *target_version)
    163 {
    164   if (target_version[0] == '1' && target_version[1] == '.'
    165       && (target_version[2] >= '1' && target_version[2] <= '6')
    166       && target_version[3] == '\0')
    167     return target_version[2] - '1';
    168   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
    169   return 0;
    170 }
    171 
    172 /* Return the class file version number corresponding to a given target
    173    version.  */
    174 static int
    175 corresponding_classfile_version (const char *target_version)
    176 {
    177   if (strcmp (target_version, "1.1") == 0)
    178     return 45;
    179   if (strcmp (target_version, "1.2") == 0)
    180     return 46;
    181   if (strcmp (target_version, "1.3") == 0)
    182     return 47;
    183   if (strcmp (target_version, "1.4") == 0)
    184     return 48;
    185   if (strcmp (target_version, "1.5") == 0)
    186     return 49;
    187   if (strcmp (target_version, "1.6") == 0)
    188     return 50;
    189   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
    190   return 0;
    191 }
    192 
    193 /* ======================== Compilation subroutines ======================== */
    194 
    195 /* Try to compile a set of Java sources with $JAVAC.
    196    Return a failure indicator (true upon error).  */
    197 static bool
    198 compile_using_envjavac (const char *javac,
    199 			const char * const *java_sources,
    200 			unsigned int java_sources_count,
    201 			const char *directory,
    202 			bool optimize, bool debug,
    203 			bool verbose, bool null_stderr)
    204 {
    205   /* Because $JAVAC may consist of a command and options, we use the
    206      shell.  Because $JAVAC has been set by the user, we leave all
    207      environment variables in place, including JAVA_HOME, and we don't
    208      erase the user's CLASSPATH.  */
    209   bool err;
    210   unsigned int command_length;
    211   char *command;
    212   char *argv[4];
    213   int exitstatus;
    214   unsigned int i;
    215   char *p;
    216 
    217   command_length = strlen (javac);
    218   if (optimize)
    219     command_length += 3;
    220   if (debug)
    221     command_length += 3;
    222   if (directory != NULL)
    223     command_length += 4 + shell_quote_length (directory);
    224   for (i = 0; i < java_sources_count; i++)
    225     command_length += 1 + shell_quote_length (java_sources[i]);
    226   command_length += 1;
    227 
    228   command = (char *) xallocsa (command_length);
    229   p = command;
    230   /* Don't shell_quote $JAVAC, because it may consist of a command
    231      and options.  */
    232   memcpy (p, javac, strlen (javac));
    233   p += strlen (javac);
    234   if (optimize)
    235     {
    236       memcpy (p, " -O", 3);
    237       p += 3;
    238     }
    239   if (debug)
    240     {
    241       memcpy (p, " -g", 3);
    242       p += 3;
    243     }
    244   if (directory != NULL)
    245     {
    246       memcpy (p, " -d ", 4);
    247       p += 4;
    248       p = shell_quote_copy (p, directory);
    249     }
    250   for (i = 0; i < java_sources_count; i++)
    251     {
    252       *p++ = ' ';
    253       p = shell_quote_copy (p, java_sources[i]);
    254     }
    255   *p++ = '\0';
    256   /* Ensure command_length was correctly calculated.  */
    257   if (p - command > command_length)
    258     abort ();
    259 
    260   if (verbose)
    261     printf ("%s\n", command);
    262 
    263   argv[0] = "/bin/sh";
    264   argv[1] = "-c";
    265   argv[2] = command;
    266   argv[3] = NULL;
    267   exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
    268 			null_stderr, true, true);
    269   err = (exitstatus != 0);
    270 
    271   freesa (command);
    272 
    273   return err;
    274 }
    275 
    276 /* Try to compile a set of Java sources with gcj.
    277    Return a failure indicator (true upon error).  */
    278 static bool
    279 compile_using_gcj (const char * const *java_sources,
    280 		   unsigned int java_sources_count,
    281 		   bool no_assert_option,
    282 		   const char *directory,
    283 		   bool optimize, bool debug,
    284 		   bool verbose, bool null_stderr)
    285 {
    286   bool err;
    287   unsigned int argc;
    288   char **argv;
    289   char **argp;
    290   int exitstatus;
    291   unsigned int i;
    292 
    293   argc =
    294     2 + (no_assert_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
    295     + (directory != NULL ? 2 : 0) + java_sources_count;
    296   argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
    297 
    298   argp = argv;
    299   *argp++ = "gcj";
    300   *argp++ = "-C";
    301   if (no_assert_option)
    302     *argp++ = "-fno-assert";
    303   if (optimize)
    304     *argp++ = "-O";
    305   if (debug)
    306     *argp++ = "-g";
    307   if (directory != NULL)
    308     {
    309       *argp++ = "-d";
    310       *argp++ = (char *) directory;
    311     }
    312   for (i = 0; i < java_sources_count; i++)
    313     *argp++ = (char *) java_sources[i];
    314   *argp = NULL;
    315   /* Ensure argv length was correctly calculated.  */
    316   if (argp - argv != argc)
    317     abort ();
    318 
    319   if (verbose)
    320     {
    321       char *command = shell_quote_argv (argv);
    322       printf ("%s\n", command);
    323       free (command);
    324     }
    325 
    326   exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
    327 			true, true);
    328   err = (exitstatus != 0);
    329 
    330   freesa (argv);
    331 
    332   return err;
    333 }
    334 
    335 /* Try to compile a set of Java sources with javac.
    336    Return a failure indicator (true upon error).  */
    337 static bool
    338 compile_using_javac (const char * const *java_sources,
    339 		     unsigned int java_sources_count,
    340 		     bool source_option, const char *source_version,
    341 		     bool target_option, const char *target_version,
    342 		     const char *directory,
    343 		     bool optimize, bool debug,
    344 		     bool verbose, bool null_stderr)
    345 {
    346   bool err;
    347   unsigned int argc;
    348   char **argv;
    349   char **argp;
    350   int exitstatus;
    351   unsigned int i;
    352 
    353   argc =
    354     1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
    355     + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
    356   argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
    357 
    358   argp = argv;
    359   *argp++ = "javac";
    360   if (source_option)
    361     {
    362       *argp++ = "-source";
    363       *argp++ = (char *) source_version;
    364     }
    365   if (target_option)
    366     {
    367       *argp++ = "-target";
    368       *argp++ = (char *) target_version;
    369     }
    370   if (optimize)
    371     *argp++ = "-O";
    372   if (debug)
    373     *argp++ = "-g";
    374   if (directory != NULL)
    375     {
    376       *argp++ = "-d";
    377       *argp++ = (char *) directory;
    378     }
    379   for (i = 0; i < java_sources_count; i++)
    380     *argp++ = (char *) java_sources[i];
    381   *argp = NULL;
    382   /* Ensure argv length was correctly calculated.  */
    383   if (argp - argv != argc)
    384     abort ();
    385 
    386   if (verbose)
    387     {
    388       char *command = shell_quote_argv (argv);
    389       printf ("%s\n", command);
    390       free (command);
    391     }
    392 
    393   exitstatus = execute ("javac", "javac", argv, false, false, false,
    394 			null_stderr, true, true);
    395   err = (exitstatus != 0);
    396 
    397   freesa (argv);
    398 
    399   return err;
    400 }
    401 
    402 /* Try to compile a set of Java sources with jikes.
    403    Return a failure indicator (true upon error).  */
    404 static bool
    405 compile_using_jikes (const char * const *java_sources,
    406 		     unsigned int java_sources_count,
    407 		     const char *directory,
    408 		     bool optimize, bool debug,
    409 		     bool verbose, bool null_stderr)
    410 {
    411   bool err;
    412   unsigned int argc;
    413   char **argv;
    414   char **argp;
    415   int exitstatus;
    416   unsigned int i;
    417 
    418   argc =
    419     1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
    420     + java_sources_count;
    421   argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
    422 
    423   argp = argv;
    424   *argp++ = "jikes";
    425   if (optimize)
    426     *argp++ = "-O";
    427   if (debug)
    428     *argp++ = "-g";
    429   if (directory != NULL)
    430     {
    431       *argp++ = "-d";
    432       *argp++ = (char *) directory;
    433     }
    434   for (i = 0; i < java_sources_count; i++)
    435     *argp++ = (char *) java_sources[i];
    436   *argp = NULL;
    437   /* Ensure argv length was correctly calculated.  */
    438   if (argp - argv != argc)
    439     abort ();
    440 
    441   if (verbose)
    442     {
    443       char *command = shell_quote_argv (argv);
    444       printf ("%s\n", command);
    445       free (command);
    446     }
    447 
    448   exitstatus = execute ("jikes", "jikes", argv, false, false, false,
    449 			null_stderr, true, true);
    450   err = (exitstatus != 0);
    451 
    452   freesa (argv);
    453 
    454   return err;
    455 }
    456 
    457 /* ====================== Usability test subroutines ====================== */
    458 
    459 /* Write a given contents to a temporary file.
    460    FILE_NAME is the name of a file inside TMPDIR that is known not to exist
    461    yet.
    462    Return a failure indicator (true upon error).  */
    463 static bool
    464 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
    465 		 const char *contents)
    466 {
    467   FILE *fp;
    468 
    469   register_temp_file (tmpdir, file_name);
    470   fp = fopen_temp (file_name, "w");
    471   if (fp == NULL)
    472     {
    473       error (0, errno, _("failed to create \"%s\""), file_name);
    474       unregister_temp_file (tmpdir, file_name);
    475       return true;
    476     }
    477   fputs (contents, fp);
    478   if (fwriteerror_temp (fp))
    479     {
    480       error (0, errno, _("error while writing \"%s\" file"), file_name);
    481       return true;
    482     }
    483   return false;
    484 }
    485 
    486 /* Return the class file version number of a class file on disk.  */
    487 static int
    488 get_classfile_version (const char *compiled_file_name)
    489 {
    490   unsigned char header[8];
    491   int fd;
    492 
    493   /* Open the class file.  */
    494   fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
    495   if (fd >= 0)
    496     {
    497       /* Read its first 8 bytes.  */
    498       if (safe_read (fd, header, 8) == 8)
    499 	{
    500 	  /* Verify the class file signature.  */
    501 	  if (header[0] == 0xCA && header[1] == 0xFE
    502 	      && header[2] == 0xBA && header[3] == 0xBE)
    503 	    return header[7];
    504 	}
    505       close (fd);
    506     }
    507 
    508   /* Could not get the class file version.  Return a very large one.  */
    509   return INT_MAX;
    510 }
    511 
    512 /* Return true if $JAVAC is a version of gcj.  */
    513 static bool
    514 is_envjavac_gcj (const char *javac)
    515 {
    516   static bool envjavac_tested;
    517   static bool envjavac_gcj;
    518 
    519   if (!envjavac_tested)
    520     {
    521       /* Test whether $JAVAC is gcj:
    522 	 "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null"  */
    523       unsigned int command_length;
    524       char *command;
    525       char *argv[4];
    526       pid_t child;
    527       int fd[1];
    528       FILE *fp;
    529       char *line;
    530       size_t linesize;
    531       size_t linelen;
    532       int exitstatus;
    533       char *p;
    534 
    535       /* Setup the command "$JAVAC --version".  */
    536       command_length = strlen (javac) + 1 + 9 + 1;
    537       command = (char *) xallocsa (command_length);
    538       p = command;
    539       /* Don't shell_quote $JAVAC, because it may consist of a command
    540 	 and options.  */
    541       memcpy (p, javac, strlen (javac));
    542       p += strlen (javac);
    543       memcpy (p, " --version", 1 + 9 + 1);
    544       p += 1 + 9 + 1;
    545       /* Ensure command_length was correctly calculated.  */
    546       if (p - command > command_length)
    547 	abort ();
    548 
    549       /* Call $JAVAC --version 2>/dev/null.  */
    550       argv[0] = "/bin/sh";
    551       argv[1] = "-c";
    552       argv[2] = command;
    553       argv[3] = NULL;
    554       child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
    555 			      false, fd);
    556       if (child == -1)
    557 	goto failed;
    558 
    559       /* Retrieve its result.  */
    560       fp = fdopen (fd[0], "r");
    561       if (fp == NULL)
    562 	goto failed;
    563 
    564       line = NULL; linesize = 0;
    565       linelen = getline (&line, &linesize, fp);
    566       if (linelen == (size_t)(-1))
    567 	{
    568 	  fclose (fp);
    569 	  goto failed;
    570 	}
    571       envjavac_gcj = (c_strstr (line, "gcj") != NULL);
    572 
    573       fclose (fp);
    574 
    575       /* Remove zombie process from process list, and retrieve exit status.  */
    576       exitstatus = wait_subprocess (child, javac, true, true, true, false);
    577       if (exitstatus != 0)
    578 	envjavac_gcj = false;
    579 
    580      failed:
    581       freesa (command);
    582 
    583       envjavac_tested = true;
    584     }
    585 
    586   return envjavac_gcj;
    587 }
    588 
    589 /* Test whether $JAVAC, known to be a version of gcj, can be used for
    590    compiling with target_version = 1.4 and source_version = 1.4.
    591    Return a failure indicator (true upon error).  */
    592 static bool
    593 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
    594 {
    595   static bool envjavac_tested;
    596   static bool envjavac_usable;
    597 
    598   if (!envjavac_tested)
    599     {
    600       /* Try $JAVAC.  */
    601       struct temp_dir *tmpdir;
    602       char *conftest_file_name;
    603       char *compiled_file_name;
    604       const char *java_sources[1];
    605       struct stat statbuf;
    606 
    607       tmpdir = create_temp_dir ("java", NULL, false);
    608       if (tmpdir == NULL)
    609 	return true;
    610 
    611       conftest_file_name =
    612 	concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
    613       if (write_temp_file (tmpdir, conftest_file_name,
    614 			   get_goodcode_snippet ("1.4")))
    615 	{
    616 	  free (conftest_file_name);
    617 	  cleanup_temp_dir (tmpdir);
    618 	  return true;
    619 	}
    620 
    621       compiled_file_name =
    622 	concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
    623       register_temp_file (tmpdir, compiled_file_name);
    624 
    625       java_sources[0] = conftest_file_name;
    626       if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
    627 				   false, false, false, true)
    628 	  && stat (compiled_file_name, &statbuf) >= 0)
    629 	/* Compilation succeeded.  */
    630 	envjavac_usable = true;
    631 
    632       free (compiled_file_name);
    633       free (conftest_file_name);
    634 
    635       cleanup_temp_dir (tmpdir);
    636 
    637       envjavac_tested = true;
    638     }
    639 
    640   *usablep = envjavac_usable;
    641   return false;
    642 }
    643 
    644 /* Test whether $JAVAC, known to be a version of gcj, can be used for
    645    compiling with target_version = 1.4 and source_version = 1.3.
    646    Return a failure indicator (true upon error).  */
    647 static bool
    648 is_envjavac_gcj_14_13_usable (const char *javac,
    649 			      bool *usablep, bool *need_no_assert_option_p)
    650 {
    651   static bool envjavac_tested;
    652   static bool envjavac_usable;
    653   static bool envjavac_need_no_assert_option;
    654 
    655   if (!envjavac_tested)
    656     {
    657       /* Try $JAVAC and "$JAVAC -fno-assert".  But add -fno-assert only if
    658 	 it makes a difference.  (It could already be part of $JAVAC.)  */
    659       struct temp_dir *tmpdir;
    660       char *conftest_file_name;
    661       char *compiled_file_name;
    662       const char *java_sources[1];
    663       struct stat statbuf;
    664       bool javac_works;
    665       char *javac_noassert;
    666       bool javac_noassert_works;
    667 
    668       tmpdir = create_temp_dir ("java", NULL, false);
    669       if (tmpdir == NULL)
    670 	return true;
    671 
    672       conftest_file_name =
    673 	concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
    674       if (write_temp_file (tmpdir, conftest_file_name,
    675 			   get_goodcode_snippet ("1.3")))
    676 	{
    677 	  free (conftest_file_name);
    678 	  cleanup_temp_dir (tmpdir);
    679 	  return true;
    680 	}
    681 
    682       compiled_file_name =
    683 	concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
    684       register_temp_file (tmpdir, compiled_file_name);
    685 
    686       java_sources[0] = conftest_file_name;
    687       if (!compile_using_envjavac (javac,
    688 				   java_sources, 1, tmpdir->dir_name,
    689 				   false, false, false, true)
    690 	  && stat (compiled_file_name, &statbuf) >= 0)
    691 	/* Compilation succeeded.  */
    692 	javac_works = true;
    693       else
    694 	javac_works = false;
    695 
    696       unlink (compiled_file_name);
    697 
    698       javac_noassert = xasprintf ("%s -fno-assert", javac);
    699 
    700       java_sources[0] = conftest_file_name;
    701       if (!compile_using_envjavac (javac_noassert,
    702 				   java_sources, 1, tmpdir->dir_name,
    703 				   false, false, false, true)
    704 	  && stat (compiled_file_name, &statbuf) >= 0)
    705 	/* Compilation succeeded.  */
    706 	javac_noassert_works = true;
    707       else
    708 	javac_noassert_works = false;
    709 
    710       free (compiled_file_name);
    711       free (conftest_file_name);
    712 
    713       if (javac_works && javac_noassert_works)
    714 	{
    715 	  conftest_file_name =
    716 	    concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
    717 				   NULL);
    718 	  if (write_temp_file (tmpdir, conftest_file_name,
    719 			       get_failcode_snippet ("1.3")))
    720 	    {
    721 	      free (conftest_file_name);
    722 	      free (javac_noassert);
    723 	      cleanup_temp_dir (tmpdir);
    724 	      return true;
    725 	    }
    726 
    727 	  compiled_file_name =
    728 	    concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
    729 				   NULL);
    730 	  register_temp_file (tmpdir, compiled_file_name);
    731 
    732 	  java_sources[0] = conftest_file_name;
    733 	  if (!compile_using_envjavac (javac,
    734 				       java_sources, 1, tmpdir->dir_name,
    735 				       false, false, false, true)
    736 	      && stat (compiled_file_name, &statbuf) >= 0)
    737 	    {
    738 	      /* Compilation succeeded.  */
    739 	      unlink (compiled_file_name);
    740 
    741 	      java_sources[0] = conftest_file_name;
    742 	      if (!(!compile_using_envjavac (javac_noassert,
    743 					     java_sources, 1, tmpdir->dir_name,
    744 					     false, false, false, true)
    745 		    && stat (compiled_file_name, &statbuf) >= 0))
    746 		/* Compilation failed.  */
    747 		/* "$JAVAC -fno-assert" works better than $JAVAC.  */
    748 		javac_works = true;
    749 	    }
    750 
    751 	  free (compiled_file_name);
    752 	  free (conftest_file_name);
    753 	}
    754 
    755       cleanup_temp_dir (tmpdir);
    756 
    757       if (javac_works)
    758 	{
    759 	  envjavac_usable = true;
    760 	  envjavac_need_no_assert_option = false;
    761 	}
    762       else if (javac_noassert_works)
    763 	{
    764 	  envjavac_usable = true;
    765 	  envjavac_need_no_assert_option = true;
    766 	}
    767 
    768       envjavac_tested = true;
    769     }
    770 
    771   *usablep = envjavac_usable;
    772   *need_no_assert_option_p = envjavac_need_no_assert_option;
    773   return false;
    774 }
    775 
    776 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
    777    whether it needs a -source and/or -target option.
    778    Return a failure indicator (true upon error).  */
    779 static bool
    780 is_envjavac_nongcj_usable (const char *javac,
    781 			   const char *source_version,
    782 			   const char *target_version,
    783 			   bool *usablep,
    784 			   bool *source_option_p, bool *target_option_p)
    785 {
    786   /* The cache depends on the source_version and target_version.  */
    787   struct result_t
    788   {
    789     bool tested;
    790     bool usable;
    791     bool source_option;
    792     bool target_option;
    793   };
    794   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
    795   struct result_t *resultp;
    796 
    797   resultp = &result_cache[source_version_index (source_version)]
    798 			 [target_version_index (target_version)];
    799   if (!resultp->tested)
    800     {
    801       /* Try $JAVAC.  */
    802       struct temp_dir *tmpdir;
    803       char *conftest_file_name;
    804       char *compiled_file_name;
    805       const char *java_sources[1];
    806       struct stat statbuf;
    807 
    808       tmpdir = create_temp_dir ("java", NULL, false);
    809       if (tmpdir == NULL)
    810 	return true;
    811 
    812       conftest_file_name =
    813 	concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
    814       if (write_temp_file (tmpdir, conftest_file_name,
    815 			   get_goodcode_snippet (source_version)))
    816 	{
    817 	  free (conftest_file_name);
    818 	  cleanup_temp_dir (tmpdir);
    819 	  return true;
    820 	}
    821 
    822       compiled_file_name =
    823 	concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
    824       register_temp_file (tmpdir, compiled_file_name);
    825 
    826       java_sources[0] = conftest_file_name;
    827       if (!compile_using_envjavac (javac,
    828 				   java_sources, 1, tmpdir->dir_name,
    829 				   false, false, false, true)
    830 	  && stat (compiled_file_name, &statbuf) >= 0
    831 	  && get_classfile_version (compiled_file_name)
    832 	     <= corresponding_classfile_version (target_version))
    833 	{
    834 	  /* $JAVAC compiled conftest.java successfully.  */
    835 	  /* Try adding -source option if it is useful.  */
    836 	  char *javac_source =
    837 	    xasprintf ("%s -source %s", javac, source_version);
    838 
    839 	  unlink (compiled_file_name);
    840 
    841 	  java_sources[0] = conftest_file_name;
    842 	  if (!compile_using_envjavac (javac_source,
    843 				       java_sources, 1, tmpdir->dir_name,
    844 				       false, false, false, true)
    845 	      && stat (compiled_file_name, &statbuf) >= 0
    846 	      && get_classfile_version (compiled_file_name)
    847 		 <= corresponding_classfile_version (target_version))
    848 	    {
    849 	      const char *failcode = get_failcode_snippet (source_version);
    850 
    851 	      if (failcode != NULL)
    852 		{
    853 		  free (compiled_file_name);
    854 		  free (conftest_file_name);
    855 
    856 		  conftest_file_name =
    857 		    concatenated_pathname (tmpdir->dir_name,
    858 					   "conftestfail.java",
    859 					   NULL);
    860 		  if (write_temp_file (tmpdir, conftest_file_name, failcode))
    861 		    {
    862 		      free (conftest_file_name);
    863 		      free (javac_source);
    864 		      cleanup_temp_dir (tmpdir);
    865 		      return true;
    866 		    }
    867 
    868 		  compiled_file_name =
    869 		    concatenated_pathname (tmpdir->dir_name,
    870 					   "conftestfail.class",
    871 					   NULL);
    872 		  register_temp_file (tmpdir, compiled_file_name);
    873 
    874 		  java_sources[0] = conftest_file_name;
    875 		  if (!compile_using_envjavac (javac,
    876 					       java_sources, 1,
    877 					       tmpdir->dir_name,
    878 					       false, false, false, true)
    879 		      && stat (compiled_file_name, &statbuf) >= 0)
    880 		    {
    881 		      unlink (compiled_file_name);
    882 
    883 		      java_sources[0] = conftest_file_name;
    884 		      if (compile_using_envjavac (javac_source,
    885 						  java_sources, 1,
    886 						  tmpdir->dir_name,
    887 						  false, false, false, true))
    888 			/* $JAVAC compiled conftestfail.java successfully, and
    889 			   "$JAVAC -source $source_version" rejects it.  So the
    890 			   -source option is useful.  */
    891 			resultp->source_option = true;
    892 		    }
    893 		}
    894 	    }
    895 
    896 	  free (javac_source);
    897 
    898 	  resultp->usable = true;
    899 	}
    900       else
    901 	{
    902 	  /* Try with -target option alone. (Sun javac 1.3.1 has the -target
    903 	     option but no -source option.)  */
    904 	  char *javac_target =
    905 	    xasprintf ("%s -target %s", javac, target_version);
    906 
    907 	  unlink (compiled_file_name);
    908 
    909 	  java_sources[0] = conftest_file_name;
    910 	  if (!compile_using_envjavac (javac_target,
    911 				       java_sources, 1, tmpdir->dir_name,
    912 				       false, false, false, true)
    913 	      && stat (compiled_file_name, &statbuf) >= 0
    914 	      && get_classfile_version (compiled_file_name)
    915 		 <= corresponding_classfile_version (target_version))
    916 	    {
    917 	      /* "$JAVAC -target $target_version" compiled conftest.java
    918 		 successfully.  */
    919 	      /* Try adding -source option if it is useful.  */
    920 	      char *javac_target_source =
    921 		xasprintf ("%s -source %s", javac_target, source_version);
    922 
    923 	      unlink (compiled_file_name);
    924 
    925 	      java_sources[0] = conftest_file_name;
    926 	      if (!compile_using_envjavac (javac_target_source,
    927 					   java_sources, 1, tmpdir->dir_name,
    928 					   false, false, false, true)
    929 		  && stat (compiled_file_name, &statbuf) >= 0
    930 		  && get_classfile_version (compiled_file_name)
    931 		     <= corresponding_classfile_version (target_version))
    932 		{
    933 		  const char *failcode = get_failcode_snippet (source_version);
    934 
    935 		  if (failcode != NULL)
    936 		    {
    937 		      free (compiled_file_name);
    938 		      free (conftest_file_name);
    939 
    940 		      conftest_file_name =
    941 			concatenated_pathname (tmpdir->dir_name,
    942 					       "conftestfail.java",
    943 					       NULL);
    944 		      if (write_temp_file (tmpdir, conftest_file_name,
    945 					   failcode))
    946 			{
    947 			  free (conftest_file_name);
    948 			  free (javac_target_source);
    949 			  free (javac_target);
    950 			  cleanup_temp_dir (tmpdir);
    951 			  return true;
    952 			}
    953 
    954 		      compiled_file_name =
    955 			concatenated_pathname (tmpdir->dir_name,
    956 					       "conftestfail.class",
    957 					       NULL);
    958 		      register_temp_file (tmpdir, compiled_file_name);
    959 
    960 		      java_sources[0] = conftest_file_name;
    961 		      if (!compile_using_envjavac (javac_target,
    962 						   java_sources, 1,
    963 						   tmpdir->dir_name,
    964 						   false, false, false, true)
    965 			  && stat (compiled_file_name, &statbuf) >= 0)
    966 			{
    967 			  unlink (compiled_file_name);
    968 
    969 			  java_sources[0] = conftest_file_name;
    970 			  if (compile_using_envjavac (javac_target_source,
    971 						      java_sources, 1,
    972 						      tmpdir->dir_name,
    973 						      false, false, false,
    974 						      true))
    975 			    /* "$JAVAC -target $target_version" compiled
    976 			       conftestfail.java successfully, and
    977 			       "$JAVAC -target $target_version -source $source_version"
    978 			       rejects it.  So the -source option is useful.  */
    979 			    resultp->source_option = true;
    980 			}
    981 		    }
    982 		}
    983 
    984 	      free (javac_target_source);
    985 
    986 	      resultp->target_option = true;
    987 	      resultp->usable = true;
    988 	    }
    989 	  else
    990 	    {
    991 	      /* Maybe this -target option requires a -source option? Try with
    992 		 -target and -source options. (Supported by Sun javac 1.4 and
    993 		 higher.)  */
    994 	      char *javac_target_source =
    995 		xasprintf ("%s -source %s", javac_target, source_version);
    996 
    997 	      unlink (compiled_file_name);
    998 
    999 	      java_sources[0] = conftest_file_name;
   1000 	      if (!compile_using_envjavac (javac_target_source,
   1001 					   java_sources, 1, tmpdir->dir_name,
   1002 					   false, false, false, true)
   1003 		  && stat (compiled_file_name, &statbuf) >= 0
   1004 		  && get_classfile_version (compiled_file_name)
   1005 		     <= corresponding_classfile_version (target_version))
   1006 		{
   1007 		  /* "$JAVAC -target $target_version -source $source_version"
   1008 		     compiled conftest.java successfully.  */
   1009 		  resultp->source_option = true;
   1010 		  resultp->target_option = true;
   1011 		  resultp->usable = true;
   1012 		}
   1013 
   1014 	      free (javac_target_source);
   1015 	    }
   1016 
   1017 	  free (javac_target);
   1018 	}
   1019 
   1020       free (compiled_file_name);
   1021       free (conftest_file_name);
   1022 
   1023       resultp->tested = true;
   1024     }
   1025 
   1026   *usablep = resultp->usable;
   1027   *source_option_p = resultp->source_option;
   1028   *target_option_p = resultp->target_option;
   1029   return false;
   1030 }
   1031 
   1032 static bool
   1033 is_gcj_present (void)
   1034 {
   1035   static bool gcj_tested;
   1036   static bool gcj_present;
   1037 
   1038   if (!gcj_tested)
   1039     {
   1040       /* Test for presence of gcj:
   1041 	 "gcj --version 2> /dev/null | \
   1042 	  sed -e 's,^[^0-9]*,,' -e 1q | \
   1043 	  sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null"  */
   1044       char *argv[3];
   1045       pid_t child;
   1046       int fd[1];
   1047       int exitstatus;
   1048 
   1049       argv[0] = "gcj";
   1050       argv[1] = "--version";
   1051       argv[2] = NULL;
   1052       child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
   1053 			      false, fd);
   1054       gcj_present = false;
   1055       if (child != -1)
   1056 	{
   1057 	  /* Read the subprocess output, drop all lines except the first,
   1058 	     drop all characters before the first digit, and test whether
   1059 	     the remaining string starts with a digit >= 3, but not with
   1060 	     "3.0" or "3.1".  */
   1061 	  char c[3];
   1062 	  size_t count = 0;
   1063 
   1064 	  while (safe_read (fd[0], &c[count], 1) > 0)
   1065 	    {
   1066 	      if (c[count] == '\n')
   1067 		break;
   1068 	      if (count == 0)
   1069 		{
   1070 		  if (!(c[0] >= '0' && c[0] <= '9'))
   1071 		    continue;
   1072 		  gcj_present = (c[0] >= '3');
   1073 		}
   1074 	      count++;
   1075 	      if (count == 3)
   1076 		{
   1077 		  if (c[0] == '3' && c[1] == '.'
   1078 		      && (c[2] == '0' || c[2] == '1'))
   1079 		    gcj_present = false;
   1080 		  break;
   1081 		}
   1082 	    }
   1083 	  while (safe_read (fd[0], &c[0], 1) > 0)
   1084 	    ;
   1085 
   1086 	  close (fd[0]);
   1087 
   1088 	  /* Remove zombie process from process list, and retrieve exit
   1089 	     status.  */
   1090 	  exitstatus =
   1091 	    wait_subprocess (child, "gcj", false, true, true, false);
   1092 	  if (exitstatus != 0)
   1093 	    gcj_present = false;
   1094 	}
   1095 
   1096       if (gcj_present)
   1097 	{
   1098 	  /* See if libgcj.jar is well installed.  */
   1099 	  struct temp_dir *tmpdir;
   1100 
   1101 	  tmpdir = create_temp_dir ("java", NULL, false);
   1102 	  if (tmpdir == NULL)
   1103 	    gcj_present = false;
   1104 	  else
   1105 	    {
   1106 	      char *conftest_file_name;
   1107 
   1108 	      conftest_file_name =
   1109 		concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
   1110 				       NULL);
   1111 	      if (write_temp_file (tmpdir, conftest_file_name,
   1112 "public class conftestlib {\n"
   1113 "  public static void main (String[] args) {\n"
   1114 "  }\n"
   1115 "}\n"))
   1116 		gcj_present = false;
   1117 	      else
   1118 		{
   1119 		  char *compiled_file_name;
   1120 		  const char *java_sources[1];
   1121 
   1122 		  compiled_file_name =
   1123 		    concatenated_pathname (tmpdir->dir_name,
   1124 					   "conftestlib.class",
   1125 					   NULL);
   1126 		  register_temp_file (tmpdir, compiled_file_name);
   1127 
   1128 		  java_sources[0] = conftest_file_name;
   1129 		  if (compile_using_gcj (java_sources, 1, false,
   1130 					 tmpdir->dir_name,
   1131 					 false, false, false, true))
   1132 		    gcj_present = false;
   1133 
   1134 		  free (compiled_file_name);
   1135 		}
   1136 	      free (conftest_file_name);
   1137 	    }
   1138 	  cleanup_temp_dir (tmpdir);
   1139 	}
   1140 
   1141       gcj_tested = true;
   1142     }
   1143 
   1144   return gcj_present;
   1145 }
   1146 
   1147 /* Test gcj can be used for compiling with target_version = 1.4 and
   1148    source_version = 1.4.
   1149    Return a failure indicator (true upon error).  */
   1150 static bool
   1151 is_gcj_14_14_usable (bool *usablep)
   1152 {
   1153   static bool gcj_tested;
   1154   static bool gcj_usable;
   1155 
   1156   if (!gcj_tested)
   1157     {
   1158       /* Try gcj.  */
   1159       struct temp_dir *tmpdir;
   1160       char *conftest_file_name;
   1161       char *compiled_file_name;
   1162       const char *java_sources[1];
   1163       struct stat statbuf;
   1164 
   1165       tmpdir = create_temp_dir ("java", NULL, false);
   1166       if (tmpdir == NULL)
   1167 	return true;
   1168 
   1169       conftest_file_name =
   1170 	concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
   1171       if (write_temp_file (tmpdir, conftest_file_name,
   1172 			   get_goodcode_snippet ("1.4")))
   1173 	{
   1174 	  free (conftest_file_name);
   1175 	  cleanup_temp_dir (tmpdir);
   1176 	  return true;
   1177 	}
   1178 
   1179       compiled_file_name =
   1180 	concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
   1181       register_temp_file (tmpdir, compiled_file_name);
   1182 
   1183       java_sources[0] = conftest_file_name;
   1184       if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
   1185 			      false, false, false, true)
   1186 	  && stat (compiled_file_name, &statbuf) >= 0)
   1187 	/* Compilation succeeded.  */
   1188 	gcj_usable = true;
   1189 
   1190       free (compiled_file_name);
   1191       free (conftest_file_name);
   1192 
   1193       cleanup_temp_dir (tmpdir);
   1194 
   1195       gcj_tested = true;
   1196     }
   1197 
   1198   *usablep = gcj_usable;
   1199   return false;
   1200 }
   1201 
   1202 /* Test whether gcj can be used for compiling with target_version = 1.4 and
   1203    source_version = 1.3.
   1204    Return a failure indicator (true upon error).  */
   1205 static bool
   1206 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
   1207 {
   1208   static bool gcj_tested;
   1209   static bool gcj_usable;
   1210   static bool gcj_need_no_assert_option;
   1211 
   1212   if (!gcj_tested)
   1213     {
   1214       /* Try gcj and "gcj -fno-assert".  But add -fno-assert only if
   1215 	 it works (not gcj < 3.3).  */
   1216       struct temp_dir *tmpdir;
   1217       char *conftest_file_name;
   1218       char *compiled_file_name;
   1219       const char *java_sources[1];
   1220       struct stat statbuf;
   1221 
   1222       tmpdir = create_temp_dir ("java", NULL, false);
   1223       if (tmpdir == NULL)
   1224 	return true;
   1225 
   1226       conftest_file_name =
   1227 	concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
   1228       if (write_temp_file (tmpdir, conftest_file_name,
   1229 			   get_goodcode_snippet ("1.3")))
   1230 	{
   1231 	  free (conftest_file_name);
   1232 	  cleanup_temp_dir (tmpdir);
   1233 	  return true;
   1234 	}
   1235 
   1236       compiled_file_name =
   1237 	concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
   1238       register_temp_file (tmpdir, compiled_file_name);
   1239 
   1240       java_sources[0] = conftest_file_name;
   1241       if (!compile_using_gcj (java_sources, 1, true, tmpdir->dir_name,
   1242 			      false, false, false, true)
   1243 	  && stat (compiled_file_name, &statbuf) >= 0)
   1244 	/* Compilation succeeded.  */
   1245 	{
   1246 	  gcj_usable = true;
   1247 	  gcj_need_no_assert_option = true;
   1248 	}
   1249       else
   1250 	{
   1251 	  unlink (compiled_file_name);
   1252 
   1253 	  java_sources[0] = conftest_file_name;
   1254 	  if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
   1255 				  false, false, false, true)
   1256 	      && stat (compiled_file_name, &statbuf) >= 0)
   1257 	    /* Compilation succeeded.  */
   1258 	    {
   1259 	      gcj_usable = true;
   1260 	      gcj_need_no_assert_option = false;
   1261 	    }
   1262 	}
   1263 
   1264       free (compiled_file_name);
   1265       free (conftest_file_name);
   1266 
   1267       cleanup_temp_dir (tmpdir);
   1268 
   1269       gcj_tested = true;
   1270     }
   1271 
   1272   *usablep = gcj_usable;
   1273   *need_no_assert_option_p = gcj_need_no_assert_option;
   1274   return false;
   1275 }
   1276 
   1277 static bool
   1278 is_javac_present (void)
   1279 {
   1280   static bool javac_tested;
   1281   static bool javac_present;
   1282 
   1283   if (!javac_tested)
   1284     {
   1285       /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2"  */
   1286       char *argv[2];
   1287       int exitstatus;
   1288 
   1289       argv[0] = "javac";
   1290       argv[1] = NULL;
   1291       exitstatus = execute ("javac", "javac", argv, false, false, true, true,
   1292 			    true, false);
   1293       javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
   1294       javac_tested = true;
   1295     }
   1296 
   1297   return javac_present;
   1298 }
   1299 
   1300 /* Test whether javac can be used and whether it needs a -source and/or
   1301    -target option.
   1302    Return a failure indicator (true upon error).  */
   1303 static bool
   1304 is_javac_usable (const char *source_version, const char *target_version,
   1305 		 bool *usablep, bool *source_option_p, bool *target_option_p)
   1306 {
   1307   /* The cache depends on the source_version and target_version.  */
   1308   struct result_t
   1309   {
   1310     bool tested;
   1311     bool usable;
   1312     bool source_option;
   1313     bool target_option;
   1314   };
   1315   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
   1316   struct result_t *resultp;
   1317 
   1318   resultp = &result_cache[source_version_index (source_version)]
   1319 			 [target_version_index (target_version)];
   1320   if (!resultp->tested)
   1321     {
   1322       /* Try javac.  */
   1323       struct temp_dir *tmpdir;
   1324       char *conftest_file_name;
   1325       char *compiled_file_name;
   1326       const char *java_sources[1];
   1327       struct stat statbuf;
   1328 
   1329       tmpdir = create_temp_dir ("java", NULL, false);
   1330       if (tmpdir == NULL)
   1331 	return true;
   1332 
   1333       conftest_file_name =
   1334 	concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
   1335       if (write_temp_file (tmpdir, conftest_file_name,
   1336 			   get_goodcode_snippet (source_version)))
   1337 	{
   1338 	  free (conftest_file_name);
   1339 	  cleanup_temp_dir (tmpdir);
   1340 	  return true;
   1341 	}
   1342 
   1343       compiled_file_name =
   1344 	concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
   1345       register_temp_file (tmpdir, compiled_file_name);
   1346 
   1347       java_sources[0] = conftest_file_name;
   1348       if (!compile_using_javac (java_sources, 1,
   1349 				false, source_version,
   1350 				false, target_version,
   1351 				tmpdir->dir_name, false, false, false, true)
   1352 	  && stat (compiled_file_name, &statbuf) >= 0
   1353 	  && get_classfile_version (compiled_file_name)
   1354 	     <= corresponding_classfile_version (target_version))
   1355 	{
   1356 	  /* javac compiled conftest.java successfully.  */
   1357 	  /* Try adding -source option if it is useful.  */
   1358 	  unlink (compiled_file_name);
   1359 
   1360 	  java_sources[0] = conftest_file_name;
   1361 	  if (!compile_using_javac (java_sources, 1,
   1362 				    true, source_version,
   1363 				    false, target_version,
   1364 				    tmpdir->dir_name, false, false, false, true)
   1365 	      && stat (compiled_file_name, &statbuf) >= 0
   1366 	      && get_classfile_version (compiled_file_name)
   1367 		 <= corresponding_classfile_version (target_version))
   1368 	    {
   1369 	      const char *failcode = get_failcode_snippet (source_version);
   1370 
   1371 	      if (failcode != NULL)
   1372 		{
   1373 		  free (compiled_file_name);
   1374 		  free (conftest_file_name);
   1375 
   1376 		  conftest_file_name =
   1377 		    concatenated_pathname (tmpdir->dir_name,
   1378 					   "conftestfail.java",
   1379 					   NULL);
   1380 		  if (write_temp_file (tmpdir, conftest_file_name, failcode))
   1381 		    {
   1382 		      free (conftest_file_name);
   1383 		      cleanup_temp_dir (tmpdir);
   1384 		      return true;
   1385 		    }
   1386 
   1387 		  compiled_file_name =
   1388 		    concatenated_pathname (tmpdir->dir_name,
   1389 					   "conftestfail.class",
   1390 					   NULL);
   1391 		  register_temp_file (tmpdir, compiled_file_name);
   1392 
   1393 		  java_sources[0] = conftest_file_name;
   1394 		  if (!compile_using_javac (java_sources, 1,
   1395 					    false, source_version,
   1396 					    false, target_version,
   1397 					    tmpdir->dir_name,
   1398 					    false, false, false, true)
   1399 		      && stat (compiled_file_name, &statbuf) >= 0)
   1400 		    {
   1401 		      unlink (compiled_file_name);
   1402 
   1403 		      java_sources[0] = conftest_file_name;
   1404 		      if (compile_using_javac (java_sources, 1,
   1405 					       true, source_version,
   1406 					       false, target_version,
   1407 					       tmpdir->dir_name,
   1408 					       false, false, false, true))
   1409 			/* javac compiled conftestfail.java successfully, and
   1410 			   "javac -source $source_version" rejects it.  So the
   1411 			   -source option is useful.  */
   1412 			resultp->source_option = true;
   1413 		    }
   1414 		}
   1415 	    }
   1416 
   1417 	  resultp->usable = true;
   1418 	}
   1419       else
   1420 	{
   1421 	  /* Try with -target option alone. (Sun javac 1.3.1 has the -target
   1422 	     option but no -source option.)  */
   1423 	  unlink (compiled_file_name);
   1424 
   1425 	  java_sources[0] = conftest_file_name;
   1426 	  if (!compile_using_javac (java_sources, 1,
   1427 				    false, source_version,
   1428 				    true, target_version,
   1429 				    tmpdir->dir_name,
   1430 				    false, false, false, true)
   1431 	      && stat (compiled_file_name, &statbuf) >= 0
   1432 	      && get_classfile_version (compiled_file_name)
   1433 		 <= corresponding_classfile_version (target_version))
   1434 	    {
   1435 	      /* "javac -target $target_version" compiled conftest.java
   1436 		 successfully.  */
   1437 	      /* Try adding -source option if it is useful.  */
   1438 	      unlink (compiled_file_name);
   1439 
   1440 	      java_sources[0] = conftest_file_name;
   1441 	      if (!compile_using_javac (java_sources, 1,
   1442 					true, source_version,
   1443 					true, target_version,
   1444 					tmpdir->dir_name,
   1445 					false, false, false, true)
   1446 		  && stat (compiled_file_name, &statbuf) >= 0
   1447 		  && get_classfile_version (compiled_file_name)
   1448 		     <= corresponding_classfile_version (target_version))
   1449 		{
   1450 		  const char *failcode = get_failcode_snippet (source_version);
   1451 
   1452 		  if (failcode != NULL)
   1453 		    {
   1454 		      free (compiled_file_name);
   1455 		      free (conftest_file_name);
   1456 
   1457 		      conftest_file_name =
   1458 			concatenated_pathname (tmpdir->dir_name,
   1459 					       "conftestfail.java",
   1460 					       NULL);
   1461 		      if (write_temp_file (tmpdir, conftest_file_name,
   1462 					   failcode))
   1463 			{
   1464 			  free (conftest_file_name);
   1465 			  cleanup_temp_dir (tmpdir);
   1466 			  return true;
   1467 			}
   1468 
   1469 		      compiled_file_name =
   1470 			concatenated_pathname (tmpdir->dir_name,
   1471 					       "conftestfail.class",
   1472 					       NULL);
   1473 		      register_temp_file (tmpdir, compiled_file_name);
   1474 
   1475 		      java_sources[0] = conftest_file_name;
   1476 		      if (!compile_using_javac (java_sources, 1,
   1477 						false, source_version,
   1478 						true, target_version,
   1479 						tmpdir->dir_name,
   1480 						false, false, false, true)
   1481 			  && stat (compiled_file_name, &statbuf) >= 0)
   1482 			{
   1483 			  unlink (compiled_file_name);
   1484 
   1485 			  java_sources[0] = conftest_file_name;
   1486 			  if (compile_using_javac (java_sources, 1,
   1487 						   true, source_version,
   1488 						   true, target_version,
   1489 						   tmpdir->dir_name,
   1490 						   false, false, false, true))
   1491 			    /* "javac -target $target_version" compiled
   1492 			       conftestfail.java successfully, and
   1493 			       "javac -target $target_version -source $source_version"
   1494 			       rejects it.  So the -source option is useful.  */
   1495 			    resultp->source_option = true;
   1496 			}
   1497 		    }
   1498 		}
   1499 
   1500 	      resultp->target_option = true;
   1501 	      resultp->usable = true;
   1502 	    }
   1503 	  else
   1504 	    {
   1505 	      /* Maybe this -target option requires a -source option? Try with
   1506 		 -target and -source options. (Supported by Sun javac 1.4 and
   1507 		 higher.)  */
   1508 	      unlink (compiled_file_name);
   1509 
   1510 	      java_sources[0] = conftest_file_name;
   1511 	      if (!compile_using_javac (java_sources, 1,
   1512 					true, source_version,
   1513 					true, target_version,
   1514 					tmpdir->dir_name,
   1515 					false, false, false, true)
   1516 		  && stat (compiled_file_name, &statbuf) >= 0
   1517 		  && get_classfile_version (compiled_file_name)
   1518 		     <= corresponding_classfile_version (target_version))
   1519 		{
   1520 		  /* "javac -target $target_version -source $source_version"
   1521 		     compiled conftest.java successfully.  */
   1522 		  resultp->source_option = true;
   1523 		  resultp->target_option = true;
   1524 		  resultp->usable = true;
   1525 		}
   1526 	    }
   1527 	}
   1528 
   1529       free (compiled_file_name);
   1530       free (conftest_file_name);
   1531 
   1532       resultp->tested = true;
   1533     }
   1534 
   1535   *usablep = resultp->usable;
   1536   *source_option_p = resultp->source_option;
   1537   *target_option_p = resultp->target_option;
   1538   return false;
   1539 }
   1540 
   1541 static bool
   1542 is_jikes_present (void)
   1543 {
   1544   static bool jikes_tested;
   1545   static bool jikes_present;
   1546 
   1547   if (!jikes_tested)
   1548     {
   1549       /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1"  */
   1550       char *argv[2];
   1551       int exitstatus;
   1552 
   1553       argv[0] = "jikes";
   1554       argv[1] = NULL;
   1555       exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
   1556 			    true, false);
   1557       jikes_present = (exitstatus == 0 || exitstatus == 1);
   1558       jikes_tested = true;
   1559     }
   1560 
   1561   return jikes_present;
   1562 }
   1563 
   1564 /* ============================= Main function ============================= */
   1565 
   1566 bool
   1567 compile_java_class (const char * const *java_sources,
   1568 		    unsigned int java_sources_count,
   1569 		    const char * const *classpaths,
   1570 		    unsigned int classpaths_count,
   1571 		    const char *source_version,
   1572 		    const char *target_version,
   1573 		    const char *directory,
   1574 		    bool optimize, bool debug,
   1575 		    bool use_minimal_classpath,
   1576 		    bool verbose)
   1577 {
   1578   bool err = false;
   1579   char *old_JAVA_HOME;
   1580 
   1581   {
   1582     const char *javac = getenv ("JAVAC");
   1583     if (javac != NULL && javac[0] != '\0')
   1584       {
   1585 	bool usable = false;
   1586 	bool no_assert_option = false;
   1587 	bool source_option = false;
   1588 	bool target_option = false;
   1589 
   1590 	if (target_version == NULL)
   1591 	  target_version = default_target_version ();
   1592 
   1593 	if (is_envjavac_gcj (javac))
   1594 	  {
   1595 	    /* It's a version of gcj.  Ignore the version of the class files
   1596 	       that it creates.  */
   1597 	    if (strcmp (target_version, "1.4") == 0
   1598 		&& strcmp (source_version, "1.4") == 0)
   1599 	      {
   1600 		if (is_envjavac_gcj_14_14_usable (javac, &usable))
   1601 		  {
   1602 		    err = true;
   1603 		    goto done1;
   1604 		  }
   1605 	      }
   1606 	    else if (strcmp (target_version, "1.4") == 0
   1607 		     && strcmp (source_version, "1.3") == 0)
   1608 	      {
   1609 		if (is_envjavac_gcj_14_13_usable (javac,
   1610 						  &usable, &no_assert_option))
   1611 		  {
   1612 		    err = true;
   1613 		    goto done1;
   1614 		  }
   1615 	      }
   1616 	  }
   1617 	else
   1618 	  {
   1619 	    /* It's not gcj.  Assume the classfile versions are correct.  */
   1620 	    if (is_envjavac_nongcj_usable (javac,
   1621 					   source_version, target_version,
   1622 					   &usable,
   1623 					   &source_option, &target_option))
   1624 	      {
   1625 		err = true;
   1626 		goto done1;
   1627 	      }
   1628 	  }
   1629 
   1630 	if (usable)
   1631 	  {
   1632 	    char *old_classpath;
   1633 	    char *javac_with_options;
   1634 
   1635 	    /* Set CLASSPATH.  */
   1636 	    old_classpath =
   1637 	      set_classpath (classpaths, classpaths_count, false, verbose);
   1638 
   1639 	    javac_with_options =
   1640 	      (no_assert_option
   1641 	       ? xasprintf ("%s -fno-assert", javac)
   1642 	       : xasprintf ("%s%s%s%s%s",
   1643 			    javac,
   1644 			    source_option ? " -source " : "",
   1645 			    source_option ? source_version : "",
   1646 			    target_option ? " -target " : "",
   1647 			    target_option ? target_version : ""));
   1648 
   1649 	    err = compile_using_envjavac (javac_with_options,
   1650 					  java_sources, java_sources_count,
   1651 					  directory, optimize, debug, verbose,
   1652 					  false);
   1653 
   1654 	    free (javac_with_options);
   1655 
   1656 	    /* Reset CLASSPATH.  */
   1657 	    reset_classpath (old_classpath);
   1658 
   1659 	    goto done1;
   1660 	  }
   1661       }
   1662   }
   1663 
   1664   /* Unset the JAVA_HOME environment variable.  */
   1665   old_JAVA_HOME = getenv ("JAVA_HOME");
   1666   if (old_JAVA_HOME != NULL)
   1667     {
   1668       old_JAVA_HOME = xstrdup (old_JAVA_HOME);
   1669       unsetenv ("JAVA_HOME");
   1670     }
   1671 
   1672   if (is_gcj_present ())
   1673     {
   1674       /* Test whether it supports the desired target-version and
   1675 	 source-version.  But ignore the version of the class files that
   1676 	 it creates.  */
   1677       bool usable = false;
   1678       bool no_assert_option = false;
   1679 
   1680       if (target_version == NULL)
   1681 	target_version = default_target_version ();
   1682 
   1683       if (strcmp (target_version, "1.4") == 0
   1684 	  && strcmp (source_version, "1.4") == 0)
   1685 	{
   1686 	  if (is_gcj_14_14_usable (&usable))
   1687 	    {
   1688 	      err = true;
   1689 	      goto done1;
   1690 	    }
   1691 	}
   1692       else if (strcmp (target_version, "1.4") == 0
   1693 	       && strcmp (source_version, "1.3") == 0)
   1694 	{
   1695 	  if (is_gcj_14_13_usable (&usable, &no_assert_option))
   1696 	    {
   1697 	      err = true;
   1698 	      goto done1;
   1699 	    }
   1700 	}
   1701 
   1702       if (usable)
   1703 	{
   1704 	  char *old_classpath;
   1705 
   1706 	  /* Set CLASSPATH.  We could also use the --CLASSPATH=... option
   1707 	     of gcj.  Note that --classpath=... option is different: its
   1708 	     argument should also contain gcj's libgcj.jar, but we don't
   1709 	     know its location.  */
   1710 	  old_classpath =
   1711 	    set_classpath (classpaths, classpaths_count, use_minimal_classpath,
   1712 			   verbose);
   1713 
   1714 	  err = compile_using_gcj (java_sources, java_sources_count,
   1715 				   no_assert_option,
   1716 				   directory, optimize, debug, verbose, false);
   1717 
   1718 	  /* Reset CLASSPATH.  */
   1719 	  reset_classpath (old_classpath);
   1720 
   1721 	  goto done2;
   1722 	}
   1723     }
   1724 
   1725   if (is_javac_present ())
   1726     {
   1727       bool usable = false;
   1728       bool source_option = false;
   1729       bool target_option = false;
   1730 
   1731       if (target_version == NULL)
   1732 	target_version = default_target_version ();
   1733 
   1734       if (is_javac_usable (source_version, target_version,
   1735 			   &usable, &source_option, &target_option))
   1736 	{
   1737 	  err = true;
   1738 	  goto done1;
   1739 	}
   1740 
   1741       if (usable)
   1742 	{
   1743 	  char *old_classpath;
   1744 
   1745 	  /* Set CLASSPATH.  We don't use the "-classpath ..." option because
   1746 	     in JDK 1.1.x its argument should also contain the JDK's
   1747 	     classes.zip, but we don't know its location.  (In JDK 1.3.0 it
   1748 	     would work.)  */
   1749 	  old_classpath =
   1750 	    set_classpath (classpaths, classpaths_count, use_minimal_classpath,
   1751 			   verbose);
   1752 
   1753 	  err = compile_using_javac (java_sources, java_sources_count,
   1754 				     source_option, source_version,
   1755 				     target_option, target_version,
   1756 				     directory, optimize, debug, verbose,
   1757 				     false);
   1758 
   1759 	  /* Reset CLASSPATH.  */
   1760 	  reset_classpath (old_classpath);
   1761 
   1762 	  goto done2;
   1763 	}
   1764     }
   1765 
   1766   if (is_jikes_present ())
   1767     {
   1768       /* Test whether it supports the desired target-version and
   1769 	 source-version.  */
   1770       bool usable = (strcmp (source_version, "1.3") == 0);
   1771 
   1772       if (usable)
   1773 	{
   1774 	  char *old_classpath;
   1775 
   1776 	  /* Set CLASSPATH.  We could also use the "-classpath ..." option.
   1777 	     Since jikes doesn't come with its own standard library, it
   1778 	     needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
   1779 	     To increase the chance of success, we reuse the current CLASSPATH
   1780 	     if the user has set it.  */
   1781 	  old_classpath =
   1782 	    set_classpath (classpaths, classpaths_count, false, verbose);
   1783 
   1784 	  err = compile_using_jikes (java_sources, java_sources_count,
   1785 				     directory, optimize, debug, verbose,
   1786 				     false);
   1787 
   1788 	  /* Reset CLASSPATH.  */
   1789 	  reset_classpath (old_classpath);
   1790 
   1791 	  goto done2;
   1792 	}
   1793     }
   1794 
   1795   error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
   1796   err = true;
   1797 
   1798  done2:
   1799   if (old_JAVA_HOME != NULL)
   1800     {
   1801       xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
   1802       free (old_JAVA_HOME);
   1803     }
   1804 
   1805  done1:
   1806   return err;
   1807 }
   1808