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