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