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