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