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