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.
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)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
31 #include <sys/types.h>
34 #include "javaversion.h"
37 #include "wait-process.h"
38 #include "classpath.h"
41 #include "binary-io.h"
42 #include "safe-read.h"
47 #include "fwriteerror.h"
48 #include "clean-temp.h"
50 #include "xvasprintf.h"
54 #define _(str) gettext (str)
57 /* Survey of Java compilers.
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
65 Program from A C O g T
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
73 All compilers support the option "-d DIRECTORY" for the base directory
74 of the classes to be written.
76 The CLASSPATH is a colon separated list of pathnames. (On Windows: a
77 semicolon separated list of pathnames.)
79 We try the Java compilers in the following order:
80 1. getenv ("JAVAC"), because the user must be able to override our
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.
88 We unset the JAVA_HOME environment variable, because a wrong setting of
89 this variable can confuse the JDK's javac.
92 /* Return the default target_version. */
94 default_target_version (void)
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)
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";
109 return java_version_cache;
112 /* ======================= Source version dependent ======================= */
114 /* Convert a source version to an index. */
115 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
117 source_version_index (const char *source_version)
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"));
127 /* Return a snippet of code that should compile in the given source version. */
129 get_goodcode_snippet (const char *source_version)
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"));
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
145 get_failcode_snippet (const char *source_version)
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)
153 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
157 /* ======================= Target version dependent ======================= */
159 /* Convert a target version to an index. */
160 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
162 target_version_index (const char *target_version)
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"));
172 /* Return the class file version number corresponding to a given target
175 corresponding_classfile_version (const char *target_version)
177 if (strcmp (target_version, "1.1") == 0)
179 if (strcmp (target_version, "1.2") == 0)
181 if (strcmp (target_version, "1.3") == 0)
183 if (strcmp (target_version, "1.4") == 0)
185 if (strcmp (target_version, "1.5") == 0)
187 if (strcmp (target_version, "1.6") == 0)
189 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
193 /* ======================== Compilation subroutines ======================== */
195 /* Try to compile a set of Java sources with $JAVAC.
196 Return a failure indicator (true upon error). */
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)
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. */
210 unsigned int command_length;
217 command_length = strlen (javac);
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]);
228 command = (char *) xallocsa (command_length);
230 /* Don't shell_quote $JAVAC, because it may consist of a command
232 memcpy (p, javac, strlen (javac));
236 memcpy (p, " -O", 3);
241 memcpy (p, " -g", 3);
244 if (directory != NULL)
246 memcpy (p, " -d ", 4);
248 p = shell_quote_copy (p, directory);
250 for (i = 0; i < java_sources_count; i++)
253 p = shell_quote_copy (p, java_sources[i]);
256 /* Ensure command_length was correctly calculated. */
257 if (p - command > command_length)
261 printf ("%s\n", command);
267 exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
268 null_stderr, true, true);
269 err = (exitstatus != 0);
276 /* Try to compile a set of Java sources with gcj.
277 Return a failure indicator (true upon error). */
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)
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 *));
301 if (no_assert_option)
302 *argp++ = "-fno-assert";
307 if (directory != NULL)
310 *argp++ = (char *) directory;
312 for (i = 0; i < java_sources_count; i++)
313 *argp++ = (char *) java_sources[i];
315 /* Ensure argv length was correctly calculated. */
316 if (argp - argv != argc)
321 char *command = shell_quote_argv (argv);
322 printf ("%s\n", command);
326 exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
328 err = (exitstatus != 0);
335 /* Try to compile a set of Java sources with javac.
336 Return a failure indicator (true upon error). */
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)
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 *));
363 *argp++ = (char *) source_version;
368 *argp++ = (char *) target_version;
374 if (directory != NULL)
377 *argp++ = (char *) directory;
379 for (i = 0; i < java_sources_count; i++)
380 *argp++ = (char *) java_sources[i];
382 /* Ensure argv length was correctly calculated. */
383 if (argp - argv != argc)
388 char *command = shell_quote_argv (argv);
389 printf ("%s\n", command);
393 exitstatus = execute ("javac", "javac", argv, false, false, false,
394 null_stderr, true, true);
395 err = (exitstatus != 0);
402 /* Try to compile a set of Java sources with jikes.
403 Return a failure indicator (true upon error). */
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)
419 1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
420 + java_sources_count;
421 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
429 if (directory != NULL)
432 *argp++ = (char *) directory;
434 for (i = 0; i < java_sources_count; i++)
435 *argp++ = (char *) java_sources[i];
437 /* Ensure argv length was correctly calculated. */
438 if (argp - argv != argc)
443 char *command = shell_quote_argv (argv);
444 printf ("%s\n", command);
448 exitstatus = execute ("jikes", "jikes", argv, false, false, false,
449 null_stderr, true, true);
450 err = (exitstatus != 0);
457 /* ====================== Usability test subroutines ====================== */
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
462 Return a failure indicator (true upon error). */
464 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
465 const char *contents)
469 register_temp_file (tmpdir, file_name);
470 fp = fopen (file_name, "w");
473 error (0, errno, _("failed to create \"%s\""), file_name);
474 unregister_temp_file (tmpdir, file_name);
477 fputs (contents, fp);
478 if (fwriteerror (fp))
480 error (0, errno, _("error while writing \"%s\" file"), file_name);
486 /* Return the class file version number of a class file on disk. */
488 get_classfile_version (const char *compiled_file_name)
490 unsigned char header[8];
493 /* Open the class file. */
494 fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
497 /* Read its first 8 bytes. */
498 if (safe_read (fd, header, 8) == 8)
500 /* Verify the class file signature. */
501 if (header[0] == 0xCA && header[1] == 0xFE
502 && header[2] == 0xBA && header[3] == 0xBE)
508 /* Could not get the class file version. Return a very large one. */
512 /* Return true if $JAVAC is a version of gcj. */
514 is_envjavac_gcj (const char *javac)
516 static bool envjavac_tested;
517 static bool envjavac_gcj;
519 if (!envjavac_tested)
521 /* Test whether $JAVAC is gcj:
522 "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null" */
523 unsigned int command_length;
535 /* Setup the command "$JAVAC --version". */
536 command_length = strlen (javac) + 1 + 9 + 1;
537 command = (char *) xallocsa (command_length);
539 /* Don't shell_quote $JAVAC, because it may consist of a command
541 memcpy (p, javac, strlen (javac));
543 memcpy (p, " --version", 1 + 9 + 1);
545 /* Ensure command_length was correctly calculated. */
546 if (p - command > command_length)
549 /* Call $JAVAC --version 2>/dev/null. */
554 child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
559 /* Retrieve its result. */
560 fp = fdopen (fd[0], "r");
564 line = NULL; linesize = 0;
565 linelen = getline (&line, &linesize, fp);
566 if (linelen == (size_t)(-1))
571 envjavac_gcj = (strstr (line, "gcj") != NULL);
575 /* Remove zombie process from process list, and retrieve exit status. */
576 exitstatus = wait_subprocess (child, javac, true, true, true, false);
578 envjavac_gcj = false;
583 envjavac_tested = true;
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). */
593 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
595 static bool envjavac_tested;
596 static bool envjavac_usable;
598 if (!envjavac_tested)
601 struct temp_dir *tmpdir;
602 char *conftest_file_name;
603 char *compiled_file_name;
604 const char *java_sources[1];
607 tmpdir = create_temp_dir ("java", NULL, false);
612 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
613 if (write_temp_file (tmpdir, conftest_file_name,
614 get_goodcode_snippet ("1.4")))
616 free (conftest_file_name);
617 cleanup_temp_dir (tmpdir);
622 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
623 register_temp_file (tmpdir, compiled_file_name);
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;
632 free (compiled_file_name);
633 free (conftest_file_name);
635 cleanup_temp_dir (tmpdir);
637 envjavac_tested = true;
640 *usablep = envjavac_usable;
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). */
648 is_envjavac_gcj_14_13_usable (const char *javac,
649 bool *usablep, bool *need_no_assert_option_p)
651 static bool envjavac_tested;
652 static bool envjavac_usable;
653 static bool envjavac_need_no_assert_option;
655 if (!envjavac_tested)
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];
665 char *javac_noassert;
666 bool javac_noassert_works;
668 tmpdir = create_temp_dir ("java", NULL, false);
673 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
674 if (write_temp_file (tmpdir, conftest_file_name,
675 get_goodcode_snippet ("1.3")))
677 free (conftest_file_name);
678 cleanup_temp_dir (tmpdir);
683 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
684 register_temp_file (tmpdir, compiled_file_name);
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. */
696 unlink (compiled_file_name);
698 javac_noassert = xasprintf ("%s -fno-assert", javac);
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;
708 javac_noassert_works = false;
710 free (compiled_file_name);
711 free (conftest_file_name);
713 if (javac_works && javac_noassert_works)
716 concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
718 if (write_temp_file (tmpdir, conftest_file_name,
719 get_failcode_snippet ("1.3")))
721 free (conftest_file_name);
722 free (javac_noassert);
723 cleanup_temp_dir (tmpdir);
728 concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
730 register_temp_file (tmpdir, compiled_file_name);
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)
738 /* Compilation succeeded. */
739 unlink (compiled_file_name);
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. */
751 free (compiled_file_name);
752 free (conftest_file_name);
755 cleanup_temp_dir (tmpdir);
759 envjavac_usable = true;
760 envjavac_need_no_assert_option = false;
762 else if (javac_noassert_works)
764 envjavac_usable = true;
765 envjavac_need_no_assert_option = true;
768 envjavac_tested = true;
771 *usablep = envjavac_usable;
772 *need_no_assert_option_p = envjavac_need_no_assert_option;
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). */
780 is_envjavac_nongcj_usable (const char *javac,
781 const char *source_version,
782 const char *target_version,
784 bool *source_option_p, bool *target_option_p)
786 /* The cache depends on the source_version and target_version. */
794 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
795 struct result_t *resultp;
797 resultp = &result_cache[source_version_index (source_version)]
798 [target_version_index (target_version)];
799 if (!resultp->tested)
802 struct temp_dir *tmpdir;
803 char *conftest_file_name;
804 char *compiled_file_name;
805 const char *java_sources[1];
808 tmpdir = create_temp_dir ("java", NULL, false);
813 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
814 if (write_temp_file (tmpdir, conftest_file_name,
815 get_goodcode_snippet (source_version)))
817 free (conftest_file_name);
818 cleanup_temp_dir (tmpdir);
823 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
824 register_temp_file (tmpdir, compiled_file_name);
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))
834 /* $JAVAC compiled conftest.java successfully. */
835 /* Try adding -source option if it is useful. */
837 xasprintf ("%s -source %s", javac, source_version);
839 unlink (compiled_file_name);
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))
849 const char *failcode = get_failcode_snippet (source_version);
851 if (failcode != NULL)
853 free (compiled_file_name);
854 free (conftest_file_name);
857 concatenated_pathname (tmpdir->dir_name,
860 if (write_temp_file (tmpdir, conftest_file_name, failcode))
862 free (conftest_file_name);
864 cleanup_temp_dir (tmpdir);
869 concatenated_pathname (tmpdir->dir_name,
870 "conftestfail.class",
872 register_temp_file (tmpdir, compiled_file_name);
874 java_sources[0] = conftest_file_name;
875 if (!compile_using_envjavac (javac,
878 false, false, false, true)
879 && stat (compiled_file_name, &statbuf) >= 0)
881 unlink (compiled_file_name);
883 java_sources[0] = conftest_file_name;
884 if (compile_using_envjavac (javac_source,
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;
898 resultp->usable = true;
902 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
903 option but no -source option.) */
905 xasprintf ("%s -target %s", javac, target_version);
907 unlink (compiled_file_name);
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))
917 /* "$JAVAC -target $target_version" compiled conftest.java
919 /* Try adding -source option if it is useful. */
920 char *javac_target_source =
921 xasprintf ("%s -source %s", javac_target, source_version);
923 unlink (compiled_file_name);
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))
933 const char *failcode = get_failcode_snippet (source_version);
935 if (failcode != NULL)
937 free (compiled_file_name);
938 free (conftest_file_name);
941 concatenated_pathname (tmpdir->dir_name,
944 if (write_temp_file (tmpdir, conftest_file_name,
947 free (conftest_file_name);
948 free (javac_target_source);
950 cleanup_temp_dir (tmpdir);
955 concatenated_pathname (tmpdir->dir_name,
956 "conftestfail.class",
958 register_temp_file (tmpdir, compiled_file_name);
960 java_sources[0] = conftest_file_name;
961 if (!compile_using_envjavac (javac_target,
964 false, false, false, true)
965 && stat (compiled_file_name, &statbuf) >= 0)
967 unlink (compiled_file_name);
969 java_sources[0] = conftest_file_name;
970 if (compile_using_envjavac (javac_target_source,
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;
984 free (javac_target_source);
986 resultp->target_option = true;
987 resultp->usable = true;
991 /* Maybe this -target option requires a -source option? Try with
992 -target and -source options. (Supported by Sun javac 1.4 and
994 char *javac_target_source =
995 xasprintf ("%s -source %s", javac_target, source_version);
997 unlink (compiled_file_name);
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))
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;
1014 free (javac_target_source);
1017 free (javac_target);
1020 free (compiled_file_name);
1021 free (conftest_file_name);
1023 resultp->tested = true;
1026 *usablep = resultp->usable;
1027 *source_option_p = resultp->source_option;
1028 *target_option_p = resultp->target_option;
1033 is_gcj_present (void)
1035 static bool gcj_tested;
1036 static bool gcj_present;
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" */
1050 argv[1] = "--version";
1052 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1054 gcj_present = false;
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
1064 while (safe_read (fd[0], &c[count], 1) > 0)
1066 if (c[count] == '\n')
1070 if (!(c[0] >= '0' && c[0] <= '9'))
1072 gcj_present = (c[0] >= '3');
1077 if (c[0] == '3' && c[1] == '.'
1078 && (c[2] == '0' || c[2] == '1'))
1079 gcj_present = false;
1083 while (safe_read (fd[0], &c[0], 1) > 0)
1088 /* Remove zombie process from process list, and retrieve exit
1091 wait_subprocess (child, "gcj", false, true, true, false);
1092 if (exitstatus != 0)
1093 gcj_present = false;
1098 /* See if libgcj.jar is well installed. */
1099 struct temp_dir *tmpdir;
1101 tmpdir = create_temp_dir ("java", NULL, false);
1103 gcj_present = false;
1106 char *conftest_file_name;
1108 conftest_file_name =
1109 concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
1111 if (write_temp_file (tmpdir, conftest_file_name,
1112 "public class conftestlib {\n"
1113 " public static void main (String[] args) {\n"
1116 gcj_present = false;
1119 char *compiled_file_name;
1120 const char *java_sources[1];
1122 compiled_file_name =
1123 concatenated_pathname (tmpdir->dir_name,
1124 "conftestlib.class",
1126 register_temp_file (tmpdir, compiled_file_name);
1128 java_sources[0] = conftest_file_name;
1129 if (compile_using_gcj (java_sources, 1, false,
1131 false, false, false, true))
1132 gcj_present = false;
1134 free (compiled_file_name);
1136 free (conftest_file_name);
1138 cleanup_temp_dir (tmpdir);
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). */
1151 is_gcj_14_14_usable (bool *usablep)
1153 static bool gcj_tested;
1154 static bool gcj_usable;
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;
1165 tmpdir = create_temp_dir ("java", NULL, false);
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")))
1174 free (conftest_file_name);
1175 cleanup_temp_dir (tmpdir);
1179 compiled_file_name =
1180 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1181 register_temp_file (tmpdir, compiled_file_name);
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. */
1190 free (compiled_file_name);
1191 free (conftest_file_name);
1193 cleanup_temp_dir (tmpdir);
1198 *usablep = gcj_usable;
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). */
1206 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1208 static bool gcj_tested;
1209 static bool gcj_usable;
1210 static bool gcj_need_no_assert_option;
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;
1222 tmpdir = create_temp_dir ("java", NULL, false);
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")))
1231 free (conftest_file_name);
1232 cleanup_temp_dir (tmpdir);
1236 compiled_file_name =
1237 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1238 register_temp_file (tmpdir, compiled_file_name);
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. */
1247 gcj_need_no_assert_option = true;
1251 unlink (compiled_file_name);
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. */
1260 gcj_need_no_assert_option = false;
1264 free (compiled_file_name);
1265 free (conftest_file_name);
1267 cleanup_temp_dir (tmpdir);
1272 *usablep = gcj_usable;
1273 *need_no_assert_option_p = gcj_need_no_assert_option;
1278 is_javac_present (void)
1280 static bool javac_tested;
1281 static bool javac_present;
1285 /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
1291 exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1293 javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1294 javac_tested = true;
1297 return javac_present;
1300 /* Test whether javac can be used and whether it needs a -source and/or
1302 Return a failure indicator (true upon error). */
1304 is_javac_usable (const char *source_version, const char *target_version,
1305 bool *usablep, bool *source_option_p, bool *target_option_p)
1307 /* The cache depends on the source_version and target_version. */
1315 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1316 struct result_t *resultp;
1318 resultp = &result_cache[source_version_index (source_version)]
1319 [target_version_index (target_version)];
1320 if (!resultp->tested)
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;
1329 tmpdir = create_temp_dir ("java", NULL, false);
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)))
1338 free (conftest_file_name);
1339 cleanup_temp_dir (tmpdir);
1343 compiled_file_name =
1344 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1345 register_temp_file (tmpdir, compiled_file_name);
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))
1356 /* javac compiled conftest.java successfully. */
1357 /* Try adding -source option if it is useful. */
1358 unlink (compiled_file_name);
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))
1369 const char *failcode = get_failcode_snippet (source_version);
1371 if (failcode != NULL)
1373 free (compiled_file_name);
1374 free (conftest_file_name);
1376 conftest_file_name =
1377 concatenated_pathname (tmpdir->dir_name,
1378 "conftestfail.java",
1380 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1382 free (conftest_file_name);
1383 cleanup_temp_dir (tmpdir);
1387 compiled_file_name =
1388 concatenated_pathname (tmpdir->dir_name,
1389 "conftestfail.class",
1391 register_temp_file (tmpdir, compiled_file_name);
1393 java_sources[0] = conftest_file_name;
1394 if (!compile_using_javac (java_sources, 1,
1395 false, source_version,
1396 false, target_version,
1398 false, false, false, true)
1399 && stat (compiled_file_name, &statbuf) >= 0)
1401 unlink (compiled_file_name);
1403 java_sources[0] = conftest_file_name;
1404 if (compile_using_javac (java_sources, 1,
1405 true, source_version,
1406 false, target_version,
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;
1417 resultp->usable = true;
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);
1425 java_sources[0] = conftest_file_name;
1426 if (!compile_using_javac (java_sources, 1,
1427 false, source_version,
1428 true, target_version,
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))
1435 /* "javac -target $target_version" compiled conftest.java
1437 /* Try adding -source option if it is useful. */
1438 unlink (compiled_file_name);
1440 java_sources[0] = conftest_file_name;
1441 if (!compile_using_javac (java_sources, 1,
1442 true, source_version,
1443 true, target_version,
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))
1450 const char *failcode = get_failcode_snippet (source_version);
1452 if (failcode != NULL)
1454 free (compiled_file_name);
1455 free (conftest_file_name);
1457 conftest_file_name =
1458 concatenated_pathname (tmpdir->dir_name,
1459 "conftestfail.java",
1461 if (write_temp_file (tmpdir, conftest_file_name,
1464 free (conftest_file_name);
1465 cleanup_temp_dir (tmpdir);
1469 compiled_file_name =
1470 concatenated_pathname (tmpdir->dir_name,
1471 "conftestfail.class",
1473 register_temp_file (tmpdir, compiled_file_name);
1475 java_sources[0] = conftest_file_name;
1476 if (!compile_using_javac (java_sources, 1,
1477 false, source_version,
1478 true, target_version,
1480 false, false, false, true)
1481 && stat (compiled_file_name, &statbuf) >= 0)
1483 unlink (compiled_file_name);
1485 java_sources[0] = conftest_file_name;
1486 if (compile_using_javac (java_sources, 1,
1487 true, source_version,
1488 true, target_version,
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;
1500 resultp->target_option = true;
1501 resultp->usable = true;
1505 /* Maybe this -target option requires a -source option? Try with
1506 -target and -source options. (Supported by Sun javac 1.4 and
1508 unlink (compiled_file_name);
1510 java_sources[0] = conftest_file_name;
1511 if (!compile_using_javac (java_sources, 1,
1512 true, source_version,
1513 true, target_version,
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))
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;
1529 free (compiled_file_name);
1530 free (conftest_file_name);
1532 resultp->tested = true;
1535 *usablep = resultp->usable;
1536 *source_option_p = resultp->source_option;
1537 *target_option_p = resultp->target_option;
1542 is_jikes_present (void)
1544 static bool jikes_tested;
1545 static bool jikes_present;
1549 /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1" */
1555 exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
1557 jikes_present = (exitstatus == 0 || exitstatus == 1);
1558 jikes_tested = true;
1561 return jikes_present;
1564 /* ============================= Main function ============================= */
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,
1579 char *old_JAVA_HOME;
1582 const char *javac = getenv ("JAVAC");
1583 if (javac != NULL && javac[0] != '\0')
1585 bool usable = false;
1586 bool no_assert_option = false;
1587 bool source_option = false;
1588 bool target_option = false;
1590 if (target_version == NULL)
1591 target_version = default_target_version ();
1593 if (is_envjavac_gcj (javac))
1595 /* It's a version of gcj. Ignore the version of the class files
1597 if (strcmp (target_version, "1.4") == 0
1598 && strcmp (source_version, "1.4") == 0)
1600 if (is_envjavac_gcj_14_14_usable (javac, &usable))
1606 else if (strcmp (target_version, "1.4") == 0
1607 && strcmp (source_version, "1.3") == 0)
1609 if (is_envjavac_gcj_14_13_usable (javac,
1610 &usable, &no_assert_option))
1619 /* It's not gcj. Assume the classfile versions are correct. */
1620 if (is_envjavac_nongcj_usable (javac,
1621 source_version, target_version,
1623 &source_option, &target_option))
1632 char *old_classpath;
1633 char *javac_with_options;
1635 /* Set CLASSPATH. */
1637 set_classpath (classpaths, classpaths_count, false, verbose);
1639 javac_with_options =
1641 ? xasprintf ("%s -fno-assert", javac)
1642 : xasprintf ("%s%s%s%s%s",
1644 source_option ? " -source " : "",
1645 source_option ? source_version : "",
1646 target_option ? " -target " : "",
1647 target_option ? target_version : ""));
1649 err = compile_using_envjavac (javac_with_options,
1650 java_sources, java_sources_count,
1651 directory, optimize, debug, verbose,
1654 free (javac_with_options);
1656 /* Reset CLASSPATH. */
1657 reset_classpath (old_classpath);
1664 /* Unset the JAVA_HOME environment variable. */
1665 old_JAVA_HOME = getenv ("JAVA_HOME");
1666 if (old_JAVA_HOME != NULL)
1668 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
1669 unsetenv ("JAVA_HOME");
1672 if (is_gcj_present ())
1674 /* Test whether it supports the desired target-version and
1675 source-version. But ignore the version of the class files that
1677 bool usable = false;
1678 bool no_assert_option = false;
1680 if (target_version == NULL)
1681 target_version = default_target_version ();
1683 if (strcmp (target_version, "1.4") == 0
1684 && strcmp (source_version, "1.4") == 0)
1686 if (is_gcj_14_14_usable (&usable))
1692 else if (strcmp (target_version, "1.4") == 0
1693 && strcmp (source_version, "1.3") == 0)
1695 if (is_gcj_14_13_usable (&usable, &no_assert_option))
1704 char *old_classpath;
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. */
1711 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1714 err = compile_using_gcj (java_sources, java_sources_count,
1716 directory, optimize, debug, verbose, false);
1718 /* Reset CLASSPATH. */
1719 reset_classpath (old_classpath);
1725 if (is_javac_present ())
1727 bool usable = false;
1728 bool source_option = false;
1729 bool target_option = false;
1731 if (target_version == NULL)
1732 target_version = default_target_version ();
1734 if (is_javac_usable (source_version, target_version,
1735 &usable, &source_option, &target_option))
1743 char *old_classpath;
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
1750 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
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,
1759 /* Reset CLASSPATH. */
1760 reset_classpath (old_classpath);
1766 if (is_jikes_present ())
1768 /* Test whether it supports the desired target-version and
1770 bool usable = (strcmp (source_version, "1.3") == 0);
1774 char *old_classpath;
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. */
1782 set_classpath (classpaths, classpaths_count, false, verbose);
1784 err = compile_using_jikes (java_sources, java_sources_count,
1785 directory, optimize, debug, verbose,
1788 /* Reset CLASSPATH. */
1789 reset_classpath (old_classpath);
1795 error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
1799 if (old_JAVA_HOME != NULL)
1801 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
1802 free (old_JAVA_HOME);