1 /* Compile a Java program.
2    Copyright (C) 2001-2003, 2006-2020 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 3 of the License, or
8    (at your option) 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, see <https://www.gnu.org/licenses/>.  */
17 
18 #include <config.h>
19 #include <alloca.h>
20 
21 /* Specification.  */
22 #include "javacomp.h"
23 
24 #include <errno.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 
33 #include "javaversion.h"
34 #include "execute.h"
35 #include "spawn-pipe.h"
36 #include "wait-process.h"
37 #include "classpath.h"
38 #include "xsetenv.h"
39 #include "sh-quote.h"
40 #include "binary-io.h"
41 #include "safe-read.h"
42 #include "xalloc.h"
43 #include "xmalloca.h"
44 #include "concat-filename.h"
45 #include "fwriteerror.h"
46 #include "clean-temp.h"
47 #include "error.h"
48 #include "xvasprintf.h"
49 #include "c-strstr.h"
50 #include "gettext.h"
51 
52 #define _(str) gettext (str)
53 
54 
55 /* Survey of Java compilers.
56 
57    A = does it work without CLASSPATH being set
58    C = option to set CLASSPATH, other than setting it in the environment
59    O = option for optimizing
60    g = option for debugging
61    T = test for presence
62 
63    Program  from        A  C               O  g  T
64 
65    $JAVAC   unknown     N  n/a            -O -g  true
66    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
67    javac    JDK 1.1.8   Y  -classpath P   -O -g  javac 2>/dev/null; test $? = 1
68    javac    JDK 1.3.0   Y  -classpath P   -O -g  javac 2>/dev/null; test $? -le 2
69    jikes    Jikes 1.14  N  -classpath P   -O -g  jikes 2>/dev/null; test $? = 1
70 
71    All compilers support the option "-d DIRECTORY" for the base directory
72    of the classes to be written.
73 
74    The CLASSPATH is a colon separated list of pathnames. (On Windows: a
75    semicolon separated list of pathnames.)
76 
77    We try the Java compilers in the following order:
78      1. getenv ("JAVAC"), because the user must be able to override our
79         preferences,
80      2. "gcj -C", because it is a completely free compiler,
81      3. "javac", because it is a standard compiler,
82      4. "jikes", comes last because it has some deviating interpretation
83         of the Java Language Specification and because it requires a
84         CLASSPATH environment variable.
85 
86    We unset the JAVA_HOME environment variable, because a wrong setting of
87    this variable can confuse the JDK's javac.
88  */
89 
90 /* Return the default target_version.  */
91 static const char *
default_target_version(void)92 default_target_version (void)
93 {
94   /* Use a cache.  Assumes that the PATH environment variable doesn't change
95      during the lifetime of the program.  */
96   static const char *java_version_cache;
97   if (java_version_cache == NULL)
98     {
99       /* Determine the version from the found JVM.  */
100       java_version_cache = javaexec_version ();
101       if (java_version_cache == NULL)
102         java_version_cache = "1.1";
103       else if ((java_version_cache[0] == '1'
104                 && java_version_cache[1] == '.'
105                 && java_version_cache[2] >= '1' && java_version_cache[2] <= '8'
106                 && java_version_cache[3] == '\0')
107                || (java_version_cache[0] == '9'
108                    && java_version_cache[1] == '\0')
109                || (java_version_cache[0] == '1'
110                    && (java_version_cache[1] >= '0'
111                        && java_version_cache[1] <= '1')
112                    && java_version_cache[2] == '\0'))
113         /* It's one of the valid target version values.  */
114         ;
115       else if (java_version_cache[0] == '1'
116                && (java_version_cache[1] >= '2'
117                    && java_version_cache[1] <= '7')
118                && java_version_cache[2] == '\0')
119         /* Assume that these (not yet released) Java versions will behave
120            like the preceding ones.  */
121         java_version_cache = "11";
122       else
123         java_version_cache = "1.1";
124     }
125   return java_version_cache;
126 }
127 
128 /* ======================= Source version dependent ======================= */
129 
130 /* Convert a source version to an index.  */
131 #define SOURCE_VERSION_BOUND 8 /* exclusive upper bound */
132 static unsigned int
source_version_index(const char * source_version)133 source_version_index (const char *source_version)
134 {
135   if (source_version[0] == '1' && source_version[1] == '.')
136     {
137       if ((source_version[2] >= '3' && source_version[2] <= '5')
138           && source_version[3] == '\0')
139         return source_version[2] - '3';
140       if ((source_version[2] >= '7' && source_version[2] <= '8')
141           && source_version[3] == '\0')
142         return source_version[2] - '4';
143     }
144   else if (source_version[0] == '9' && source_version[1] == '\0')
145     return 5;
146   else if (source_version[0] == '1'
147            && (source_version[1] >= '0' && source_version[1] <= '1')
148            && source_version[2] == '\0')
149     return source_version[1] - '0' + 6;
150   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
151   return 0;
152 }
153 
154 /* Return a snippet of code that should compile in the given source version.  */
155 static const char *
get_goodcode_snippet(const char * source_version)156 get_goodcode_snippet (const char *source_version)
157 {
158   if (strcmp (source_version, "1.3") == 0)
159     return "class conftest {}\n";
160   if (strcmp (source_version, "1.4") == 0)
161     return "class conftest { static { assert(true); } }\n";
162   if (strcmp (source_version, "1.5") == 0)
163     return "class conftest<T> { T foo() { return null; } }\n";
164   if (strcmp (source_version, "1.7") == 0)
165     return "class conftest { void foo () { switch (\"A\") {} } }\n";
166   if (strcmp (source_version, "1.8") == 0)
167     return "class conftest { void foo () { Runnable r = () -> {}; } }\n";
168   if (strcmp (source_version, "9") == 0)
169     return "interface conftest { private void foo () {} }\n";
170   if (strcmp (source_version, "10") == 0)
171     return "class conftest { public void m() { var i = new Integer(0); } }\n";
172   if (strcmp (source_version, "11") == 0)
173     return "class conftest { Readable r = (var b) -> 0; }\n";
174   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
175   return NULL;
176 }
177 
178 /* Return a snippet of code that should fail to compile in the given source
179    version, or NULL (standing for a snippet that would fail to compile with
180    any compiler).  */
181 static const char *
get_failcode_snippet(const char * source_version)182 get_failcode_snippet (const char *source_version)
183 {
184   if (strcmp (source_version, "1.3") == 0)
185     return "class conftestfail { static { assert(true); } }\n";
186   if (strcmp (source_version, "1.4") == 0)
187     return "class conftestfail<T> { T foo() { return null; } }\n";
188   if (strcmp (source_version, "1.5") == 0)
189     return "class conftestfail { void foo () { switch (\"A\") {} } }\n";
190   if (strcmp (source_version, "1.7") == 0)
191     return "class conftestfail { void foo () { Runnable r = () -> {}; } }\n";
192   if (strcmp (source_version, "1.8") == 0)
193     return "interface conftestfail { private void foo () {} }\n";
194   if (strcmp (source_version, "9") == 0)
195     return "class conftestfail { public void m() { var i = new Integer(0); } }\n";
196   if (strcmp (source_version, "10") == 0)
197     return "class conftestfail { Readable r = (var b) -> 0; }\n";
198   if (strcmp (source_version, "11") == 0)
199     return NULL;
200   error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
201   return NULL;
202 }
203 
204 /* ======================= Target version dependent ======================= */
205 
206 /* Convert a target version to an index.  */
207 #define TARGET_VERSION_BOUND 11 /* exclusive upper bound */
208 static unsigned int
target_version_index(const char * target_version)209 target_version_index (const char *target_version)
210 {
211   if (target_version[0] == '1' && target_version[1] == '.'
212       && (target_version[2] >= '1' && target_version[2] <= '8')
213       && target_version[3] == '\0')
214     return target_version[2] - '1';
215   else if (target_version[0] == '9' && target_version[1] == '\0')
216     return 8;
217   else if (target_version[0] == '1'
218            && (target_version[1] >= '0' && target_version[1] <= '1')
219            && target_version[2] == '\0')
220     return target_version[1] - '0' + 9;
221   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
222   return 0;
223 }
224 
225 /* Return the class file version number corresponding to a given target
226    version.  */
227 static int
corresponding_classfile_version(const char * target_version)228 corresponding_classfile_version (const char *target_version)
229 {
230   if (strcmp (target_version, "1.1") == 0)
231     return 45;
232   if (strcmp (target_version, "1.2") == 0)
233     return 46;
234   if (strcmp (target_version, "1.3") == 0)
235     return 47;
236   if (strcmp (target_version, "1.4") == 0)
237     return 48;
238   if (strcmp (target_version, "1.5") == 0)
239     return 49;
240   if (strcmp (target_version, "1.6") == 0)
241     return 50;
242   if (strcmp (target_version, "1.7") == 0)
243     return 51;
244   if (strcmp (target_version, "1.8") == 0)
245     return 52;
246   if (strcmp (target_version, "9") == 0)
247     return 53;
248   if (strcmp (target_version, "10") == 0)
249     return 54;
250   if (strcmp (target_version, "11") == 0)
251     return 55;
252   error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
253   return 0;
254 }
255 
256 /* Return the source version to pass to javac.  */
257 static const char *
get_source_version_for_javac(const char * source_version,const char * target_version)258 get_source_version_for_javac (const char *source_version,
259                               const char *target_version)
260 {
261   /* The javac option '-source 1.5' has the same meaning as '-source 1.6',
262      but since Java 9 supports only the latter, prefer the latter if a
263      target_version >= 1.6 is requested.  */
264   if (strcmp (source_version, "1.5") == 0
265       && !(target_version[0] == '1' && target_version[1] == '.'
266            && (target_version[2] >= '1' && target_version[2] <= '5')
267            && target_version[3] == '\0'))
268     return "1.6";
269   return source_version;
270 }
271 
272 /* ======================== Compilation subroutines ======================== */
273 
274 /* Try to compile a set of Java sources with $JAVAC.
275    Return a failure indicator (true upon error).  */
276 static bool
compile_using_envjavac(const char * javac,const char * const * java_sources,unsigned int java_sources_count,const char * directory,bool optimize,bool debug,bool verbose,bool null_stderr)277 compile_using_envjavac (const char *javac,
278                         const char * const *java_sources,
279                         unsigned int java_sources_count,
280                         const char *directory,
281                         bool optimize, bool debug,
282                         bool verbose, bool null_stderr)
283 {
284   /* Because $JAVAC may consist of a command and options, we use the
285      shell.  Because $JAVAC has been set by the user, we leave all
286      environment variables in place, including JAVA_HOME, and we don't
287      erase the user's CLASSPATH.  */
288   bool err;
289   unsigned int command_length;
290   char *command;
291   char *argv[4];
292   int exitstatus;
293   unsigned int i;
294   char *p;
295 
296   command_length = strlen (javac);
297   if (optimize)
298     command_length += 3;
299   if (debug)
300     command_length += 3;
301   if (directory != NULL)
302     command_length += 4 + shell_quote_length (directory);
303   for (i = 0; i < java_sources_count; i++)
304     command_length += 1 + shell_quote_length (java_sources[i]);
305   command_length += 1;
306 
307   command = (char *) xmalloca (command_length);
308   p = command;
309   /* Don't shell_quote $JAVAC, because it may consist of a command
310      and options.  */
311   memcpy (p, javac, strlen (javac));
312   p += strlen (javac);
313   if (optimize)
314     {
315       memcpy (p, " -O", 3);
316       p += 3;
317     }
318   if (debug)
319     {
320       memcpy (p, " -g", 3);
321       p += 3;
322     }
323   if (directory != NULL)
324     {
325       memcpy (p, " -d ", 4);
326       p += 4;
327       p = shell_quote_copy (p, directory);
328     }
329   for (i = 0; i < java_sources_count; i++)
330     {
331       *p++ = ' ';
332       p = shell_quote_copy (p, java_sources[i]);
333     }
334   *p++ = '\0';
335   /* Ensure command_length was correctly calculated.  */
336   if (p - command > command_length)
337     abort ();
338 
339   if (verbose)
340     printf ("%s\n", command);
341 
342   argv[0] = BOURNE_SHELL;
343   argv[1] = "-c";
344   argv[2] = command;
345   argv[3] = NULL;
346   exitstatus = execute (javac, BOURNE_SHELL, argv, false, false, false,
347                         null_stderr, true, true, NULL);
348   err = (exitstatus != 0);
349 
350   freea (command);
351 
352   return err;
353 }
354 
355 /* Try to compile a set of Java sources with gcj.
356    Return a failure indicator (true upon error).  */
357 static bool
compile_using_gcj(const char * const * java_sources,unsigned int java_sources_count,bool no_assert_option,bool fsource_option,const char * source_version,bool ftarget_option,const char * target_version,const char * directory,bool optimize,bool debug,bool verbose,bool null_stderr)358 compile_using_gcj (const char * const *java_sources,
359                    unsigned int java_sources_count,
360                    bool no_assert_option,
361                    bool fsource_option, const char *source_version,
362                    bool ftarget_option, const char *target_version,
363                    const char *directory,
364                    bool optimize, bool debug,
365                    bool verbose, bool null_stderr)
366 {
367   bool err;
368   unsigned int argc;
369   char **argv;
370   char **argp;
371   char *fsource_arg;
372   char *ftarget_arg;
373   int exitstatus;
374   unsigned int i;
375 
376   argc =
377     2 + (no_assert_option ? 1 : 0) + (fsource_option ? 1 : 0)
378     + (ftarget_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
379     + (directory != NULL ? 2 : 0) + java_sources_count;
380   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
381 
382   argp = argv;
383   *argp++ = "gcj";
384   *argp++ = "-C";
385   if (no_assert_option)
386     *argp++ = "-fno-assert";
387   if (fsource_option)
388     {
389       fsource_arg = (char *) xmalloca (9 + strlen (source_version) + 1);
390       memcpy (fsource_arg, "-fsource=", 9);
391       strcpy (fsource_arg + 9, source_version);
392       *argp++ = fsource_arg;
393     }
394   else
395     fsource_arg = NULL;
396   if (ftarget_option)
397     {
398       ftarget_arg = (char *) xmalloca (9 + strlen (target_version) + 1);
399       memcpy (ftarget_arg, "-ftarget=", 9);
400       strcpy (ftarget_arg + 9, target_version);
401       *argp++ = ftarget_arg;
402     }
403   else
404     ftarget_arg = NULL;
405   if (optimize)
406     *argp++ = "-O";
407   if (debug)
408     *argp++ = "-g";
409   if (directory != NULL)
410     {
411       *argp++ = "-d";
412       *argp++ = (char *) directory;
413     }
414   for (i = 0; i < java_sources_count; i++)
415     *argp++ = (char *) java_sources[i];
416   *argp = NULL;
417   /* Ensure argv length was correctly calculated.  */
418   if (argp - argv != argc)
419     abort ();
420 
421   if (verbose)
422     {
423       char *command = shell_quote_argv (argv);
424       printf ("%s\n", command);
425       free (command);
426     }
427 
428   exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
429                         true, true, NULL);
430   err = (exitstatus != 0);
431 
432   if (ftarget_arg != NULL)
433     freea (ftarget_arg);
434   if (fsource_arg != NULL)
435     freea (fsource_arg);
436   freea (argv);
437 
438   return err;
439 }
440 
441 /* Try to compile a set of Java sources with javac.
442    Return a failure indicator (true upon error).  */
443 static bool
compile_using_javac(const char * const * java_sources,unsigned int java_sources_count,bool source_option,const char * source_version,bool target_option,const char * target_version,const char * directory,bool optimize,bool debug,bool verbose,bool null_stderr)444 compile_using_javac (const char * const *java_sources,
445                      unsigned int java_sources_count,
446                      bool source_option, const char *source_version,
447                      bool target_option, const char *target_version,
448                      const char *directory,
449                      bool optimize, bool debug,
450                      bool verbose, bool null_stderr)
451 {
452   bool err;
453   unsigned int argc;
454   char **argv;
455   char **argp;
456   int exitstatus;
457   unsigned int i;
458 
459   argc =
460     1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
461     + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
462   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
463 
464   argp = argv;
465   *argp++ = "javac";
466   if (source_option)
467     {
468       *argp++ = "-source";
469       *argp++ = (char *) source_version;
470     }
471   if (target_option)
472     {
473       *argp++ = "-target";
474       *argp++ = (char *) target_version;
475     }
476   if (optimize)
477     *argp++ = "-O";
478   if (debug)
479     *argp++ = "-g";
480   if (directory != NULL)
481     {
482       *argp++ = "-d";
483       *argp++ = (char *) directory;
484     }
485   for (i = 0; i < java_sources_count; i++)
486     *argp++ = (char *) java_sources[i];
487   *argp = NULL;
488   /* Ensure argv length was correctly calculated.  */
489   if (argp - argv != argc)
490     abort ();
491 
492   if (verbose)
493     {
494       char *command = shell_quote_argv (argv);
495       printf ("%s\n", command);
496       free (command);
497     }
498 
499   exitstatus = execute ("javac", "javac", argv, false, false, false,
500                         null_stderr, true, true, NULL);
501   err = (exitstatus != 0);
502 
503   freea (argv);
504 
505   return err;
506 }
507 
508 /* Try to compile a set of Java sources with jikes.
509    Return a failure indicator (true upon error).  */
510 static bool
compile_using_jikes(const char * const * java_sources,unsigned int java_sources_count,const char * directory,bool optimize,bool debug,bool verbose,bool null_stderr)511 compile_using_jikes (const char * const *java_sources,
512                      unsigned int java_sources_count,
513                      const char *directory,
514                      bool optimize, bool debug,
515                      bool verbose, bool null_stderr)
516 {
517   bool err;
518   unsigned int argc;
519   char **argv;
520   char **argp;
521   int exitstatus;
522   unsigned int i;
523 
524   argc =
525     1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
526     + java_sources_count;
527   argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
528 
529   argp = argv;
530   *argp++ = "jikes";
531   if (optimize)
532     *argp++ = "-O";
533   if (debug)
534     *argp++ = "-g";
535   if (directory != NULL)
536     {
537       *argp++ = "-d";
538       *argp++ = (char *) directory;
539     }
540   for (i = 0; i < java_sources_count; i++)
541     *argp++ = (char *) java_sources[i];
542   *argp = NULL;
543   /* Ensure argv length was correctly calculated.  */
544   if (argp - argv != argc)
545     abort ();
546 
547   if (verbose)
548     {
549       char *command = shell_quote_argv (argv);
550       printf ("%s\n", command);
551       free (command);
552     }
553 
554   exitstatus = execute ("jikes", "jikes", argv, false, false, false,
555                         null_stderr, true, true, NULL);
556   err = (exitstatus != 0);
557 
558   freea (argv);
559 
560   return err;
561 }
562 
563 /* ====================== Usability test subroutines ====================== */
564 
565 /* Write a given contents to a temporary file.
566    FILE_NAME is the name of a file inside TMPDIR that is known not to exist
567    yet.
568    Return a failure indicator (true upon error).  */
569 static bool
write_temp_file(struct temp_dir * tmpdir,const char * file_name,const char * contents)570 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
571                  const char *contents)
572 {
573   FILE *fp;
574 
575   register_temp_file (tmpdir, file_name);
576   fp = fopen_temp (file_name, "we", false);
577   if (fp == NULL)
578     {
579       error (0, errno, _("failed to create \"%s\""), file_name);
580       unregister_temp_file (tmpdir, file_name);
581       return true;
582     }
583   fputs (contents, fp);
584   if (fwriteerror_temp (fp))
585     {
586       error (0, errno, _("error while writing \"%s\" file"), file_name);
587       return true;
588     }
589   return false;
590 }
591 
592 /* Return the class file version number of a class file on disk.  */
593 static int
get_classfile_version(const char * compiled_file_name)594 get_classfile_version (const char *compiled_file_name)
595 {
596   unsigned char header[8];
597   int fd;
598 
599   /* Open the class file.  */
600   fd = open (compiled_file_name, O_RDONLY | O_BINARY | O_CLOEXEC, 0);
601   if (fd >= 0)
602     {
603       /* Read its first 8 bytes.  */
604       if (safe_read (fd, header, 8) == 8)
605         {
606           /* Verify the class file signature.  */
607           if (header[0] == 0xCA && header[1] == 0xFE
608               && header[2] == 0xBA && header[3] == 0xBE)
609             {
610               close (fd);
611               return header[7];
612             }
613         }
614       close (fd);
615     }
616 
617   /* Could not get the class file version.  Return a very large one.  */
618   return INT_MAX;
619 }
620 
621 /* Return true if $JAVAC is a version of gcj.  */
622 static bool
is_envjavac_gcj(const char * javac)623 is_envjavac_gcj (const char *javac)
624 {
625   static bool envjavac_tested;
626   static bool envjavac_gcj;
627 
628   if (!envjavac_tested)
629     {
630       /* Test whether $JAVAC is gcj:
631          "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null"  */
632       unsigned int command_length;
633       char *command;
634       char *argv[4];
635       pid_t child;
636       int fd[1];
637       FILE *fp;
638       char *line;
639       size_t linesize;
640       size_t linelen;
641       int exitstatus;
642       char *p;
643 
644       /* Setup the command "$JAVAC --version".  */
645       command_length = strlen (javac) + 1 + 9 + 1;
646       command = (char *) xmalloca (command_length);
647       p = command;
648       /* Don't shell_quote $JAVAC, because it may consist of a command
649          and options.  */
650       memcpy (p, javac, strlen (javac));
651       p += strlen (javac);
652       memcpy (p, " --version", 1 + 9 + 1);
653       p += 1 + 9 + 1;
654       /* Ensure command_length was correctly calculated.  */
655       if (p - command > command_length)
656         abort ();
657 
658       /* Call $JAVAC --version 2>/dev/null.  */
659       argv[0] = BOURNE_SHELL;
660       argv[1] = "-c";
661       argv[2] = command;
662       argv[3] = NULL;
663       child = create_pipe_in (javac, BOURNE_SHELL, argv, DEV_NULL, true, true,
664                               false, fd);
665       if (child == -1)
666         goto failed;
667 
668       /* Retrieve its result.  */
669       fp = fdopen (fd[0], "r");
670       if (fp == NULL)
671         goto failed;
672 
673       line = NULL; linesize = 0;
674       linelen = getline (&line, &linesize, fp);
675       if (linelen == (size_t)(-1))
676         {
677           fclose (fp);
678           goto failed;
679         }
680       /* It is safe to call c_strstr() instead of strstr() here; see the
681          comments in c-strstr.h.  */
682       envjavac_gcj = (c_strstr (line, "gcj") != NULL);
683 
684       fclose (fp);
685 
686       /* Remove zombie process from process list, and retrieve exit status.  */
687       exitstatus =
688         wait_subprocess (child, javac, true, true, true, false, NULL);
689       if (exitstatus != 0)
690         envjavac_gcj = false;
691 
692      failed:
693       freea (command);
694 
695       envjavac_tested = true;
696     }
697 
698   return envjavac_gcj;
699 }
700 
701 /* Return true if $JAVAC, known to be a version of gcj, is a version >= 4.3
702    of gcj.  */
703 static bool
is_envjavac_gcj43(const char * javac)704 is_envjavac_gcj43 (const char *javac)
705 {
706   static bool envjavac_tested;
707   static bool envjavac_gcj43;
708 
709   if (!envjavac_tested)
710     {
711       /* Test whether $JAVAC is gcj:
712          "$JAVAC --version 2>/dev/null | sed -e 's,^[^0-9]*,,' -e 1q \
713           | sed -e '/^4\.[012]/d' | grep '^[4-9]' >/dev/null"  */
714       unsigned int command_length;
715       char *command;
716       char *argv[4];
717       pid_t child;
718       int fd[1];
719       FILE *fp;
720       char *line;
721       size_t linesize;
722       size_t linelen;
723       int exitstatus;
724       char *p;
725 
726       /* Setup the command "$JAVAC --version".  */
727       command_length = strlen (javac) + 1 + 9 + 1;
728       command = (char *) xmalloca (command_length);
729       p = command;
730       /* Don't shell_quote $JAVAC, because it may consist of a command
731          and options.  */
732       memcpy (p, javac, strlen (javac));
733       p += strlen (javac);
734       memcpy (p, " --version", 1 + 9 + 1);
735       p += 1 + 9 + 1;
736       /* Ensure command_length was correctly calculated.  */
737       if (p - command > command_length)
738         abort ();
739 
740       /* Call $JAVAC --version 2>/dev/null.  */
741       argv[0] = BOURNE_SHELL;
742       argv[1] = "-c";
743       argv[2] = command;
744       argv[3] = NULL;
745       child = create_pipe_in (javac, BOURNE_SHELL, argv, DEV_NULL, true, true,
746                               false, fd);
747       if (child == -1)
748         goto failed;
749 
750       /* Retrieve its result.  */
751       fp = fdopen (fd[0], "r");
752       if (fp == NULL)
753         goto failed;
754 
755       line = NULL; linesize = 0;
756       linelen = getline (&line, &linesize, fp);
757       if (linelen == (size_t)(-1))
758         {
759           fclose (fp);
760           goto failed;
761         }
762       p = line;
763       while (*p != '\0' && !(*p >= '0' && *p <= '9'))
764         p++;
765       envjavac_gcj43 =
766         !(*p == '4' && p[1] == '.' && p[2] >= '0' && p[2] <= '2')
767         && (*p >= '4' && *p <= '9');
768 
769       fclose (fp);
770 
771       /* Remove zombie process from process list, and retrieve exit status.  */
772       exitstatus =
773         wait_subprocess (child, javac, true, true, true, false, NULL);
774       if (exitstatus != 0)
775         envjavac_gcj43 = false;
776 
777      failed:
778       freea (command);
779 
780       envjavac_tested = true;
781     }
782 
783   return envjavac_gcj43;
784 }
785 
786 /* Test whether $JAVAC, known to be a version of gcj >= 4.3, can be used, and
787    whether it needs a -fsource and/or -ftarget option.
788    Return a failure indicator (true upon error).  */
789 static bool
is_envjavac_gcj43_usable(const char * javac,const char * source_version,const char * target_version,bool * usablep,bool * fsource_option_p,bool * ftarget_option_p)790 is_envjavac_gcj43_usable (const char *javac,
791                           const char *source_version,
792                           const char *target_version,
793                           bool *usablep,
794                           bool *fsource_option_p, bool *ftarget_option_p)
795 {
796   /* The cache depends on the source_version and target_version.  */
797   struct result_t
798   {
799     bool tested;
800     bool usable;
801     bool fsource_option;
802     bool ftarget_option;
803   };
804   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
805   struct result_t *resultp;
806 
807   resultp = &result_cache[source_version_index (source_version)]
808                          [target_version_index (target_version)];
809   if (!resultp->tested)
810     {
811       /* Try $JAVAC.  */
812       struct temp_dir *tmpdir;
813       char *conftest_file_name;
814       char *compiled_file_name;
815       const char *java_sources[1];
816       struct stat statbuf;
817 
818       tmpdir = create_temp_dir ("java", NULL, false);
819       if (tmpdir == NULL)
820         return true;
821 
822       conftest_file_name =
823         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
824       if (write_temp_file (tmpdir, conftest_file_name,
825                            get_goodcode_snippet (source_version)))
826         {
827           free (conftest_file_name);
828           cleanup_temp_dir (tmpdir);
829           return true;
830         }
831 
832       compiled_file_name =
833         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
834       register_temp_file (tmpdir, compiled_file_name);
835 
836       java_sources[0] = conftest_file_name;
837       if (!compile_using_envjavac (javac,
838                                    java_sources, 1, tmpdir->dir_name,
839                                    false, false, false, true)
840           && stat (compiled_file_name, &statbuf) >= 0
841           && get_classfile_version (compiled_file_name)
842              <= corresponding_classfile_version (target_version))
843         {
844           /* $JAVAC compiled conftest.java successfully.  */
845           /* Try adding -fsource option if it is useful.  */
846           char *javac_source =
847             xasprintf ("%s -fsource=%s", javac, source_version);
848 
849           unlink (compiled_file_name);
850 
851           java_sources[0] = conftest_file_name;
852           if (!compile_using_envjavac (javac_source,
853                                        java_sources, 1, tmpdir->dir_name,
854                                        false, false, false, true)
855               && stat (compiled_file_name, &statbuf) >= 0
856               && get_classfile_version (compiled_file_name)
857                  <= corresponding_classfile_version (target_version))
858             {
859               const char *failcode = get_failcode_snippet (source_version);
860 
861               if (failcode != NULL)
862                 {
863                   free (compiled_file_name);
864                   free (conftest_file_name);
865 
866                   conftest_file_name =
867                     xconcatenated_filename (tmpdir->dir_name,
868                                             "conftestfail.java",
869                                             NULL);
870                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
871                     {
872                       free (conftest_file_name);
873                       free (javac_source);
874                       cleanup_temp_dir (tmpdir);
875                       return true;
876                     }
877 
878                   compiled_file_name =
879                     xconcatenated_filename (tmpdir->dir_name,
880                                             "conftestfail.class",
881                                             NULL);
882                   register_temp_file (tmpdir, compiled_file_name);
883 
884                   java_sources[0] = conftest_file_name;
885                   if (!compile_using_envjavac (javac,
886                                                java_sources, 1,
887                                                tmpdir->dir_name,
888                                                false, false, false, true)
889                       && stat (compiled_file_name, &statbuf) >= 0)
890                     {
891                       unlink (compiled_file_name);
892 
893                       java_sources[0] = conftest_file_name;
894                       if (compile_using_envjavac (javac_source,
895                                                   java_sources, 1,
896                                                   tmpdir->dir_name,
897                                                   false, false, false, true))
898                         /* $JAVAC compiled conftestfail.java successfully, and
899                            "$JAVAC -fsource=$source_version" rejects it.  So
900                            the -fsource option is useful.  */
901                         resultp->fsource_option = true;
902                     }
903                 }
904             }
905 
906           free (javac_source);
907 
908           resultp->usable = true;
909         }
910       else
911         {
912           /* Try with -fsource and -ftarget options.  */
913           char *javac_target =
914             xasprintf ("%s -fsource=%s -ftarget=%s",
915                        javac, source_version, target_version);
916 
917           unlink (compiled_file_name);
918 
919           java_sources[0] = conftest_file_name;
920           if (!compile_using_envjavac (javac_target,
921                                        java_sources, 1, tmpdir->dir_name,
922                                        false, false, false, true)
923               && stat (compiled_file_name, &statbuf) >= 0
924               && get_classfile_version (compiled_file_name)
925                  <= corresponding_classfile_version (target_version))
926             {
927               /* "$JAVAC -fsource $source_version -ftarget $target_version"
928                  compiled conftest.java successfully.  */
929               resultp->fsource_option = true;
930               resultp->ftarget_option = true;
931               resultp->usable = true;
932             }
933 
934           free (javac_target);
935         }
936 
937       free (compiled_file_name);
938       free (conftest_file_name);
939 
940       resultp->tested = true;
941     }
942 
943   *usablep = resultp->usable;
944   *fsource_option_p = resultp->fsource_option;
945   *ftarget_option_p = resultp->ftarget_option;
946   return false;
947 }
948 
949 /* Test whether $JAVAC, known to be a version of gcj < 4.3, can be used for
950    compiling with target_version = 1.4 and source_version = 1.4.
951    Return a failure indicator (true upon error).  */
952 static bool
is_envjavac_oldgcj_14_14_usable(const char * javac,bool * usablep)953 is_envjavac_oldgcj_14_14_usable (const char *javac, bool *usablep)
954 {
955   static bool envjavac_tested;
956   static bool envjavac_usable;
957 
958   if (!envjavac_tested)
959     {
960       /* Try $JAVAC.  */
961       struct temp_dir *tmpdir;
962       char *conftest_file_name;
963       char *compiled_file_name;
964       const char *java_sources[1];
965       struct stat statbuf;
966 
967       tmpdir = create_temp_dir ("java", NULL, false);
968       if (tmpdir == NULL)
969         return true;
970 
971       conftest_file_name =
972         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
973       if (write_temp_file (tmpdir, conftest_file_name,
974                            get_goodcode_snippet ("1.4")))
975         {
976           free (conftest_file_name);
977           cleanup_temp_dir (tmpdir);
978           return true;
979         }
980 
981       compiled_file_name =
982         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
983       register_temp_file (tmpdir, compiled_file_name);
984 
985       java_sources[0] = conftest_file_name;
986       if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
987                                    false, false, false, true)
988           && stat (compiled_file_name, &statbuf) >= 0)
989         /* Compilation succeeded.  */
990         envjavac_usable = true;
991 
992       free (compiled_file_name);
993       free (conftest_file_name);
994 
995       cleanup_temp_dir (tmpdir);
996 
997       envjavac_tested = true;
998     }
999 
1000   *usablep = envjavac_usable;
1001   return false;
1002 }
1003 
1004 /* Test whether $JAVAC, known to be a version of gcj < 4.3, can be used for
1005    compiling with target_version = 1.4 and source_version = 1.3.
1006    Return a failure indicator (true upon error).  */
1007 static bool
is_envjavac_oldgcj_14_13_usable(const char * javac,bool * usablep,bool * need_no_assert_option_p)1008 is_envjavac_oldgcj_14_13_usable (const char *javac,
1009                                  bool *usablep, bool *need_no_assert_option_p)
1010 {
1011   static bool envjavac_tested;
1012   static bool envjavac_usable;
1013   static bool envjavac_need_no_assert_option;
1014 
1015   if (!envjavac_tested)
1016     {
1017       /* Try $JAVAC and "$JAVAC -fno-assert".  But add -fno-assert only if
1018          it makes a difference.  (It could already be part of $JAVAC.)  */
1019       struct temp_dir *tmpdir;
1020       char *conftest_file_name;
1021       char *compiled_file_name;
1022       const char *java_sources[1];
1023       struct stat statbuf;
1024       bool javac_works;
1025       char *javac_noassert;
1026       bool javac_noassert_works;
1027 
1028       tmpdir = create_temp_dir ("java", NULL, false);
1029       if (tmpdir == NULL)
1030         return true;
1031 
1032       conftest_file_name =
1033         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1034       if (write_temp_file (tmpdir, conftest_file_name,
1035                            get_goodcode_snippet ("1.3")))
1036         {
1037           free (conftest_file_name);
1038           cleanup_temp_dir (tmpdir);
1039           return true;
1040         }
1041 
1042       compiled_file_name =
1043         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1044       register_temp_file (tmpdir, compiled_file_name);
1045 
1046       java_sources[0] = conftest_file_name;
1047       if (!compile_using_envjavac (javac,
1048                                    java_sources, 1, tmpdir->dir_name,
1049                                    false, false, false, true)
1050           && stat (compiled_file_name, &statbuf) >= 0)
1051         /* Compilation succeeded.  */
1052         javac_works = true;
1053       else
1054         javac_works = false;
1055 
1056       unlink (compiled_file_name);
1057 
1058       javac_noassert = xasprintf ("%s -fno-assert", javac);
1059 
1060       java_sources[0] = conftest_file_name;
1061       if (!compile_using_envjavac (javac_noassert,
1062                                    java_sources, 1, tmpdir->dir_name,
1063                                    false, false, false, true)
1064           && stat (compiled_file_name, &statbuf) >= 0)
1065         /* Compilation succeeded.  */
1066         javac_noassert_works = true;
1067       else
1068         javac_noassert_works = false;
1069 
1070       free (compiled_file_name);
1071       free (conftest_file_name);
1072 
1073       if (javac_works && javac_noassert_works)
1074         {
1075           conftest_file_name =
1076             xconcatenated_filename (tmpdir->dir_name, "conftestfail.java",
1077                                     NULL);
1078           if (write_temp_file (tmpdir, conftest_file_name,
1079                                get_failcode_snippet ("1.3")))
1080             {
1081               free (conftest_file_name);
1082               free (javac_noassert);
1083               cleanup_temp_dir (tmpdir);
1084               return true;
1085             }
1086 
1087           compiled_file_name =
1088             xconcatenated_filename (tmpdir->dir_name, "conftestfail.class",
1089                                     NULL);
1090           register_temp_file (tmpdir, compiled_file_name);
1091 
1092           java_sources[0] = conftest_file_name;
1093           if (!compile_using_envjavac (javac,
1094                                        java_sources, 1, tmpdir->dir_name,
1095                                        false, false, false, true)
1096               && stat (compiled_file_name, &statbuf) >= 0)
1097             {
1098               /* Compilation succeeded.  */
1099               unlink (compiled_file_name);
1100 
1101               java_sources[0] = conftest_file_name;
1102               if (!(!compile_using_envjavac (javac_noassert,
1103                                              java_sources, 1, tmpdir->dir_name,
1104                                              false, false, false, true)
1105                     && stat (compiled_file_name, &statbuf) >= 0))
1106                 /* Compilation failed.  */
1107                 /* "$JAVAC -fno-assert" works better than $JAVAC.  */
1108                 javac_works = true;
1109             }
1110 
1111           free (compiled_file_name);
1112           free (conftest_file_name);
1113         }
1114 
1115       cleanup_temp_dir (tmpdir);
1116 
1117       if (javac_works)
1118         {
1119           envjavac_usable = true;
1120           envjavac_need_no_assert_option = false;
1121         }
1122       else if (javac_noassert_works)
1123         {
1124           envjavac_usable = true;
1125           envjavac_need_no_assert_option = true;
1126         }
1127 
1128       envjavac_tested = true;
1129     }
1130 
1131   *usablep = envjavac_usable;
1132   *need_no_assert_option_p = envjavac_need_no_assert_option;
1133   return false;
1134 }
1135 
1136 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
1137    whether it needs a -source and/or -target option.
1138    Return a failure indicator (true upon error).  */
1139 static bool
is_envjavac_nongcj_usable(const char * javac,const char * source_version,const char * source_version_for_javac,const char * target_version,bool * usablep,bool * source_option_p,bool * target_option_p)1140 is_envjavac_nongcj_usable (const char *javac,
1141                            const char *source_version,
1142                            const char *source_version_for_javac,
1143                            const char *target_version,
1144                            bool *usablep,
1145                            bool *source_option_p, bool *target_option_p)
1146 {
1147   /* The cache depends on the source_version and target_version.  */
1148   struct result_t
1149   {
1150     bool tested;
1151     bool usable;
1152     bool source_option;
1153     bool target_option;
1154   };
1155   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1156   struct result_t *resultp;
1157 
1158   resultp = &result_cache[source_version_index (source_version)]
1159                          [target_version_index (target_version)];
1160   if (!resultp->tested)
1161     {
1162       /* Try $JAVAC.  */
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         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1175       if (write_temp_file (tmpdir, conftest_file_name,
1176                            get_goodcode_snippet (source_version)))
1177         {
1178           free (conftest_file_name);
1179           cleanup_temp_dir (tmpdir);
1180           return true;
1181         }
1182 
1183       compiled_file_name =
1184         xconcatenated_filename (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_envjavac (javac,
1189                                    java_sources, 1, tmpdir->dir_name,
1190                                    false, false, false, true)
1191           && stat (compiled_file_name, &statbuf) >= 0
1192           && get_classfile_version (compiled_file_name)
1193              <= corresponding_classfile_version (target_version))
1194         {
1195           /* $JAVAC compiled conftest.java successfully.  */
1196           /* Try adding -source option if it is useful.  */
1197           char *javac_source =
1198             xasprintf ("%s -source %s", javac, source_version_for_javac);
1199 
1200           unlink (compiled_file_name);
1201 
1202           java_sources[0] = conftest_file_name;
1203           if (!compile_using_envjavac (javac_source,
1204                                        java_sources, 1, tmpdir->dir_name,
1205                                        false, false, false, true)
1206               && stat (compiled_file_name, &statbuf) >= 0
1207               && get_classfile_version (compiled_file_name)
1208                  <= corresponding_classfile_version (target_version))
1209             {
1210               const char *failcode = get_failcode_snippet (source_version);
1211 
1212               if (failcode != NULL)
1213                 {
1214                   free (compiled_file_name);
1215                   free (conftest_file_name);
1216 
1217                   conftest_file_name =
1218                     xconcatenated_filename (tmpdir->dir_name,
1219                                             "conftestfail.java",
1220                                             NULL);
1221                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1222                     {
1223                       free (conftest_file_name);
1224                       free (javac_source);
1225                       cleanup_temp_dir (tmpdir);
1226                       return true;
1227                     }
1228 
1229                   compiled_file_name =
1230                     xconcatenated_filename (tmpdir->dir_name,
1231                                             "conftestfail.class",
1232                                             NULL);
1233                   register_temp_file (tmpdir, compiled_file_name);
1234 
1235                   java_sources[0] = conftest_file_name;
1236                   if (!compile_using_envjavac (javac,
1237                                                java_sources, 1,
1238                                                tmpdir->dir_name,
1239                                                false, false, false, true)
1240                       && stat (compiled_file_name, &statbuf) >= 0)
1241                     {
1242                       unlink (compiled_file_name);
1243 
1244                       java_sources[0] = conftest_file_name;
1245                       if (compile_using_envjavac (javac_source,
1246                                                   java_sources, 1,
1247                                                   tmpdir->dir_name,
1248                                                   false, false, false, true))
1249                         /* $JAVAC compiled conftestfail.java successfully, and
1250                            "$JAVAC -source $source_version_for_javac" rejects it.
1251                            So the -source option is useful.  */
1252                         resultp->source_option = true;
1253                     }
1254                 }
1255             }
1256 
1257           free (javac_source);
1258 
1259           resultp->usable = true;
1260         }
1261       else
1262         {
1263           /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1264              option but no -source option.)  */
1265           char *javac_target =
1266             xasprintf ("%s -target %s", javac, target_version);
1267 
1268           unlink (compiled_file_name);
1269 
1270           java_sources[0] = conftest_file_name;
1271           if (!compile_using_envjavac (javac_target,
1272                                        java_sources, 1, tmpdir->dir_name,
1273                                        false, false, false, true)
1274               && stat (compiled_file_name, &statbuf) >= 0
1275               && get_classfile_version (compiled_file_name)
1276                  <= corresponding_classfile_version (target_version))
1277             {
1278               /* "$JAVAC -target $target_version" compiled conftest.java
1279                  successfully.  */
1280               /* Try adding -source option if it is useful.  */
1281               char *javac_target_source =
1282                 xasprintf ("%s -source %s", javac_target, source_version_for_javac);
1283 
1284               unlink (compiled_file_name);
1285 
1286               java_sources[0] = conftest_file_name;
1287               if (!compile_using_envjavac (javac_target_source,
1288                                            java_sources, 1, tmpdir->dir_name,
1289                                            false, false, false, true)
1290                   && stat (compiled_file_name, &statbuf) >= 0
1291                   && get_classfile_version (compiled_file_name)
1292                      <= corresponding_classfile_version (target_version))
1293                 {
1294                   const char *failcode = get_failcode_snippet (source_version);
1295 
1296                   if (failcode != NULL)
1297                     {
1298                       free (compiled_file_name);
1299                       free (conftest_file_name);
1300 
1301                       conftest_file_name =
1302                         xconcatenated_filename (tmpdir->dir_name,
1303                                                 "conftestfail.java",
1304                                                 NULL);
1305                       if (write_temp_file (tmpdir, conftest_file_name,
1306                                            failcode))
1307                         {
1308                           free (conftest_file_name);
1309                           free (javac_target_source);
1310                           free (javac_target);
1311                           cleanup_temp_dir (tmpdir);
1312                           return true;
1313                         }
1314 
1315                       compiled_file_name =
1316                         xconcatenated_filename (tmpdir->dir_name,
1317                                                 "conftestfail.class",
1318                                                 NULL);
1319                       register_temp_file (tmpdir, compiled_file_name);
1320 
1321                       java_sources[0] = conftest_file_name;
1322                       if (!compile_using_envjavac (javac_target,
1323                                                    java_sources, 1,
1324                                                    tmpdir->dir_name,
1325                                                    false, false, false, true)
1326                           && stat (compiled_file_name, &statbuf) >= 0)
1327                         {
1328                           unlink (compiled_file_name);
1329 
1330                           java_sources[0] = conftest_file_name;
1331                           if (compile_using_envjavac (javac_target_source,
1332                                                       java_sources, 1,
1333                                                       tmpdir->dir_name,
1334                                                       false, false, false,
1335                                                       true))
1336                             /* "$JAVAC -target $target_version" compiled
1337                                conftestfail.java successfully, and
1338                                "$JAVAC -target $target_version -source $source_version_for_javac"
1339                                rejects it.  So the -source option is useful.  */
1340                             resultp->source_option = true;
1341                         }
1342                     }
1343                 }
1344 
1345               free (javac_target_source);
1346 
1347               resultp->target_option = true;
1348               resultp->usable = true;
1349             }
1350           else
1351             {
1352               /* Maybe this -target option requires a -source option? Try with
1353                  -target and -source options. (Supported by Sun javac 1.4 and
1354                  higher.)  */
1355               char *javac_target_source =
1356                 xasprintf ("%s -source %s", javac_target, source_version_for_javac);
1357 
1358               unlink (compiled_file_name);
1359 
1360               java_sources[0] = conftest_file_name;
1361               if (!compile_using_envjavac (javac_target_source,
1362                                            java_sources, 1, tmpdir->dir_name,
1363                                            false, false, false, true)
1364                   && stat (compiled_file_name, &statbuf) >= 0
1365                   && get_classfile_version (compiled_file_name)
1366                      <= corresponding_classfile_version (target_version))
1367                 {
1368                   /* "$JAVAC -target $target_version -source $source_version_for_javac"
1369                      compiled conftest.java successfully.  */
1370                   resultp->source_option = true;
1371                   resultp->target_option = true;
1372                   resultp->usable = true;
1373                 }
1374 
1375               free (javac_target_source);
1376             }
1377 
1378           free (javac_target);
1379         }
1380 
1381       free (compiled_file_name);
1382       free (conftest_file_name);
1383 
1384       resultp->tested = true;
1385     }
1386 
1387   *usablep = resultp->usable;
1388   *source_option_p = resultp->source_option;
1389   *target_option_p = resultp->target_option;
1390   return false;
1391 }
1392 
1393 static bool
is_gcj_present(void)1394 is_gcj_present (void)
1395 {
1396   static bool gcj_tested;
1397   static bool gcj_present;
1398 
1399   if (!gcj_tested)
1400     {
1401       /* Test for presence of gcj:
1402          "gcj --version 2> /dev/null | \
1403           sed -e 's,^[^0-9]*,,' -e 1q | \
1404           sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null"  */
1405       char *argv[3];
1406       pid_t child;
1407       int fd[1];
1408       int exitstatus;
1409 
1410       argv[0] = "gcj";
1411       argv[1] = "--version";
1412       argv[2] = NULL;
1413       child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1414                               false, fd);
1415       gcj_present = false;
1416       if (child != -1)
1417         {
1418           /* Read the subprocess output, drop all lines except the first,
1419              drop all characters before the first digit, and test whether
1420              the remaining string starts with a digit >= 3, but not with
1421              "3.0" or "3.1".  */
1422           char c[3];
1423           size_t count = 0;
1424 
1425           while (safe_read (fd[0], &c[count], 1) > 0)
1426             {
1427               if (c[count] == '\n')
1428                 break;
1429               if (count == 0)
1430                 {
1431                   if (!(c[0] >= '0' && c[0] <= '9'))
1432                     continue;
1433                   gcj_present = (c[0] >= '3');
1434                 }
1435               count++;
1436               if (count == 3)
1437                 {
1438                   if (c[0] == '3' && c[1] == '.'
1439                       && (c[2] == '0' || c[2] == '1'))
1440                     gcj_present = false;
1441                   break;
1442                 }
1443             }
1444           while (safe_read (fd[0], &c[0], 1) > 0)
1445             ;
1446 
1447           close (fd[0]);
1448 
1449           /* Remove zombie process from process list, and retrieve exit
1450              status.  */
1451           exitstatus =
1452             wait_subprocess (child, "gcj", false, true, true, false, NULL);
1453           if (exitstatus != 0)
1454             gcj_present = false;
1455         }
1456 
1457       if (gcj_present)
1458         {
1459           /* See if libgcj.jar is well installed.  */
1460           struct temp_dir *tmpdir;
1461 
1462           tmpdir = create_temp_dir ("java", NULL, false);
1463           if (tmpdir == NULL)
1464             gcj_present = false;
1465           else
1466             {
1467               char *conftest_file_name;
1468 
1469               conftest_file_name =
1470                 xconcatenated_filename (tmpdir->dir_name, "conftestlib.java",
1471                                         NULL);
1472               if (write_temp_file (tmpdir, conftest_file_name,
1473 "public class conftestlib {\n"
1474 "  public static void main (String[] args) {\n"
1475 "  }\n"
1476 "}\n"))
1477                 gcj_present = false;
1478               else
1479                 {
1480                   char *compiled_file_name;
1481                   const char *java_sources[1];
1482 
1483                   compiled_file_name =
1484                     xconcatenated_filename (tmpdir->dir_name,
1485                                             "conftestlib.class",
1486                                             NULL);
1487                   register_temp_file (tmpdir, compiled_file_name);
1488 
1489                   java_sources[0] = conftest_file_name;
1490                   if (compile_using_gcj (java_sources, 1, false,
1491                                          false, NULL, false, NULL,
1492                                          tmpdir->dir_name,
1493                                          false, false, false, true))
1494                     gcj_present = false;
1495 
1496                   free (compiled_file_name);
1497                 }
1498               free (conftest_file_name);
1499             }
1500           cleanup_temp_dir (tmpdir);
1501         }
1502 
1503       gcj_tested = true;
1504     }
1505 
1506   return gcj_present;
1507 }
1508 
1509 static bool
is_gcj_43(void)1510 is_gcj_43 (void)
1511 {
1512   static bool gcj_tested;
1513   static bool gcj_43;
1514 
1515   if (!gcj_tested)
1516     {
1517       /* Test for presence of gcj:
1518          "gcj --version 2> /dev/null | \
1519           sed -e 's,^[^0-9]*,,' -e 1q | \
1520           sed -e '/^4\.[012]/d' | grep '^[4-9]'"  */
1521       char *argv[3];
1522       pid_t child;
1523       int fd[1];
1524       int exitstatus;
1525 
1526       argv[0] = "gcj";
1527       argv[1] = "--version";
1528       argv[2] = NULL;
1529       child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1530                               false, fd);
1531       gcj_43 = false;
1532       if (child != -1)
1533         {
1534           /* Read the subprocess output, drop all lines except the first,
1535              drop all characters before the first digit, and test whether
1536              the remaining string starts with a digit >= 4, but not with
1537              "4.0" or "4.1" or "4.2".  */
1538           char c[3];
1539           size_t count = 0;
1540 
1541           while (safe_read (fd[0], &c[count], 1) > 0)
1542             {
1543               if (c[count] == '\n')
1544                 break;
1545               if (count == 0)
1546                 {
1547                   if (!(c[0] >= '0' && c[0] <= '9'))
1548                     continue;
1549                   gcj_43 = (c[0] >= '4');
1550                 }
1551               count++;
1552               if (count == 3)
1553                 {
1554                   if (c[0] == '4' && c[1] == '.' && c[2] >= '0' && c[2] <= '2')
1555                     gcj_43 = false;
1556                   break;
1557                 }
1558             }
1559           while (safe_read (fd[0], &c[0], 1) > 0)
1560             ;
1561 
1562           close (fd[0]);
1563 
1564           /* Remove zombie process from process list, and retrieve exit
1565              status.  */
1566           exitstatus =
1567             wait_subprocess (child, "gcj", false, true, true, false, NULL);
1568           if (exitstatus != 0)
1569             gcj_43 = false;
1570         }
1571 
1572       gcj_tested = true;
1573     }
1574 
1575   return gcj_43;
1576 }
1577 
1578 /* Test whether gcj >= 4.3 can be used, and whether it needs a -fsource and/or
1579    -ftarget option.
1580    Return a failure indicator (true upon error).  */
1581 static bool
is_gcj43_usable(const char * source_version,const char * target_version,bool * usablep,bool * fsource_option_p,bool * ftarget_option_p)1582 is_gcj43_usable (const char *source_version,
1583                  const char *target_version,
1584                  bool *usablep,
1585                  bool *fsource_option_p, bool *ftarget_option_p)
1586 {
1587   /* The cache depends on the source_version and target_version.  */
1588   struct result_t
1589   {
1590     bool tested;
1591     bool usable;
1592     bool fsource_option;
1593     bool ftarget_option;
1594   };
1595   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1596   struct result_t *resultp;
1597 
1598   resultp = &result_cache[source_version_index (source_version)]
1599                          [target_version_index (target_version)];
1600   if (!resultp->tested)
1601     {
1602       /* Try gcj.  */
1603       struct temp_dir *tmpdir;
1604       char *conftest_file_name;
1605       char *compiled_file_name;
1606       const char *java_sources[1];
1607       struct stat statbuf;
1608 
1609       tmpdir = create_temp_dir ("java", NULL, false);
1610       if (tmpdir == NULL)
1611         return true;
1612 
1613       conftest_file_name =
1614         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1615       if (write_temp_file (tmpdir, conftest_file_name,
1616                            get_goodcode_snippet (source_version)))
1617         {
1618           free (conftest_file_name);
1619           cleanup_temp_dir (tmpdir);
1620           return true;
1621         }
1622 
1623       compiled_file_name =
1624         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1625       register_temp_file (tmpdir, compiled_file_name);
1626 
1627       java_sources[0] = conftest_file_name;
1628       if (!compile_using_gcj (java_sources, 1, false, false, NULL, false, NULL,
1629                               tmpdir->dir_name, false, false, false, true)
1630           && stat (compiled_file_name, &statbuf) >= 0
1631           && get_classfile_version (compiled_file_name)
1632              <= corresponding_classfile_version (target_version))
1633         {
1634           /* gcj compiled conftest.java successfully.  */
1635           /* Try adding -fsource option if it is useful.  */
1636           unlink (compiled_file_name);
1637 
1638           java_sources[0] = conftest_file_name;
1639           if (!compile_using_gcj (java_sources, 1,
1640                                   false, true, source_version, false, NULL,
1641                                   tmpdir->dir_name, false, false, false, true)
1642               && stat (compiled_file_name, &statbuf) >= 0
1643               && get_classfile_version (compiled_file_name)
1644                  <= corresponding_classfile_version (target_version))
1645             {
1646               const char *failcode = get_failcode_snippet (source_version);
1647 
1648               if (failcode != NULL)
1649                 {
1650                   free (compiled_file_name);
1651                   free (conftest_file_name);
1652 
1653                   conftest_file_name =
1654                     xconcatenated_filename (tmpdir->dir_name,
1655                                             "conftestfail.java",
1656                                             NULL);
1657                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1658                     {
1659                       free (conftest_file_name);
1660                       cleanup_temp_dir (tmpdir);
1661                       return true;
1662                     }
1663 
1664                   compiled_file_name =
1665                     xconcatenated_filename (tmpdir->dir_name,
1666                                             "conftestfail.class",
1667                                             NULL);
1668                   register_temp_file (tmpdir, compiled_file_name);
1669 
1670                   java_sources[0] = conftest_file_name;
1671                   if (!compile_using_gcj (java_sources, 1,
1672                                           false, false, NULL, false, NULL,
1673                                           tmpdir->dir_name,
1674                                           false, false, false, true)
1675                       && stat (compiled_file_name, &statbuf) >= 0)
1676                     {
1677                       unlink (compiled_file_name);
1678 
1679                       java_sources[0] = conftest_file_name;
1680                       if (compile_using_gcj (java_sources, 1,
1681                                              false, true, source_version,
1682                                              false, NULL,
1683                                              tmpdir->dir_name,
1684                                              false, false, false, true))
1685                         /* gcj compiled conftestfail.java successfully, and
1686                            "gcj -fsource=$source_version" rejects it.  So
1687                            the -fsource option is useful.  */
1688                         resultp->fsource_option = true;
1689                     }
1690                 }
1691             }
1692 
1693           resultp->usable = true;
1694         }
1695       else
1696         {
1697           /* Try with -fsource and -ftarget options.  */
1698           unlink (compiled_file_name);
1699 
1700           java_sources[0] = conftest_file_name;
1701           if (!compile_using_gcj (java_sources, 1,
1702                                   false, true, source_version,
1703                                   true, target_version,
1704                                   tmpdir->dir_name,
1705                                   false, false, false, true)
1706               && stat (compiled_file_name, &statbuf) >= 0
1707               && get_classfile_version (compiled_file_name)
1708                  <= corresponding_classfile_version (target_version))
1709             {
1710               /* "gcj -fsource $source_version -ftarget $target_version"
1711                  compiled conftest.java successfully.  */
1712               resultp->fsource_option = true;
1713               resultp->ftarget_option = true;
1714               resultp->usable = true;
1715             }
1716         }
1717 
1718       free (compiled_file_name);
1719       free (conftest_file_name);
1720 
1721       resultp->tested = true;
1722     }
1723 
1724   *usablep = resultp->usable;
1725   *fsource_option_p = resultp->fsource_option;
1726   *ftarget_option_p = resultp->ftarget_option;
1727   return false;
1728 }
1729 
1730 /* Test whether gcj < 4.3 can be used for compiling with target_version = 1.4
1731    and source_version = 1.4.
1732    Return a failure indicator (true upon error).  */
1733 static bool
is_oldgcj_14_14_usable(bool * usablep)1734 is_oldgcj_14_14_usable (bool *usablep)
1735 {
1736   static bool gcj_tested;
1737   static bool gcj_usable;
1738 
1739   if (!gcj_tested)
1740     {
1741       /* Try gcj.  */
1742       struct temp_dir *tmpdir;
1743       char *conftest_file_name;
1744       char *compiled_file_name;
1745       const char *java_sources[1];
1746       struct stat statbuf;
1747 
1748       tmpdir = create_temp_dir ("java", NULL, false);
1749       if (tmpdir == NULL)
1750         return true;
1751 
1752       conftest_file_name =
1753         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1754       if (write_temp_file (tmpdir, conftest_file_name,
1755                            get_goodcode_snippet ("1.4")))
1756         {
1757           free (conftest_file_name);
1758           cleanup_temp_dir (tmpdir);
1759           return true;
1760         }
1761 
1762       compiled_file_name =
1763         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1764       register_temp_file (tmpdir, compiled_file_name);
1765 
1766       java_sources[0] = conftest_file_name;
1767       if (!compile_using_gcj (java_sources, 1, false, false, NULL, false, NULL,
1768                               tmpdir->dir_name, false, false, false, true)
1769           && stat (compiled_file_name, &statbuf) >= 0)
1770         /* Compilation succeeded.  */
1771         gcj_usable = true;
1772 
1773       free (compiled_file_name);
1774       free (conftest_file_name);
1775 
1776       cleanup_temp_dir (tmpdir);
1777 
1778       gcj_tested = true;
1779     }
1780 
1781   *usablep = gcj_usable;
1782   return false;
1783 }
1784 
1785 /* Test whether gcj < 4.3 can be used for compiling with target_version = 1.4
1786    and source_version = 1.3.
1787    Return a failure indicator (true upon error).  */
1788 static bool
is_oldgcj_14_13_usable(bool * usablep,bool * need_no_assert_option_p)1789 is_oldgcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1790 {
1791   static bool gcj_tested;
1792   static bool gcj_usable;
1793   static bool gcj_need_no_assert_option;
1794 
1795   if (!gcj_tested)
1796     {
1797       /* Try gcj and "gcj -fno-assert".  But add -fno-assert only if
1798          it works (not gcj < 3.3).  */
1799       struct temp_dir *tmpdir;
1800       char *conftest_file_name;
1801       char *compiled_file_name;
1802       const char *java_sources[1];
1803       struct stat statbuf;
1804 
1805       tmpdir = create_temp_dir ("java", NULL, false);
1806       if (tmpdir == NULL)
1807         return true;
1808 
1809       conftest_file_name =
1810         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1811       if (write_temp_file (tmpdir, conftest_file_name,
1812                            get_goodcode_snippet ("1.3")))
1813         {
1814           free (conftest_file_name);
1815           cleanup_temp_dir (tmpdir);
1816           return true;
1817         }
1818 
1819       compiled_file_name =
1820         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1821       register_temp_file (tmpdir, compiled_file_name);
1822 
1823       java_sources[0] = conftest_file_name;
1824       if (!compile_using_gcj (java_sources, 1, true, false, NULL, false, NULL,
1825                               tmpdir->dir_name, false, false, false, true)
1826           && stat (compiled_file_name, &statbuf) >= 0)
1827         /* Compilation succeeded.  */
1828         {
1829           gcj_usable = true;
1830           gcj_need_no_assert_option = true;
1831         }
1832       else
1833         {
1834           unlink (compiled_file_name);
1835 
1836           java_sources[0] = conftest_file_name;
1837           if (!compile_using_gcj (java_sources, 1, false,
1838                                   false, NULL, false, NULL,
1839                                   tmpdir->dir_name, false, false, false, true)
1840               && stat (compiled_file_name, &statbuf) >= 0)
1841             /* Compilation succeeded.  */
1842             {
1843               gcj_usable = true;
1844               gcj_need_no_assert_option = false;
1845             }
1846         }
1847 
1848       free (compiled_file_name);
1849       free (conftest_file_name);
1850 
1851       cleanup_temp_dir (tmpdir);
1852 
1853       gcj_tested = true;
1854     }
1855 
1856   *usablep = gcj_usable;
1857   *need_no_assert_option_p = gcj_need_no_assert_option;
1858   return false;
1859 }
1860 
1861 static bool
is_javac_present(void)1862 is_javac_present (void)
1863 {
1864   static bool javac_tested;
1865   static bool javac_present;
1866 
1867   if (!javac_tested)
1868     {
1869       /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2"  */
1870       char *argv[2];
1871       int exitstatus;
1872 
1873       argv[0] = "javac";
1874       argv[1] = NULL;
1875       exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1876                             true, false, NULL);
1877       javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1878       javac_tested = true;
1879     }
1880 
1881   return javac_present;
1882 }
1883 
1884 /* Test whether javac can be used and whether it needs a -source and/or
1885    -target option.
1886    Return a failure indicator (true upon error).  */
1887 static bool
is_javac_usable(const char * source_version,const char * source_version_for_javac,const char * target_version,bool * usablep,bool * source_option_p,bool * target_option_p)1888 is_javac_usable (const char *source_version,
1889                  const char *source_version_for_javac,
1890                  const char *target_version,
1891                  bool *usablep, bool *source_option_p, bool *target_option_p)
1892 {
1893   /* The cache depends on the source_version and target_version.  */
1894   struct result_t
1895   {
1896     bool tested;
1897     bool usable;
1898     bool source_option;
1899     bool target_option;
1900   };
1901   static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1902   struct result_t *resultp;
1903 
1904   resultp = &result_cache[source_version_index (source_version)]
1905                          [target_version_index (target_version)];
1906   if (!resultp->tested)
1907     {
1908       /* Try javac.  */
1909       struct temp_dir *tmpdir;
1910       char *conftest_file_name;
1911       char *compiled_file_name;
1912       const char *java_sources[1];
1913       struct stat statbuf;
1914 
1915       tmpdir = create_temp_dir ("java", NULL, false);
1916       if (tmpdir == NULL)
1917         return true;
1918 
1919       conftest_file_name =
1920         xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1921       if (write_temp_file (tmpdir, conftest_file_name,
1922                            get_goodcode_snippet (source_version)))
1923         {
1924           free (conftest_file_name);
1925           cleanup_temp_dir (tmpdir);
1926           return true;
1927         }
1928 
1929       compiled_file_name =
1930         xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1931       register_temp_file (tmpdir, compiled_file_name);
1932 
1933       java_sources[0] = conftest_file_name;
1934       if (!compile_using_javac (java_sources, 1,
1935                                 false, source_version_for_javac,
1936                                 false, target_version,
1937                                 tmpdir->dir_name, false, false, false, true)
1938           && stat (compiled_file_name, &statbuf) >= 0
1939           && get_classfile_version (compiled_file_name)
1940              <= corresponding_classfile_version (target_version))
1941         {
1942           /* javac compiled conftest.java successfully.  */
1943           /* Try adding -source option if it is useful.  */
1944           unlink (compiled_file_name);
1945 
1946           java_sources[0] = conftest_file_name;
1947           if (!compile_using_javac (java_sources, 1,
1948                                     true, source_version_for_javac,
1949                                     false, target_version,
1950                                     tmpdir->dir_name, false, false, false, true)
1951               && stat (compiled_file_name, &statbuf) >= 0
1952               && get_classfile_version (compiled_file_name)
1953                  <= corresponding_classfile_version (target_version))
1954             {
1955               const char *failcode = get_failcode_snippet (source_version);
1956 
1957               if (failcode != NULL)
1958                 {
1959                   free (compiled_file_name);
1960                   free (conftest_file_name);
1961 
1962                   conftest_file_name =
1963                     xconcatenated_filename (tmpdir->dir_name,
1964                                             "conftestfail.java",
1965                                             NULL);
1966                   if (write_temp_file (tmpdir, conftest_file_name, failcode))
1967                     {
1968                       free (conftest_file_name);
1969                       cleanup_temp_dir (tmpdir);
1970                       return true;
1971                     }
1972 
1973                   compiled_file_name =
1974                     xconcatenated_filename (tmpdir->dir_name,
1975                                             "conftestfail.class",
1976                                             NULL);
1977                   register_temp_file (tmpdir, compiled_file_name);
1978 
1979                   java_sources[0] = conftest_file_name;
1980                   if (!compile_using_javac (java_sources, 1,
1981                                             false, source_version_for_javac,
1982                                             false, target_version,
1983                                             tmpdir->dir_name,
1984                                             false, false, false, true)
1985                       && stat (compiled_file_name, &statbuf) >= 0)
1986                     {
1987                       unlink (compiled_file_name);
1988 
1989                       java_sources[0] = conftest_file_name;
1990                       if (compile_using_javac (java_sources, 1,
1991                                                true, source_version_for_javac,
1992                                                false, target_version,
1993                                                tmpdir->dir_name,
1994                                                false, false, false, true))
1995                         /* javac compiled conftestfail.java successfully, and
1996                            "javac -source $source_version_for_javac" rejects it.
1997                            So the -source option is useful.  */
1998                         resultp->source_option = true;
1999                     }
2000                 }
2001             }
2002 
2003           resultp->usable = true;
2004         }
2005       else
2006         {
2007           /* Try with -target option alone. (Sun javac 1.3.1 has the -target
2008              option but no -source option.)  */
2009           unlink (compiled_file_name);
2010 
2011           java_sources[0] = conftest_file_name;
2012           if (!compile_using_javac (java_sources, 1,
2013                                     false, source_version_for_javac,
2014                                     true, target_version,
2015                                     tmpdir->dir_name,
2016                                     false, false, false, true)
2017               && stat (compiled_file_name, &statbuf) >= 0
2018               && get_classfile_version (compiled_file_name)
2019                  <= corresponding_classfile_version (target_version))
2020             {
2021               /* "javac -target $target_version" compiled conftest.java
2022                  successfully.  */
2023               /* Try adding -source option if it is useful.  */
2024               unlink (compiled_file_name);
2025 
2026               java_sources[0] = conftest_file_name;
2027               if (!compile_using_javac (java_sources, 1,
2028                                         true, source_version_for_javac,
2029                                         true, target_version,
2030                                         tmpdir->dir_name,
2031                                         false, false, false, true)
2032                   && stat (compiled_file_name, &statbuf) >= 0
2033                   && get_classfile_version (compiled_file_name)
2034                      <= corresponding_classfile_version (target_version))
2035                 {
2036                   const char *failcode = get_failcode_snippet (source_version);
2037 
2038                   if (failcode != NULL)
2039                     {
2040                       free (compiled_file_name);
2041                       free (conftest_file_name);
2042 
2043                       conftest_file_name =
2044                         xconcatenated_filename (tmpdir->dir_name,
2045                                                 "conftestfail.java",
2046                                                 NULL);
2047                       if (write_temp_file (tmpdir, conftest_file_name,
2048                                            failcode))
2049                         {
2050                           free (conftest_file_name);
2051                           cleanup_temp_dir (tmpdir);
2052                           return true;
2053                         }
2054 
2055                       compiled_file_name =
2056                         xconcatenated_filename (tmpdir->dir_name,
2057                                                 "conftestfail.class",
2058                                                 NULL);
2059                       register_temp_file (tmpdir, compiled_file_name);
2060 
2061                       java_sources[0] = conftest_file_name;
2062                       if (!compile_using_javac (java_sources, 1,
2063                                                 false, source_version_for_javac,
2064                                                 true, target_version,
2065                                                 tmpdir->dir_name,
2066                                                 false, false, false, true)
2067                           && stat (compiled_file_name, &statbuf) >= 0)
2068                         {
2069                           unlink (compiled_file_name);
2070 
2071                           java_sources[0] = conftest_file_name;
2072                           if (compile_using_javac (java_sources, 1,
2073                                                    true, source_version_for_javac,
2074                                                    true, target_version,
2075                                                    tmpdir->dir_name,
2076                                                    false, false, false, true))
2077                             /* "javac -target $target_version" compiled
2078                                conftestfail.java successfully, and
2079                                "javac -target $target_version -source $source_version_for_javac"
2080                                rejects it.  So the -source option is useful.  */
2081                             resultp->source_option = true;
2082                         }
2083                     }
2084                 }
2085 
2086               resultp->target_option = true;
2087               resultp->usable = true;
2088             }
2089           else
2090             {
2091               /* Maybe this -target option requires a -source option? Try with
2092                  -target and -source options. (Supported by Sun javac 1.4 and
2093                  higher.)  */
2094               unlink (compiled_file_name);
2095 
2096               java_sources[0] = conftest_file_name;
2097               if (!compile_using_javac (java_sources, 1,
2098                                         true, source_version_for_javac,
2099                                         true, target_version,
2100                                         tmpdir->dir_name,
2101                                         false, false, false, true)
2102                   && stat (compiled_file_name, &statbuf) >= 0
2103                   && get_classfile_version (compiled_file_name)
2104                      <= corresponding_classfile_version (target_version))
2105                 {
2106                   /* "javac -target $target_version -source $source_version_for_javac"
2107                      compiled conftest.java successfully.  */
2108                   resultp->source_option = true;
2109                   resultp->target_option = true;
2110                   resultp->usable = true;
2111                 }
2112             }
2113         }
2114 
2115       free (compiled_file_name);
2116       free (conftest_file_name);
2117 
2118       resultp->tested = true;
2119     }
2120 
2121   *usablep = resultp->usable;
2122   *source_option_p = resultp->source_option;
2123   *target_option_p = resultp->target_option;
2124   return false;
2125 }
2126 
2127 static bool
is_jikes_present(void)2128 is_jikes_present (void)
2129 {
2130   static bool jikes_tested;
2131   static bool jikes_present;
2132 
2133   if (!jikes_tested)
2134     {
2135       /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1"  */
2136       char *argv[2];
2137       int exitstatus;
2138 
2139       argv[0] = "jikes";
2140       argv[1] = NULL;
2141       exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
2142                             true, false, NULL);
2143       jikes_present = (exitstatus == 0 || exitstatus == 1);
2144       jikes_tested = true;
2145     }
2146 
2147   return jikes_present;
2148 }
2149 
2150 /* ============================= Main function ============================= */
2151 
2152 bool
compile_java_class(const char * const * java_sources,unsigned int java_sources_count,const char * const * classpaths,unsigned int classpaths_count,const char * source_version,const char * target_version,const char * directory,bool optimize,bool debug,bool use_minimal_classpath,bool verbose)2153 compile_java_class (const char * const *java_sources,
2154                     unsigned int java_sources_count,
2155                     const char * const *classpaths,
2156                     unsigned int classpaths_count,
2157                     const char *source_version,
2158                     const char *target_version,
2159                     const char *directory,
2160                     bool optimize, bool debug,
2161                     bool use_minimal_classpath,
2162                     bool verbose)
2163 {
2164   bool err = false;
2165   char *old_JAVA_HOME;
2166 
2167   {
2168     const char *javac = getenv ("JAVAC");
2169     if (javac != NULL && javac[0] != '\0')
2170       {
2171         bool usable = false;
2172         bool no_assert_option = false;
2173         bool source_option = false;
2174         bool target_option = false;
2175         bool fsource_option = false;
2176         bool ftarget_option = false;
2177         const char *source_version_for_javac;
2178 
2179         if (target_version == NULL)
2180           target_version = default_target_version ();
2181 
2182         source_version_for_javac =
2183           get_source_version_for_javac (source_version, target_version);
2184 
2185         if (is_envjavac_gcj (javac))
2186           {
2187             /* It's a version of gcj.  */
2188             if (is_envjavac_gcj43 (javac))
2189               {
2190                 /* It's a version of gcj >= 4.3.  Assume the classfile versions
2191                    are correct.  */
2192                 if (is_envjavac_gcj43_usable (javac,
2193                                               source_version, target_version,
2194                                               &usable,
2195                                               &fsource_option, &ftarget_option))
2196                   {
2197                     err = true;
2198                     goto done1;
2199                   }
2200               }
2201             else
2202               {
2203                 /* It's a version of gcj < 4.3.  Ignore the version of the
2204                    class files that it creates.  */
2205                 if (strcmp (target_version, "1.4") == 0
2206                     && strcmp (source_version, "1.4") == 0)
2207                   {
2208                     if (is_envjavac_oldgcj_14_14_usable (javac, &usable))
2209                       {
2210                         err = true;
2211                         goto done1;
2212                       }
2213                   }
2214                 else if (strcmp (target_version, "1.4") == 0
2215                          && strcmp (source_version, "1.3") == 0)
2216                   {
2217                     if (is_envjavac_oldgcj_14_13_usable (javac,
2218                                                          &usable,
2219                                                          &no_assert_option))
2220                       {
2221                         err = true;
2222                         goto done1;
2223                       }
2224                   }
2225               }
2226           }
2227         else
2228           {
2229             /* It's not gcj.  Assume the classfile versions are correct.  */
2230             if (is_envjavac_nongcj_usable (javac,
2231                                            source_version,
2232                                            source_version_for_javac,
2233                                            target_version,
2234                                            &usable,
2235                                            &source_option, &target_option))
2236               {
2237                 err = true;
2238                 goto done1;
2239               }
2240           }
2241 
2242         if (usable)
2243           {
2244             char *old_classpath;
2245             char *javac_with_options;
2246 
2247             /* Set CLASSPATH.  */
2248             old_classpath =
2249               set_classpath (classpaths, classpaths_count, false, verbose);
2250 
2251             javac_with_options =
2252               (no_assert_option
2253                ? xasprintf ("%s -fno-assert", javac)
2254                : xasprintf ("%s%s%s%s%s%s%s%s%s",
2255                             javac,
2256                             source_option ? " -source " : "",
2257                             source_option ? source_version_for_javac : "",
2258                             target_option ? " -target " : "",
2259                             target_option ? target_version : "",
2260                             fsource_option ? " -fsource=" : "",
2261                             fsource_option ? source_version : "",
2262                             ftarget_option ? " -ftarget=" : "",
2263                             ftarget_option ? target_version : ""));
2264 
2265             err = compile_using_envjavac (javac_with_options,
2266                                           java_sources, java_sources_count,
2267                                           directory, optimize, debug, verbose,
2268                                           false);
2269 
2270             free (javac_with_options);
2271 
2272             /* Reset CLASSPATH.  */
2273             reset_classpath (old_classpath);
2274 
2275             goto done1;
2276           }
2277       }
2278   }
2279 
2280   /* Unset the JAVA_HOME environment variable.  */
2281   old_JAVA_HOME = getenv ("JAVA_HOME");
2282   if (old_JAVA_HOME != NULL)
2283     {
2284       old_JAVA_HOME = xstrdup (old_JAVA_HOME);
2285       unsetenv ("JAVA_HOME");
2286     }
2287 
2288   if (is_gcj_present ())
2289     {
2290       /* It's a version of gcj.  */
2291       bool usable = false;
2292       bool no_assert_option = false;
2293       bool fsource_option = false;
2294       bool ftarget_option = false;
2295 
2296       if (target_version == NULL)
2297         target_version = default_target_version ();
2298 
2299       if (is_gcj_43 ())
2300         {
2301           /* It's a version of gcj >= 4.3.  Assume the classfile versions
2302              are correct.  */
2303           if (is_gcj43_usable (source_version, target_version,
2304                                &usable, &fsource_option, &ftarget_option))
2305             {
2306               err = true;
2307               goto done1;
2308             }
2309         }
2310       else
2311         {
2312           /* It's a version of gcj < 4.3.  Ignore the version of the class
2313              files that it creates.
2314              Test whether it supports the desired target-version and
2315              source-version.  */
2316           if (strcmp (target_version, "1.4") == 0
2317               && strcmp (source_version, "1.4") == 0)
2318             {
2319               if (is_oldgcj_14_14_usable (&usable))
2320                 {
2321                   err = true;
2322                   goto done1;
2323                 }
2324             }
2325           else if (strcmp (target_version, "1.4") == 0
2326                    && strcmp (source_version, "1.3") == 0)
2327             {
2328               if (is_oldgcj_14_13_usable (&usable, &no_assert_option))
2329                 {
2330                   err = true;
2331                   goto done1;
2332                 }
2333             }
2334         }
2335 
2336       if (usable)
2337         {
2338           char *old_classpath;
2339 
2340           /* Set CLASSPATH.  We could also use the --CLASSPATH=... option
2341              of gcj.  Note that --classpath=... option is different: its
2342              argument should also contain gcj's libgcj.jar, but we don't
2343              know its location.  */
2344           old_classpath =
2345             set_classpath (classpaths, classpaths_count, use_minimal_classpath,
2346                            verbose);
2347 
2348           err = compile_using_gcj (java_sources, java_sources_count,
2349                                    no_assert_option,
2350                                    fsource_option, source_version,
2351                                    ftarget_option, target_version,
2352                                    directory, optimize, debug, verbose, false);
2353 
2354           /* Reset CLASSPATH.  */
2355           reset_classpath (old_classpath);
2356 
2357           goto done2;
2358         }
2359     }
2360 
2361   if (is_javac_present ())
2362     {
2363       bool usable = false;
2364       bool source_option = false;
2365       bool target_option = false;
2366       const char *source_version_for_javac;
2367 
2368       if (target_version == NULL)
2369         target_version = default_target_version ();
2370 
2371       source_version_for_javac =
2372         get_source_version_for_javac (source_version, target_version);
2373 
2374       if (is_javac_usable (source_version, source_version_for_javac,
2375                            target_version,
2376                            &usable, &source_option, &target_option))
2377         {
2378           err = true;
2379           goto done1;
2380         }
2381 
2382       if (usable)
2383         {
2384           char *old_classpath;
2385 
2386           /* Set CLASSPATH.  We don't use the "-classpath ..." option because
2387              in JDK 1.1.x its argument should also contain the JDK's
2388              classes.zip, but we don't know its location.  (In JDK 1.3.0 it
2389              would work.)  */
2390           old_classpath =
2391             set_classpath (classpaths, classpaths_count, use_minimal_classpath,
2392                            verbose);
2393 
2394           err = compile_using_javac (java_sources, java_sources_count,
2395                                      source_option, source_version_for_javac,
2396                                      target_option, target_version,
2397                                      directory, optimize, debug, verbose,
2398                                      false);
2399 
2400           /* Reset CLASSPATH.  */
2401           reset_classpath (old_classpath);
2402 
2403           goto done2;
2404         }
2405     }
2406 
2407   if (is_jikes_present ())
2408     {
2409       /* Test whether it supports the desired target-version and
2410          source-version.  */
2411       bool usable = (strcmp (source_version, "1.3") == 0);
2412 
2413       if (usable)
2414         {
2415           char *old_classpath;
2416 
2417           /* Set CLASSPATH.  We could also use the "-classpath ..." option.
2418              Since jikes doesn't come with its own standard library, it
2419              needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
2420              To increase the chance of success, we reuse the current CLASSPATH
2421              if the user has set it.  */
2422           old_classpath =
2423             set_classpath (classpaths, classpaths_count, false, verbose);
2424 
2425           err = compile_using_jikes (java_sources, java_sources_count,
2426                                      directory, optimize, debug, verbose,
2427                                      false);
2428 
2429           /* Reset CLASSPATH.  */
2430           reset_classpath (old_classpath);
2431 
2432           goto done2;
2433         }
2434     }
2435 
2436   error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
2437   err = true;
2438 
2439  done2:
2440   if (old_JAVA_HOME != NULL)
2441     {
2442       xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
2443       free (old_JAVA_HOME);
2444     }
2445 
2446  done1:
2447   return err;
2448 }
2449