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