maint: update copyright
[gnulib.git] / lib / javacomp.c
1 /* Compile a Java program.
2    Copyright (C) 2001-2003, 2006-2014 Free Software Foundation, Inc.
3    Written by Bruno Haible <haible@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 3 of the License, or
8    (at your option) 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, see <http://www.gnu.org/licenses/>.  */
17
18 #include <config.h>
19 #include <alloca.h>
20
21 /* Specification.  */
22 #include "javacomp.h"
23
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32
33 #include "javaversion.h"
34 #include "execute.h"
35 #include "spawn-pipe.h"
36 #include "wait-process.h"
37 #include "classpath.h"
38 #include "xsetenv.h"
39 #include "sh-quote.h"
40 #include "binary-io.h"
41 #include "safe-read.h"
42 #include "xalloc.h"
43 #include "xmalloca.h"
44 #include "concat-filename.h"
45 #include "fwriteerror.h"
46 #include "clean-temp.h"
47 #include "error.h"
48 #include "xvasprintf.h"
49 #include "c-strstr.h"
50 #include "gettext.h"
51
52 #define _(str) gettext (str)
53
54 /* The results of open() in this file are not used with fchdir,
55    therefore save some unnecessary work in fchdir.c.  */
56 #undef open
57 #undef close
58
59
60 /* Survey of Java compilers.
61
62    A = does it work without CLASSPATH being set
63    C = option to set CLASSPATH, other than setting it in the environment
64    O = option for optimizing
65    g = option for debugging
66    T = test for presence
67
68    Program  from        A  C               O  g  T
69
70    $JAVAC   unknown     N  n/a            -O -g  true
71    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
72    javac    JDK 1.1.8   Y  -classpath P   -O -g  javac 2>/dev/null; test $? = 1
73    javac    JDK 1.3.0   Y  -classpath P   -O -g  javac 2>/dev/null; test $? -le 2
74    jikes    Jikes 1.14  N  -classpath P   -O -g  jikes 2>/dev/null; test $? = 1
75
76    All compilers support the option "-d DIRECTORY" for the base directory
77    of the classes to be written.
78
79    The CLASSPATH is a colon separated list of pathnames. (On Windows: a
80    semicolon separated list of pathnames.)
81
82    We try the Java compilers in the following order:
83      1. getenv ("JAVAC"), because the user must be able to override our
84         preferences,
85      2. "gcj -C", because it is a completely free compiler,
86      3. "javac", because it is a standard compiler,
87      4. "jikes", comes last because it has some deviating interpretation
88         of the Java Language Specification and because it requires a
89         CLASSPATH environment variable.
90
91    We unset the JAVA_HOME environment variable, because a wrong setting of
92    this variable can confuse the JDK's javac.
93  */
94
95 /* Return the default target_version.  */
96 static const char *
97 default_target_version (void)
98 {
99   /* Use a cache.  Assumes that the PATH environment variable doesn't change
100      during the lifetime of the program.  */
101   static const char *java_version_cache;
102   if (java_version_cache == NULL)
103     {
104       /* Determine the version from the found JVM.  */
105       java_version_cache = javaexec_version ();
106       if (java_version_cache == NULL
107           || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
108                && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
109                && java_version_cache[3] == '\0'))
110         java_version_cache = "1.1";
111     }
112   return java_version_cache;
113 }
114
115 /* ======================= Source version dependent ======================= */
116
117 /* Convert a source version to an index.  */
118 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
119 static unsigned int
120 source_version_index (const char *source_version)
121 {
122   if (source_version[0] == '1' && source_version[1] == '.'
123       && (source_version[2] >= '3' && source_version[2] <= '5')
124       && source_version[3] == '\0')
125     return source_version[2] - '3';
126   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
127   return 0;
128 }
129
130 /* Return a snippet of code that should compile in the given source version.  */
131 static const char *
132 get_goodcode_snippet (const char *source_version)
133 {
134   if (strcmp (source_version, "1.3") == 0)
135     return "class conftest {}\n";
136   if (strcmp (source_version, "1.4") == 0)
137     return "class conftest { static { assert(true); } }\n";
138   if (strcmp (source_version, "1.5") == 0)
139     return "class conftest<T> { T foo() { return null; } }\n";
140   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
141   return NULL;
142 }
143
144 /* Return a snippet of code that should fail to compile in the given source
145    version, or NULL (standing for a snippet that would fail to compile with
146    any compiler).  */
147 static const char *
148 get_failcode_snippet (const char *source_version)
149 {
150   if (strcmp (source_version, "1.3") == 0)
151     return "class conftestfail { static { assert(true); } }\n";
152   if (strcmp (source_version, "1.4") == 0)
153     return "class conftestfail<T> { T foo() { return null; } }\n";
154   if (strcmp (source_version, "1.5") == 0)
155     return NULL;
156   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
157   return NULL;
158 }
159
160 /* ======================= Target version dependent ======================= */
161
162 /* Convert a target version to an index.  */
163 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
164 static unsigned int
165 target_version_index (const char *target_version)
166 {
167   if (target_version[0] == '1' && target_version[1] == '.'
168       && (target_version[2] >= '1' && target_version[2] <= '6')
169       && target_version[3] == '\0')
170     return target_version[2] - '1';
171   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
172   return 0;
173 }
174
175 /* Return the class file version number corresponding to a given target
176    version.  */
177 static int
178 corresponding_classfile_version (const char *target_version)
179 {
180   if (strcmp (target_version, "1.1") == 0)
181     return 45;
182   if (strcmp (target_version, "1.2") == 0)
183     return 46;
184   if (strcmp (target_version, "1.3") == 0)
185     return 47;
186   if (strcmp (target_version, "1.4") == 0)
187     return 48;
188   if (strcmp (target_version, "1.5") == 0)
189     return 49;
190   if (strcmp (target_version, "1.6") == 0)
191     return 50;
192   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
193   return 0;
194 }
195
196 /* ======================== Compilation subroutines ======================== */
197
198 /* Try to compile a set of Java sources with $JAVAC.
199    Return a failure indicator (true upon error).  */
200 static bool
201 compile_using_envjavac (const char *javac,
202                         const char * const *java_sources,
203                         unsigned int java_sources_count,
204                         const char *directory,
205                         bool optimize, bool debug,
206                         bool verbose, bool null_stderr)
207 {
208   /* Because $JAVAC may consist of a command and options, we use the
209      shell.  Because $JAVAC has been set by the user, we leave all
210      environment variables in place, including JAVA_HOME, and we don't
211      erase the user's CLASSPATH.  */
212   bool err;
213   unsigned int command_length;
214   char *command;
215   char *argv[4];
216   int exitstatus;
217   unsigned int i;
218   char *p;
219
220   command_length = strlen (javac);
221   if (optimize)
222     command_length += 3;
223   if (debug)
224     command_length += 3;
225   if (directory != NULL)
226     command_length += 4 + shell_quote_length (directory);
227   for (i = 0; i < java_sources_count; i++)
228     command_length += 1 + shell_quote_length (java_sources[i]);
229   command_length += 1;
230
231   command = (char *) xmalloca (command_length);
232   p = command;
233   /* Don't shell_quote $JAVAC, because it may consist of a command
234      and options.  */
235   memcpy (p, javac, strlen (javac));
236   p += strlen (javac);
237   if (optimize)
238     {
239       memcpy (p, " -O", 3);
240       p += 3;
241     }
242   if (debug)
243     {
244       memcpy (p, " -g", 3);
245       p += 3;
246     }
247   if (directory != NULL)
248     {
249       memcpy (p, " -d ", 4);
250       p += 4;
251       p = shell_quote_copy (p, directory);
252     }
253   for (i = 0; i < java_sources_count; i++)
254     {
255       *p++ = ' ';
256       p = shell_quote_copy (p, java_sources[i]);
257     }
258   *p++ = '\0';
259   /* Ensure command_length was correctly calculated.  */
260   if (p - command > command_length)
261     abort ();
262
263   if (verbose)
264     printf ("%s\n", command);
265
266   argv[0] = "/bin/sh";
267   argv[1] = "-c";
268   argv[2] = command;
269   argv[3] = NULL;
270   exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
271                         null_stderr, true, true, NULL);
272   err = (exitstatus != 0);
273
274   freea (command);
275
276   return err;
277 }
278
279 /* Try to compile a set of Java sources with gcj.
280    Return a failure indicator (true upon error).  */
281 static bool
282 compile_using_gcj (const char * const *java_sources,
283                    unsigned int java_sources_count,
284                    bool no_assert_option,
285                    bool fsource_option, const char *source_version,
286                    bool ftarget_option, const char *target_version,
287                    const char *directory,
288                    bool optimize, bool debug,
289                    bool verbose, bool null_stderr)
290 {
291   bool err;
292   unsigned int argc;
293   char **argv;
294   char **argp;
295   char *fsource_arg;
296   char *ftarget_arg;
297   int exitstatus;
298   unsigned int i;
299
300   argc =
301     2 + (no_assert_option ? 1 : 0) + (fsource_option ? 1 : 0)
302     + (ftarget_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
303     + (directory != NULL ? 2 : 0) + java_sources_count;
304   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
305
306   argp = argv;
307   *argp++ = "gcj";
308   *argp++ = "-C";
309   if (no_assert_option)
310     *argp++ = "-fno-assert";
311   if (fsource_option)
312     {
313       fsource_arg = (char *) xmalloca (9 + strlen (source_version) + 1);
314       memcpy (fsource_arg, "-fsource=", 9);
315       strcpy (fsource_arg + 9, source_version);
316       *argp++ = fsource_arg;
317     }
318   else
319     fsource_arg = NULL;
320   if (ftarget_option)
321     {
322       ftarget_arg = (char *) xmalloca (9 + strlen (target_version) + 1);
323       memcpy (ftarget_arg, "-ftarget=", 9);
324       strcpy (ftarget_arg + 9, target_version);
325       *argp++ = ftarget_arg;
326     }
327   else
328     ftarget_arg = NULL;
329   if (optimize)
330     *argp++ = "-O";
331   if (debug)
332     *argp++ = "-g";
333   if (directory != NULL)
334     {
335       *argp++ = "-d";
336       *argp++ = (char *) directory;
337     }
338   for (i = 0; i < java_sources_count; i++)
339     *argp++ = (char *) java_sources[i];
340   *argp = NULL;
341   /* Ensure argv length was correctly calculated.  */
342   if (argp - argv != argc)
343     abort ();
344
345   if (verbose)
346     {
347       char *command = shell_quote_argv (argv);
348       printf ("%s\n", command);
349       free (command);
350     }
351
352   exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
353                         true, true, NULL);
354   err = (exitstatus != 0);
355
356   if (ftarget_arg != NULL)
357     freea (ftarget_arg);
358   if (fsource_arg != NULL)
359     freea (fsource_arg);
360   freea (argv);
361
362   return err;
363 }
364
365 /* Try to compile a set of Java sources with javac.
366    Return a failure indicator (true upon error).  */
367 static bool
368 compile_using_javac (const char * const *java_sources,
369                      unsigned int java_sources_count,
370                      bool source_option, const char *source_version,
371                      bool target_option, const char *target_version,
372                      const char *directory,
373                      bool optimize, bool debug,
374                      bool verbose, bool null_stderr)
375 {
376   bool err;
377   unsigned int argc;
378   char **argv;
379   char **argp;
380   int exitstatus;
381   unsigned int i;
382
383   argc =
384     1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
385     + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
386   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
387
388   argp = argv;
389   *argp++ = "javac";
390   if (source_option)
391     {
392       *argp++ = "-source";
393       *argp++ = (char *) source_version;
394     }
395   if (target_option)
396     {
397       *argp++ = "-target";
398       *argp++ = (char *) target_version;
399     }
400   if (optimize)
401     *argp++ = "-O";
402   if (debug)
403     *argp++ = "-g";
404   if (directory != NULL)
405     {
406       *argp++ = "-d";
407       *argp++ = (char *) directory;
408     }
409   for (i = 0; i < java_sources_count; i++)
410     *argp++ = (char *) java_sources[i];
411   *argp = NULL;
412   /* Ensure argv length was correctly calculated.  */
413   if (argp - argv != argc)
414     abort ();
415
416   if (verbose)
417     {
418       char *command = shell_quote_argv (argv);
419       printf ("%s\n", command);
420       free (command);
421     }
422
423   exitstatus = execute ("javac", "javac", argv, false, false, false,
424                         null_stderr, true, true, NULL);
425   err = (exitstatus != 0);
426
427   freea (argv);
428
429   return err;
430 }
431
432 /* Try to compile a set of Java sources with jikes.
433    Return a failure indicator (true upon error).  */
434 static bool
435 compile_using_jikes (const char * const *java_sources,
436                      unsigned int java_sources_count,
437                      const char *directory,
438                      bool optimize, bool debug,
439                      bool verbose, bool null_stderr)
440 {
441   bool err;
442   unsigned int argc;
443   char **argv;
444   char **argp;
445   int exitstatus;
446   unsigned int i;
447
448   argc =
449     1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
450     + java_sources_count;
451   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
452
453   argp = argv;
454   *argp++ = "jikes";
455   if (optimize)
456     *argp++ = "-O";
457   if (debug)
458     *argp++ = "-g";
459   if (directory != NULL)
460     {
461       *argp++ = "-d";
462       *argp++ = (char *) directory;
463     }
464   for (i = 0; i < java_sources_count; i++)
465     *argp++ = (char *) java_sources[i];
466   *argp = NULL;
467   /* Ensure argv length was correctly calculated.  */
468   if (argp - argv != argc)
469     abort ();
470
471   if (verbose)
472     {
473       char *command = shell_quote_argv (argv);
474       printf ("%s\n", command);
475       free (command);
476     }
477
478   exitstatus = execute ("jikes", "jikes", argv, false, false, false,
479                         null_stderr, true, true, NULL);
480   err = (exitstatus != 0);
481
482   freea (argv);
483
484   return err;
485 }
486
487 /* ====================== Usability test subroutines ====================== */
488
489 /* Write a given contents to a temporary file.
490    FILE_NAME is the name of a file inside TMPDIR that is known not to exist
491    yet.
492    Return a failure indicator (true upon error).  */
493 static bool
494 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
495                  const char *contents)
496 {
497   FILE *fp;
498
499   register_temp_file (tmpdir, file_name);
500   fp = fopen_temp (file_name, "w");
501   if (fp == NULL)
502     {
503       error (0, errno, _("failed to create \"%s\""), file_name);
504       unregister_temp_file (tmpdir, file_name);
505       return true;
506     }
507   fputs (contents, fp);
508   if (fwriteerror_temp (fp))
509     {
510       error (0, errno, _("error while writing \"%s\" file"), file_name);
511       return true;
512     }
513   return false;
514 }
515
516 /* Return the class file version number of a class file on disk.  */
517 static int
518 get_classfile_version (const char *compiled_file_name)
519 {
520   unsigned char header[8];
521   int fd;
522
523   /* Open the class file.  */
524   fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
525   if (fd >= 0)
526     {
527       /* Read its first 8 bytes.  */
528       if (safe_read (fd, header, 8) == 8)
529         {
530           /* Verify the class file signature.  */
531           if (header[0] == 0xCA && header[1] == 0xFE
532               && header[2] == 0xBA && header[3] == 0xBE)
533             return header[7];
534         }
535       close (fd);
536     }
537
538   /* Could not get the class file version.  Return a very large one.  */
539   return INT_MAX;
540 }
541
542 /* Return true if $JAVAC is a version of gcj.  */
543 static bool
544 is_envjavac_gcj (const char *javac)
545 {
546   static bool envjavac_tested;
547   static bool envjavac_gcj;
548
549   if (!envjavac_tested)
550     {
551       /* Test whether $JAVAC is gcj:
552          "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null"  */
553       unsigned int command_length;
554       char *command;
555       char *argv[4];
556       pid_t child;
557       int fd[1];
558       FILE *fp;
559       char *line;
560       size_t linesize;
561       size_t linelen;
562       int exitstatus;
563       char *p;
564
565       /* Setup the command "$JAVAC --version".  */
566       command_length = strlen (javac) + 1 + 9 + 1;
567       command = (char *) xmalloca (command_length);
568       p = command;
569       /* Don't shell_quote $JAVAC, because it may consist of a command
570          and options.  */
571       memcpy (p, javac, strlen (javac));
572       p += strlen (javac);
573       memcpy (p, " --version", 1 + 9 + 1);
574       p += 1 + 9 + 1;
575       /* Ensure command_length was correctly calculated.  */
576       if (p - command > command_length)
577         abort ();
578
579       /* Call $JAVAC --version 2>/dev/null.  */
580       argv[0] = "/bin/sh";
581       argv[1] = "-c";
582       argv[2] = command;
583       argv[3] = NULL;
584       child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
585                               false, fd);
586       if (child == -1)
587         goto failed;
588
589       /* Retrieve its result.  */
590       fp = fdopen (fd[0], "r");
591       if (fp == NULL)
592         goto failed;
593
594       line = NULL; linesize = 0;
595       linelen = getline (&line, &linesize, fp);
596       if (linelen == (size_t)(-1))
597         {
598           fclose (fp);
599           goto failed;
600         }
601       /* It is safe to call c_strstr() instead of strstr() here; see the
602          comments in c-strstr.h.  */
603       envjavac_gcj = (c_strstr (line, "gcj") != NULL);
604
605       fclose (fp);
606
607       /* Remove zombie process from process list, and retrieve exit status.  */
608       exitstatus =
609         wait_subprocess (child, javac, true, true, true, false, NULL);
610       if (exitstatus != 0)
611         envjavac_gcj = false;
612
613      failed:
614       freea (command);
615
616       envjavac_tested = true;
617     }
618
619   return envjavac_gcj;
620 }
621
622 /* Return true if $JAVAC, known to be a version of gcj, is a version >= 4.3
623    of gcj.  */
624 static bool
625 is_envjavac_gcj43 (const char *javac)
626 {
627   static bool envjavac_tested;
628   static bool envjavac_gcj43;
629
630   if (!envjavac_tested)
631     {
632       /* Test whether $JAVAC is gcj:
633          "$JAVAC --version 2>/dev/null | sed -e 's,^[^0-9]*,,' -e 1q \
634           | sed -e '/^4\.[012]/d' | grep '^[4-9]' >/dev/null"  */
635       unsigned int command_length;
636       char *command;
637       char *argv[4];
638       pid_t child;
639       int fd[1];
640       FILE *fp;
641       char *line;
642       size_t linesize;
643       size_t linelen;
644       int exitstatus;
645       char *p;
646
647       /* Setup the command "$JAVAC --version".  */
648       command_length = strlen (javac) + 1 + 9 + 1;
649       command = (char *) xmalloca (command_length);
650       p = command;
651       /* Don't shell_quote $JAVAC, because it may consist of a command
652          and options.  */
653       memcpy (p, javac, strlen (javac));
654       p += strlen (javac);
655       memcpy (p, " --version", 1 + 9 + 1);
656       p += 1 + 9 + 1;
657       /* Ensure command_length was correctly calculated.  */
658       if (p - command > command_length)
659         abort ();
660
661       /* Call $JAVAC --version 2>/dev/null.  */
662       argv[0] = "/bin/sh";
663       argv[1] = "-c";
664       argv[2] = command;
665       argv[3] = NULL;
666       child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
667                               false, fd);
668       if (child == -1)
669         goto failed;
670
671       /* Retrieve its result.  */
672       fp = fdopen (fd[0], "r");
673       if (fp == NULL)
674         goto failed;
675
676       line = NULL; linesize = 0;
677       linelen = getline (&line, &linesize, fp);
678       if (linelen == (size_t)(-1))
679         {
680           fclose (fp);
681           goto failed;
682         }
683       p = line;
684       while (*p != '\0' && !(*p >= '0' && *p <= '9'))
685         p++;
686       envjavac_gcj43 =
687         !(*p == '4' && p[1] == '.' && p[2] >= '0' && p[2] <= '2')
688         && (*p >= '4' && *p <= '9');
689
690       fclose (fp);
691
692       /* Remove zombie process from process list, and retrieve exit status.  */
693       exitstatus =
694         wait_subprocess (child, javac, true, true, true, false, NULL);
695       if (exitstatus != 0)
696         envjavac_gcj43 = false;
697
698      failed:
699       freea (command);
700
701       envjavac_tested = true;
702     }
703
704   return envjavac_gcj43;
705 }
706
707 /* Test whether $JAVAC, known to be a version of gcj >= 4.3, can be used, and
708    whether it needs a -fsource and/or -ftarget option.
709    Return a failure indicator (true upon error).  */
710 static bool
711 is_envjavac_gcj43_usable (const char *javac,
712                           const char *source_version,
713                           const char *target_version,
714                           bool *usablep,
715                           bool *fsource_option_p, bool *ftarget_option_p)
716 {
717   /* The cache depends on the source_version and target_version.  */
718   struct result_t
719   {
720     bool tested;
721     bool usable;
722     bool fsource_option;
723     bool ftarget_option;
724   };
725   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
726   struct result_t *resultp;
727
728   resultp = &result_cache[source_version_index (source_version)]
729                          [target_version_index (target_version)];
730   if (!resultp->tested)
731     {
732       /* Try $JAVAC.  */
733       struct temp_dir *tmpdir;
734       char *conftest_file_name;
735       char *compiled_file_name;
736       const char *java_sources[1];
737       struct stat statbuf;
738
739       tmpdir = create_temp_dir ("java", NULL, false);
740       if (tmpdir == NULL)
741         return true;
742
743       conftest_file_name =
744         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
745       if (write_temp_file (tmpdir, conftest_file_name,
746                            get_goodcode_snippet (source_version)))
747         {
748           free (conftest_file_name);
749           cleanup_temp_dir (tmpdir);
750           return true;
751         }
752
753       compiled_file_name =
754         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
755       register_temp_file (tmpdir, compiled_file_name);
756
757       java_sources[0] = conftest_file_name;
758       if (!compile_using_envjavac (javac,
759                                    java_sources, 1, tmpdir->dir_name,
760                                    false, false, false, true)
761           && stat (compiled_file_name, &statbuf) >= 0
762           && get_classfile_version (compiled_file_name)
763              <= corresponding_classfile_version (target_version))
764         {
765           /* $JAVAC compiled conftest.java successfully.  */
766           /* Try adding -fsource option if it is useful.  */
767           char *javac_source =
768             xasprintf ("%s -fsource=%s", javac, source_version);
769
770           unlink (compiled_file_name);
771
772           java_sources[0] = conftest_file_name;
773           if (!compile_using_envjavac (javac_source,
774                                        java_sources, 1, tmpdir->dir_name,
775                                        false, false, false, true)
776               && stat (compiled_file_name, &statbuf) >= 0
777               && get_classfile_version (compiled_file_name)
778                  <= corresponding_classfile_version (target_version))
779             {
780               const char *failcode = get_failcode_snippet (source_version);
781
782               if (failcode != NULL)
783                 {
784                   free (compiled_file_name);
785                   free (conftest_file_name);
786
787                   conftest_file_name =
788                     xconcatenated_filename (tmpdir->dir_name,
789                                             "conftestfail.java",
790                                             NULL);
791                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
792                     {
793                       free (conftest_file_name);
794                       free (javac_source);
795                       cleanup_temp_dir (tmpdir);
796                       return true;
797                     }
798
799                   compiled_file_name =
800                     xconcatenated_filename (tmpdir->dir_name,
801                                             "conftestfail.class",
802                                             NULL);
803                   register_temp_file (tmpdir, compiled_file_name);
804
805                   java_sources[0] = conftest_file_name;
806                   if (!compile_using_envjavac (javac,
807                                                java_sources, 1,
808                                                tmpdir->dir_name,
809                                                false, false, false, true)
810                       && stat (compiled_file_name, &statbuf) >= 0)
811                     {
812                       unlink (compiled_file_name);
813
814                       java_sources[0] = conftest_file_name;
815                       if (compile_using_envjavac (javac_source,
816                                                   java_sources, 1,
817                                                   tmpdir->dir_name,
818                                                   false, false, false, true))
819                         /* $JAVAC compiled conftestfail.java successfully, and
820                            "$JAVAC -fsource=$source_version" rejects it.  So
821                            the -fsource option is useful.  */
822                         resultp->fsource_option = true;
823                     }
824                 }
825             }
826
827           free (javac_source);
828
829           resultp->usable = true;
830         }
831       else
832         {
833           /* Try with -fsource and -ftarget options.  */
834           char *javac_target =
835             xasprintf ("%s -fsource=%s -ftarget=%s",
836                        javac, source_version, target_version);
837
838           unlink (compiled_file_name);
839
840           java_sources[0] = conftest_file_name;
841           if (!compile_using_envjavac (javac_target,
842                                        java_sources, 1, tmpdir->dir_name,
843                                        false, false, false, true)
844               && stat (compiled_file_name, &statbuf) >= 0
845               && get_classfile_version (compiled_file_name)
846                  <= corresponding_classfile_version (target_version))
847             {
848               /* "$JAVAC -fsource $source_version -ftarget $target_version"
849                  compiled conftest.java successfully.  */
850               resultp->fsource_option = true;
851               resultp->ftarget_option = true;
852               resultp->usable = true;
853             }
854
855           free (javac_target);
856         }
857
858       free (compiled_file_name);
859       free (conftest_file_name);
860
861       resultp->tested = true;
862     }
863
864   *usablep = resultp->usable;
865   *fsource_option_p = resultp->fsource_option;
866   *ftarget_option_p = resultp->ftarget_option;
867   return false;
868 }
869
870 /* Test whether $JAVAC, known to be a version of gcj < 4.3, can be used for
871    compiling with target_version = 1.4 and source_version = 1.4.
872    Return a failure indicator (true upon error).  */
873 static bool
874 is_envjavac_oldgcj_14_14_usable (const char *javac, bool *usablep)
875 {
876   static bool envjavac_tested;
877   static bool envjavac_usable;
878
879   if (!envjavac_tested)
880     {
881       /* Try $JAVAC.  */
882       struct temp_dir *tmpdir;
883       char *conftest_file_name;
884       char *compiled_file_name;
885       const char *java_sources[1];
886       struct stat statbuf;
887
888       tmpdir = create_temp_dir ("java", NULL, false);
889       if (tmpdir == NULL)
890         return true;
891
892       conftest_file_name =
893         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
894       if (write_temp_file (tmpdir, conftest_file_name,
895                            get_goodcode_snippet ("1.4")))
896         {
897           free (conftest_file_name);
898           cleanup_temp_dir (tmpdir);
899           return true;
900         }
901
902       compiled_file_name =
903         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
904       register_temp_file (tmpdir, compiled_file_name);
905
906       java_sources[0] = conftest_file_name;
907       if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
908                                    false, false, false, true)
909           && stat (compiled_file_name, &statbuf) >= 0)
910         /* Compilation succeeded.  */
911         envjavac_usable = true;
912
913       free (compiled_file_name);
914       free (conftest_file_name);
915
916       cleanup_temp_dir (tmpdir);
917
918       envjavac_tested = true;
919     }
920
921   *usablep = envjavac_usable;
922   return false;
923 }
924
925 /* Test whether $JAVAC, known to be a version of gcj < 4.3, can be used for
926    compiling with target_version = 1.4 and source_version = 1.3.
927    Return a failure indicator (true upon error).  */
928 static bool
929 is_envjavac_oldgcj_14_13_usable (const char *javac,
930                                  bool *usablep, bool *need_no_assert_option_p)
931 {
932   static bool envjavac_tested;
933   static bool envjavac_usable;
934   static bool envjavac_need_no_assert_option;
935
936   if (!envjavac_tested)
937     {
938       /* Try $JAVAC and "$JAVAC -fno-assert".  But add -fno-assert only if
939          it makes a difference.  (It could already be part of $JAVAC.)  */
940       struct temp_dir *tmpdir;
941       char *conftest_file_name;
942       char *compiled_file_name;
943       const char *java_sources[1];
944       struct stat statbuf;
945       bool javac_works;
946       char *javac_noassert;
947       bool javac_noassert_works;
948
949       tmpdir = create_temp_dir ("java", NULL, false);
950       if (tmpdir == NULL)
951         return true;
952
953       conftest_file_name =
954         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
955       if (write_temp_file (tmpdir, conftest_file_name,
956                            get_goodcode_snippet ("1.3")))
957         {
958           free (conftest_file_name);
959           cleanup_temp_dir (tmpdir);
960           return true;
961         }
962
963       compiled_file_name =
964         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
965       register_temp_file (tmpdir, compiled_file_name);
966
967       java_sources[0] = conftest_file_name;
968       if (!compile_using_envjavac (javac,
969                                    java_sources, 1, tmpdir->dir_name,
970                                    false, false, false, true)
971           && stat (compiled_file_name, &statbuf) >= 0)
972         /* Compilation succeeded.  */
973         javac_works = true;
974       else
975         javac_works = false;
976
977       unlink (compiled_file_name);
978
979       javac_noassert = xasprintf ("%s -fno-assert", javac);
980
981       java_sources[0] = conftest_file_name;
982       if (!compile_using_envjavac (javac_noassert,
983                                    java_sources, 1, tmpdir->dir_name,
984                                    false, false, false, true)
985           && stat (compiled_file_name, &statbuf) >= 0)
986         /* Compilation succeeded.  */
987         javac_noassert_works = true;
988       else
989         javac_noassert_works = false;
990
991       free (compiled_file_name);
992       free (conftest_file_name);
993
994       if (javac_works && javac_noassert_works)
995         {
996           conftest_file_name =
997             xconcatenated_filename (tmpdir->dir_name, "conftestfail.java",
998                                     NULL);
999           if (write_temp_file (tmpdir, conftest_file_name,
1000                                get_failcode_snippet ("1.3")))
1001             {
1002               free (conftest_file_name);
1003               free (javac_noassert);
1004               cleanup_temp_dir (tmpdir);
1005               return true;
1006             }
1007
1008           compiled_file_name =
1009             xconcatenated_filename (tmpdir->dir_name, "conftestfail.class",
1010                                     NULL);
1011           register_temp_file (tmpdir, compiled_file_name);
1012
1013           java_sources[0] = conftest_file_name;
1014           if (!compile_using_envjavac (javac,
1015                                        java_sources, 1, tmpdir->dir_name,
1016                                        false, false, false, true)
1017               && stat (compiled_file_name, &statbuf) >= 0)
1018             {
1019               /* Compilation succeeded.  */
1020               unlink (compiled_file_name);
1021
1022               java_sources[0] = conftest_file_name;
1023               if (!(!compile_using_envjavac (javac_noassert,
1024                                              java_sources, 1, tmpdir->dir_name,
1025                                              false, false, false, true)
1026                     && stat (compiled_file_name, &statbuf) >= 0))
1027                 /* Compilation failed.  */
1028                 /* "$JAVAC -fno-assert" works better than $JAVAC.  */
1029                 javac_works = true;
1030             }
1031
1032           free (compiled_file_name);
1033           free (conftest_file_name);
1034         }
1035
1036       cleanup_temp_dir (tmpdir);
1037
1038       if (javac_works)
1039         {
1040           envjavac_usable = true;
1041           envjavac_need_no_assert_option = false;
1042         }
1043       else if (javac_noassert_works)
1044         {
1045           envjavac_usable = true;
1046           envjavac_need_no_assert_option = true;
1047         }
1048
1049       envjavac_tested = true;
1050     }
1051
1052   *usablep = envjavac_usable;
1053   *need_no_assert_option_p = envjavac_need_no_assert_option;
1054   return false;
1055 }
1056
1057 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
1058    whether it needs a -source and/or -target option.
1059    Return a failure indicator (true upon error).  */
1060 static bool
1061 is_envjavac_nongcj_usable (const char *javac,
1062                            const char *source_version,
1063                            const char *target_version,
1064                            bool *usablep,
1065                            bool *source_option_p, bool *target_option_p)
1066 {
1067   /* The cache depends on the source_version and target_version.  */
1068   struct result_t
1069   {
1070     bool tested;
1071     bool usable;
1072     bool source_option;
1073     bool target_option;
1074   };
1075   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1076   struct result_t *resultp;
1077
1078   resultp = &result_cache[source_version_index (source_version)]
1079                          [target_version_index (target_version)];
1080   if (!resultp->tested)
1081     {
1082       /* Try $JAVAC.  */
1083       struct temp_dir *tmpdir;
1084       char *conftest_file_name;
1085       char *compiled_file_name;
1086       const char *java_sources[1];
1087       struct stat statbuf;
1088
1089       tmpdir = create_temp_dir ("java", NULL, false);
1090       if (tmpdir == NULL)
1091         return true;
1092
1093       conftest_file_name =
1094         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1095       if (write_temp_file (tmpdir, conftest_file_name,
1096                            get_goodcode_snippet (source_version)))
1097         {
1098           free (conftest_file_name);
1099           cleanup_temp_dir (tmpdir);
1100           return true;
1101         }
1102
1103       compiled_file_name =
1104         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1105       register_temp_file (tmpdir, compiled_file_name);
1106
1107       java_sources[0] = conftest_file_name;
1108       if (!compile_using_envjavac (javac,
1109                                    java_sources, 1, tmpdir->dir_name,
1110                                    false, false, false, true)
1111           && stat (compiled_file_name, &statbuf) >= 0
1112           && get_classfile_version (compiled_file_name)
1113              <= corresponding_classfile_version (target_version))
1114         {
1115           /* $JAVAC compiled conftest.java successfully.  */
1116           /* Try adding -source option if it is useful.  */
1117           char *javac_source =
1118             xasprintf ("%s -source %s", javac, source_version);
1119
1120           unlink (compiled_file_name);
1121
1122           java_sources[0] = conftest_file_name;
1123           if (!compile_using_envjavac (javac_source,
1124                                        java_sources, 1, tmpdir->dir_name,
1125                                        false, false, false, true)
1126               && stat (compiled_file_name, &statbuf) >= 0
1127               && get_classfile_version (compiled_file_name)
1128                  <= corresponding_classfile_version (target_version))
1129             {
1130               const char *failcode = get_failcode_snippet (source_version);
1131
1132               if (failcode != NULL)
1133                 {
1134                   free (compiled_file_name);
1135                   free (conftest_file_name);
1136
1137                   conftest_file_name =
1138                     xconcatenated_filename (tmpdir->dir_name,
1139                                             "conftestfail.java",
1140                                             NULL);
1141                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1142                     {
1143                       free (conftest_file_name);
1144                       free (javac_source);
1145                       cleanup_temp_dir (tmpdir);
1146                       return true;
1147                     }
1148
1149                   compiled_file_name =
1150                     xconcatenated_filename (tmpdir->dir_name,
1151                                             "conftestfail.class",
1152                                             NULL);
1153                   register_temp_file (tmpdir, compiled_file_name);
1154
1155                   java_sources[0] = conftest_file_name;
1156                   if (!compile_using_envjavac (javac,
1157                                                java_sources, 1,
1158                                                tmpdir->dir_name,
1159                                                false, false, false, true)
1160                       && stat (compiled_file_name, &statbuf) >= 0)
1161                     {
1162                       unlink (compiled_file_name);
1163
1164                       java_sources[0] = conftest_file_name;
1165                       if (compile_using_envjavac (javac_source,
1166                                                   java_sources, 1,
1167                                                   tmpdir->dir_name,
1168                                                   false, false, false, true))
1169                         /* $JAVAC compiled conftestfail.java successfully, and
1170                            "$JAVAC -source $source_version" rejects it.  So the
1171                            -source option is useful.  */
1172                         resultp->source_option = true;
1173                     }
1174                 }
1175             }
1176
1177           free (javac_source);
1178
1179           resultp->usable = true;
1180         }
1181       else
1182         {
1183           /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1184              option but no -source option.)  */
1185           char *javac_target =
1186             xasprintf ("%s -target %s", javac, target_version);
1187
1188           unlink (compiled_file_name);
1189
1190           java_sources[0] = conftest_file_name;
1191           if (!compile_using_envjavac (javac_target,
1192                                        java_sources, 1, tmpdir->dir_name,
1193                                        false, false, false, true)
1194               && stat (compiled_file_name, &statbuf) >= 0
1195               && get_classfile_version (compiled_file_name)
1196                  <= corresponding_classfile_version (target_version))
1197             {
1198               /* "$JAVAC -target $target_version" compiled conftest.java
1199                  successfully.  */
1200               /* Try adding -source option if it is useful.  */
1201               char *javac_target_source =
1202                 xasprintf ("%s -source %s", javac_target, source_version);
1203
1204               unlink (compiled_file_name);
1205
1206               java_sources[0] = conftest_file_name;
1207               if (!compile_using_envjavac (javac_target_source,
1208                                            java_sources, 1, tmpdir->dir_name,
1209                                            false, false, false, true)
1210                   && stat (compiled_file_name, &statbuf) >= 0
1211                   && get_classfile_version (compiled_file_name)
1212                      <= corresponding_classfile_version (target_version))
1213                 {
1214                   const char *failcode = get_failcode_snippet (source_version);
1215
1216                   if (failcode != NULL)
1217                     {
1218                       free (compiled_file_name);
1219                       free (conftest_file_name);
1220
1221                       conftest_file_name =
1222                         xconcatenated_filename (tmpdir->dir_name,
1223                                                 "conftestfail.java",
1224                                                 NULL);
1225                       if (write_temp_file (tmpdir, conftest_file_name,
1226                                            failcode))
1227                         {
1228                           free (conftest_file_name);
1229                           free (javac_target_source);
1230                           free (javac_target);
1231                           cleanup_temp_dir (tmpdir);
1232                           return true;
1233                         }
1234
1235                       compiled_file_name =
1236                         xconcatenated_filename (tmpdir->dir_name,
1237                                                 "conftestfail.class",
1238                                                 NULL);
1239                       register_temp_file (tmpdir, compiled_file_name);
1240
1241                       java_sources[0] = conftest_file_name;
1242                       if (!compile_using_envjavac (javac_target,
1243                                                    java_sources, 1,
1244                                                    tmpdir->dir_name,
1245                                                    false, false, false, true)
1246                           && stat (compiled_file_name, &statbuf) >= 0)
1247                         {
1248                           unlink (compiled_file_name);
1249
1250                           java_sources[0] = conftest_file_name;
1251                           if (compile_using_envjavac (javac_target_source,
1252                                                       java_sources, 1,
1253                                                       tmpdir->dir_name,
1254                                                       false, false, false,
1255                                                       true))
1256                             /* "$JAVAC -target $target_version" compiled
1257                                conftestfail.java successfully, and
1258                                "$JAVAC -target $target_version -source $source_version"
1259                                rejects it.  So the -source option is useful.  */
1260                             resultp->source_option = true;
1261                         }
1262                     }
1263                 }
1264
1265               free (javac_target_source);
1266
1267               resultp->target_option = true;
1268               resultp->usable = true;
1269             }
1270           else
1271             {
1272               /* Maybe this -target option requires a -source option? Try with
1273                  -target and -source options. (Supported by Sun javac 1.4 and
1274                  higher.)  */
1275               char *javac_target_source =
1276                 xasprintf ("%s -source %s", javac_target, source_version);
1277
1278               unlink (compiled_file_name);
1279
1280               java_sources[0] = conftest_file_name;
1281               if (!compile_using_envjavac (javac_target_source,
1282                                            java_sources, 1, tmpdir->dir_name,
1283                                            false, false, false, true)
1284                   && stat (compiled_file_name, &statbuf) >= 0
1285                   && get_classfile_version (compiled_file_name)
1286                      <= corresponding_classfile_version (target_version))
1287                 {
1288                   /* "$JAVAC -target $target_version -source $source_version"
1289                      compiled conftest.java successfully.  */
1290                   resultp->source_option = true;
1291                   resultp->target_option = true;
1292                   resultp->usable = true;
1293                 }
1294
1295               free (javac_target_source);
1296             }
1297
1298           free (javac_target);
1299         }
1300
1301       free (compiled_file_name);
1302       free (conftest_file_name);
1303
1304       resultp->tested = true;
1305     }
1306
1307   *usablep = resultp->usable;
1308   *source_option_p = resultp->source_option;
1309   *target_option_p = resultp->target_option;
1310   return false;
1311 }
1312
1313 static bool
1314 is_gcj_present (void)
1315 {
1316   static bool gcj_tested;
1317   static bool gcj_present;
1318
1319   if (!gcj_tested)
1320     {
1321       /* Test for presence of gcj:
1322          "gcj --version 2> /dev/null | \
1323           sed -e 's,^[^0-9]*,,' -e 1q | \
1324           sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null"  */
1325       char *argv[3];
1326       pid_t child;
1327       int fd[1];
1328       int exitstatus;
1329
1330       argv[0] = "gcj";
1331       argv[1] = "--version";
1332       argv[2] = NULL;
1333       child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1334                               false, fd);
1335       gcj_present = false;
1336       if (child != -1)
1337         {
1338           /* Read the subprocess output, drop all lines except the first,
1339              drop all characters before the first digit, and test whether
1340              the remaining string starts with a digit >= 3, but not with
1341              "3.0" or "3.1".  */
1342           char c[3];
1343           size_t count = 0;
1344
1345           while (safe_read (fd[0], &c[count], 1) > 0)
1346             {
1347               if (c[count] == '\n')
1348                 break;
1349               if (count == 0)
1350                 {
1351                   if (!(c[0] >= '0' && c[0] <= '9'))
1352                     continue;
1353                   gcj_present = (c[0] >= '3');
1354                 }
1355               count++;
1356               if (count == 3)
1357                 {
1358                   if (c[0] == '3' && c[1] == '.'
1359                       && (c[2] == '0' || c[2] == '1'))
1360                     gcj_present = false;
1361                   break;
1362                 }
1363             }
1364           while (safe_read (fd[0], &c[0], 1) > 0)
1365             ;
1366
1367           close (fd[0]);
1368
1369           /* Remove zombie process from process list, and retrieve exit
1370              status.  */
1371           exitstatus =
1372             wait_subprocess (child, "gcj", false, true, true, false, NULL);
1373           if (exitstatus != 0)
1374             gcj_present = false;
1375         }
1376
1377       if (gcj_present)
1378         {
1379           /* See if libgcj.jar is well installed.  */
1380           struct temp_dir *tmpdir;
1381
1382           tmpdir = create_temp_dir ("java", NULL, false);
1383           if (tmpdir == NULL)
1384             gcj_present = false;
1385           else
1386             {
1387               char *conftest_file_name;
1388
1389               conftest_file_name =
1390                 xconcatenated_filename (tmpdir->dir_name, "conftestlib.java",
1391                                         NULL);
1392               if (write_temp_file (tmpdir, conftest_file_name,
1393 "public class conftestlib {\n"
1394 "  public static void main (String[] args) {\n"
1395 "  }\n"
1396 "}\n"))
1397                 gcj_present = false;
1398               else
1399                 {
1400                   char *compiled_file_name;
1401                   const char *java_sources[1];
1402
1403                   compiled_file_name =
1404                     xconcatenated_filename (tmpdir->dir_name,
1405                                             "conftestlib.class",
1406                                             NULL);
1407                   register_temp_file (tmpdir, compiled_file_name);
1408
1409                   java_sources[0] = conftest_file_name;
1410                   if (compile_using_gcj (java_sources, 1, false,
1411                                          false, NULL, false, NULL,
1412                                          tmpdir->dir_name,
1413                                          false, false, false, true))
1414                     gcj_present = false;
1415
1416                   free (compiled_file_name);
1417                 }
1418               free (conftest_file_name);
1419             }
1420           cleanup_temp_dir (tmpdir);
1421         }
1422
1423       gcj_tested = true;
1424     }
1425
1426   return gcj_present;
1427 }
1428
1429 static bool
1430 is_gcj_43 (void)
1431 {
1432   static bool gcj_tested;
1433   static bool gcj_43;
1434
1435   if (!gcj_tested)
1436     {
1437       /* Test for presence of gcj:
1438          "gcj --version 2> /dev/null | \
1439           sed -e 's,^[^0-9]*,,' -e 1q | \
1440           sed -e '/^4\.[012]/d' | grep '^[4-9]'"  */
1441       char *argv[3];
1442       pid_t child;
1443       int fd[1];
1444       int exitstatus;
1445
1446       argv[0] = "gcj";
1447       argv[1] = "--version";
1448       argv[2] = NULL;
1449       child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1450                               false, fd);
1451       gcj_43 = false;
1452       if (child != -1)
1453         {
1454           /* Read the subprocess output, drop all lines except the first,
1455              drop all characters before the first digit, and test whether
1456              the remaining string starts with a digit >= 4, but not with
1457              "4.0" or "4.1" or "4.2".  */
1458           char c[3];
1459           size_t count = 0;
1460
1461           while (safe_read (fd[0], &c[count], 1) > 0)
1462             {
1463               if (c[count] == '\n')
1464                 break;
1465               if (count == 0)
1466                 {
1467                   if (!(c[0] >= '0' && c[0] <= '9'))
1468                     continue;
1469                   gcj_43 = (c[0] >= '4');
1470                 }
1471               count++;
1472               if (count == 3)
1473                 {
1474                   if (c[0] == '4' && c[1] == '.' && c[2] >= '0' && c[2] <= '2')
1475                     gcj_43 = false;
1476                   break;
1477                 }
1478             }
1479           while (safe_read (fd[0], &c[0], 1) > 0)
1480             ;
1481
1482           close (fd[0]);
1483
1484           /* Remove zombie process from process list, and retrieve exit
1485              status.  */
1486           exitstatus =
1487             wait_subprocess (child, "gcj", false, true, true, false, NULL);
1488           if (exitstatus != 0)
1489             gcj_43 = false;
1490         }
1491
1492       gcj_tested = true;
1493     }
1494
1495   return gcj_43;
1496 }
1497
1498 /* Test whether gcj >= 4.3 can be used, and whether it needs a -fsource and/or
1499    -ftarget option.
1500    Return a failure indicator (true upon error).  */
1501 static bool
1502 is_gcj43_usable (const char *source_version,
1503                  const char *target_version,
1504                  bool *usablep,
1505                  bool *fsource_option_p, bool *ftarget_option_p)
1506 {
1507   /* The cache depends on the source_version and target_version.  */
1508   struct result_t
1509   {
1510     bool tested;
1511     bool usable;
1512     bool fsource_option;
1513     bool ftarget_option;
1514   };
1515   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1516   struct result_t *resultp;
1517
1518   resultp = &result_cache[source_version_index (source_version)]
1519                          [target_version_index (target_version)];
1520   if (!resultp->tested)
1521     {
1522       /* Try gcj.  */
1523       struct temp_dir *tmpdir;
1524       char *conftest_file_name;
1525       char *compiled_file_name;
1526       const char *java_sources[1];
1527       struct stat statbuf;
1528
1529       tmpdir = create_temp_dir ("java", NULL, false);
1530       if (tmpdir == NULL)
1531         return true;
1532
1533       conftest_file_name =
1534         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1535       if (write_temp_file (tmpdir, conftest_file_name,
1536                            get_goodcode_snippet (source_version)))
1537         {
1538           free (conftest_file_name);
1539           cleanup_temp_dir (tmpdir);
1540           return true;
1541         }
1542
1543       compiled_file_name =
1544         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1545       register_temp_file (tmpdir, compiled_file_name);
1546
1547       java_sources[0] = conftest_file_name;
1548       if (!compile_using_gcj (java_sources, 1, false, false, NULL, false, NULL,
1549                               tmpdir->dir_name, false, false, false, true)
1550           && stat (compiled_file_name, &statbuf) >= 0
1551           && get_classfile_version (compiled_file_name)
1552              <= corresponding_classfile_version (target_version))
1553         {
1554           /* gcj compiled conftest.java successfully.  */
1555           /* Try adding -fsource option if it is useful.  */
1556           unlink (compiled_file_name);
1557
1558           java_sources[0] = conftest_file_name;
1559           if (!compile_using_gcj (java_sources, 1,
1560                                   false, true, source_version, false, NULL,
1561                                   tmpdir->dir_name, false, false, false, true)
1562               && stat (compiled_file_name, &statbuf) >= 0
1563               && get_classfile_version (compiled_file_name)
1564                  <= corresponding_classfile_version (target_version))
1565             {
1566               const char *failcode = get_failcode_snippet (source_version);
1567
1568               if (failcode != NULL)
1569                 {
1570                   free (compiled_file_name);
1571                   free (conftest_file_name);
1572
1573                   conftest_file_name =
1574                     xconcatenated_filename (tmpdir->dir_name,
1575                                             "conftestfail.java",
1576                                             NULL);
1577                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1578                     {
1579                       free (conftest_file_name);
1580                       cleanup_temp_dir (tmpdir);
1581                       return true;
1582                     }
1583
1584                   compiled_file_name =
1585                     xconcatenated_filename (tmpdir->dir_name,
1586                                             "conftestfail.class",
1587                                             NULL);
1588                   register_temp_file (tmpdir, compiled_file_name);
1589
1590                   java_sources[0] = conftest_file_name;
1591                   if (!compile_using_gcj (java_sources, 1,
1592                                           false, false, NULL, false, NULL,
1593                                           tmpdir->dir_name,
1594                                           false, false, false, true)
1595                       && stat (compiled_file_name, &statbuf) >= 0)
1596                     {
1597                       unlink (compiled_file_name);
1598
1599                       java_sources[0] = conftest_file_name;
1600                       if (compile_using_gcj (java_sources, 1,
1601                                              false, true, source_version,
1602                                              false, NULL,
1603                                              tmpdir->dir_name,
1604                                              false, false, false, true))
1605                         /* gcj compiled conftestfail.java successfully, and
1606                            "gcj -fsource=$source_version" rejects it.  So
1607                            the -fsource option is useful.  */
1608                         resultp->fsource_option = true;
1609                     }
1610                 }
1611             }
1612
1613           resultp->usable = true;
1614         }
1615       else
1616         {
1617           /* Try with -fsource and -ftarget options.  */
1618           unlink (compiled_file_name);
1619
1620           java_sources[0] = conftest_file_name;
1621           if (!compile_using_gcj (java_sources, 1,
1622                                   false, true, source_version,
1623                                   true, target_version,
1624                                   tmpdir->dir_name,
1625                                   false, false, false, true)
1626               && stat (compiled_file_name, &statbuf) >= 0
1627               && get_classfile_version (compiled_file_name)
1628                  <= corresponding_classfile_version (target_version))
1629             {
1630               /* "gcj -fsource $source_version -ftarget $target_version"
1631                  compiled conftest.java successfully.  */
1632               resultp->fsource_option = true;
1633               resultp->ftarget_option = true;
1634               resultp->usable = true;
1635             }
1636         }
1637
1638       free (compiled_file_name);
1639       free (conftest_file_name);
1640
1641       resultp->tested = true;
1642     }
1643
1644   *usablep = resultp->usable;
1645   *fsource_option_p = resultp->fsource_option;
1646   *ftarget_option_p = resultp->ftarget_option;
1647   return false;
1648 }
1649
1650 /* Test whether gcj < 4.3 can be used for compiling with target_version = 1.4
1651    and source_version = 1.4.
1652    Return a failure indicator (true upon error).  */
1653 static bool
1654 is_oldgcj_14_14_usable (bool *usablep)
1655 {
1656   static bool gcj_tested;
1657   static bool gcj_usable;
1658
1659   if (!gcj_tested)
1660     {
1661       /* Try gcj.  */
1662       struct temp_dir *tmpdir;
1663       char *conftest_file_name;
1664       char *compiled_file_name;
1665       const char *java_sources[1];
1666       struct stat statbuf;
1667
1668       tmpdir = create_temp_dir ("java", NULL, false);
1669       if (tmpdir == NULL)
1670         return true;
1671
1672       conftest_file_name =
1673         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1674       if (write_temp_file (tmpdir, conftest_file_name,
1675                            get_goodcode_snippet ("1.4")))
1676         {
1677           free (conftest_file_name);
1678           cleanup_temp_dir (tmpdir);
1679           return true;
1680         }
1681
1682       compiled_file_name =
1683         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1684       register_temp_file (tmpdir, compiled_file_name);
1685
1686       java_sources[0] = conftest_file_name;
1687       if (!compile_using_gcj (java_sources, 1, false, false, NULL, false, NULL,
1688                               tmpdir->dir_name, false, false, false, true)
1689           && stat (compiled_file_name, &statbuf) >= 0)
1690         /* Compilation succeeded.  */
1691         gcj_usable = true;
1692
1693       free (compiled_file_name);
1694       free (conftest_file_name);
1695
1696       cleanup_temp_dir (tmpdir);
1697
1698       gcj_tested = true;
1699     }
1700
1701   *usablep = gcj_usable;
1702   return false;
1703 }
1704
1705 /* Test whether gcj < 4.3 can be used for compiling with target_version = 1.4
1706    and source_version = 1.3.
1707    Return a failure indicator (true upon error).  */
1708 static bool
1709 is_oldgcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1710 {
1711   static bool gcj_tested;
1712   static bool gcj_usable;
1713   static bool gcj_need_no_assert_option;
1714
1715   if (!gcj_tested)
1716     {
1717       /* Try gcj and "gcj -fno-assert".  But add -fno-assert only if
1718          it works (not gcj < 3.3).  */
1719       struct temp_dir *tmpdir;
1720       char *conftest_file_name;
1721       char *compiled_file_name;
1722       const char *java_sources[1];
1723       struct stat statbuf;
1724
1725       tmpdir = create_temp_dir ("java", NULL, false);
1726       if (tmpdir == NULL)
1727         return true;
1728
1729       conftest_file_name =
1730         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1731       if (write_temp_file (tmpdir, conftest_file_name,
1732                            get_goodcode_snippet ("1.3")))
1733         {
1734           free (conftest_file_name);
1735           cleanup_temp_dir (tmpdir);
1736           return true;
1737         }
1738
1739       compiled_file_name =
1740         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1741       register_temp_file (tmpdir, compiled_file_name);
1742
1743       java_sources[0] = conftest_file_name;
1744       if (!compile_using_gcj (java_sources, 1, true, false, NULL, false, NULL,
1745                               tmpdir->dir_name, false, false, false, true)
1746           && stat (compiled_file_name, &statbuf) >= 0)
1747         /* Compilation succeeded.  */
1748         {
1749           gcj_usable = true;
1750           gcj_need_no_assert_option = true;
1751         }
1752       else
1753         {
1754           unlink (compiled_file_name);
1755
1756           java_sources[0] = conftest_file_name;
1757           if (!compile_using_gcj (java_sources, 1, false,
1758                                   false, NULL, false, NULL,
1759                                   tmpdir->dir_name, false, false, false, true)
1760               && stat (compiled_file_name, &statbuf) >= 0)
1761             /* Compilation succeeded.  */
1762             {
1763               gcj_usable = true;
1764               gcj_need_no_assert_option = false;
1765             }
1766         }
1767
1768       free (compiled_file_name);
1769       free (conftest_file_name);
1770
1771       cleanup_temp_dir (tmpdir);
1772
1773       gcj_tested = true;
1774     }
1775
1776   *usablep = gcj_usable;
1777   *need_no_assert_option_p = gcj_need_no_assert_option;
1778   return false;
1779 }
1780
1781 static bool
1782 is_javac_present (void)
1783 {
1784   static bool javac_tested;
1785   static bool javac_present;
1786
1787   if (!javac_tested)
1788     {
1789       /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2"  */
1790       char *argv[2];
1791       int exitstatus;
1792
1793       argv[0] = "javac";
1794       argv[1] = NULL;
1795       exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1796                             true, false, NULL);
1797       javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1798       javac_tested = true;
1799     }
1800
1801   return javac_present;
1802 }
1803
1804 /* Test whether javac can be used and whether it needs a -source and/or
1805    -target option.
1806    Return a failure indicator (true upon error).  */
1807 static bool
1808 is_javac_usable (const char *source_version, const char *target_version,
1809                  bool *usablep, bool *source_option_p, bool *target_option_p)
1810 {
1811   /* The cache depends on the source_version and target_version.  */
1812   struct result_t
1813   {
1814     bool tested;
1815     bool usable;
1816     bool source_option;
1817     bool target_option;
1818   };
1819   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1820   struct result_t *resultp;
1821
1822   resultp = &result_cache[source_version_index (source_version)]
1823                          [target_version_index (target_version)];
1824   if (!resultp->tested)
1825     {
1826       /* Try javac.  */
1827       struct temp_dir *tmpdir;
1828       char *conftest_file_name;
1829       char *compiled_file_name;
1830       const char *java_sources[1];
1831       struct stat statbuf;
1832
1833       tmpdir = create_temp_dir ("java", NULL, false);
1834       if (tmpdir == NULL)
1835         return true;
1836
1837       conftest_file_name =
1838         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1839       if (write_temp_file (tmpdir, conftest_file_name,
1840                            get_goodcode_snippet (source_version)))
1841         {
1842           free (conftest_file_name);
1843           cleanup_temp_dir (tmpdir);
1844           return true;
1845         }
1846
1847       compiled_file_name =
1848         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1849       register_temp_file (tmpdir, compiled_file_name);
1850
1851       java_sources[0] = conftest_file_name;
1852       if (!compile_using_javac (java_sources, 1,
1853                                 false, source_version,
1854                                 false, target_version,
1855                                 tmpdir->dir_name, false, false, false, true)
1856           && stat (compiled_file_name, &statbuf) >= 0
1857           && get_classfile_version (compiled_file_name)
1858              <= corresponding_classfile_version (target_version))
1859         {
1860           /* javac compiled conftest.java successfully.  */
1861           /* Try adding -source option if it is useful.  */
1862           unlink (compiled_file_name);
1863
1864           java_sources[0] = conftest_file_name;
1865           if (!compile_using_javac (java_sources, 1,
1866                                     true, source_version,
1867                                     false, target_version,
1868                                     tmpdir->dir_name, false, false, false, true)
1869               && stat (compiled_file_name, &statbuf) >= 0
1870               && get_classfile_version (compiled_file_name)
1871                  <= corresponding_classfile_version (target_version))
1872             {
1873               const char *failcode = get_failcode_snippet (source_version);
1874
1875               if (failcode != NULL)
1876                 {
1877                   free (compiled_file_name);
1878                   free (conftest_file_name);
1879
1880                   conftest_file_name =
1881                     xconcatenated_filename (tmpdir->dir_name,
1882                                             "conftestfail.java",
1883                                             NULL);
1884                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1885                     {
1886                       free (conftest_file_name);
1887                       cleanup_temp_dir (tmpdir);
1888                       return true;
1889                     }
1890
1891                   compiled_file_name =
1892                     xconcatenated_filename (tmpdir->dir_name,
1893                                             "conftestfail.class",
1894                                             NULL);
1895                   register_temp_file (tmpdir, compiled_file_name);
1896
1897                   java_sources[0] = conftest_file_name;
1898                   if (!compile_using_javac (java_sources, 1,
1899                                             false, source_version,
1900                                             false, target_version,
1901                                             tmpdir->dir_name,
1902                                             false, false, false, true)
1903                       && stat (compiled_file_name, &statbuf) >= 0)
1904                     {
1905                       unlink (compiled_file_name);
1906
1907                       java_sources[0] = conftest_file_name;
1908                       if (compile_using_javac (java_sources, 1,
1909                                                true, source_version,
1910                                                false, target_version,
1911                                                tmpdir->dir_name,
1912                                                false, false, false, true))
1913                         /* javac compiled conftestfail.java successfully, and
1914                            "javac -source $source_version" rejects it.  So the
1915                            -source option is useful.  */
1916                         resultp->source_option = true;
1917                     }
1918                 }
1919             }
1920
1921           resultp->usable = true;
1922         }
1923       else
1924         {
1925           /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1926              option but no -source option.)  */
1927           unlink (compiled_file_name);
1928
1929           java_sources[0] = conftest_file_name;
1930           if (!compile_using_javac (java_sources, 1,
1931                                     false, source_version,
1932                                     true, target_version,
1933                                     tmpdir->dir_name,
1934                                     false, false, false, true)
1935               && stat (compiled_file_name, &statbuf) >= 0
1936               && get_classfile_version (compiled_file_name)
1937                  <= corresponding_classfile_version (target_version))
1938             {
1939               /* "javac -target $target_version" compiled conftest.java
1940                  successfully.  */
1941               /* Try adding -source option if it is useful.  */
1942               unlink (compiled_file_name);
1943
1944               java_sources[0] = conftest_file_name;
1945               if (!compile_using_javac (java_sources, 1,
1946                                         true, source_version,
1947                                         true, target_version,
1948                                         tmpdir->dir_name,
1949                                         false, false, false, true)
1950                   && stat (compiled_file_name, &statbuf) >= 0
1951                   && get_classfile_version (compiled_file_name)
1952                      <= corresponding_classfile_version (target_version))
1953                 {
1954                   const char *failcode = get_failcode_snippet (source_version);
1955
1956                   if (failcode != NULL)
1957                     {
1958                       free (compiled_file_name);
1959                       free (conftest_file_name);
1960
1961                       conftest_file_name =
1962                         xconcatenated_filename (tmpdir->dir_name,
1963                                                 "conftestfail.java",
1964                                                 NULL);
1965                       if (write_temp_file (tmpdir, conftest_file_name,
1966                                            failcode))
1967                         {
1968                           free (conftest_file_name);
1969                           cleanup_temp_dir (tmpdir);
1970                           return true;
1971                         }
1972
1973                       compiled_file_name =
1974                         xconcatenated_filename (tmpdir->dir_name,
1975                                                 "conftestfail.class",
1976                                                 NULL);
1977                       register_temp_file (tmpdir, compiled_file_name);
1978
1979                       java_sources[0] = conftest_file_name;
1980                       if (!compile_using_javac (java_sources, 1,
1981                                                 false, source_version,
1982                                                 true, target_version,
1983                                                 tmpdir->dir_name,
1984                                                 false, false, false, true)
1985                           && stat (compiled_file_name, &statbuf) >= 0)
1986                         {
1987                           unlink (compiled_file_name);
1988
1989                           java_sources[0] = conftest_file_name;
1990                           if (compile_using_javac (java_sources, 1,
1991                                                    true, source_version,
1992                                                    true, target_version,
1993                                                    tmpdir->dir_name,
1994                                                    false, false, false, true))
1995                             /* "javac -target $target_version" compiled
1996                                conftestfail.java successfully, and
1997                                "javac -target $target_version -source $source_version"
1998                                rejects it.  So the -source option is useful.  */
1999                             resultp->source_option = true;
2000                         }
2001                     }
2002                 }
2003
2004               resultp->target_option = true;
2005               resultp->usable = true;
2006             }
2007           else
2008             {
2009               /* Maybe this -target option requires a -source option? Try with
2010                  -target and -source options. (Supported by Sun javac 1.4 and
2011                  higher.)  */
2012               unlink (compiled_file_name);
2013
2014               java_sources[0] = conftest_file_name;
2015               if (!compile_using_javac (java_sources, 1,
2016                                         true, source_version,
2017                                         true, target_version,
2018                                         tmpdir->dir_name,
2019                                         false, false, false, true)
2020                   && stat (compiled_file_name, &statbuf) >= 0
2021                   && get_classfile_version (compiled_file_name)
2022                      <= corresponding_classfile_version (target_version))
2023                 {
2024                   /* "javac -target $target_version -source $source_version"
2025                      compiled conftest.java successfully.  */
2026                   resultp->source_option = true;
2027                   resultp->target_option = true;
2028                   resultp->usable = true;
2029                 }
2030             }
2031         }
2032
2033       free (compiled_file_name);
2034       free (conftest_file_name);
2035
2036       resultp->tested = true;
2037     }
2038
2039   *usablep = resultp->usable;
2040   *source_option_p = resultp->source_option;
2041   *target_option_p = resultp->target_option;
2042   return false;
2043 }
2044
2045 static bool
2046 is_jikes_present (void)
2047 {
2048   static bool jikes_tested;
2049   static bool jikes_present;
2050
2051   if (!jikes_tested)
2052     {
2053       /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1"  */
2054       char *argv[2];
2055       int exitstatus;
2056
2057       argv[0] = "jikes";
2058       argv[1] = NULL;
2059       exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
2060                             true, false, NULL);
2061       jikes_present = (exitstatus == 0 || exitstatus == 1);
2062       jikes_tested = true;
2063     }
2064
2065   return jikes_present;
2066 }
2067
2068 /* ============================= Main function ============================= */
2069
2070 bool
2071 compile_java_class (const char * const *java_sources,
2072                     unsigned int java_sources_count,
2073                     const char * const *classpaths,
2074                     unsigned int classpaths_count,
2075                     const char *source_version,
2076                     const char *target_version,
2077                     const char *directory,
2078                     bool optimize, bool debug,
2079                     bool use_minimal_classpath,
2080                     bool verbose)
2081 {
2082   bool err = false;
2083   char *old_JAVA_HOME;
2084
2085   {
2086     const char *javac = getenv ("JAVAC");
2087     if (javac != NULL && javac[0] != '\0')
2088       {
2089         bool usable = false;
2090         bool no_assert_option = false;
2091         bool source_option = false;
2092         bool target_option = false;
2093         bool fsource_option = false;
2094         bool ftarget_option = false;
2095
2096         if (target_version == NULL)
2097           target_version = default_target_version ();
2098
2099         if (is_envjavac_gcj (javac))
2100           {
2101             /* It's a version of gcj.  */
2102             if (is_envjavac_gcj43 (javac))
2103               {
2104                 /* It's a version of gcj >= 4.3.  Assume the classfile versions
2105                    are correct.  */
2106                 if (is_envjavac_gcj43_usable (javac,
2107                                               source_version, target_version,
2108                                               &usable,
2109                                               &fsource_option, &ftarget_option))
2110                   {
2111                     err = true;
2112                     goto done1;
2113                   }
2114               }
2115             else
2116               {
2117                 /* It's a version of gcj < 4.3.  Ignore the version of the
2118                    class files that it creates.  */
2119                 if (strcmp (target_version, "1.4") == 0
2120                     && strcmp (source_version, "1.4") == 0)
2121                   {
2122                     if (is_envjavac_oldgcj_14_14_usable (javac, &usable))
2123                       {
2124                         err = true;
2125                         goto done1;
2126                       }
2127                   }
2128                 else if (strcmp (target_version, "1.4") == 0
2129                          && strcmp (source_version, "1.3") == 0)
2130                   {
2131                     if (is_envjavac_oldgcj_14_13_usable (javac,
2132                                                          &usable,
2133                                                          &no_assert_option))
2134                       {
2135                         err = true;
2136                         goto done1;
2137                       }
2138                   }
2139               }
2140           }
2141         else
2142           {
2143             /* It's not gcj.  Assume the classfile versions are correct.  */
2144             if (is_envjavac_nongcj_usable (javac,
2145                                            source_version, target_version,
2146                                            &usable,
2147                                            &source_option, &target_option))
2148               {
2149                 err = true;
2150                 goto done1;
2151               }
2152           }
2153
2154         if (usable)
2155           {
2156             char *old_classpath;
2157             char *javac_with_options;
2158
2159             /* Set CLASSPATH.  */
2160             old_classpath =
2161               set_classpath (classpaths, classpaths_count, false, verbose);
2162
2163             javac_with_options =
2164               (no_assert_option
2165                ? xasprintf ("%s -fno-assert", javac)
2166                : xasprintf ("%s%s%s%s%s%s%s%s%s",
2167                             javac,
2168                             source_option ? " -source " : "",
2169                             source_option ? source_version : "",
2170                             target_option ? " -target " : "",
2171                             target_option ? target_version : "",
2172                             fsource_option ? " -fsource=" : "",
2173                             fsource_option ? source_version : "",
2174                             ftarget_option ? " -ftarget=" : "",
2175                             ftarget_option ? target_version : ""));
2176
2177             err = compile_using_envjavac (javac_with_options,
2178                                           java_sources, java_sources_count,
2179                                           directory, optimize, debug, verbose,
2180                                           false);
2181
2182             free (javac_with_options);
2183
2184             /* Reset CLASSPATH.  */
2185             reset_classpath (old_classpath);
2186
2187             goto done1;
2188           }
2189       }
2190   }
2191
2192   /* Unset the JAVA_HOME environment variable.  */
2193   old_JAVA_HOME = getenv ("JAVA_HOME");
2194   if (old_JAVA_HOME != NULL)
2195     {
2196       old_JAVA_HOME = xstrdup (old_JAVA_HOME);
2197       unsetenv ("JAVA_HOME");
2198     }
2199
2200   if (is_gcj_present ())
2201     {
2202       /* It's a version of gcj.  */
2203       bool usable = false;
2204       bool no_assert_option = false;
2205       bool fsource_option = false;
2206       bool ftarget_option = false;
2207
2208       if (target_version == NULL)
2209         target_version = default_target_version ();
2210
2211       if (is_gcj_43 ())
2212         {
2213           /* It's a version of gcj >= 4.3.  Assume the classfile versions
2214              are correct.  */
2215           if (is_gcj43_usable (source_version, target_version,
2216                                &usable, &fsource_option, &ftarget_option))
2217             {
2218               err = true;
2219               goto done1;
2220             }
2221         }
2222       else
2223         {
2224           /* It's a version of gcj < 4.3.  Ignore the version of the class
2225              files that it creates.
2226              Test whether it supports the desired target-version and
2227              source-version.  */
2228           if (strcmp (target_version, "1.4") == 0
2229               && strcmp (source_version, "1.4") == 0)
2230             {
2231               if (is_oldgcj_14_14_usable (&usable))
2232                 {
2233                   err = true;
2234                   goto done1;
2235                 }
2236             }
2237           else if (strcmp (target_version, "1.4") == 0
2238                    && strcmp (source_version, "1.3") == 0)
2239             {
2240               if (is_oldgcj_14_13_usable (&usable, &no_assert_option))
2241                 {
2242                   err = true;
2243                   goto done1;
2244                 }
2245             }
2246         }
2247
2248       if (usable)
2249         {
2250           char *old_classpath;
2251
2252           /* Set CLASSPATH.  We could also use the --CLASSPATH=... option
2253              of gcj.  Note that --classpath=... option is different: its
2254              argument should also contain gcj's libgcj.jar, but we don't
2255              know its location.  */
2256           old_classpath =
2257             set_classpath (classpaths, classpaths_count, use_minimal_classpath,
2258                            verbose);
2259
2260           err = compile_using_gcj (java_sources, java_sources_count,
2261                                    no_assert_option,
2262                                    fsource_option, source_version,
2263                                    ftarget_option, target_version,
2264                                    directory, optimize, debug, verbose, false);
2265
2266           /* Reset CLASSPATH.  */
2267           reset_classpath (old_classpath);
2268
2269           goto done2;
2270         }
2271     }
2272
2273   if (is_javac_present ())
2274     {
2275       bool usable = false;
2276       bool source_option = false;
2277       bool target_option = false;
2278
2279       if (target_version == NULL)
2280         target_version = default_target_version ();
2281
2282       if (is_javac_usable (source_version, target_version,
2283                            &usable, &source_option, &target_option))
2284         {
2285           err = true;
2286           goto done1;
2287         }
2288
2289       if (usable)
2290         {
2291           char *old_classpath;
2292
2293           /* Set CLASSPATH.  We don't use the "-classpath ..." option because
2294              in JDK 1.1.x its argument should also contain the JDK's
2295              classes.zip, but we don't know its location.  (In JDK 1.3.0 it
2296              would work.)  */
2297           old_classpath =
2298             set_classpath (classpaths, classpaths_count, use_minimal_classpath,
2299                            verbose);
2300
2301           err = compile_using_javac (java_sources, java_sources_count,
2302                                      source_option, source_version,
2303                                      target_option, target_version,
2304                                      directory, optimize, debug, verbose,
2305                                      false);
2306
2307           /* Reset CLASSPATH.  */
2308           reset_classpath (old_classpath);
2309
2310           goto done2;
2311         }
2312     }
2313
2314   if (is_jikes_present ())
2315     {
2316       /* Test whether it supports the desired target-version and
2317          source-version.  */
2318       bool usable = (strcmp (source_version, "1.3") == 0);
2319
2320       if (usable)
2321         {
2322           char *old_classpath;
2323
2324           /* Set CLASSPATH.  We could also use the "-classpath ..." option.
2325              Since jikes doesn't come with its own standard library, it
2326              needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
2327              To increase the chance of success, we reuse the current CLASSPATH
2328              if the user has set it.  */
2329           old_classpath =
2330             set_classpath (classpaths, classpaths_count, false, verbose);
2331
2332           err = compile_using_jikes (java_sources, java_sources_count,
2333                                      directory, optimize, debug, verbose,
2334                                      false);
2335
2336           /* Reset CLASSPATH.  */
2337           reset_classpath (old_classpath);
2338
2339           goto done2;
2340         }
2341     }
2342
2343   error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
2344   err = true;
2345
2346  done2:
2347   if (old_JAVA_HOME != NULL)
2348     {
2349       xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
2350       free (old_JAVA_HOME);
2351     }
2352
2353  done1:
2354   return err;
2355 }