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