Update after allocsa -> malloca renaming.
[gnulib.git] / lib / javacomp.c
1 /* Compile a Java program.
2    Copyright (C) 2001-2003, 2006-2007 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 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 "xmalloca.h"
45 #include "getline.h"
46 #include "filename.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 /* The results of open() in this file are not used with fchdir,
57    therefore save some unnecessary work in fchdir.c.  */
58 #undef open
59 #undef close
60
61
62 /* Survey of Java compilers.
63
64    A = does it work without CLASSPATH being set
65    C = option to set CLASSPATH, other than setting it in the environment
66    O = option for optimizing
67    g = option for debugging
68    T = test for presence
69
70    Program  from        A  C               O  g  T
71
72    $JAVAC   unknown     N  n/a            -O -g  true
73    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
74    javac    JDK 1.1.8   Y  -classpath P   -O -g  javac 2>/dev/null; test $? = 1
75    javac    JDK 1.3.0   Y  -classpath P   -O -g  javac 2>/dev/null; test $? -le 2
76    jikes    Jikes 1.14  N  -classpath P   -O -g  jikes 2>/dev/null; test $? = 1
77
78    All compilers support the option "-d DIRECTORY" for the base directory
79    of the classes to be written.
80
81    The CLASSPATH is a colon separated list of pathnames. (On Windows: a
82    semicolon separated list of pathnames.)
83
84    We try the Java compilers in the following order:
85      1. getenv ("JAVAC"), because the user must be able to override our
86         preferences,
87      2. "gcj -C", because it is a completely free compiler,
88      3. "javac", because it is a standard compiler,
89      4. "jikes", comes last because it has some deviating interpretation
90         of the Java Language Specification and because it requires a
91         CLASSPATH environment variable.
92
93    We unset the JAVA_HOME environment variable, because a wrong setting of
94    this variable can confuse the JDK's javac.
95  */
96
97 /* Return the default target_version.  */
98 static const char *
99 default_target_version (void)
100 {
101   /* Use a cache.  Assumes that the PATH environment variable doesn't change
102      during the lifetime of the program.  */
103   static const char *java_version_cache;
104   if (java_version_cache == NULL)
105     {
106       /* Determine the version from the found JVM.  */
107       java_version_cache = javaexec_version ();
108       if (java_version_cache == NULL
109           || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
110                && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
111                && java_version_cache[3] == '\0'))
112         java_version_cache = "1.1";
113     }
114   return java_version_cache;
115 }
116
117 /* ======================= Source version dependent ======================= */
118
119 /* Convert a source version to an index.  */
120 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
121 static unsigned int
122 source_version_index (const char *source_version)
123 {
124   if (source_version[0] == '1' && source_version[1] == '.'
125       && (source_version[2] >= '3' && source_version[2] <= '5')
126       && source_version[3] == '\0')
127     return source_version[2] - '3';
128   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
129   return 0;
130 }
131
132 /* Return a snippet of code that should compile in the given source version.  */
133 static const char *
134 get_goodcode_snippet (const char *source_version)
135 {
136   if (strcmp (source_version, "1.3") == 0)
137     return "class conftest {}\n";
138   if (strcmp (source_version, "1.4") == 0)
139     return "class conftest { static { assert(true); } }\n";
140   if (strcmp (source_version, "1.5") == 0)
141     return "class conftest<T> { T foo() { return null; } }\n";
142   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
143   return NULL;
144 }
145
146 /* Return a snippet of code that should fail to compile in the given source
147    version, or NULL (standing for a snippet that would fail to compile with
148    any compiler).  */
149 static const char *
150 get_failcode_snippet (const char *source_version)
151 {
152   if (strcmp (source_version, "1.3") == 0)
153     return "class conftestfail { static { assert(true); } }\n";
154   if (strcmp (source_version, "1.4") == 0)
155     return "class conftestfail<T> { T foo() { return null; } }\n";
156   if (strcmp (source_version, "1.5") == 0)
157     return NULL;
158   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
159   return NULL;
160 }
161
162 /* ======================= Target version dependent ======================= */
163
164 /* Convert a target version to an index.  */
165 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
166 static unsigned int
167 target_version_index (const char *target_version)
168 {
169   if (target_version[0] == '1' && target_version[1] == '.'
170       && (target_version[2] >= '1' && target_version[2] <= '6')
171       && target_version[3] == '\0')
172     return target_version[2] - '1';
173   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
174   return 0;
175 }
176
177 /* Return the class file version number corresponding to a given target
178    version.  */
179 static int
180 corresponding_classfile_version (const char *target_version)
181 {
182   if (strcmp (target_version, "1.1") == 0)
183     return 45;
184   if (strcmp (target_version, "1.2") == 0)
185     return 46;
186   if (strcmp (target_version, "1.3") == 0)
187     return 47;
188   if (strcmp (target_version, "1.4") == 0)
189     return 48;
190   if (strcmp (target_version, "1.5") == 0)
191     return 49;
192   if (strcmp (target_version, "1.6") == 0)
193     return 50;
194   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
195   return 0;
196 }
197
198 /* ======================== Compilation subroutines ======================== */
199
200 /* Try to compile a set of Java sources with $JAVAC.
201    Return a failure indicator (true upon error).  */
202 static bool
203 compile_using_envjavac (const char *javac,
204                         const char * const *java_sources,
205                         unsigned int java_sources_count,
206                         const char *directory,
207                         bool optimize, bool debug,
208                         bool verbose, bool null_stderr)
209 {
210   /* Because $JAVAC may consist of a command and options, we use the
211      shell.  Because $JAVAC has been set by the user, we leave all
212      environment variables in place, including JAVA_HOME, and we don't
213      erase the user's CLASSPATH.  */
214   bool err;
215   unsigned int command_length;
216   char *command;
217   char *argv[4];
218   int exitstatus;
219   unsigned int i;
220   char *p;
221
222   command_length = strlen (javac);
223   if (optimize)
224     command_length += 3;
225   if (debug)
226     command_length += 3;
227   if (directory != NULL)
228     command_length += 4 + shell_quote_length (directory);
229   for (i = 0; i < java_sources_count; i++)
230     command_length += 1 + shell_quote_length (java_sources[i]);
231   command_length += 1;
232
233   command = (char *) xmalloca (command_length);
234   p = command;
235   /* Don't shell_quote $JAVAC, because it may consist of a command
236      and options.  */
237   memcpy (p, javac, strlen (javac));
238   p += strlen (javac);
239   if (optimize)
240     {
241       memcpy (p, " -O", 3);
242       p += 3;
243     }
244   if (debug)
245     {
246       memcpy (p, " -g", 3);
247       p += 3;
248     }
249   if (directory != NULL)
250     {
251       memcpy (p, " -d ", 4);
252       p += 4;
253       p = shell_quote_copy (p, directory);
254     }
255   for (i = 0; i < java_sources_count; i++)
256     {
257       *p++ = ' ';
258       p = shell_quote_copy (p, java_sources[i]);
259     }
260   *p++ = '\0';
261   /* Ensure command_length was correctly calculated.  */
262   if (p - command > command_length)
263     abort ();
264
265   if (verbose)
266     printf ("%s\n", command);
267
268   argv[0] = "/bin/sh";
269   argv[1] = "-c";
270   argv[2] = command;
271   argv[3] = NULL;
272   exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
273                         null_stderr, true, true);
274   err = (exitstatus != 0);
275
276   freea (command);
277
278   return err;
279 }
280
281 /* Try to compile a set of Java sources with gcj.
282    Return a failure indicator (true upon error).  */
283 static bool
284 compile_using_gcj (const char * const *java_sources,
285                    unsigned int java_sources_count,
286                    bool no_assert_option,
287                    bool fsource_option, const char *source_version,
288                    bool ftarget_option, const char *target_version,
289                    const char *directory,
290                    bool optimize, bool debug,
291                    bool verbose, bool null_stderr)
292 {
293   bool err;
294   unsigned int argc;
295   char **argv;
296   char **argp;
297   char *fsource_arg;
298   char *ftarget_arg;
299   int exitstatus;
300   unsigned int i;
301
302   argc =
303     2 + (no_assert_option ? 1 : 0) + (fsource_option ? 1 : 0)
304     + (ftarget_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
305     + (directory != NULL ? 2 : 0) + java_sources_count;
306   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
307
308   argp = argv;
309   *argp++ = "gcj";
310   *argp++ = "-C";
311   if (no_assert_option)
312     *argp++ = "-fno-assert";
313   if (fsource_option)
314     {
315       fsource_arg = (char *) xmalloca (9 + strlen (source_version) + 1);
316       memcpy (fsource_arg, "-fsource=", 9);
317       strcpy (fsource_arg + 9, source_version);
318       *argp++ = fsource_arg;
319     }
320   else
321     fsource_arg = NULL;
322   if (ftarget_option)
323     {
324       ftarget_arg = (char *) xmalloca (9 + strlen (target_version) + 1);
325       memcpy (ftarget_arg, "-ftarget=", 9);
326       strcpy (ftarget_arg + 9, target_version);
327       *argp++ = ftarget_arg;
328     }
329   else
330     ftarget_arg = NULL;
331   if (optimize)
332     *argp++ = "-O";
333   if (debug)
334     *argp++ = "-g";
335   if (directory != NULL)
336     {
337       *argp++ = "-d";
338       *argp++ = (char *) directory;
339     }
340   for (i = 0; i < java_sources_count; i++)
341     *argp++ = (char *) java_sources[i];
342   *argp = NULL;
343   /* Ensure argv length was correctly calculated.  */
344   if (argp - argv != argc)
345     abort ();
346
347   if (verbose)
348     {
349       char *command = shell_quote_argv (argv);
350       printf ("%s\n", command);
351       free (command);
352     }
353
354   exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
355                         true, true);
356   err = (exitstatus != 0);
357
358   if (ftarget_arg != NULL)
359     freea (ftarget_arg);
360   if (fsource_arg != NULL)
361     freea (fsource_arg);
362   freea (argv);
363
364   return err;
365 }
366
367 /* Try to compile a set of Java sources with javac.
368    Return a failure indicator (true upon error).  */
369 static bool
370 compile_using_javac (const char * const *java_sources,
371                      unsigned int java_sources_count,
372                      bool source_option, const char *source_version,
373                      bool target_option, const char *target_version,
374                      const char *directory,
375                      bool optimize, bool debug,
376                      bool verbose, bool null_stderr)
377 {
378   bool err;
379   unsigned int argc;
380   char **argv;
381   char **argp;
382   int exitstatus;
383   unsigned int i;
384
385   argc =
386     1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
387     + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
388   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
389
390   argp = argv;
391   *argp++ = "javac";
392   if (source_option)
393     {
394       *argp++ = "-source";
395       *argp++ = (char *) source_version;
396     }
397   if (target_option)
398     {
399       *argp++ = "-target";
400       *argp++ = (char *) target_version;
401     }
402   if (optimize)
403     *argp++ = "-O";
404   if (debug)
405     *argp++ = "-g";
406   if (directory != NULL)
407     {
408       *argp++ = "-d";
409       *argp++ = (char *) directory;
410     }
411   for (i = 0; i < java_sources_count; i++)
412     *argp++ = (char *) java_sources[i];
413   *argp = NULL;
414   /* Ensure argv length was correctly calculated.  */
415   if (argp - argv != argc)
416     abort ();
417
418   if (verbose)
419     {
420       char *command = shell_quote_argv (argv);
421       printf ("%s\n", command);
422       free (command);
423     }
424
425   exitstatus = execute ("javac", "javac", argv, false, false, false,
426                         null_stderr, true, true);
427   err = (exitstatus != 0);
428
429   freea (argv);
430
431   return err;
432 }
433
434 /* Try to compile a set of Java sources with jikes.
435    Return a failure indicator (true upon error).  */
436 static bool
437 compile_using_jikes (const char * const *java_sources,
438                      unsigned int java_sources_count,
439                      const char *directory,
440                      bool optimize, bool debug,
441                      bool verbose, bool null_stderr)
442 {
443   bool err;
444   unsigned int argc;
445   char **argv;
446   char **argp;
447   int exitstatus;
448   unsigned int i;
449
450   argc =
451     1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
452     + java_sources_count;
453   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
454
455   argp = argv;
456   *argp++ = "jikes";
457   if (optimize)
458     *argp++ = "-O";
459   if (debug)
460     *argp++ = "-g";
461   if (directory != NULL)
462     {
463       *argp++ = "-d";
464       *argp++ = (char *) directory;
465     }
466   for (i = 0; i < java_sources_count; i++)
467     *argp++ = (char *) java_sources[i];
468   *argp = NULL;
469   /* Ensure argv length was correctly calculated.  */
470   if (argp - argv != argc)
471     abort ();
472
473   if (verbose)
474     {
475       char *command = shell_quote_argv (argv);
476       printf ("%s\n", command);
477       free (command);
478     }
479
480   exitstatus = execute ("jikes", "jikes", argv, false, false, false,
481                         null_stderr, true, true);
482   err = (exitstatus != 0);
483
484   freea (argv);
485
486   return err;
487 }
488
489 /* ====================== Usability test subroutines ====================== */
490
491 /* Write a given contents to a temporary file.
492    FILE_NAME is the name of a file inside TMPDIR that is known not to exist
493    yet.
494    Return a failure indicator (true upon error).  */
495 static bool
496 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
497                  const char *contents)
498 {
499   FILE *fp;
500
501   register_temp_file (tmpdir, file_name);
502   fp = fopen_temp (file_name, "w");
503   if (fp == NULL)
504     {
505       error (0, errno, _("failed to create \"%s\""), file_name);
506       unregister_temp_file (tmpdir, file_name);
507       return true;
508     }
509   fputs (contents, fp);
510   if (fwriteerror_temp (fp))
511     {
512       error (0, errno, _("error while writing \"%s\" file"), file_name);
513       return true;
514     }
515   return false;
516 }
517
518 /* Return the class file version number of a class file on disk.  */
519 static int
520 get_classfile_version (const char *compiled_file_name)
521 {
522   unsigned char header[8];
523   int fd;
524
525   /* Open the class file.  */
526   fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
527   if (fd >= 0)
528     {
529       /* Read its first 8 bytes.  */
530       if (safe_read (fd, header, 8) == 8)
531         {
532           /* Verify the class file signature.  */
533           if (header[0] == 0xCA && header[1] == 0xFE
534               && header[2] == 0xBA && header[3] == 0xBE)
535             return header[7];
536         }
537       close (fd);
538     }
539
540   /* Could not get the class file version.  Return a very large one.  */
541   return INT_MAX;
542 }
543
544 /* Return true if $JAVAC is a version of gcj.  */
545 static bool
546 is_envjavac_gcj (const char *javac)
547 {
548   static bool envjavac_tested;
549   static bool envjavac_gcj;
550
551   if (!envjavac_tested)
552     {
553       /* Test whether $JAVAC is gcj:
554          "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null"  */
555       unsigned int command_length;
556       char *command;
557       char *argv[4];
558       pid_t child;
559       int fd[1];
560       FILE *fp;
561       char *line;
562       size_t linesize;
563       size_t linelen;
564       int exitstatus;
565       char *p;
566
567       /* Setup the command "$JAVAC --version".  */
568       command_length = strlen (javac) + 1 + 9 + 1;
569       command = (char *) xmalloca (command_length);
570       p = command;
571       /* Don't shell_quote $JAVAC, because it may consist of a command
572          and options.  */
573       memcpy (p, javac, strlen (javac));
574       p += strlen (javac);
575       memcpy (p, " --version", 1 + 9 + 1);
576       p += 1 + 9 + 1;
577       /* Ensure command_length was correctly calculated.  */
578       if (p - command > command_length)
579         abort ();
580
581       /* Call $JAVAC --version 2>/dev/null.  */
582       argv[0] = "/bin/sh";
583       argv[1] = "-c";
584       argv[2] = command;
585       argv[3] = NULL;
586       child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
587                               false, fd);
588       if (child == -1)
589         goto failed;
590
591       /* Retrieve its result.  */
592       fp = fdopen (fd[0], "r");
593       if (fp == NULL)
594         goto failed;
595
596       line = NULL; linesize = 0;
597       linelen = getline (&line, &linesize, fp);
598       if (linelen == (size_t)(-1))
599         {
600           fclose (fp);
601           goto failed;
602         }
603       /* It is safe to call c_strstr() instead of strstr() here; see the
604          comments in c-strstr.h.  */
605       envjavac_gcj = (c_strstr (line, "gcj") != NULL);
606
607       fclose (fp);
608
609       /* Remove zombie process from process list, and retrieve exit status.  */
610       exitstatus = wait_subprocess (child, javac, true, true, true, false);
611       if (exitstatus != 0)
612         envjavac_gcj = false;
613
614      failed:
615       freea (command);
616
617       envjavac_tested = true;
618     }
619
620   return envjavac_gcj;
621 }
622
623 /* Return true if $JAVAC, known to be a version of gcj, is a version >= 4.3
624    of gcj.  */
625 static bool
626 is_envjavac_gcj43 (const char *javac)
627 {
628   static bool envjavac_tested;
629   static bool envjavac_gcj43;
630
631   if (!envjavac_tested)
632     {
633       /* Test whether $JAVAC is gcj:
634          "$JAVAC --version 2>/dev/null | sed -e 's,^[^0-9]*,,' -e 1q \
635           | sed -e '/^4\.[012]/d' | grep '^[4-9]' >/dev/null"  */
636       unsigned int command_length;
637       char *command;
638       char *argv[4];
639       pid_t child;
640       int fd[1];
641       FILE *fp;
642       char *line;
643       size_t linesize;
644       size_t linelen;
645       int exitstatus;
646       char *p;
647
648       /* Setup the command "$JAVAC --version".  */
649       command_length = strlen (javac) + 1 + 9 + 1;
650       command = (char *) xmalloca (command_length);
651       p = command;
652       /* Don't shell_quote $JAVAC, because it may consist of a command
653          and options.  */
654       memcpy (p, javac, strlen (javac));
655       p += strlen (javac);
656       memcpy (p, " --version", 1 + 9 + 1);
657       p += 1 + 9 + 1;
658       /* Ensure command_length was correctly calculated.  */
659       if (p - command > command_length)
660         abort ();
661
662       /* Call $JAVAC --version 2>/dev/null.  */
663       argv[0] = "/bin/sh";
664       argv[1] = "-c";
665       argv[2] = command;
666       argv[3] = NULL;
667       child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
668                               false, fd);
669       if (child == -1)
670         goto failed;
671
672       /* Retrieve its result.  */
673       fp = fdopen (fd[0], "r");
674       if (fp == NULL)
675         goto failed;
676
677       line = NULL; linesize = 0;
678       linelen = getline (&line, &linesize, fp);
679       if (linelen == (size_t)(-1))
680         {
681           fclose (fp);
682           goto failed;
683         }
684       p = line;
685       while (*p != '\0' && !(*p >= '0' && *p <= '9'))
686         p++;
687       envjavac_gcj43 =
688         !(*p == '4' && p[1] == '.' && p[2] >= '0' && p[2] <= '2')
689         && (*p >= '4' && *p <= '9');
690
691       fclose (fp);
692
693       /* Remove zombie process from process list, and retrieve exit status.  */
694       exitstatus = wait_subprocess (child, javac, true, true, true, false);
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         concatenated_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         concatenated_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                     concatenated_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                     concatenated_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         concatenated_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         concatenated_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         concatenated_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         concatenated_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             concatenated_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             concatenated_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         concatenated_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         concatenated_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                     concatenated_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                     concatenated_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                         concatenated_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                         concatenated_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);
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                 concatenated_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                     concatenated_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);
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         concatenated_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         concatenated_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                     concatenated_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                     concatenated_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         concatenated_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         concatenated_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         concatenated_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         concatenated_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);
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         concatenated_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         concatenated_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                     concatenated_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                     concatenated_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                         concatenated_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                         concatenated_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);
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 }