af_alg: Improve comments.
[gnulib.git] / lib / javacomp.c
blobd7100857d70da9b3ec34429caf96634df4a60afd
1 /* Compile a Java program.
2 Copyright (C) 2001-2003, 2006-2018 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
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.
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.
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/>. */
18 #include <config.h>
19 #include <alloca.h>
21 /* Specification. */
22 #include "javacomp.h"
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>
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"
52 #define _(str) gettext (str)
55 /* Survey of Java compilers.
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
63 Program from A C O g T
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
71 All compilers support the option "-d DIRECTORY" for the base directory
72 of the classes to be written.
74 The CLASSPATH is a colon separated list of pathnames. (On Windows: a
75 semicolon separated list of pathnames.)
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.
86 We unset the JAVA_HOME environment variable, because a wrong setting of
87 this variable can confuse the JDK's javac.
90 /* Return the default target_version. */
91 static const char *
92 default_target_version (void)
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)
99 /* Determine the version from the found JVM. */
100 java_version_cache = javaexec_version ();
101 if (java_version_cache == NULL
102 || !((java_version_cache[0] == '1'
103 && java_version_cache[1] == '.'
104 && java_version_cache[2] >= '1' && java_version_cache[2] <= '8'
105 && java_version_cache[3] == '\0')
106 || (java_version_cache[0] == '9'
107 && java_version_cache[1] == '\0')
108 || (java_version_cache[0] == '1'
109 && java_version_cache[1] == '0'
110 && java_version_cache[2] == '\0')))
111 java_version_cache = "1.1";
113 return java_version_cache;
116 /* ======================= Source version dependent ======================= */
118 /* Convert a source version to an index. */
119 #define SOURCE_VERSION_BOUND 7 /* exclusive upper bound */
120 static unsigned int
121 source_version_index (const char *source_version)
123 if (source_version[0] == '1' && source_version[1] == '.')
125 if ((source_version[2] >= '3' && source_version[2] <= '5')
126 && source_version[3] == '\0')
127 return source_version[2] - '3';
128 if ((source_version[2] >= '7' && source_version[2] <= '8')
129 && source_version[3] == '\0')
130 return source_version[2] - '4';
132 else if (source_version[0] == '9' && source_version[1] == '\0')
133 return 5;
134 else if (source_version[0] == '1' && source_version[1] == '0'
135 && source_version[2] == '\0')
136 return 6;
137 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
138 return 0;
141 /* Return a snippet of code that should compile in the given source version. */
142 static const char *
143 get_goodcode_snippet (const char *source_version)
145 if (strcmp (source_version, "1.3") == 0)
146 return "class conftest {}\n";
147 if (strcmp (source_version, "1.4") == 0)
148 return "class conftest { static { assert(true); } }\n";
149 if (strcmp (source_version, "1.5") == 0)
150 return "class conftest<T> { T foo() { return null; } }\n";
151 if (strcmp (source_version, "1.7") == 0)
152 return "class conftest { void foo () { switch (\"A\") {} } }\n";
153 if (strcmp (source_version, "1.8") == 0)
154 return "class conftest { void foo () { Runnable r = () -> {}; } }\n";
155 if (strcmp (source_version, "9") == 0)
156 return "interface conftest { private void foo () {} }\n";
157 if (strcmp (source_version, "10") == 0)
158 return "class conftest { public void m() { var i = new Integer(0); } }\n";
159 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
160 return NULL;
163 /* Return a snippet of code that should fail to compile in the given source
164 version, or NULL (standing for a snippet that would fail to compile with
165 any compiler). */
166 static const char *
167 get_failcode_snippet (const char *source_version)
169 if (strcmp (source_version, "1.3") == 0)
170 return "class conftestfail { static { assert(true); } }\n";
171 if (strcmp (source_version, "1.4") == 0)
172 return "class conftestfail<T> { T foo() { return null; } }\n";
173 if (strcmp (source_version, "1.5") == 0)
174 return "class conftestfail { void foo () { switch (\"A\") {} } }\n";
175 if (strcmp (source_version, "1.7") == 0)
176 return "class conftestfail { void foo () { Runnable r = () -> {}; } }\n";
177 if (strcmp (source_version, "1.8") == 0)
178 return "interface conftestfail { private void foo () {} }\n";
179 if (strcmp (source_version, "9") == 0)
180 return "class conftestfail { public void m() { var i = new Integer(0); } }\n";
181 if (strcmp (source_version, "10") == 0)
182 return NULL;
183 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
184 return NULL;
187 /* ======================= Target version dependent ======================= */
189 /* Convert a target version to an index. */
190 #define TARGET_VERSION_BOUND 10 /* exclusive upper bound */
191 static unsigned int
192 target_version_index (const char *target_version)
194 if (target_version[0] == '1' && target_version[1] == '.'
195 && (target_version[2] >= '1' && target_version[2] <= '8')
196 && target_version[3] == '\0')
197 return target_version[2] - '1';
198 else if (target_version[0] == '9' && target_version[1] == '\0')
199 return 8;
200 else if (target_version[0] == '1' && target_version[1] == '0'
201 && target_version[2] == '\0')
202 return 9;
203 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
204 return 0;
207 /* Return the class file version number corresponding to a given target
208 version. */
209 static int
210 corresponding_classfile_version (const char *target_version)
212 if (strcmp (target_version, "1.1") == 0)
213 return 45;
214 if (strcmp (target_version, "1.2") == 0)
215 return 46;
216 if (strcmp (target_version, "1.3") == 0)
217 return 47;
218 if (strcmp (target_version, "1.4") == 0)
219 return 48;
220 if (strcmp (target_version, "1.5") == 0)
221 return 49;
222 if (strcmp (target_version, "1.6") == 0)
223 return 50;
224 if (strcmp (target_version, "1.7") == 0)
225 return 51;
226 if (strcmp (target_version, "1.8") == 0)
227 return 52;
228 if (strcmp (target_version, "9") == 0)
229 return 53;
230 if (strcmp (target_version, "10") == 0)
231 return 54;
232 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
233 return 0;
236 /* Return the source version to pass to javac. */
237 static const char *
238 get_source_version_for_javac (const char *source_version,
239 const char *target_version)
241 /* The javac option '-source 1.5' has the same meaning as '-source 1.6',
242 but since Java 9 supports only the latter, prefer the latter if a
243 target_version >= 1.6 is requested. */
244 if (strcmp (source_version, "1.5") == 0
245 && !(target_version[0] == '1' && target_version[1] == '.'
246 && (target_version[2] >= '1' && target_version[2] <= '5')
247 && target_version[3] == '\0'))
248 return "1.6";
249 return source_version;
252 /* ======================== Compilation subroutines ======================== */
254 /* Try to compile a set of Java sources with $JAVAC.
255 Return a failure indicator (true upon error). */
256 static bool
257 compile_using_envjavac (const char *javac,
258 const char * const *java_sources,
259 unsigned int java_sources_count,
260 const char *directory,
261 bool optimize, bool debug,
262 bool verbose, bool null_stderr)
264 /* Because $JAVAC may consist of a command and options, we use the
265 shell. Because $JAVAC has been set by the user, we leave all
266 environment variables in place, including JAVA_HOME, and we don't
267 erase the user's CLASSPATH. */
268 bool err;
269 unsigned int command_length;
270 char *command;
271 char *argv[4];
272 int exitstatus;
273 unsigned int i;
274 char *p;
276 command_length = strlen (javac);
277 if (optimize)
278 command_length += 3;
279 if (debug)
280 command_length += 3;
281 if (directory != NULL)
282 command_length += 4 + shell_quote_length (directory);
283 for (i = 0; i < java_sources_count; i++)
284 command_length += 1 + shell_quote_length (java_sources[i]);
285 command_length += 1;
287 command = (char *) xmalloca (command_length);
288 p = command;
289 /* Don't shell_quote $JAVAC, because it may consist of a command
290 and options. */
291 memcpy (p, javac, strlen (javac));
292 p += strlen (javac);
293 if (optimize)
295 memcpy (p, " -O", 3);
296 p += 3;
298 if (debug)
300 memcpy (p, " -g", 3);
301 p += 3;
303 if (directory != NULL)
305 memcpy (p, " -d ", 4);
306 p += 4;
307 p = shell_quote_copy (p, directory);
309 for (i = 0; i < java_sources_count; i++)
311 *p++ = ' ';
312 p = shell_quote_copy (p, java_sources[i]);
314 *p++ = '\0';
315 /* Ensure command_length was correctly calculated. */
316 if (p - command > command_length)
317 abort ();
319 if (verbose)
320 printf ("%s\n", command);
322 argv[0] = "/bin/sh";
323 argv[1] = "-c";
324 argv[2] = command;
325 argv[3] = NULL;
326 exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
327 null_stderr, true, true, NULL);
328 err = (exitstatus != 0);
330 freea (command);
332 return err;
335 /* Try to compile a set of Java sources with gcj.
336 Return a failure indicator (true upon error). */
337 static bool
338 compile_using_gcj (const char * const *java_sources,
339 unsigned int java_sources_count,
340 bool no_assert_option,
341 bool fsource_option, const char *source_version,
342 bool ftarget_option, const char *target_version,
343 const char *directory,
344 bool optimize, bool debug,
345 bool verbose, bool null_stderr)
347 bool err;
348 unsigned int argc;
349 char **argv;
350 char **argp;
351 char *fsource_arg;
352 char *ftarget_arg;
353 int exitstatus;
354 unsigned int i;
356 argc =
357 2 + (no_assert_option ? 1 : 0) + (fsource_option ? 1 : 0)
358 + (ftarget_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
359 + (directory != NULL ? 2 : 0) + java_sources_count;
360 argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
362 argp = argv;
363 *argp++ = "gcj";
364 *argp++ = "-C";
365 if (no_assert_option)
366 *argp++ = "-fno-assert";
367 if (fsource_option)
369 fsource_arg = (char *) xmalloca (9 + strlen (source_version) + 1);
370 memcpy (fsource_arg, "-fsource=", 9);
371 strcpy (fsource_arg + 9, source_version);
372 *argp++ = fsource_arg;
374 else
375 fsource_arg = NULL;
376 if (ftarget_option)
378 ftarget_arg = (char *) xmalloca (9 + strlen (target_version) + 1);
379 memcpy (ftarget_arg, "-ftarget=", 9);
380 strcpy (ftarget_arg + 9, target_version);
381 *argp++ = ftarget_arg;
383 else
384 ftarget_arg = NULL;
385 if (optimize)
386 *argp++ = "-O";
387 if (debug)
388 *argp++ = "-g";
389 if (directory != NULL)
391 *argp++ = "-d";
392 *argp++ = (char *) directory;
394 for (i = 0; i < java_sources_count; i++)
395 *argp++ = (char *) java_sources[i];
396 *argp = NULL;
397 /* Ensure argv length was correctly calculated. */
398 if (argp - argv != argc)
399 abort ();
401 if (verbose)
403 char *command = shell_quote_argv (argv);
404 printf ("%s\n", command);
405 free (command);
408 exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
409 true, true, NULL);
410 err = (exitstatus != 0);
412 if (ftarget_arg != NULL)
413 freea (ftarget_arg);
414 if (fsource_arg != NULL)
415 freea (fsource_arg);
416 freea (argv);
418 return err;
421 /* Try to compile a set of Java sources with javac.
422 Return a failure indicator (true upon error). */
423 static bool
424 compile_using_javac (const char * const *java_sources,
425 unsigned int java_sources_count,
426 bool source_option, const char *source_version,
427 bool target_option, const char *target_version,
428 const char *directory,
429 bool optimize, bool debug,
430 bool verbose, bool null_stderr)
432 bool err;
433 unsigned int argc;
434 char **argv;
435 char **argp;
436 int exitstatus;
437 unsigned int i;
439 argc =
440 1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
441 + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
442 argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
444 argp = argv;
445 *argp++ = "javac";
446 if (source_option)
448 *argp++ = "-source";
449 *argp++ = (char *) source_version;
451 if (target_option)
453 *argp++ = "-target";
454 *argp++ = (char *) target_version;
456 if (optimize)
457 *argp++ = "-O";
458 if (debug)
459 *argp++ = "-g";
460 if (directory != NULL)
462 *argp++ = "-d";
463 *argp++ = (char *) directory;
465 for (i = 0; i < java_sources_count; i++)
466 *argp++ = (char *) java_sources[i];
467 *argp = NULL;
468 /* Ensure argv length was correctly calculated. */
469 if (argp - argv != argc)
470 abort ();
472 if (verbose)
474 char *command = shell_quote_argv (argv);
475 printf ("%s\n", command);
476 free (command);
479 exitstatus = execute ("javac", "javac", argv, false, false, false,
480 null_stderr, true, true, NULL);
481 err = (exitstatus != 0);
483 freea (argv);
485 return err;
488 /* Try to compile a set of Java sources with jikes.
489 Return a failure indicator (true upon error). */
490 static bool
491 compile_using_jikes (const char * const *java_sources,
492 unsigned int java_sources_count,
493 const char *directory,
494 bool optimize, bool debug,
495 bool verbose, bool null_stderr)
497 bool err;
498 unsigned int argc;
499 char **argv;
500 char **argp;
501 int exitstatus;
502 unsigned int i;
504 argc =
505 1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
506 + java_sources_count;
507 argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
509 argp = argv;
510 *argp++ = "jikes";
511 if (optimize)
512 *argp++ = "-O";
513 if (debug)
514 *argp++ = "-g";
515 if (directory != NULL)
517 *argp++ = "-d";
518 *argp++ = (char *) directory;
520 for (i = 0; i < java_sources_count; i++)
521 *argp++ = (char *) java_sources[i];
522 *argp = NULL;
523 /* Ensure argv length was correctly calculated. */
524 if (argp - argv != argc)
525 abort ();
527 if (verbose)
529 char *command = shell_quote_argv (argv);
530 printf ("%s\n", command);
531 free (command);
534 exitstatus = execute ("jikes", "jikes", argv, false, false, false,
535 null_stderr, true, true, NULL);
536 err = (exitstatus != 0);
538 freea (argv);
540 return err;
543 /* ====================== Usability test subroutines ====================== */
545 /* Write a given contents to a temporary file.
546 FILE_NAME is the name of a file inside TMPDIR that is known not to exist
547 yet.
548 Return a failure indicator (true upon error). */
549 static bool
550 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
551 const char *contents)
553 FILE *fp;
555 register_temp_file (tmpdir, file_name);
556 fp = fopen_temp (file_name, "w");
557 if (fp == NULL)
559 error (0, errno, _("failed to create \"%s\""), file_name);
560 unregister_temp_file (tmpdir, file_name);
561 return true;
563 fputs (contents, fp);
564 if (fwriteerror_temp (fp))
566 error (0, errno, _("error while writing \"%s\" file"), file_name);
567 return true;
569 return false;
572 /* Return the class file version number of a class file on disk. */
573 static int
574 get_classfile_version (const char *compiled_file_name)
576 unsigned char header[8];
577 int fd;
579 /* Open the class file. */
580 fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
581 if (fd >= 0)
583 /* Read its first 8 bytes. */
584 if (safe_read (fd, header, 8) == 8)
586 /* Verify the class file signature. */
587 if (header[0] == 0xCA && header[1] == 0xFE
588 && header[2] == 0xBA && header[3] == 0xBE)
590 close (fd);
591 return header[7];
594 close (fd);
597 /* Could not get the class file version. Return a very large one. */
598 return INT_MAX;
601 /* Return true if $JAVAC is a version of gcj. */
602 static bool
603 is_envjavac_gcj (const char *javac)
605 static bool envjavac_tested;
606 static bool envjavac_gcj;
608 if (!envjavac_tested)
610 /* Test whether $JAVAC is gcj:
611 "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null" */
612 unsigned int command_length;
613 char *command;
614 char *argv[4];
615 pid_t child;
616 int fd[1];
617 FILE *fp;
618 char *line;
619 size_t linesize;
620 size_t linelen;
621 int exitstatus;
622 char *p;
624 /* Setup the command "$JAVAC --version". */
625 command_length = strlen (javac) + 1 + 9 + 1;
626 command = (char *) xmalloca (command_length);
627 p = command;
628 /* Don't shell_quote $JAVAC, because it may consist of a command
629 and options. */
630 memcpy (p, javac, strlen (javac));
631 p += strlen (javac);
632 memcpy (p, " --version", 1 + 9 + 1);
633 p += 1 + 9 + 1;
634 /* Ensure command_length was correctly calculated. */
635 if (p - command > command_length)
636 abort ();
638 /* Call $JAVAC --version 2>/dev/null. */
639 argv[0] = "/bin/sh";
640 argv[1] = "-c";
641 argv[2] = command;
642 argv[3] = NULL;
643 child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
644 false, fd);
645 if (child == -1)
646 goto failed;
648 /* Retrieve its result. */
649 fp = fdopen (fd[0], "r");
650 if (fp == NULL)
651 goto failed;
653 line = NULL; linesize = 0;
654 linelen = getline (&line, &linesize, fp);
655 if (linelen == (size_t)(-1))
657 fclose (fp);
658 goto failed;
660 /* It is safe to call c_strstr() instead of strstr() here; see the
661 comments in c-strstr.h. */
662 envjavac_gcj = (c_strstr (line, "gcj") != NULL);
664 fclose (fp);
666 /* Remove zombie process from process list, and retrieve exit status. */
667 exitstatus =
668 wait_subprocess (child, javac, true, true, true, false, NULL);
669 if (exitstatus != 0)
670 envjavac_gcj = false;
672 failed:
673 freea (command);
675 envjavac_tested = true;
678 return envjavac_gcj;
681 /* Return true if $JAVAC, known to be a version of gcj, is a version >= 4.3
682 of gcj. */
683 static bool
684 is_envjavac_gcj43 (const char *javac)
686 static bool envjavac_tested;
687 static bool envjavac_gcj43;
689 if (!envjavac_tested)
691 /* Test whether $JAVAC is gcj:
692 "$JAVAC --version 2>/dev/null | sed -e 's,^[^0-9]*,,' -e 1q \
693 | sed -e '/^4\.[012]/d' | grep '^[4-9]' >/dev/null" */
694 unsigned int command_length;
695 char *command;
696 char *argv[4];
697 pid_t child;
698 int fd[1];
699 FILE *fp;
700 char *line;
701 size_t linesize;
702 size_t linelen;
703 int exitstatus;
704 char *p;
706 /* Setup the command "$JAVAC --version". */
707 command_length = strlen (javac) + 1 + 9 + 1;
708 command = (char *) xmalloca (command_length);
709 p = command;
710 /* Don't shell_quote $JAVAC, because it may consist of a command
711 and options. */
712 memcpy (p, javac, strlen (javac));
713 p += strlen (javac);
714 memcpy (p, " --version", 1 + 9 + 1);
715 p += 1 + 9 + 1;
716 /* Ensure command_length was correctly calculated. */
717 if (p - command > command_length)
718 abort ();
720 /* Call $JAVAC --version 2>/dev/null. */
721 argv[0] = "/bin/sh";
722 argv[1] = "-c";
723 argv[2] = command;
724 argv[3] = NULL;
725 child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
726 false, fd);
727 if (child == -1)
728 goto failed;
730 /* Retrieve its result. */
731 fp = fdopen (fd[0], "r");
732 if (fp == NULL)
733 goto failed;
735 line = NULL; linesize = 0;
736 linelen = getline (&line, &linesize, fp);
737 if (linelen == (size_t)(-1))
739 fclose (fp);
740 goto failed;
742 p = line;
743 while (*p != '\0' && !(*p >= '0' && *p <= '9'))
744 p++;
745 envjavac_gcj43 =
746 !(*p == '4' && p[1] == '.' && p[2] >= '0' && p[2] <= '2')
747 && (*p >= '4' && *p <= '9');
749 fclose (fp);
751 /* Remove zombie process from process list, and retrieve exit status. */
752 exitstatus =
753 wait_subprocess (child, javac, true, true, true, false, NULL);
754 if (exitstatus != 0)
755 envjavac_gcj43 = false;
757 failed:
758 freea (command);
760 envjavac_tested = true;
763 return envjavac_gcj43;
766 /* Test whether $JAVAC, known to be a version of gcj >= 4.3, can be used, and
767 whether it needs a -fsource and/or -ftarget option.
768 Return a failure indicator (true upon error). */
769 static bool
770 is_envjavac_gcj43_usable (const char *javac,
771 const char *source_version,
772 const char *target_version,
773 bool *usablep,
774 bool *fsource_option_p, bool *ftarget_option_p)
776 /* The cache depends on the source_version and target_version. */
777 struct result_t
779 bool tested;
780 bool usable;
781 bool fsource_option;
782 bool ftarget_option;
784 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
785 struct result_t *resultp;
787 resultp = &result_cache[source_version_index (source_version)]
788 [target_version_index (target_version)];
789 if (!resultp->tested)
791 /* Try $JAVAC. */
792 struct temp_dir *tmpdir;
793 char *conftest_file_name;
794 char *compiled_file_name;
795 const char *java_sources[1];
796 struct stat statbuf;
798 tmpdir = create_temp_dir ("java", NULL, false);
799 if (tmpdir == NULL)
800 return true;
802 conftest_file_name =
803 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
804 if (write_temp_file (tmpdir, conftest_file_name,
805 get_goodcode_snippet (source_version)))
807 free (conftest_file_name);
808 cleanup_temp_dir (tmpdir);
809 return true;
812 compiled_file_name =
813 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
814 register_temp_file (tmpdir, compiled_file_name);
816 java_sources[0] = conftest_file_name;
817 if (!compile_using_envjavac (javac,
818 java_sources, 1, tmpdir->dir_name,
819 false, false, false, true)
820 && stat (compiled_file_name, &statbuf) >= 0
821 && get_classfile_version (compiled_file_name)
822 <= corresponding_classfile_version (target_version))
824 /* $JAVAC compiled conftest.java successfully. */
825 /* Try adding -fsource option if it is useful. */
826 char *javac_source =
827 xasprintf ("%s -fsource=%s", javac, source_version);
829 unlink (compiled_file_name);
831 java_sources[0] = conftest_file_name;
832 if (!compile_using_envjavac (javac_source,
833 java_sources, 1, tmpdir->dir_name,
834 false, false, false, true)
835 && stat (compiled_file_name, &statbuf) >= 0
836 && get_classfile_version (compiled_file_name)
837 <= corresponding_classfile_version (target_version))
839 const char *failcode = get_failcode_snippet (source_version);
841 if (failcode != NULL)
843 free (compiled_file_name);
844 free (conftest_file_name);
846 conftest_file_name =
847 xconcatenated_filename (tmpdir->dir_name,
848 "conftestfail.java",
849 NULL);
850 if (write_temp_file (tmpdir, conftest_file_name, failcode))
852 free (conftest_file_name);
853 free (javac_source);
854 cleanup_temp_dir (tmpdir);
855 return true;
858 compiled_file_name =
859 xconcatenated_filename (tmpdir->dir_name,
860 "conftestfail.class",
861 NULL);
862 register_temp_file (tmpdir, compiled_file_name);
864 java_sources[0] = conftest_file_name;
865 if (!compile_using_envjavac (javac,
866 java_sources, 1,
867 tmpdir->dir_name,
868 false, false, false, true)
869 && stat (compiled_file_name, &statbuf) >= 0)
871 unlink (compiled_file_name);
873 java_sources[0] = conftest_file_name;
874 if (compile_using_envjavac (javac_source,
875 java_sources, 1,
876 tmpdir->dir_name,
877 false, false, false, true))
878 /* $JAVAC compiled conftestfail.java successfully, and
879 "$JAVAC -fsource=$source_version" rejects it. So
880 the -fsource option is useful. */
881 resultp->fsource_option = true;
886 free (javac_source);
888 resultp->usable = true;
890 else
892 /* Try with -fsource and -ftarget options. */
893 char *javac_target =
894 xasprintf ("%s -fsource=%s -ftarget=%s",
895 javac, source_version, target_version);
897 unlink (compiled_file_name);
899 java_sources[0] = conftest_file_name;
900 if (!compile_using_envjavac (javac_target,
901 java_sources, 1, tmpdir->dir_name,
902 false, false, false, true)
903 && stat (compiled_file_name, &statbuf) >= 0
904 && get_classfile_version (compiled_file_name)
905 <= corresponding_classfile_version (target_version))
907 /* "$JAVAC -fsource $source_version -ftarget $target_version"
908 compiled conftest.java successfully. */
909 resultp->fsource_option = true;
910 resultp->ftarget_option = true;
911 resultp->usable = true;
914 free (javac_target);
917 free (compiled_file_name);
918 free (conftest_file_name);
920 resultp->tested = true;
923 *usablep = resultp->usable;
924 *fsource_option_p = resultp->fsource_option;
925 *ftarget_option_p = resultp->ftarget_option;
926 return false;
929 /* Test whether $JAVAC, known to be a version of gcj < 4.3, can be used for
930 compiling with target_version = 1.4 and source_version = 1.4.
931 Return a failure indicator (true upon error). */
932 static bool
933 is_envjavac_oldgcj_14_14_usable (const char *javac, bool *usablep)
935 static bool envjavac_tested;
936 static bool envjavac_usable;
938 if (!envjavac_tested)
940 /* Try $JAVAC. */
941 struct temp_dir *tmpdir;
942 char *conftest_file_name;
943 char *compiled_file_name;
944 const char *java_sources[1];
945 struct stat statbuf;
947 tmpdir = create_temp_dir ("java", NULL, false);
948 if (tmpdir == NULL)
949 return true;
951 conftest_file_name =
952 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
953 if (write_temp_file (tmpdir, conftest_file_name,
954 get_goodcode_snippet ("1.4")))
956 free (conftest_file_name);
957 cleanup_temp_dir (tmpdir);
958 return true;
961 compiled_file_name =
962 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
963 register_temp_file (tmpdir, compiled_file_name);
965 java_sources[0] = conftest_file_name;
966 if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
967 false, false, false, true)
968 && stat (compiled_file_name, &statbuf) >= 0)
969 /* Compilation succeeded. */
970 envjavac_usable = true;
972 free (compiled_file_name);
973 free (conftest_file_name);
975 cleanup_temp_dir (tmpdir);
977 envjavac_tested = true;
980 *usablep = envjavac_usable;
981 return false;
984 /* Test whether $JAVAC, known to be a version of gcj < 4.3, can be used for
985 compiling with target_version = 1.4 and source_version = 1.3.
986 Return a failure indicator (true upon error). */
987 static bool
988 is_envjavac_oldgcj_14_13_usable (const char *javac,
989 bool *usablep, bool *need_no_assert_option_p)
991 static bool envjavac_tested;
992 static bool envjavac_usable;
993 static bool envjavac_need_no_assert_option;
995 if (!envjavac_tested)
997 /* Try $JAVAC and "$JAVAC -fno-assert". But add -fno-assert only if
998 it makes a difference. (It could already be part of $JAVAC.) */
999 struct temp_dir *tmpdir;
1000 char *conftest_file_name;
1001 char *compiled_file_name;
1002 const char *java_sources[1];
1003 struct stat statbuf;
1004 bool javac_works;
1005 char *javac_noassert;
1006 bool javac_noassert_works;
1008 tmpdir = create_temp_dir ("java", NULL, false);
1009 if (tmpdir == NULL)
1010 return true;
1012 conftest_file_name =
1013 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1014 if (write_temp_file (tmpdir, conftest_file_name,
1015 get_goodcode_snippet ("1.3")))
1017 free (conftest_file_name);
1018 cleanup_temp_dir (tmpdir);
1019 return true;
1022 compiled_file_name =
1023 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1024 register_temp_file (tmpdir, compiled_file_name);
1026 java_sources[0] = conftest_file_name;
1027 if (!compile_using_envjavac (javac,
1028 java_sources, 1, tmpdir->dir_name,
1029 false, false, false, true)
1030 && stat (compiled_file_name, &statbuf) >= 0)
1031 /* Compilation succeeded. */
1032 javac_works = true;
1033 else
1034 javac_works = false;
1036 unlink (compiled_file_name);
1038 javac_noassert = xasprintf ("%s -fno-assert", javac);
1040 java_sources[0] = conftest_file_name;
1041 if (!compile_using_envjavac (javac_noassert,
1042 java_sources, 1, tmpdir->dir_name,
1043 false, false, false, true)
1044 && stat (compiled_file_name, &statbuf) >= 0)
1045 /* Compilation succeeded. */
1046 javac_noassert_works = true;
1047 else
1048 javac_noassert_works = false;
1050 free (compiled_file_name);
1051 free (conftest_file_name);
1053 if (javac_works && javac_noassert_works)
1055 conftest_file_name =
1056 xconcatenated_filename (tmpdir->dir_name, "conftestfail.java",
1057 NULL);
1058 if (write_temp_file (tmpdir, conftest_file_name,
1059 get_failcode_snippet ("1.3")))
1061 free (conftest_file_name);
1062 free (javac_noassert);
1063 cleanup_temp_dir (tmpdir);
1064 return true;
1067 compiled_file_name =
1068 xconcatenated_filename (tmpdir->dir_name, "conftestfail.class",
1069 NULL);
1070 register_temp_file (tmpdir, compiled_file_name);
1072 java_sources[0] = conftest_file_name;
1073 if (!compile_using_envjavac (javac,
1074 java_sources, 1, tmpdir->dir_name,
1075 false, false, false, true)
1076 && stat (compiled_file_name, &statbuf) >= 0)
1078 /* Compilation succeeded. */
1079 unlink (compiled_file_name);
1081 java_sources[0] = conftest_file_name;
1082 if (!(!compile_using_envjavac (javac_noassert,
1083 java_sources, 1, tmpdir->dir_name,
1084 false, false, false, true)
1085 && stat (compiled_file_name, &statbuf) >= 0))
1086 /* Compilation failed. */
1087 /* "$JAVAC -fno-assert" works better than $JAVAC. */
1088 javac_works = true;
1091 free (compiled_file_name);
1092 free (conftest_file_name);
1095 cleanup_temp_dir (tmpdir);
1097 if (javac_works)
1099 envjavac_usable = true;
1100 envjavac_need_no_assert_option = false;
1102 else if (javac_noassert_works)
1104 envjavac_usable = true;
1105 envjavac_need_no_assert_option = true;
1108 envjavac_tested = true;
1111 *usablep = envjavac_usable;
1112 *need_no_assert_option_p = envjavac_need_no_assert_option;
1113 return false;
1116 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
1117 whether it needs a -source and/or -target option.
1118 Return a failure indicator (true upon error). */
1119 static bool
1120 is_envjavac_nongcj_usable (const char *javac,
1121 const char *source_version,
1122 const char *source_version_for_javac,
1123 const char *target_version,
1124 bool *usablep,
1125 bool *source_option_p, bool *target_option_p)
1127 /* The cache depends on the source_version and target_version. */
1128 struct result_t
1130 bool tested;
1131 bool usable;
1132 bool source_option;
1133 bool target_option;
1135 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1136 struct result_t *resultp;
1138 resultp = &result_cache[source_version_index (source_version)]
1139 [target_version_index (target_version)];
1140 if (!resultp->tested)
1142 /* Try $JAVAC. */
1143 struct temp_dir *tmpdir;
1144 char *conftest_file_name;
1145 char *compiled_file_name;
1146 const char *java_sources[1];
1147 struct stat statbuf;
1149 tmpdir = create_temp_dir ("java", NULL, false);
1150 if (tmpdir == NULL)
1151 return true;
1153 conftest_file_name =
1154 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1155 if (write_temp_file (tmpdir, conftest_file_name,
1156 get_goodcode_snippet (source_version)))
1158 free (conftest_file_name);
1159 cleanup_temp_dir (tmpdir);
1160 return true;
1163 compiled_file_name =
1164 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1165 register_temp_file (tmpdir, compiled_file_name);
1167 java_sources[0] = conftest_file_name;
1168 if (!compile_using_envjavac (javac,
1169 java_sources, 1, tmpdir->dir_name,
1170 false, false, false, true)
1171 && stat (compiled_file_name, &statbuf) >= 0
1172 && get_classfile_version (compiled_file_name)
1173 <= corresponding_classfile_version (target_version))
1175 /* $JAVAC compiled conftest.java successfully. */
1176 /* Try adding -source option if it is useful. */
1177 char *javac_source =
1178 xasprintf ("%s -source %s", javac, source_version_for_javac);
1180 unlink (compiled_file_name);
1182 java_sources[0] = conftest_file_name;
1183 if (!compile_using_envjavac (javac_source,
1184 java_sources, 1, tmpdir->dir_name,
1185 false, false, false, true)
1186 && stat (compiled_file_name, &statbuf) >= 0
1187 && get_classfile_version (compiled_file_name)
1188 <= corresponding_classfile_version (target_version))
1190 const char *failcode = get_failcode_snippet (source_version);
1192 if (failcode != NULL)
1194 free (compiled_file_name);
1195 free (conftest_file_name);
1197 conftest_file_name =
1198 xconcatenated_filename (tmpdir->dir_name,
1199 "conftestfail.java",
1200 NULL);
1201 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1203 free (conftest_file_name);
1204 free (javac_source);
1205 cleanup_temp_dir (tmpdir);
1206 return true;
1209 compiled_file_name =
1210 xconcatenated_filename (tmpdir->dir_name,
1211 "conftestfail.class",
1212 NULL);
1213 register_temp_file (tmpdir, compiled_file_name);
1215 java_sources[0] = conftest_file_name;
1216 if (!compile_using_envjavac (javac,
1217 java_sources, 1,
1218 tmpdir->dir_name,
1219 false, false, false, true)
1220 && stat (compiled_file_name, &statbuf) >= 0)
1222 unlink (compiled_file_name);
1224 java_sources[0] = conftest_file_name;
1225 if (compile_using_envjavac (javac_source,
1226 java_sources, 1,
1227 tmpdir->dir_name,
1228 false, false, false, true))
1229 /* $JAVAC compiled conftestfail.java successfully, and
1230 "$JAVAC -source $source_version_for_javac" rejects it.
1231 So the -source option is useful. */
1232 resultp->source_option = true;
1237 free (javac_source);
1239 resultp->usable = true;
1241 else
1243 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1244 option but no -source option.) */
1245 char *javac_target =
1246 xasprintf ("%s -target %s", javac, target_version);
1248 unlink (compiled_file_name);
1250 java_sources[0] = conftest_file_name;
1251 if (!compile_using_envjavac (javac_target,
1252 java_sources, 1, tmpdir->dir_name,
1253 false, false, false, true)
1254 && stat (compiled_file_name, &statbuf) >= 0
1255 && get_classfile_version (compiled_file_name)
1256 <= corresponding_classfile_version (target_version))
1258 /* "$JAVAC -target $target_version" compiled conftest.java
1259 successfully. */
1260 /* Try adding -source option if it is useful. */
1261 char *javac_target_source =
1262 xasprintf ("%s -source %s", javac_target, source_version_for_javac);
1264 unlink (compiled_file_name);
1266 java_sources[0] = conftest_file_name;
1267 if (!compile_using_envjavac (javac_target_source,
1268 java_sources, 1, tmpdir->dir_name,
1269 false, false, false, true)
1270 && stat (compiled_file_name, &statbuf) >= 0
1271 && get_classfile_version (compiled_file_name)
1272 <= corresponding_classfile_version (target_version))
1274 const char *failcode = get_failcode_snippet (source_version);
1276 if (failcode != NULL)
1278 free (compiled_file_name);
1279 free (conftest_file_name);
1281 conftest_file_name =
1282 xconcatenated_filename (tmpdir->dir_name,
1283 "conftestfail.java",
1284 NULL);
1285 if (write_temp_file (tmpdir, conftest_file_name,
1286 failcode))
1288 free (conftest_file_name);
1289 free (javac_target_source);
1290 free (javac_target);
1291 cleanup_temp_dir (tmpdir);
1292 return true;
1295 compiled_file_name =
1296 xconcatenated_filename (tmpdir->dir_name,
1297 "conftestfail.class",
1298 NULL);
1299 register_temp_file (tmpdir, compiled_file_name);
1301 java_sources[0] = conftest_file_name;
1302 if (!compile_using_envjavac (javac_target,
1303 java_sources, 1,
1304 tmpdir->dir_name,
1305 false, false, false, true)
1306 && stat (compiled_file_name, &statbuf) >= 0)
1308 unlink (compiled_file_name);
1310 java_sources[0] = conftest_file_name;
1311 if (compile_using_envjavac (javac_target_source,
1312 java_sources, 1,
1313 tmpdir->dir_name,
1314 false, false, false,
1315 true))
1316 /* "$JAVAC -target $target_version" compiled
1317 conftestfail.java successfully, and
1318 "$JAVAC -target $target_version -source $source_version_for_javac"
1319 rejects it. So the -source option is useful. */
1320 resultp->source_option = true;
1325 free (javac_target_source);
1327 resultp->target_option = true;
1328 resultp->usable = true;
1330 else
1332 /* Maybe this -target option requires a -source option? Try with
1333 -target and -source options. (Supported by Sun javac 1.4 and
1334 higher.) */
1335 char *javac_target_source =
1336 xasprintf ("%s -source %s", javac_target, source_version_for_javac);
1338 unlink (compiled_file_name);
1340 java_sources[0] = conftest_file_name;
1341 if (!compile_using_envjavac (javac_target_source,
1342 java_sources, 1, tmpdir->dir_name,
1343 false, false, false, true)
1344 && stat (compiled_file_name, &statbuf) >= 0
1345 && get_classfile_version (compiled_file_name)
1346 <= corresponding_classfile_version (target_version))
1348 /* "$JAVAC -target $target_version -source $source_version_for_javac"
1349 compiled conftest.java successfully. */
1350 resultp->source_option = true;
1351 resultp->target_option = true;
1352 resultp->usable = true;
1355 free (javac_target_source);
1358 free (javac_target);
1361 free (compiled_file_name);
1362 free (conftest_file_name);
1364 resultp->tested = true;
1367 *usablep = resultp->usable;
1368 *source_option_p = resultp->source_option;
1369 *target_option_p = resultp->target_option;
1370 return false;
1373 static bool
1374 is_gcj_present (void)
1376 static bool gcj_tested;
1377 static bool gcj_present;
1379 if (!gcj_tested)
1381 /* Test for presence of gcj:
1382 "gcj --version 2> /dev/null | \
1383 sed -e 's,^[^0-9]*,,' -e 1q | \
1384 sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null" */
1385 char *argv[3];
1386 pid_t child;
1387 int fd[1];
1388 int exitstatus;
1390 argv[0] = "gcj";
1391 argv[1] = "--version";
1392 argv[2] = NULL;
1393 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1394 false, fd);
1395 gcj_present = false;
1396 if (child != -1)
1398 /* Read the subprocess output, drop all lines except the first,
1399 drop all characters before the first digit, and test whether
1400 the remaining string starts with a digit >= 3, but not with
1401 "3.0" or "3.1". */
1402 char c[3];
1403 size_t count = 0;
1405 while (safe_read (fd[0], &c[count], 1) > 0)
1407 if (c[count] == '\n')
1408 break;
1409 if (count == 0)
1411 if (!(c[0] >= '0' && c[0] <= '9'))
1412 continue;
1413 gcj_present = (c[0] >= '3');
1415 count++;
1416 if (count == 3)
1418 if (c[0] == '3' && c[1] == '.'
1419 && (c[2] == '0' || c[2] == '1'))
1420 gcj_present = false;
1421 break;
1424 while (safe_read (fd[0], &c[0], 1) > 0)
1427 close (fd[0]);
1429 /* Remove zombie process from process list, and retrieve exit
1430 status. */
1431 exitstatus =
1432 wait_subprocess (child, "gcj", false, true, true, false, NULL);
1433 if (exitstatus != 0)
1434 gcj_present = false;
1437 if (gcj_present)
1439 /* See if libgcj.jar is well installed. */
1440 struct temp_dir *tmpdir;
1442 tmpdir = create_temp_dir ("java", NULL, false);
1443 if (tmpdir == NULL)
1444 gcj_present = false;
1445 else
1447 char *conftest_file_name;
1449 conftest_file_name =
1450 xconcatenated_filename (tmpdir->dir_name, "conftestlib.java",
1451 NULL);
1452 if (write_temp_file (tmpdir, conftest_file_name,
1453 "public class conftestlib {\n"
1454 " public static void main (String[] args) {\n"
1455 " }\n"
1456 "}\n"))
1457 gcj_present = false;
1458 else
1460 char *compiled_file_name;
1461 const char *java_sources[1];
1463 compiled_file_name =
1464 xconcatenated_filename (tmpdir->dir_name,
1465 "conftestlib.class",
1466 NULL);
1467 register_temp_file (tmpdir, compiled_file_name);
1469 java_sources[0] = conftest_file_name;
1470 if (compile_using_gcj (java_sources, 1, false,
1471 false, NULL, false, NULL,
1472 tmpdir->dir_name,
1473 false, false, false, true))
1474 gcj_present = false;
1476 free (compiled_file_name);
1478 free (conftest_file_name);
1480 cleanup_temp_dir (tmpdir);
1483 gcj_tested = true;
1486 return gcj_present;
1489 static bool
1490 is_gcj_43 (void)
1492 static bool gcj_tested;
1493 static bool gcj_43;
1495 if (!gcj_tested)
1497 /* Test for presence of gcj:
1498 "gcj --version 2> /dev/null | \
1499 sed -e 's,^[^0-9]*,,' -e 1q | \
1500 sed -e '/^4\.[012]/d' | grep '^[4-9]'" */
1501 char *argv[3];
1502 pid_t child;
1503 int fd[1];
1504 int exitstatus;
1506 argv[0] = "gcj";
1507 argv[1] = "--version";
1508 argv[2] = NULL;
1509 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1510 false, fd);
1511 gcj_43 = false;
1512 if (child != -1)
1514 /* Read the subprocess output, drop all lines except the first,
1515 drop all characters before the first digit, and test whether
1516 the remaining string starts with a digit >= 4, but not with
1517 "4.0" or "4.1" or "4.2". */
1518 char c[3];
1519 size_t count = 0;
1521 while (safe_read (fd[0], &c[count], 1) > 0)
1523 if (c[count] == '\n')
1524 break;
1525 if (count == 0)
1527 if (!(c[0] >= '0' && c[0] <= '9'))
1528 continue;
1529 gcj_43 = (c[0] >= '4');
1531 count++;
1532 if (count == 3)
1534 if (c[0] == '4' && c[1] == '.' && c[2] >= '0' && c[2] <= '2')
1535 gcj_43 = false;
1536 break;
1539 while (safe_read (fd[0], &c[0], 1) > 0)
1542 close (fd[0]);
1544 /* Remove zombie process from process list, and retrieve exit
1545 status. */
1546 exitstatus =
1547 wait_subprocess (child, "gcj", false, true, true, false, NULL);
1548 if (exitstatus != 0)
1549 gcj_43 = false;
1552 gcj_tested = true;
1555 return gcj_43;
1558 /* Test whether gcj >= 4.3 can be used, and whether it needs a -fsource and/or
1559 -ftarget option.
1560 Return a failure indicator (true upon error). */
1561 static bool
1562 is_gcj43_usable (const char *source_version,
1563 const char *target_version,
1564 bool *usablep,
1565 bool *fsource_option_p, bool *ftarget_option_p)
1567 /* The cache depends on the source_version and target_version. */
1568 struct result_t
1570 bool tested;
1571 bool usable;
1572 bool fsource_option;
1573 bool ftarget_option;
1575 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1576 struct result_t *resultp;
1578 resultp = &result_cache[source_version_index (source_version)]
1579 [target_version_index (target_version)];
1580 if (!resultp->tested)
1582 /* Try gcj. */
1583 struct temp_dir *tmpdir;
1584 char *conftest_file_name;
1585 char *compiled_file_name;
1586 const char *java_sources[1];
1587 struct stat statbuf;
1589 tmpdir = create_temp_dir ("java", NULL, false);
1590 if (tmpdir == NULL)
1591 return true;
1593 conftest_file_name =
1594 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1595 if (write_temp_file (tmpdir, conftest_file_name,
1596 get_goodcode_snippet (source_version)))
1598 free (conftest_file_name);
1599 cleanup_temp_dir (tmpdir);
1600 return true;
1603 compiled_file_name =
1604 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1605 register_temp_file (tmpdir, compiled_file_name);
1607 java_sources[0] = conftest_file_name;
1608 if (!compile_using_gcj (java_sources, 1, false, false, NULL, false, NULL,
1609 tmpdir->dir_name, false, false, false, true)
1610 && stat (compiled_file_name, &statbuf) >= 0
1611 && get_classfile_version (compiled_file_name)
1612 <= corresponding_classfile_version (target_version))
1614 /* gcj compiled conftest.java successfully. */
1615 /* Try adding -fsource option if it is useful. */
1616 unlink (compiled_file_name);
1618 java_sources[0] = conftest_file_name;
1619 if (!compile_using_gcj (java_sources, 1,
1620 false, true, source_version, false, NULL,
1621 tmpdir->dir_name, false, false, false, true)
1622 && stat (compiled_file_name, &statbuf) >= 0
1623 && get_classfile_version (compiled_file_name)
1624 <= corresponding_classfile_version (target_version))
1626 const char *failcode = get_failcode_snippet (source_version);
1628 if (failcode != NULL)
1630 free (compiled_file_name);
1631 free (conftest_file_name);
1633 conftest_file_name =
1634 xconcatenated_filename (tmpdir->dir_name,
1635 "conftestfail.java",
1636 NULL);
1637 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1639 free (conftest_file_name);
1640 cleanup_temp_dir (tmpdir);
1641 return true;
1644 compiled_file_name =
1645 xconcatenated_filename (tmpdir->dir_name,
1646 "conftestfail.class",
1647 NULL);
1648 register_temp_file (tmpdir, compiled_file_name);
1650 java_sources[0] = conftest_file_name;
1651 if (!compile_using_gcj (java_sources, 1,
1652 false, false, NULL, false, NULL,
1653 tmpdir->dir_name,
1654 false, false, false, true)
1655 && stat (compiled_file_name, &statbuf) >= 0)
1657 unlink (compiled_file_name);
1659 java_sources[0] = conftest_file_name;
1660 if (compile_using_gcj (java_sources, 1,
1661 false, true, source_version,
1662 false, NULL,
1663 tmpdir->dir_name,
1664 false, false, false, true))
1665 /* gcj compiled conftestfail.java successfully, and
1666 "gcj -fsource=$source_version" rejects it. So
1667 the -fsource option is useful. */
1668 resultp->fsource_option = true;
1673 resultp->usable = true;
1675 else
1677 /* Try with -fsource and -ftarget options. */
1678 unlink (compiled_file_name);
1680 java_sources[0] = conftest_file_name;
1681 if (!compile_using_gcj (java_sources, 1,
1682 false, true, source_version,
1683 true, target_version,
1684 tmpdir->dir_name,
1685 false, false, false, true)
1686 && stat (compiled_file_name, &statbuf) >= 0
1687 && get_classfile_version (compiled_file_name)
1688 <= corresponding_classfile_version (target_version))
1690 /* "gcj -fsource $source_version -ftarget $target_version"
1691 compiled conftest.java successfully. */
1692 resultp->fsource_option = true;
1693 resultp->ftarget_option = true;
1694 resultp->usable = true;
1698 free (compiled_file_name);
1699 free (conftest_file_name);
1701 resultp->tested = true;
1704 *usablep = resultp->usable;
1705 *fsource_option_p = resultp->fsource_option;
1706 *ftarget_option_p = resultp->ftarget_option;
1707 return false;
1710 /* Test whether gcj < 4.3 can be used for compiling with target_version = 1.4
1711 and source_version = 1.4.
1712 Return a failure indicator (true upon error). */
1713 static bool
1714 is_oldgcj_14_14_usable (bool *usablep)
1716 static bool gcj_tested;
1717 static bool gcj_usable;
1719 if (!gcj_tested)
1721 /* Try gcj. */
1722 struct temp_dir *tmpdir;
1723 char *conftest_file_name;
1724 char *compiled_file_name;
1725 const char *java_sources[1];
1726 struct stat statbuf;
1728 tmpdir = create_temp_dir ("java", NULL, false);
1729 if (tmpdir == NULL)
1730 return true;
1732 conftest_file_name =
1733 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1734 if (write_temp_file (tmpdir, conftest_file_name,
1735 get_goodcode_snippet ("1.4")))
1737 free (conftest_file_name);
1738 cleanup_temp_dir (tmpdir);
1739 return true;
1742 compiled_file_name =
1743 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1744 register_temp_file (tmpdir, compiled_file_name);
1746 java_sources[0] = conftest_file_name;
1747 if (!compile_using_gcj (java_sources, 1, false, false, NULL, false, NULL,
1748 tmpdir->dir_name, false, false, false, true)
1749 && stat (compiled_file_name, &statbuf) >= 0)
1750 /* Compilation succeeded. */
1751 gcj_usable = true;
1753 free (compiled_file_name);
1754 free (conftest_file_name);
1756 cleanup_temp_dir (tmpdir);
1758 gcj_tested = true;
1761 *usablep = gcj_usable;
1762 return false;
1765 /* Test whether gcj < 4.3 can be used for compiling with target_version = 1.4
1766 and source_version = 1.3.
1767 Return a failure indicator (true upon error). */
1768 static bool
1769 is_oldgcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1771 static bool gcj_tested;
1772 static bool gcj_usable;
1773 static bool gcj_need_no_assert_option;
1775 if (!gcj_tested)
1777 /* Try gcj and "gcj -fno-assert". But add -fno-assert only if
1778 it works (not gcj < 3.3). */
1779 struct temp_dir *tmpdir;
1780 char *conftest_file_name;
1781 char *compiled_file_name;
1782 const char *java_sources[1];
1783 struct stat statbuf;
1785 tmpdir = create_temp_dir ("java", NULL, false);
1786 if (tmpdir == NULL)
1787 return true;
1789 conftest_file_name =
1790 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1791 if (write_temp_file (tmpdir, conftest_file_name,
1792 get_goodcode_snippet ("1.3")))
1794 free (conftest_file_name);
1795 cleanup_temp_dir (tmpdir);
1796 return true;
1799 compiled_file_name =
1800 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1801 register_temp_file (tmpdir, compiled_file_name);
1803 java_sources[0] = conftest_file_name;
1804 if (!compile_using_gcj (java_sources, 1, true, false, NULL, false, NULL,
1805 tmpdir->dir_name, false, false, false, true)
1806 && stat (compiled_file_name, &statbuf) >= 0)
1807 /* Compilation succeeded. */
1809 gcj_usable = true;
1810 gcj_need_no_assert_option = true;
1812 else
1814 unlink (compiled_file_name);
1816 java_sources[0] = conftest_file_name;
1817 if (!compile_using_gcj (java_sources, 1, false,
1818 false, NULL, false, NULL,
1819 tmpdir->dir_name, false, false, false, true)
1820 && stat (compiled_file_name, &statbuf) >= 0)
1821 /* Compilation succeeded. */
1823 gcj_usable = true;
1824 gcj_need_no_assert_option = false;
1828 free (compiled_file_name);
1829 free (conftest_file_name);
1831 cleanup_temp_dir (tmpdir);
1833 gcj_tested = true;
1836 *usablep = gcj_usable;
1837 *need_no_assert_option_p = gcj_need_no_assert_option;
1838 return false;
1841 static bool
1842 is_javac_present (void)
1844 static bool javac_tested;
1845 static bool javac_present;
1847 if (!javac_tested)
1849 /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
1850 char *argv[2];
1851 int exitstatus;
1853 argv[0] = "javac";
1854 argv[1] = NULL;
1855 exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1856 true, false, NULL);
1857 javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1858 javac_tested = true;
1861 return javac_present;
1864 /* Test whether javac can be used and whether it needs a -source and/or
1865 -target option.
1866 Return a failure indicator (true upon error). */
1867 static bool
1868 is_javac_usable (const char *source_version,
1869 const char *source_version_for_javac,
1870 const char *target_version,
1871 bool *usablep, bool *source_option_p, bool *target_option_p)
1873 /* The cache depends on the source_version and target_version. */
1874 struct result_t
1876 bool tested;
1877 bool usable;
1878 bool source_option;
1879 bool target_option;
1881 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1882 struct result_t *resultp;
1884 resultp = &result_cache[source_version_index (source_version)]
1885 [target_version_index (target_version)];
1886 if (!resultp->tested)
1888 /* Try javac. */
1889 struct temp_dir *tmpdir;
1890 char *conftest_file_name;
1891 char *compiled_file_name;
1892 const char *java_sources[1];
1893 struct stat statbuf;
1895 tmpdir = create_temp_dir ("java", NULL, false);
1896 if (tmpdir == NULL)
1897 return true;
1899 conftest_file_name =
1900 xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL);
1901 if (write_temp_file (tmpdir, conftest_file_name,
1902 get_goodcode_snippet (source_version)))
1904 free (conftest_file_name);
1905 cleanup_temp_dir (tmpdir);
1906 return true;
1909 compiled_file_name =
1910 xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL);
1911 register_temp_file (tmpdir, compiled_file_name);
1913 java_sources[0] = conftest_file_name;
1914 if (!compile_using_javac (java_sources, 1,
1915 false, source_version_for_javac,
1916 false, target_version,
1917 tmpdir->dir_name, false, false, false, true)
1918 && stat (compiled_file_name, &statbuf) >= 0
1919 && get_classfile_version (compiled_file_name)
1920 <= corresponding_classfile_version (target_version))
1922 /* javac compiled conftest.java successfully. */
1923 /* Try adding -source option if it is useful. */
1924 unlink (compiled_file_name);
1926 java_sources[0] = conftest_file_name;
1927 if (!compile_using_javac (java_sources, 1,
1928 true, source_version_for_javac,
1929 false, target_version,
1930 tmpdir->dir_name, false, false, false, true)
1931 && stat (compiled_file_name, &statbuf) >= 0
1932 && get_classfile_version (compiled_file_name)
1933 <= corresponding_classfile_version (target_version))
1935 const char *failcode = get_failcode_snippet (source_version);
1937 if (failcode != NULL)
1939 free (compiled_file_name);
1940 free (conftest_file_name);
1942 conftest_file_name =
1943 xconcatenated_filename (tmpdir->dir_name,
1944 "conftestfail.java",
1945 NULL);
1946 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1948 free (conftest_file_name);
1949 cleanup_temp_dir (tmpdir);
1950 return true;
1953 compiled_file_name =
1954 xconcatenated_filename (tmpdir->dir_name,
1955 "conftestfail.class",
1956 NULL);
1957 register_temp_file (tmpdir, compiled_file_name);
1959 java_sources[0] = conftest_file_name;
1960 if (!compile_using_javac (java_sources, 1,
1961 false, source_version_for_javac,
1962 false, target_version,
1963 tmpdir->dir_name,
1964 false, false, false, true)
1965 && stat (compiled_file_name, &statbuf) >= 0)
1967 unlink (compiled_file_name);
1969 java_sources[0] = conftest_file_name;
1970 if (compile_using_javac (java_sources, 1,
1971 true, source_version_for_javac,
1972 false, target_version,
1973 tmpdir->dir_name,
1974 false, false, false, true))
1975 /* javac compiled conftestfail.java successfully, and
1976 "javac -source $source_version_for_javac" rejects it.
1977 So the -source option is useful. */
1978 resultp->source_option = true;
1983 resultp->usable = true;
1985 else
1987 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1988 option but no -source option.) */
1989 unlink (compiled_file_name);
1991 java_sources[0] = conftest_file_name;
1992 if (!compile_using_javac (java_sources, 1,
1993 false, source_version_for_javac,
1994 true, target_version,
1995 tmpdir->dir_name,
1996 false, false, false, true)
1997 && stat (compiled_file_name, &statbuf) >= 0
1998 && get_classfile_version (compiled_file_name)
1999 <= corresponding_classfile_version (target_version))
2001 /* "javac -target $target_version" compiled conftest.java
2002 successfully. */
2003 /* Try adding -source option if it is useful. */
2004 unlink (compiled_file_name);
2006 java_sources[0] = conftest_file_name;
2007 if (!compile_using_javac (java_sources, 1,
2008 true, source_version_for_javac,
2009 true, target_version,
2010 tmpdir->dir_name,
2011 false, false, false, true)
2012 && stat (compiled_file_name, &statbuf) >= 0
2013 && get_classfile_version (compiled_file_name)
2014 <= corresponding_classfile_version (target_version))
2016 const char *failcode = get_failcode_snippet (source_version);
2018 if (failcode != NULL)
2020 free (compiled_file_name);
2021 free (conftest_file_name);
2023 conftest_file_name =
2024 xconcatenated_filename (tmpdir->dir_name,
2025 "conftestfail.java",
2026 NULL);
2027 if (write_temp_file (tmpdir, conftest_file_name,
2028 failcode))
2030 free (conftest_file_name);
2031 cleanup_temp_dir (tmpdir);
2032 return true;
2035 compiled_file_name =
2036 xconcatenated_filename (tmpdir->dir_name,
2037 "conftestfail.class",
2038 NULL);
2039 register_temp_file (tmpdir, compiled_file_name);
2041 java_sources[0] = conftest_file_name;
2042 if (!compile_using_javac (java_sources, 1,
2043 false, source_version_for_javac,
2044 true, target_version,
2045 tmpdir->dir_name,
2046 false, false, false, true)
2047 && stat (compiled_file_name, &statbuf) >= 0)
2049 unlink (compiled_file_name);
2051 java_sources[0] = conftest_file_name;
2052 if (compile_using_javac (java_sources, 1,
2053 true, source_version_for_javac,
2054 true, target_version,
2055 tmpdir->dir_name,
2056 false, false, false, true))
2057 /* "javac -target $target_version" compiled
2058 conftestfail.java successfully, and
2059 "javac -target $target_version -source $source_version_for_javac"
2060 rejects it. So the -source option is useful. */
2061 resultp->source_option = true;
2066 resultp->target_option = true;
2067 resultp->usable = true;
2069 else
2071 /* Maybe this -target option requires a -source option? Try with
2072 -target and -source options. (Supported by Sun javac 1.4 and
2073 higher.) */
2074 unlink (compiled_file_name);
2076 java_sources[0] = conftest_file_name;
2077 if (!compile_using_javac (java_sources, 1,
2078 true, source_version_for_javac,
2079 true, target_version,
2080 tmpdir->dir_name,
2081 false, false, false, true)
2082 && stat (compiled_file_name, &statbuf) >= 0
2083 && get_classfile_version (compiled_file_name)
2084 <= corresponding_classfile_version (target_version))
2086 /* "javac -target $target_version -source $source_version_for_javac"
2087 compiled conftest.java successfully. */
2088 resultp->source_option = true;
2089 resultp->target_option = true;
2090 resultp->usable = true;
2095 free (compiled_file_name);
2096 free (conftest_file_name);
2098 resultp->tested = true;
2101 *usablep = resultp->usable;
2102 *source_option_p = resultp->source_option;
2103 *target_option_p = resultp->target_option;
2104 return false;
2107 static bool
2108 is_jikes_present (void)
2110 static bool jikes_tested;
2111 static bool jikes_present;
2113 if (!jikes_tested)
2115 /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1" */
2116 char *argv[2];
2117 int exitstatus;
2119 argv[0] = "jikes";
2120 argv[1] = NULL;
2121 exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
2122 true, false, NULL);
2123 jikes_present = (exitstatus == 0 || exitstatus == 1);
2124 jikes_tested = true;
2127 return jikes_present;
2130 /* ============================= Main function ============================= */
2132 bool
2133 compile_java_class (const char * const *java_sources,
2134 unsigned int java_sources_count,
2135 const char * const *classpaths,
2136 unsigned int classpaths_count,
2137 const char *source_version,
2138 const char *target_version,
2139 const char *directory,
2140 bool optimize, bool debug,
2141 bool use_minimal_classpath,
2142 bool verbose)
2144 bool err = false;
2145 char *old_JAVA_HOME;
2148 const char *javac = getenv ("JAVAC");
2149 if (javac != NULL && javac[0] != '\0')
2151 bool usable = false;
2152 bool no_assert_option = false;
2153 bool source_option = false;
2154 bool target_option = false;
2155 bool fsource_option = false;
2156 bool ftarget_option = false;
2157 const char *source_version_for_javac;
2159 if (target_version == NULL)
2160 target_version = default_target_version ();
2162 source_version_for_javac =
2163 get_source_version_for_javac (source_version, target_version);
2165 if (is_envjavac_gcj (javac))
2167 /* It's a version of gcj. */
2168 if (is_envjavac_gcj43 (javac))
2170 /* It's a version of gcj >= 4.3. Assume the classfile versions
2171 are correct. */
2172 if (is_envjavac_gcj43_usable (javac,
2173 source_version, target_version,
2174 &usable,
2175 &fsource_option, &ftarget_option))
2177 err = true;
2178 goto done1;
2181 else
2183 /* It's a version of gcj < 4.3. Ignore the version of the
2184 class files that it creates. */
2185 if (strcmp (target_version, "1.4") == 0
2186 && strcmp (source_version, "1.4") == 0)
2188 if (is_envjavac_oldgcj_14_14_usable (javac, &usable))
2190 err = true;
2191 goto done1;
2194 else if (strcmp (target_version, "1.4") == 0
2195 && strcmp (source_version, "1.3") == 0)
2197 if (is_envjavac_oldgcj_14_13_usable (javac,
2198 &usable,
2199 &no_assert_option))
2201 err = true;
2202 goto done1;
2207 else
2209 /* It's not gcj. Assume the classfile versions are correct. */
2210 if (is_envjavac_nongcj_usable (javac,
2211 source_version,
2212 source_version_for_javac,
2213 target_version,
2214 &usable,
2215 &source_option, &target_option))
2217 err = true;
2218 goto done1;
2222 if (usable)
2224 char *old_classpath;
2225 char *javac_with_options;
2227 /* Set CLASSPATH. */
2228 old_classpath =
2229 set_classpath (classpaths, classpaths_count, false, verbose);
2231 javac_with_options =
2232 (no_assert_option
2233 ? xasprintf ("%s -fno-assert", javac)
2234 : xasprintf ("%s%s%s%s%s%s%s%s%s",
2235 javac,
2236 source_option ? " -source " : "",
2237 source_option ? source_version_for_javac : "",
2238 target_option ? " -target " : "",
2239 target_option ? target_version : "",
2240 fsource_option ? " -fsource=" : "",
2241 fsource_option ? source_version : "",
2242 ftarget_option ? " -ftarget=" : "",
2243 ftarget_option ? target_version : ""));
2245 err = compile_using_envjavac (javac_with_options,
2246 java_sources, java_sources_count,
2247 directory, optimize, debug, verbose,
2248 false);
2250 free (javac_with_options);
2252 /* Reset CLASSPATH. */
2253 reset_classpath (old_classpath);
2255 goto done1;
2260 /* Unset the JAVA_HOME environment variable. */
2261 old_JAVA_HOME = getenv ("JAVA_HOME");
2262 if (old_JAVA_HOME != NULL)
2264 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
2265 unsetenv ("JAVA_HOME");
2268 if (is_gcj_present ())
2270 /* It's a version of gcj. */
2271 bool usable = false;
2272 bool no_assert_option = false;
2273 bool fsource_option = false;
2274 bool ftarget_option = false;
2276 if (target_version == NULL)
2277 target_version = default_target_version ();
2279 if (is_gcj_43 ())
2281 /* It's a version of gcj >= 4.3. Assume the classfile versions
2282 are correct. */
2283 if (is_gcj43_usable (source_version, target_version,
2284 &usable, &fsource_option, &ftarget_option))
2286 err = true;
2287 goto done1;
2290 else
2292 /* It's a version of gcj < 4.3. Ignore the version of the class
2293 files that it creates.
2294 Test whether it supports the desired target-version and
2295 source-version. */
2296 if (strcmp (target_version, "1.4") == 0
2297 && strcmp (source_version, "1.4") == 0)
2299 if (is_oldgcj_14_14_usable (&usable))
2301 err = true;
2302 goto done1;
2305 else if (strcmp (target_version, "1.4") == 0
2306 && strcmp (source_version, "1.3") == 0)
2308 if (is_oldgcj_14_13_usable (&usable, &no_assert_option))
2310 err = true;
2311 goto done1;
2316 if (usable)
2318 char *old_classpath;
2320 /* Set CLASSPATH. We could also use the --CLASSPATH=... option
2321 of gcj. Note that --classpath=... option is different: its
2322 argument should also contain gcj's libgcj.jar, but we don't
2323 know its location. */
2324 old_classpath =
2325 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
2326 verbose);
2328 err = compile_using_gcj (java_sources, java_sources_count,
2329 no_assert_option,
2330 fsource_option, source_version,
2331 ftarget_option, target_version,
2332 directory, optimize, debug, verbose, false);
2334 /* Reset CLASSPATH. */
2335 reset_classpath (old_classpath);
2337 goto done2;
2341 if (is_javac_present ())
2343 bool usable = false;
2344 bool source_option = false;
2345 bool target_option = false;
2346 const char *source_version_for_javac;
2348 if (target_version == NULL)
2349 target_version = default_target_version ();
2351 source_version_for_javac =
2352 get_source_version_for_javac (source_version, target_version);
2354 if (is_javac_usable (source_version, source_version_for_javac,
2355 target_version,
2356 &usable, &source_option, &target_option))
2358 err = true;
2359 goto done1;
2362 if (usable)
2364 char *old_classpath;
2366 /* Set CLASSPATH. We don't use the "-classpath ..." option because
2367 in JDK 1.1.x its argument should also contain the JDK's
2368 classes.zip, but we don't know its location. (In JDK 1.3.0 it
2369 would work.) */
2370 old_classpath =
2371 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
2372 verbose);
2374 err = compile_using_javac (java_sources, java_sources_count,
2375 source_option, source_version_for_javac,
2376 target_option, target_version,
2377 directory, optimize, debug, verbose,
2378 false);
2380 /* Reset CLASSPATH. */
2381 reset_classpath (old_classpath);
2383 goto done2;
2387 if (is_jikes_present ())
2389 /* Test whether it supports the desired target-version and
2390 source-version. */
2391 bool usable = (strcmp (source_version, "1.3") == 0);
2393 if (usable)
2395 char *old_classpath;
2397 /* Set CLASSPATH. We could also use the "-classpath ..." option.
2398 Since jikes doesn't come with its own standard library, it
2399 needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
2400 To increase the chance of success, we reuse the current CLASSPATH
2401 if the user has set it. */
2402 old_classpath =
2403 set_classpath (classpaths, classpaths_count, false, verbose);
2405 err = compile_using_jikes (java_sources, java_sources_count,
2406 directory, optimize, debug, verbose,
2407 false);
2409 /* Reset CLASSPATH. */
2410 reset_classpath (old_classpath);
2412 goto done2;
2416 error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
2417 err = true;
2419 done2:
2420 if (old_JAVA_HOME != NULL)
2422 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
2423 free (old_JAVA_HOME);
2426 done1:
2427 return err;