New module 'fchdir'.
[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 #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 "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       envjavac_gcj = (strstr (line, "gcj") != NULL);
577
578       fclose (fp);
579
580       /* Remove zombie process from process list, and retrieve exit status.  */
581       exitstatus = wait_subprocess (child, javac, true, true, true, false);
582       if (exitstatus != 0)
583         envjavac_gcj = false;
584
585      failed:
586       freesa (command);
587
588       envjavac_tested = true;
589     }
590
591   return envjavac_gcj;
592 }
593
594 /* Test whether $JAVAC, known to be a version of gcj, can be used for
595    compiling with target_version = 1.4 and source_version = 1.4.
596    Return a failure indicator (true upon error).  */
597 static bool
598 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
599 {
600   static bool envjavac_tested;
601   static bool envjavac_usable;
602
603   if (!envjavac_tested)
604     {
605       /* Try $JAVAC.  */
606       struct temp_dir *tmpdir;
607       char *conftest_file_name;
608       char *compiled_file_name;
609       const char *java_sources[1];
610       struct stat statbuf;
611
612       tmpdir = create_temp_dir ("java", NULL, false);
613       if (tmpdir == NULL)
614         return true;
615
616       conftest_file_name =
617         concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
618       if (write_temp_file (tmpdir, conftest_file_name,
619                            get_goodcode_snippet ("1.4")))
620         {
621           free (conftest_file_name);
622           cleanup_temp_dir (tmpdir);
623           return true;
624         }
625
626       compiled_file_name =
627         concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
628       register_temp_file (tmpdir, compiled_file_name);
629
630       java_sources[0] = conftest_file_name;
631       if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
632                                    false, false, false, true)
633           && stat (compiled_file_name, &statbuf) >= 0)
634         /* Compilation succeeded.  */
635         envjavac_usable = true;
636
637       free (compiled_file_name);
638       free (conftest_file_name);
639
640       cleanup_temp_dir (tmpdir);
641
642       envjavac_tested = true;
643     }
644
645   *usablep = envjavac_usable;
646   return false;
647 }
648
649 /* Test whether $JAVAC, known to be a version of gcj, can be used for
650    compiling with target_version = 1.4 and source_version = 1.3.
651    Return a failure indicator (true upon error).  */
652 static bool
653 is_envjavac_gcj_14_13_usable (const char *javac,
654                               bool *usablep, bool *need_no_assert_option_p)
655 {
656   static bool envjavac_tested;
657   static bool envjavac_usable;
658   static bool envjavac_need_no_assert_option;
659
660   if (!envjavac_tested)
661     {
662       /* Try $JAVAC and "$JAVAC -fno-assert".  But add -fno-assert only if
663          it makes a difference.  (It could already be part of $JAVAC.)  */
664       struct temp_dir *tmpdir;
665       char *conftest_file_name;
666       char *compiled_file_name;
667       const char *java_sources[1];
668       struct stat statbuf;
669       bool javac_works;
670       char *javac_noassert;
671       bool javac_noassert_works;
672
673       tmpdir = create_temp_dir ("java", NULL, false);
674       if (tmpdir == NULL)
675         return true;
676
677       conftest_file_name =
678         concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
679       if (write_temp_file (tmpdir, conftest_file_name,
680                            get_goodcode_snippet ("1.3")))
681         {
682           free (conftest_file_name);
683           cleanup_temp_dir (tmpdir);
684           return true;
685         }
686
687       compiled_file_name =
688         concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
689       register_temp_file (tmpdir, compiled_file_name);
690
691       java_sources[0] = conftest_file_name;
692       if (!compile_using_envjavac (javac,
693                                    java_sources, 1, tmpdir->dir_name,
694                                    false, false, false, true)
695           && stat (compiled_file_name, &statbuf) >= 0)
696         /* Compilation succeeded.  */
697         javac_works = true;
698       else
699         javac_works = false;
700
701       unlink (compiled_file_name);
702
703       javac_noassert = xasprintf ("%s -fno-assert", javac);
704
705       java_sources[0] = conftest_file_name;
706       if (!compile_using_envjavac (javac_noassert,
707                                    java_sources, 1, tmpdir->dir_name,
708                                    false, false, false, true)
709           && stat (compiled_file_name, &statbuf) >= 0)
710         /* Compilation succeeded.  */
711         javac_noassert_works = true;
712       else
713         javac_noassert_works = false;
714
715       free (compiled_file_name);
716       free (conftest_file_name);
717
718       if (javac_works && javac_noassert_works)
719         {
720           conftest_file_name =
721             concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
722                                    NULL);
723           if (write_temp_file (tmpdir, conftest_file_name,
724                                get_failcode_snippet ("1.3")))
725             {
726               free (conftest_file_name);
727               free (javac_noassert);
728               cleanup_temp_dir (tmpdir);
729               return true;
730             }
731
732           compiled_file_name =
733             concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
734                                    NULL);
735           register_temp_file (tmpdir, compiled_file_name);
736
737           java_sources[0] = conftest_file_name;
738           if (!compile_using_envjavac (javac,
739                                        java_sources, 1, tmpdir->dir_name,
740                                        false, false, false, true)
741               && stat (compiled_file_name, &statbuf) >= 0)
742             {
743               /* Compilation succeeded.  */
744               unlink (compiled_file_name);
745
746               java_sources[0] = conftest_file_name;
747               if (!(!compile_using_envjavac (javac_noassert,
748                                              java_sources, 1, tmpdir->dir_name,
749                                              false, false, false, true)
750                     && stat (compiled_file_name, &statbuf) >= 0))
751                 /* Compilation failed.  */
752                 /* "$JAVAC -fno-assert" works better than $JAVAC.  */
753                 javac_works = true;
754             }
755
756           free (compiled_file_name);
757           free (conftest_file_name);
758         }
759
760       cleanup_temp_dir (tmpdir);
761
762       if (javac_works)
763         {
764           envjavac_usable = true;
765           envjavac_need_no_assert_option = false;
766         }
767       else if (javac_noassert_works)
768         {
769           envjavac_usable = true;
770           envjavac_need_no_assert_option = true;
771         }
772
773       envjavac_tested = true;
774     }
775
776   *usablep = envjavac_usable;
777   *need_no_assert_option_p = envjavac_need_no_assert_option;
778   return false;
779 }
780
781 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
782    whether it needs a -source and/or -target option.
783    Return a failure indicator (true upon error).  */
784 static bool
785 is_envjavac_nongcj_usable (const char *javac,
786                            const char *source_version,
787                            const char *target_version,
788                            bool *usablep,
789                            bool *source_option_p, bool *target_option_p)
790 {
791   /* The cache depends on the source_version and target_version.  */
792   struct result_t
793   {
794     bool tested;
795     bool usable;
796     bool source_option;
797     bool target_option;
798   };
799   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
800   struct result_t *resultp;
801
802   resultp = &result_cache[source_version_index (source_version)]
803                          [target_version_index (target_version)];
804   if (!resultp->tested)
805     {
806       /* Try $JAVAC.  */
807       struct temp_dir *tmpdir;
808       char *conftest_file_name;
809       char *compiled_file_name;
810       const char *java_sources[1];
811       struct stat statbuf;
812
813       tmpdir = create_temp_dir ("java", NULL, false);
814       if (tmpdir == NULL)
815         return true;
816
817       conftest_file_name =
818         concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
819       if (write_temp_file (tmpdir, conftest_file_name,
820                            get_goodcode_snippet (source_version)))
821         {
822           free (conftest_file_name);
823           cleanup_temp_dir (tmpdir);
824           return true;
825         }
826
827       compiled_file_name =
828         concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
829       register_temp_file (tmpdir, compiled_file_name);
830
831       java_sources[0] = conftest_file_name;
832       if (!compile_using_envjavac (javac,
833                                    java_sources, 1, tmpdir->dir_name,
834                                    false, false, false, true)
835           && stat (compiled_file_name, &statbuf) >= 0
836           && get_classfile_version (compiled_file_name)
837              <= corresponding_classfile_version (target_version))
838         {
839           /* $JAVAC compiled conftest.java successfully.  */
840           /* Try adding -source option if it is useful.  */
841           char *javac_source =
842             xasprintf ("%s -source %s", javac, source_version);
843
844           unlink (compiled_file_name);
845
846           java_sources[0] = conftest_file_name;
847           if (!compile_using_envjavac (javac_source,
848                                        java_sources, 1, tmpdir->dir_name,
849                                        false, false, false, true)
850               && stat (compiled_file_name, &statbuf) >= 0
851               && get_classfile_version (compiled_file_name)
852                  <= corresponding_classfile_version (target_version))
853             {
854               const char *failcode = get_failcode_snippet (source_version);
855
856               if (failcode != NULL)
857                 {
858                   free (compiled_file_name);
859                   free (conftest_file_name);
860
861                   conftest_file_name =
862                     concatenated_pathname (tmpdir->dir_name,
863                                            "conftestfail.java",
864                                            NULL);
865                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
866                     {
867                       free (conftest_file_name);
868                       free (javac_source);
869                       cleanup_temp_dir (tmpdir);
870                       return true;
871                     }
872
873                   compiled_file_name =
874                     concatenated_pathname (tmpdir->dir_name,
875                                            "conftestfail.class",
876                                            NULL);
877                   register_temp_file (tmpdir, compiled_file_name);
878
879                   java_sources[0] = conftest_file_name;
880                   if (!compile_using_envjavac (javac,
881                                                java_sources, 1,
882                                                tmpdir->dir_name,
883                                                false, false, false, true)
884                       && stat (compiled_file_name, &statbuf) >= 0)
885                     {
886                       unlink (compiled_file_name);
887
888                       java_sources[0] = conftest_file_name;
889                       if (compile_using_envjavac (javac_source,
890                                                   java_sources, 1,
891                                                   tmpdir->dir_name,
892                                                   false, false, false, true))
893                         /* $JAVAC compiled conftestfail.java successfully, and
894                            "$JAVAC -source $source_version" rejects it.  So the
895                            -source option is useful.  */
896                         resultp->source_option = true;
897                     }
898                 }
899             }
900
901           free (javac_source);
902
903           resultp->usable = true;
904         }
905       else
906         {
907           /* Try with -target option alone. (Sun javac 1.3.1 has the -target
908              option but no -source option.)  */
909           char *javac_target =
910             xasprintf ("%s -target %s", javac, target_version);
911
912           unlink (compiled_file_name);
913
914           java_sources[0] = conftest_file_name;
915           if (!compile_using_envjavac (javac_target,
916                                        java_sources, 1, tmpdir->dir_name,
917                                        false, false, false, true)
918               && stat (compiled_file_name, &statbuf) >= 0
919               && get_classfile_version (compiled_file_name)
920                  <= corresponding_classfile_version (target_version))
921             {
922               /* "$JAVAC -target $target_version" compiled conftest.java
923                  successfully.  */
924               /* Try adding -source option if it is useful.  */
925               char *javac_target_source =
926                 xasprintf ("%s -source %s", javac_target, source_version);
927
928               unlink (compiled_file_name);
929
930               java_sources[0] = conftest_file_name;
931               if (!compile_using_envjavac (javac_target_source,
932                                            java_sources, 1, tmpdir->dir_name,
933                                            false, false, false, true)
934                   && stat (compiled_file_name, &statbuf) >= 0
935                   && get_classfile_version (compiled_file_name)
936                      <= corresponding_classfile_version (target_version))
937                 {
938                   const char *failcode = get_failcode_snippet (source_version);
939
940                   if (failcode != NULL)
941                     {
942                       free (compiled_file_name);
943                       free (conftest_file_name);
944
945                       conftest_file_name =
946                         concatenated_pathname (tmpdir->dir_name,
947                                                "conftestfail.java",
948                                                NULL);
949                       if (write_temp_file (tmpdir, conftest_file_name,
950                                            failcode))
951                         {
952                           free (conftest_file_name);
953                           free (javac_target_source);
954                           free (javac_target);
955                           cleanup_temp_dir (tmpdir);
956                           return true;
957                         }
958
959                       compiled_file_name =
960                         concatenated_pathname (tmpdir->dir_name,
961                                                "conftestfail.class",
962                                                NULL);
963                       register_temp_file (tmpdir, compiled_file_name);
964
965                       java_sources[0] = conftest_file_name;
966                       if (!compile_using_envjavac (javac_target,
967                                                    java_sources, 1,
968                                                    tmpdir->dir_name,
969                                                    false, false, false, true)
970                           && stat (compiled_file_name, &statbuf) >= 0)
971                         {
972                           unlink (compiled_file_name);
973
974                           java_sources[0] = conftest_file_name;
975                           if (compile_using_envjavac (javac_target_source,
976                                                       java_sources, 1,
977                                                       tmpdir->dir_name,
978                                                       false, false, false,
979                                                       true))
980                             /* "$JAVAC -target $target_version" compiled
981                                conftestfail.java successfully, and
982                                "$JAVAC -target $target_version -source $source_version"
983                                rejects it.  So the -source option is useful.  */
984                             resultp->source_option = true;
985                         }
986                     }
987                 }
988
989               free (javac_target_source);
990
991               resultp->target_option = true;
992               resultp->usable = true;
993             }
994           else
995             {
996               /* Maybe this -target option requires a -source option? Try with
997                  -target and -source options. (Supported by Sun javac 1.4 and
998                  higher.)  */
999               char *javac_target_source =
1000                 xasprintf ("%s -source %s", javac_target, source_version);
1001
1002               unlink (compiled_file_name);
1003
1004               java_sources[0] = conftest_file_name;
1005               if (!compile_using_envjavac (javac_target_source,
1006                                            java_sources, 1, tmpdir->dir_name,
1007                                            false, false, false, true)
1008                   && stat (compiled_file_name, &statbuf) >= 0
1009                   && get_classfile_version (compiled_file_name)
1010                      <= corresponding_classfile_version (target_version))
1011                 {
1012                   /* "$JAVAC -target $target_version -source $source_version"
1013                      compiled conftest.java successfully.  */
1014                   resultp->source_option = true;
1015                   resultp->target_option = true;
1016                   resultp->usable = true;
1017                 }
1018
1019               free (javac_target_source);
1020             }
1021
1022           free (javac_target);
1023         }
1024
1025       free (compiled_file_name);
1026       free (conftest_file_name);
1027
1028       resultp->tested = true;
1029     }
1030
1031   *usablep = resultp->usable;
1032   *source_option_p = resultp->source_option;
1033   *target_option_p = resultp->target_option;
1034   return false;
1035 }
1036
1037 static bool
1038 is_gcj_present (void)
1039 {
1040   static bool gcj_tested;
1041   static bool gcj_present;
1042
1043   if (!gcj_tested)
1044     {
1045       /* Test for presence of gcj:
1046          "gcj --version 2> /dev/null | \
1047           sed -e 's,^[^0-9]*,,' -e 1q | \
1048           sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null"  */
1049       char *argv[3];
1050       pid_t child;
1051       int fd[1];
1052       int exitstatus;
1053
1054       argv[0] = "gcj";
1055       argv[1] = "--version";
1056       argv[2] = NULL;
1057       child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1058                               false, fd);
1059       gcj_present = false;
1060       if (child != -1)
1061         {
1062           /* Read the subprocess output, drop all lines except the first,
1063              drop all characters before the first digit, and test whether
1064              the remaining string starts with a digit >= 3, but not with
1065              "3.0" or "3.1".  */
1066           char c[3];
1067           size_t count = 0;
1068
1069           while (safe_read (fd[0], &c[count], 1) > 0)
1070             {
1071               if (c[count] == '\n')
1072                 break;
1073               if (count == 0)
1074                 {
1075                   if (!(c[0] >= '0' && c[0] <= '9'))
1076                     continue;
1077                   gcj_present = (c[0] >= '3');
1078                 }
1079               count++;
1080               if (count == 3)
1081                 {
1082                   if (c[0] == '3' && c[1] == '.'
1083                       && (c[2] == '0' || c[2] == '1'))
1084                     gcj_present = false;
1085                   break;
1086                 }
1087             }
1088           while (safe_read (fd[0], &c[0], 1) > 0)
1089             ;
1090
1091           close (fd[0]);
1092
1093           /* Remove zombie process from process list, and retrieve exit
1094              status.  */
1095           exitstatus =
1096             wait_subprocess (child, "gcj", false, true, true, false);
1097           if (exitstatus != 0)
1098             gcj_present = false;
1099         }
1100
1101       if (gcj_present)
1102         {
1103           /* See if libgcj.jar is well installed.  */
1104           struct temp_dir *tmpdir;
1105
1106           tmpdir = create_temp_dir ("java", NULL, false);
1107           if (tmpdir == NULL)
1108             gcj_present = false;
1109           else
1110             {
1111               char *conftest_file_name;
1112
1113               conftest_file_name =
1114                 concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
1115                                        NULL);
1116               if (write_temp_file (tmpdir, conftest_file_name,
1117 "public class conftestlib {\n"
1118 "  public static void main (String[] args) {\n"
1119 "  }\n"
1120 "}\n"))
1121                 gcj_present = false;
1122               else
1123                 {
1124                   char *compiled_file_name;
1125                   const char *java_sources[1];
1126
1127                   compiled_file_name =
1128                     concatenated_pathname (tmpdir->dir_name,
1129                                            "conftestlib.class",
1130                                            NULL);
1131                   register_temp_file (tmpdir, compiled_file_name);
1132
1133                   java_sources[0] = conftest_file_name;
1134                   if (compile_using_gcj (java_sources, 1, false,
1135                                          tmpdir->dir_name,
1136                                          false, false, false, true))
1137                     gcj_present = false;
1138
1139                   free (compiled_file_name);
1140                 }
1141               free (conftest_file_name);
1142             }
1143           cleanup_temp_dir (tmpdir);
1144         }
1145
1146       gcj_tested = true;
1147     }
1148
1149   return gcj_present;
1150 }
1151
1152 /* Test gcj can be used for compiling with target_version = 1.4 and
1153    source_version = 1.4.
1154    Return a failure indicator (true upon error).  */
1155 static bool
1156 is_gcj_14_14_usable (bool *usablep)
1157 {
1158   static bool gcj_tested;
1159   static bool gcj_usable;
1160
1161   if (!gcj_tested)
1162     {
1163       /* Try gcj.  */
1164       struct temp_dir *tmpdir;
1165       char *conftest_file_name;
1166       char *compiled_file_name;
1167       const char *java_sources[1];
1168       struct stat statbuf;
1169
1170       tmpdir = create_temp_dir ("java", NULL, false);
1171       if (tmpdir == NULL)
1172         return true;
1173
1174       conftest_file_name =
1175         concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1176       if (write_temp_file (tmpdir, conftest_file_name,
1177                            get_goodcode_snippet ("1.4")))
1178         {
1179           free (conftest_file_name);
1180           cleanup_temp_dir (tmpdir);
1181           return true;
1182         }
1183
1184       compiled_file_name =
1185         concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1186       register_temp_file (tmpdir, compiled_file_name);
1187
1188       java_sources[0] = conftest_file_name;
1189       if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1190                               false, false, false, true)
1191           && stat (compiled_file_name, &statbuf) >= 0)
1192         /* Compilation succeeded.  */
1193         gcj_usable = true;
1194
1195       free (compiled_file_name);
1196       free (conftest_file_name);
1197
1198       cleanup_temp_dir (tmpdir);
1199
1200       gcj_tested = true;
1201     }
1202
1203   *usablep = gcj_usable;
1204   return false;
1205 }
1206
1207 /* Test whether gcj can be used for compiling with target_version = 1.4 and
1208    source_version = 1.3.
1209    Return a failure indicator (true upon error).  */
1210 static bool
1211 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1212 {
1213   static bool gcj_tested;
1214   static bool gcj_usable;
1215   static bool gcj_need_no_assert_option;
1216
1217   if (!gcj_tested)
1218     {
1219       /* Try gcj and "gcj -fno-assert".  But add -fno-assert only if
1220          it works (not gcj < 3.3).  */
1221       struct temp_dir *tmpdir;
1222       char *conftest_file_name;
1223       char *compiled_file_name;
1224       const char *java_sources[1];
1225       struct stat statbuf;
1226
1227       tmpdir = create_temp_dir ("java", NULL, false);
1228       if (tmpdir == NULL)
1229         return true;
1230
1231       conftest_file_name =
1232         concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1233       if (write_temp_file (tmpdir, conftest_file_name,
1234                            get_goodcode_snippet ("1.3")))
1235         {
1236           free (conftest_file_name);
1237           cleanup_temp_dir (tmpdir);
1238           return true;
1239         }
1240
1241       compiled_file_name =
1242         concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1243       register_temp_file (tmpdir, compiled_file_name);
1244
1245       java_sources[0] = conftest_file_name;
1246       if (!compile_using_gcj (java_sources, 1, true, tmpdir->dir_name,
1247                               false, false, false, true)
1248           && stat (compiled_file_name, &statbuf) >= 0)
1249         /* Compilation succeeded.  */
1250         {
1251           gcj_usable = true;
1252           gcj_need_no_assert_option = true;
1253         }
1254       else
1255         {
1256           unlink (compiled_file_name);
1257
1258           java_sources[0] = conftest_file_name;
1259           if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1260                                   false, false, false, true)
1261               && stat (compiled_file_name, &statbuf) >= 0)
1262             /* Compilation succeeded.  */
1263             {
1264               gcj_usable = true;
1265               gcj_need_no_assert_option = false;
1266             }
1267         }
1268
1269       free (compiled_file_name);
1270       free (conftest_file_name);
1271
1272       cleanup_temp_dir (tmpdir);
1273
1274       gcj_tested = true;
1275     }
1276
1277   *usablep = gcj_usable;
1278   *need_no_assert_option_p = gcj_need_no_assert_option;
1279   return false;
1280 }
1281
1282 static bool
1283 is_javac_present (void)
1284 {
1285   static bool javac_tested;
1286   static bool javac_present;
1287
1288   if (!javac_tested)
1289     {
1290       /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2"  */
1291       char *argv[2];
1292       int exitstatus;
1293
1294       argv[0] = "javac";
1295       argv[1] = NULL;
1296       exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1297                             true, false);
1298       javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1299       javac_tested = true;
1300     }
1301
1302   return javac_present;
1303 }
1304
1305 /* Test whether javac can be used and whether it needs a -source and/or
1306    -target option.
1307    Return a failure indicator (true upon error).  */
1308 static bool
1309 is_javac_usable (const char *source_version, const char *target_version,
1310                  bool *usablep, bool *source_option_p, bool *target_option_p)
1311 {
1312   /* The cache depends on the source_version and target_version.  */
1313   struct result_t
1314   {
1315     bool tested;
1316     bool usable;
1317     bool source_option;
1318     bool target_option;
1319   };
1320   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1321   struct result_t *resultp;
1322
1323   resultp = &result_cache[source_version_index (source_version)]
1324                          [target_version_index (target_version)];
1325   if (!resultp->tested)
1326     {
1327       /* Try javac.  */
1328       struct temp_dir *tmpdir;
1329       char *conftest_file_name;
1330       char *compiled_file_name;
1331       const char *java_sources[1];
1332       struct stat statbuf;
1333
1334       tmpdir = create_temp_dir ("java", NULL, false);
1335       if (tmpdir == NULL)
1336         return true;
1337
1338       conftest_file_name =
1339         concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1340       if (write_temp_file (tmpdir, conftest_file_name,
1341                            get_goodcode_snippet (source_version)))
1342         {
1343           free (conftest_file_name);
1344           cleanup_temp_dir (tmpdir);
1345           return true;
1346         }
1347
1348       compiled_file_name =
1349         concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1350       register_temp_file (tmpdir, compiled_file_name);
1351
1352       java_sources[0] = conftest_file_name;
1353       if (!compile_using_javac (java_sources, 1,
1354                                 false, source_version,
1355                                 false, target_version,
1356                                 tmpdir->dir_name, false, false, false, true)
1357           && stat (compiled_file_name, &statbuf) >= 0
1358           && get_classfile_version (compiled_file_name)
1359              <= corresponding_classfile_version (target_version))
1360         {
1361           /* javac compiled conftest.java successfully.  */
1362           /* Try adding -source option if it is useful.  */
1363           unlink (compiled_file_name);
1364
1365           java_sources[0] = conftest_file_name;
1366           if (!compile_using_javac (java_sources, 1,
1367                                     true, source_version,
1368                                     false, target_version,
1369                                     tmpdir->dir_name, false, false, false, true)
1370               && stat (compiled_file_name, &statbuf) >= 0
1371               && get_classfile_version (compiled_file_name)
1372                  <= corresponding_classfile_version (target_version))
1373             {
1374               const char *failcode = get_failcode_snippet (source_version);
1375
1376               if (failcode != NULL)
1377                 {
1378                   free (compiled_file_name);
1379                   free (conftest_file_name);
1380
1381                   conftest_file_name =
1382                     concatenated_pathname (tmpdir->dir_name,
1383                                            "conftestfail.java",
1384                                            NULL);
1385                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1386                     {
1387                       free (conftest_file_name);
1388                       cleanup_temp_dir (tmpdir);
1389                       return true;
1390                     }
1391
1392                   compiled_file_name =
1393                     concatenated_pathname (tmpdir->dir_name,
1394                                            "conftestfail.class",
1395                                            NULL);
1396                   register_temp_file (tmpdir, compiled_file_name);
1397
1398                   java_sources[0] = conftest_file_name;
1399                   if (!compile_using_javac (java_sources, 1,
1400                                             false, source_version,
1401                                             false, target_version,
1402                                             tmpdir->dir_name,
1403                                             false, false, false, true)
1404                       && stat (compiled_file_name, &statbuf) >= 0)
1405                     {
1406                       unlink (compiled_file_name);
1407
1408                       java_sources[0] = conftest_file_name;
1409                       if (compile_using_javac (java_sources, 1,
1410                                                true, source_version,
1411                                                false, target_version,
1412                                                tmpdir->dir_name,
1413                                                false, false, false, true))
1414                         /* javac compiled conftestfail.java successfully, and
1415                            "javac -source $source_version" rejects it.  So the
1416                            -source option is useful.  */
1417                         resultp->source_option = true;
1418                     }
1419                 }
1420             }
1421
1422           resultp->usable = true;
1423         }
1424       else
1425         {
1426           /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1427              option but no -source option.)  */
1428           unlink (compiled_file_name);
1429
1430           java_sources[0] = conftest_file_name;
1431           if (!compile_using_javac (java_sources, 1,
1432                                     false, source_version,
1433                                     true, target_version,
1434                                     tmpdir->dir_name,
1435                                     false, false, false, true)
1436               && stat (compiled_file_name, &statbuf) >= 0
1437               && get_classfile_version (compiled_file_name)
1438                  <= corresponding_classfile_version (target_version))
1439             {
1440               /* "javac -target $target_version" compiled conftest.java
1441                  successfully.  */
1442               /* Try adding -source option if it is useful.  */
1443               unlink (compiled_file_name);
1444
1445               java_sources[0] = conftest_file_name;
1446               if (!compile_using_javac (java_sources, 1,
1447                                         true, source_version,
1448                                         true, target_version,
1449                                         tmpdir->dir_name,
1450                                         false, false, false, true)
1451                   && stat (compiled_file_name, &statbuf) >= 0
1452                   && get_classfile_version (compiled_file_name)
1453                      <= corresponding_classfile_version (target_version))
1454                 {
1455                   const char *failcode = get_failcode_snippet (source_version);
1456
1457                   if (failcode != NULL)
1458                     {
1459                       free (compiled_file_name);
1460                       free (conftest_file_name);
1461
1462                       conftest_file_name =
1463                         concatenated_pathname (tmpdir->dir_name,
1464                                                "conftestfail.java",
1465                                                NULL);
1466                       if (write_temp_file (tmpdir, conftest_file_name,
1467                                            failcode))
1468                         {
1469                           free (conftest_file_name);
1470                           cleanup_temp_dir (tmpdir);
1471                           return true;
1472                         }
1473
1474                       compiled_file_name =
1475                         concatenated_pathname (tmpdir->dir_name,
1476                                                "conftestfail.class",
1477                                                NULL);
1478                       register_temp_file (tmpdir, compiled_file_name);
1479
1480                       java_sources[0] = conftest_file_name;
1481                       if (!compile_using_javac (java_sources, 1,
1482                                                 false, source_version,
1483                                                 true, target_version,
1484                                                 tmpdir->dir_name,
1485                                                 false, false, false, true)
1486                           && stat (compiled_file_name, &statbuf) >= 0)
1487                         {
1488                           unlink (compiled_file_name);
1489
1490                           java_sources[0] = conftest_file_name;
1491                           if (compile_using_javac (java_sources, 1,
1492                                                    true, source_version,
1493                                                    true, target_version,
1494                                                    tmpdir->dir_name,
1495                                                    false, false, false, true))
1496                             /* "javac -target $target_version" compiled
1497                                conftestfail.java successfully, and
1498                                "javac -target $target_version -source $source_version"
1499                                rejects it.  So the -source option is useful.  */
1500                             resultp->source_option = true;
1501                         }
1502                     }
1503                 }
1504
1505               resultp->target_option = true;
1506               resultp->usable = true;
1507             }
1508           else
1509             {
1510               /* Maybe this -target option requires a -source option? Try with
1511                  -target and -source options. (Supported by Sun javac 1.4 and
1512                  higher.)  */
1513               unlink (compiled_file_name);
1514
1515               java_sources[0] = conftest_file_name;
1516               if (!compile_using_javac (java_sources, 1,
1517                                         true, source_version,
1518                                         true, target_version,
1519                                         tmpdir->dir_name,
1520                                         false, false, false, true)
1521                   && stat (compiled_file_name, &statbuf) >= 0
1522                   && get_classfile_version (compiled_file_name)
1523                      <= corresponding_classfile_version (target_version))
1524                 {
1525                   /* "javac -target $target_version -source $source_version"
1526                      compiled conftest.java successfully.  */
1527                   resultp->source_option = true;
1528                   resultp->target_option = true;
1529                   resultp->usable = true;
1530                 }
1531             }
1532         }
1533
1534       free (compiled_file_name);
1535       free (conftest_file_name);
1536
1537       resultp->tested = true;
1538     }
1539
1540   *usablep = resultp->usable;
1541   *source_option_p = resultp->source_option;
1542   *target_option_p = resultp->target_option;
1543   return false;
1544 }
1545
1546 static bool
1547 is_jikes_present (void)
1548 {
1549   static bool jikes_tested;
1550   static bool jikes_present;
1551
1552   if (!jikes_tested)
1553     {
1554       /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1"  */
1555       char *argv[2];
1556       int exitstatus;
1557
1558       argv[0] = "jikes";
1559       argv[1] = NULL;
1560       exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
1561                             true, false);
1562       jikes_present = (exitstatus == 0 || exitstatus == 1);
1563       jikes_tested = true;
1564     }
1565
1566   return jikes_present;
1567 }
1568
1569 /* ============================= Main function ============================= */
1570
1571 bool
1572 compile_java_class (const char * const *java_sources,
1573                     unsigned int java_sources_count,
1574                     const char * const *classpaths,
1575                     unsigned int classpaths_count,
1576                     const char *source_version,
1577                     const char *target_version,
1578                     const char *directory,
1579                     bool optimize, bool debug,
1580                     bool use_minimal_classpath,
1581                     bool verbose)
1582 {
1583   bool err = false;
1584   char *old_JAVA_HOME;
1585
1586   {
1587     const char *javac = getenv ("JAVAC");
1588     if (javac != NULL && javac[0] != '\0')
1589       {
1590         bool usable = false;
1591         bool no_assert_option = false;
1592         bool source_option = false;
1593         bool target_option = false;
1594
1595         if (target_version == NULL)
1596           target_version = default_target_version ();
1597
1598         if (is_envjavac_gcj (javac))
1599           {
1600             /* It's a version of gcj.  Ignore the version of the class files
1601                that it creates.  */
1602             if (strcmp (target_version, "1.4") == 0
1603                 && strcmp (source_version, "1.4") == 0)
1604               {
1605                 if (is_envjavac_gcj_14_14_usable (javac, &usable))
1606                   {
1607                     err = true;
1608                     goto done1;
1609                   }
1610               }
1611             else if (strcmp (target_version, "1.4") == 0
1612                      && strcmp (source_version, "1.3") == 0)
1613               {
1614                 if (is_envjavac_gcj_14_13_usable (javac,
1615                                                   &usable, &no_assert_option))
1616                   {
1617                     err = true;
1618                     goto done1;
1619                   }
1620               }
1621           }
1622         else
1623           {
1624             /* It's not gcj.  Assume the classfile versions are correct.  */
1625             if (is_envjavac_nongcj_usable (javac,
1626                                            source_version, target_version,
1627                                            &usable,
1628                                            &source_option, &target_option))
1629               {
1630                 err = true;
1631                 goto done1;
1632               }
1633           }
1634
1635         if (usable)
1636           {
1637             char *old_classpath;
1638             char *javac_with_options;
1639
1640             /* Set CLASSPATH.  */
1641             old_classpath =
1642               set_classpath (classpaths, classpaths_count, false, verbose);
1643
1644             javac_with_options =
1645               (no_assert_option
1646                ? xasprintf ("%s -fno-assert", javac)
1647                : xasprintf ("%s%s%s%s%s",
1648                             javac,
1649                             source_option ? " -source " : "",
1650                             source_option ? source_version : "",
1651                             target_option ? " -target " : "",
1652                             target_option ? target_version : ""));
1653
1654             err = compile_using_envjavac (javac_with_options,
1655                                           java_sources, java_sources_count,
1656                                           directory, optimize, debug, verbose,
1657                                           false);
1658
1659             free (javac_with_options);
1660
1661             /* Reset CLASSPATH.  */
1662             reset_classpath (old_classpath);
1663
1664             goto done1;
1665           }
1666       }
1667   }
1668
1669   /* Unset the JAVA_HOME environment variable.  */
1670   old_JAVA_HOME = getenv ("JAVA_HOME");
1671   if (old_JAVA_HOME != NULL)
1672     {
1673       old_JAVA_HOME = xstrdup (old_JAVA_HOME);
1674       unsetenv ("JAVA_HOME");
1675     }
1676
1677   if (is_gcj_present ())
1678     {
1679       /* Test whether it supports the desired target-version and
1680          source-version.  But ignore the version of the class files that
1681          it creates.  */
1682       bool usable = false;
1683       bool no_assert_option = false;
1684
1685       if (target_version == NULL)
1686         target_version = default_target_version ();
1687
1688       if (strcmp (target_version, "1.4") == 0
1689           && strcmp (source_version, "1.4") == 0)
1690         {
1691           if (is_gcj_14_14_usable (&usable))
1692             {
1693               err = true;
1694               goto done1;
1695             }
1696         }
1697       else if (strcmp (target_version, "1.4") == 0
1698                && strcmp (source_version, "1.3") == 0)
1699         {
1700           if (is_gcj_14_13_usable (&usable, &no_assert_option))
1701             {
1702               err = true;
1703               goto done1;
1704             }
1705         }
1706
1707       if (usable)
1708         {
1709           char *old_classpath;
1710
1711           /* Set CLASSPATH.  We could also use the --CLASSPATH=... option
1712              of gcj.  Note that --classpath=... option is different: its
1713              argument should also contain gcj's libgcj.jar, but we don't
1714              know its location.  */
1715           old_classpath =
1716             set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1717                            verbose);
1718
1719           err = compile_using_gcj (java_sources, java_sources_count,
1720                                    no_assert_option,
1721                                    directory, optimize, debug, verbose, false);
1722
1723           /* Reset CLASSPATH.  */
1724           reset_classpath (old_classpath);
1725
1726           goto done2;
1727         }
1728     }
1729
1730   if (is_javac_present ())
1731     {
1732       bool usable = false;
1733       bool source_option = false;
1734       bool target_option = false;
1735
1736       if (target_version == NULL)
1737         target_version = default_target_version ();
1738
1739       if (is_javac_usable (source_version, target_version,
1740                            &usable, &source_option, &target_option))
1741         {
1742           err = true;
1743           goto done1;
1744         }
1745
1746       if (usable)
1747         {
1748           char *old_classpath;
1749
1750           /* Set CLASSPATH.  We don't use the "-classpath ..." option because
1751              in JDK 1.1.x its argument should also contain the JDK's
1752              classes.zip, but we don't know its location.  (In JDK 1.3.0 it
1753              would work.)  */
1754           old_classpath =
1755             set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1756                            verbose);
1757
1758           err = compile_using_javac (java_sources, java_sources_count,
1759                                      source_option, source_version,
1760                                      target_option, target_version,
1761                                      directory, optimize, debug, verbose,
1762                                      false);
1763
1764           /* Reset CLASSPATH.  */
1765           reset_classpath (old_classpath);
1766
1767           goto done2;
1768         }
1769     }
1770
1771   if (is_jikes_present ())
1772     {
1773       /* Test whether it supports the desired target-version and
1774          source-version.  */
1775       bool usable = (strcmp (source_version, "1.3") == 0);
1776
1777       if (usable)
1778         {
1779           char *old_classpath;
1780
1781           /* Set CLASSPATH.  We could also use the "-classpath ..." option.
1782              Since jikes doesn't come with its own standard library, it
1783              needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
1784              To increase the chance of success, we reuse the current CLASSPATH
1785              if the user has set it.  */
1786           old_classpath =
1787             set_classpath (classpaths, classpaths_count, false, verbose);
1788
1789           err = compile_using_jikes (java_sources, java_sources_count,
1790                                      directory, optimize, debug, verbose,
1791                                      false);
1792
1793           /* Reset CLASSPATH.  */
1794           reset_classpath (old_classpath);
1795
1796           goto done2;
1797         }
1798     }
1799
1800   error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
1801   err = true;
1802
1803  done2:
1804   if (old_JAVA_HOME != NULL)
1805     {
1806       xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
1807       free (old_JAVA_HOME);
1808     }
1809
1810  done1:
1811   return err;
1812 }