1 /* Compile a Java program.
2 Copyright (C) 2001-2003, 2006-2007 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)
56 /* The results of open() in this file are not used with fchdir,
57 therefore save some unnecessary work in fchdir.c. */
62 /* Survey of Java compilers.
64 A = does it work without CLASSPATH being set
65 C = option to set CLASSPATH, other than setting it in the environment
66 O = option for optimizing
67 g = option for debugging
70 Program from A C O g T
72 $JAVAC unknown N n/a -O -g true
73 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
74 javac JDK 1.1.8 Y -classpath P -O -g javac 2>/dev/null; test $? = 1
75 javac JDK 1.3.0 Y -classpath P -O -g javac 2>/dev/null; test $? -le 2
76 jikes Jikes 1.14 N -classpath P -O -g jikes 2>/dev/null; test $? = 1
78 All compilers support the option "-d DIRECTORY" for the base directory
79 of the classes to be written.
81 The CLASSPATH is a colon separated list of pathnames. (On Windows: a
82 semicolon separated list of pathnames.)
84 We try the Java compilers in the following order:
85 1. getenv ("JAVAC"), because the user must be able to override our
87 2. "gcj -C", because it is a completely free compiler,
88 3. "javac", because it is a standard compiler,
89 4. "jikes", comes last because it has some deviating interpretation
90 of the Java Language Specification and because it requires a
91 CLASSPATH environment variable.
93 We unset the JAVA_HOME environment variable, because a wrong setting of
94 this variable can confuse the JDK's javac.
97 /* Return the default target_version. */
99 default_target_version (void)
101 /* Use a cache. Assumes that the PATH environment variable doesn't change
102 during the lifetime of the program. */
103 static const char *java_version_cache;
104 if (java_version_cache == NULL)
106 /* Determine the version from the found JVM. */
107 java_version_cache = javaexec_version ();
108 if (java_version_cache == NULL
109 || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
110 && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
111 && java_version_cache[3] == '\0'))
112 java_version_cache = "1.1";
114 return java_version_cache;
117 /* ======================= Source version dependent ======================= */
119 /* Convert a source version to an index. */
120 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
122 source_version_index (const char *source_version)
124 if (source_version[0] == '1' && source_version[1] == '.'
125 && (source_version[2] >= '3' && source_version[2] <= '5')
126 && source_version[3] == '\0')
127 return source_version[2] - '3';
128 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
132 /* Return a snippet of code that should compile in the given source version. */
134 get_goodcode_snippet (const char *source_version)
136 if (strcmp (source_version, "1.3") == 0)
137 return "class conftest {}\n";
138 if (strcmp (source_version, "1.4") == 0)
139 return "class conftest { static { assert(true); } }\n";
140 if (strcmp (source_version, "1.5") == 0)
141 return "class conftest<T> { T foo() { return null; } }\n";
142 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
146 /* Return a snippet of code that should fail to compile in the given source
147 version, or NULL (standing for a snippet that would fail to compile with
150 get_failcode_snippet (const char *source_version)
152 if (strcmp (source_version, "1.3") == 0)
153 return "class conftestfail { static { assert(true); } }\n";
154 if (strcmp (source_version, "1.4") == 0)
155 return "class conftestfail<T> { T foo() { return null; } }\n";
156 if (strcmp (source_version, "1.5") == 0)
158 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
162 /* ======================= Target version dependent ======================= */
164 /* Convert a target version to an index. */
165 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
167 target_version_index (const char *target_version)
169 if (target_version[0] == '1' && target_version[1] == '.'
170 && (target_version[2] >= '1' && target_version[2] <= '6')
171 && target_version[3] == '\0')
172 return target_version[2] - '1';
173 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
177 /* Return the class file version number corresponding to a given target
180 corresponding_classfile_version (const char *target_version)
182 if (strcmp (target_version, "1.1") == 0)
184 if (strcmp (target_version, "1.2") == 0)
186 if (strcmp (target_version, "1.3") == 0)
188 if (strcmp (target_version, "1.4") == 0)
190 if (strcmp (target_version, "1.5") == 0)
192 if (strcmp (target_version, "1.6") == 0)
194 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
198 /* ======================== Compilation subroutines ======================== */
200 /* Try to compile a set of Java sources with $JAVAC.
201 Return a failure indicator (true upon error). */
203 compile_using_envjavac (const char *javac,
204 const char * const *java_sources,
205 unsigned int java_sources_count,
206 const char *directory,
207 bool optimize, bool debug,
208 bool verbose, bool null_stderr)
210 /* Because $JAVAC may consist of a command and options, we use the
211 shell. Because $JAVAC has been set by the user, we leave all
212 environment variables in place, including JAVA_HOME, and we don't
213 erase the user's CLASSPATH. */
215 unsigned int command_length;
222 command_length = strlen (javac);
227 if (directory != NULL)
228 command_length += 4 + shell_quote_length (directory);
229 for (i = 0; i < java_sources_count; i++)
230 command_length += 1 + shell_quote_length (java_sources[i]);
233 command = (char *) xallocsa (command_length);
235 /* Don't shell_quote $JAVAC, because it may consist of a command
237 memcpy (p, javac, strlen (javac));
241 memcpy (p, " -O", 3);
246 memcpy (p, " -g", 3);
249 if (directory != NULL)
251 memcpy (p, " -d ", 4);
253 p = shell_quote_copy (p, directory);
255 for (i = 0; i < java_sources_count; i++)
258 p = shell_quote_copy (p, java_sources[i]);
261 /* Ensure command_length was correctly calculated. */
262 if (p - command > command_length)
266 printf ("%s\n", command);
272 exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
273 null_stderr, true, true);
274 err = (exitstatus != 0);
281 /* Try to compile a set of Java sources with gcj.
282 Return a failure indicator (true upon error). */
284 compile_using_gcj (const char * const *java_sources,
285 unsigned int java_sources_count,
286 bool no_assert_option,
287 const char *directory,
288 bool optimize, bool debug,
289 bool verbose, bool null_stderr)
299 2 + (no_assert_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
300 + (directory != NULL ? 2 : 0) + java_sources_count;
301 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
306 if (no_assert_option)
307 *argp++ = "-fno-assert";
312 if (directory != NULL)
315 *argp++ = (char *) directory;
317 for (i = 0; i < java_sources_count; i++)
318 *argp++ = (char *) java_sources[i];
320 /* Ensure argv length was correctly calculated. */
321 if (argp - argv != argc)
326 char *command = shell_quote_argv (argv);
327 printf ("%s\n", command);
331 exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
333 err = (exitstatus != 0);
340 /* Try to compile a set of Java sources with javac.
341 Return a failure indicator (true upon error). */
343 compile_using_javac (const char * const *java_sources,
344 unsigned int java_sources_count,
345 bool source_option, const char *source_version,
346 bool target_option, const char *target_version,
347 const char *directory,
348 bool optimize, bool debug,
349 bool verbose, bool null_stderr)
359 1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
360 + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
361 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
368 *argp++ = (char *) source_version;
373 *argp++ = (char *) target_version;
379 if (directory != NULL)
382 *argp++ = (char *) directory;
384 for (i = 0; i < java_sources_count; i++)
385 *argp++ = (char *) java_sources[i];
387 /* Ensure argv length was correctly calculated. */
388 if (argp - argv != argc)
393 char *command = shell_quote_argv (argv);
394 printf ("%s\n", command);
398 exitstatus = execute ("javac", "javac", argv, false, false, false,
399 null_stderr, true, true);
400 err = (exitstatus != 0);
407 /* Try to compile a set of Java sources with jikes.
408 Return a failure indicator (true upon error). */
410 compile_using_jikes (const char * const *java_sources,
411 unsigned int java_sources_count,
412 const char *directory,
413 bool optimize, bool debug,
414 bool verbose, bool null_stderr)
424 1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
425 + java_sources_count;
426 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
434 if (directory != NULL)
437 *argp++ = (char *) directory;
439 for (i = 0; i < java_sources_count; i++)
440 *argp++ = (char *) java_sources[i];
442 /* Ensure argv length was correctly calculated. */
443 if (argp - argv != argc)
448 char *command = shell_quote_argv (argv);
449 printf ("%s\n", command);
453 exitstatus = execute ("jikes", "jikes", argv, false, false, false,
454 null_stderr, true, true);
455 err = (exitstatus != 0);
462 /* ====================== Usability test subroutines ====================== */
464 /* Write a given contents to a temporary file.
465 FILE_NAME is the name of a file inside TMPDIR that is known not to exist
467 Return a failure indicator (true upon error). */
469 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
470 const char *contents)
474 register_temp_file (tmpdir, file_name);
475 fp = fopen_temp (file_name, "w");
478 error (0, errno, _("failed to create \"%s\""), file_name);
479 unregister_temp_file (tmpdir, file_name);
482 fputs (contents, fp);
483 if (fwriteerror_temp (fp))
485 error (0, errno, _("error while writing \"%s\" file"), file_name);
491 /* Return the class file version number of a class file on disk. */
493 get_classfile_version (const char *compiled_file_name)
495 unsigned char header[8];
498 /* Open the class file. */
499 fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
502 /* Read its first 8 bytes. */
503 if (safe_read (fd, header, 8) == 8)
505 /* Verify the class file signature. */
506 if (header[0] == 0xCA && header[1] == 0xFE
507 && header[2] == 0xBA && header[3] == 0xBE)
513 /* Could not get the class file version. Return a very large one. */
517 /* Return true if $JAVAC is a version of gcj. */
519 is_envjavac_gcj (const char *javac)
521 static bool envjavac_tested;
522 static bool envjavac_gcj;
524 if (!envjavac_tested)
526 /* Test whether $JAVAC is gcj:
527 "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null" */
528 unsigned int command_length;
540 /* Setup the command "$JAVAC --version". */
541 command_length = strlen (javac) + 1 + 9 + 1;
542 command = (char *) xallocsa (command_length);
544 /* Don't shell_quote $JAVAC, because it may consist of a command
546 memcpy (p, javac, strlen (javac));
548 memcpy (p, " --version", 1 + 9 + 1);
550 /* Ensure command_length was correctly calculated. */
551 if (p - command > command_length)
554 /* Call $JAVAC --version 2>/dev/null. */
559 child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
564 /* Retrieve its result. */
565 fp = fdopen (fd[0], "r");
569 line = NULL; linesize = 0;
570 linelen = getline (&line, &linesize, fp);
571 if (linelen == (size_t)(-1))
576 /* It is safe to call c_strstr() instead of strstr() here; see the
577 comments in c-strstr.h. */
578 envjavac_gcj = (c_strstr (line, "gcj") != NULL);
582 /* Remove zombie process from process list, and retrieve exit status. */
583 exitstatus = wait_subprocess (child, javac, true, true, true, false);
585 envjavac_gcj = false;
590 envjavac_tested = true;
596 /* Test whether $JAVAC, known to be a version of gcj, can be used for
597 compiling with target_version = 1.4 and source_version = 1.4.
598 Return a failure indicator (true upon error). */
600 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
602 static bool envjavac_tested;
603 static bool envjavac_usable;
605 if (!envjavac_tested)
608 struct temp_dir *tmpdir;
609 char *conftest_file_name;
610 char *compiled_file_name;
611 const char *java_sources[1];
614 tmpdir = create_temp_dir ("java", NULL, false);
619 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
620 if (write_temp_file (tmpdir, conftest_file_name,
621 get_goodcode_snippet ("1.4")))
623 free (conftest_file_name);
624 cleanup_temp_dir (tmpdir);
629 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
630 register_temp_file (tmpdir, compiled_file_name);
632 java_sources[0] = conftest_file_name;
633 if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
634 false, false, false, true)
635 && stat (compiled_file_name, &statbuf) >= 0)
636 /* Compilation succeeded. */
637 envjavac_usable = true;
639 free (compiled_file_name);
640 free (conftest_file_name);
642 cleanup_temp_dir (tmpdir);
644 envjavac_tested = true;
647 *usablep = envjavac_usable;
651 /* Test whether $JAVAC, known to be a version of gcj, can be used for
652 compiling with target_version = 1.4 and source_version = 1.3.
653 Return a failure indicator (true upon error). */
655 is_envjavac_gcj_14_13_usable (const char *javac,
656 bool *usablep, bool *need_no_assert_option_p)
658 static bool envjavac_tested;
659 static bool envjavac_usable;
660 static bool envjavac_need_no_assert_option;
662 if (!envjavac_tested)
664 /* Try $JAVAC and "$JAVAC -fno-assert". But add -fno-assert only if
665 it makes a difference. (It could already be part of $JAVAC.) */
666 struct temp_dir *tmpdir;
667 char *conftest_file_name;
668 char *compiled_file_name;
669 const char *java_sources[1];
672 char *javac_noassert;
673 bool javac_noassert_works;
675 tmpdir = create_temp_dir ("java", NULL, false);
680 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
681 if (write_temp_file (tmpdir, conftest_file_name,
682 get_goodcode_snippet ("1.3")))
684 free (conftest_file_name);
685 cleanup_temp_dir (tmpdir);
690 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
691 register_temp_file (tmpdir, compiled_file_name);
693 java_sources[0] = conftest_file_name;
694 if (!compile_using_envjavac (javac,
695 java_sources, 1, tmpdir->dir_name,
696 false, false, false, true)
697 && stat (compiled_file_name, &statbuf) >= 0)
698 /* Compilation succeeded. */
703 unlink (compiled_file_name);
705 javac_noassert = xasprintf ("%s -fno-assert", javac);
707 java_sources[0] = conftest_file_name;
708 if (!compile_using_envjavac (javac_noassert,
709 java_sources, 1, tmpdir->dir_name,
710 false, false, false, true)
711 && stat (compiled_file_name, &statbuf) >= 0)
712 /* Compilation succeeded. */
713 javac_noassert_works = true;
715 javac_noassert_works = false;
717 free (compiled_file_name);
718 free (conftest_file_name);
720 if (javac_works && javac_noassert_works)
723 concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
725 if (write_temp_file (tmpdir, conftest_file_name,
726 get_failcode_snippet ("1.3")))
728 free (conftest_file_name);
729 free (javac_noassert);
730 cleanup_temp_dir (tmpdir);
735 concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
737 register_temp_file (tmpdir, compiled_file_name);
739 java_sources[0] = conftest_file_name;
740 if (!compile_using_envjavac (javac,
741 java_sources, 1, tmpdir->dir_name,
742 false, false, false, true)
743 && stat (compiled_file_name, &statbuf) >= 0)
745 /* Compilation succeeded. */
746 unlink (compiled_file_name);
748 java_sources[0] = conftest_file_name;
749 if (!(!compile_using_envjavac (javac_noassert,
750 java_sources, 1, tmpdir->dir_name,
751 false, false, false, true)
752 && stat (compiled_file_name, &statbuf) >= 0))
753 /* Compilation failed. */
754 /* "$JAVAC -fno-assert" works better than $JAVAC. */
758 free (compiled_file_name);
759 free (conftest_file_name);
762 cleanup_temp_dir (tmpdir);
766 envjavac_usable = true;
767 envjavac_need_no_assert_option = false;
769 else if (javac_noassert_works)
771 envjavac_usable = true;
772 envjavac_need_no_assert_option = true;
775 envjavac_tested = true;
778 *usablep = envjavac_usable;
779 *need_no_assert_option_p = envjavac_need_no_assert_option;
783 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
784 whether it needs a -source and/or -target option.
785 Return a failure indicator (true upon error). */
787 is_envjavac_nongcj_usable (const char *javac,
788 const char *source_version,
789 const char *target_version,
791 bool *source_option_p, bool *target_option_p)
793 /* The cache depends on the source_version and target_version. */
801 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
802 struct result_t *resultp;
804 resultp = &result_cache[source_version_index (source_version)]
805 [target_version_index (target_version)];
806 if (!resultp->tested)
809 struct temp_dir *tmpdir;
810 char *conftest_file_name;
811 char *compiled_file_name;
812 const char *java_sources[1];
815 tmpdir = create_temp_dir ("java", NULL, false);
820 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
821 if (write_temp_file (tmpdir, conftest_file_name,
822 get_goodcode_snippet (source_version)))
824 free (conftest_file_name);
825 cleanup_temp_dir (tmpdir);
830 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
831 register_temp_file (tmpdir, compiled_file_name);
833 java_sources[0] = conftest_file_name;
834 if (!compile_using_envjavac (javac,
835 java_sources, 1, tmpdir->dir_name,
836 false, false, false, true)
837 && stat (compiled_file_name, &statbuf) >= 0
838 && get_classfile_version (compiled_file_name)
839 <= corresponding_classfile_version (target_version))
841 /* $JAVAC compiled conftest.java successfully. */
842 /* Try adding -source option if it is useful. */
844 xasprintf ("%s -source %s", javac, source_version);
846 unlink (compiled_file_name);
848 java_sources[0] = conftest_file_name;
849 if (!compile_using_envjavac (javac_source,
850 java_sources, 1, tmpdir->dir_name,
851 false, false, false, true)
852 && stat (compiled_file_name, &statbuf) >= 0
853 && get_classfile_version (compiled_file_name)
854 <= corresponding_classfile_version (target_version))
856 const char *failcode = get_failcode_snippet (source_version);
858 if (failcode != NULL)
860 free (compiled_file_name);
861 free (conftest_file_name);
864 concatenated_pathname (tmpdir->dir_name,
867 if (write_temp_file (tmpdir, conftest_file_name, failcode))
869 free (conftest_file_name);
871 cleanup_temp_dir (tmpdir);
876 concatenated_pathname (tmpdir->dir_name,
877 "conftestfail.class",
879 register_temp_file (tmpdir, compiled_file_name);
881 java_sources[0] = conftest_file_name;
882 if (!compile_using_envjavac (javac,
885 false, false, false, true)
886 && stat (compiled_file_name, &statbuf) >= 0)
888 unlink (compiled_file_name);
890 java_sources[0] = conftest_file_name;
891 if (compile_using_envjavac (javac_source,
894 false, false, false, true))
895 /* $JAVAC compiled conftestfail.java successfully, and
896 "$JAVAC -source $source_version" rejects it. So the
897 -source option is useful. */
898 resultp->source_option = true;
905 resultp->usable = true;
909 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
910 option but no -source option.) */
912 xasprintf ("%s -target %s", javac, target_version);
914 unlink (compiled_file_name);
916 java_sources[0] = conftest_file_name;
917 if (!compile_using_envjavac (javac_target,
918 java_sources, 1, tmpdir->dir_name,
919 false, false, false, true)
920 && stat (compiled_file_name, &statbuf) >= 0
921 && get_classfile_version (compiled_file_name)
922 <= corresponding_classfile_version (target_version))
924 /* "$JAVAC -target $target_version" compiled conftest.java
926 /* Try adding -source option if it is useful. */
927 char *javac_target_source =
928 xasprintf ("%s -source %s", javac_target, source_version);
930 unlink (compiled_file_name);
932 java_sources[0] = conftest_file_name;
933 if (!compile_using_envjavac (javac_target_source,
934 java_sources, 1, tmpdir->dir_name,
935 false, false, false, true)
936 && stat (compiled_file_name, &statbuf) >= 0
937 && get_classfile_version (compiled_file_name)
938 <= corresponding_classfile_version (target_version))
940 const char *failcode = get_failcode_snippet (source_version);
942 if (failcode != NULL)
944 free (compiled_file_name);
945 free (conftest_file_name);
948 concatenated_pathname (tmpdir->dir_name,
951 if (write_temp_file (tmpdir, conftest_file_name,
954 free (conftest_file_name);
955 free (javac_target_source);
957 cleanup_temp_dir (tmpdir);
962 concatenated_pathname (tmpdir->dir_name,
963 "conftestfail.class",
965 register_temp_file (tmpdir, compiled_file_name);
967 java_sources[0] = conftest_file_name;
968 if (!compile_using_envjavac (javac_target,
971 false, false, false, true)
972 && stat (compiled_file_name, &statbuf) >= 0)
974 unlink (compiled_file_name);
976 java_sources[0] = conftest_file_name;
977 if (compile_using_envjavac (javac_target_source,
982 /* "$JAVAC -target $target_version" compiled
983 conftestfail.java successfully, and
984 "$JAVAC -target $target_version -source $source_version"
985 rejects it. So the -source option is useful. */
986 resultp->source_option = true;
991 free (javac_target_source);
993 resultp->target_option = true;
994 resultp->usable = true;
998 /* Maybe this -target option requires a -source option? Try with
999 -target and -source options. (Supported by Sun javac 1.4 and
1001 char *javac_target_source =
1002 xasprintf ("%s -source %s", javac_target, source_version);
1004 unlink (compiled_file_name);
1006 java_sources[0] = conftest_file_name;
1007 if (!compile_using_envjavac (javac_target_source,
1008 java_sources, 1, tmpdir->dir_name,
1009 false, false, false, true)
1010 && stat (compiled_file_name, &statbuf) >= 0
1011 && get_classfile_version (compiled_file_name)
1012 <= corresponding_classfile_version (target_version))
1014 /* "$JAVAC -target $target_version -source $source_version"
1015 compiled conftest.java successfully. */
1016 resultp->source_option = true;
1017 resultp->target_option = true;
1018 resultp->usable = true;
1021 free (javac_target_source);
1024 free (javac_target);
1027 free (compiled_file_name);
1028 free (conftest_file_name);
1030 resultp->tested = true;
1033 *usablep = resultp->usable;
1034 *source_option_p = resultp->source_option;
1035 *target_option_p = resultp->target_option;
1040 is_gcj_present (void)
1042 static bool gcj_tested;
1043 static bool gcj_present;
1047 /* Test for presence of gcj:
1048 "gcj --version 2> /dev/null | \
1049 sed -e 's,^[^0-9]*,,' -e 1q | \
1050 sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null" */
1057 argv[1] = "--version";
1059 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1061 gcj_present = false;
1064 /* Read the subprocess output, drop all lines except the first,
1065 drop all characters before the first digit, and test whether
1066 the remaining string starts with a digit >= 3, but not with
1071 while (safe_read (fd[0], &c[count], 1) > 0)
1073 if (c[count] == '\n')
1077 if (!(c[0] >= '0' && c[0] <= '9'))
1079 gcj_present = (c[0] >= '3');
1084 if (c[0] == '3' && c[1] == '.'
1085 && (c[2] == '0' || c[2] == '1'))
1086 gcj_present = false;
1090 while (safe_read (fd[0], &c[0], 1) > 0)
1095 /* Remove zombie process from process list, and retrieve exit
1098 wait_subprocess (child, "gcj", false, true, true, false);
1099 if (exitstatus != 0)
1100 gcj_present = false;
1105 /* See if libgcj.jar is well installed. */
1106 struct temp_dir *tmpdir;
1108 tmpdir = create_temp_dir ("java", NULL, false);
1110 gcj_present = false;
1113 char *conftest_file_name;
1115 conftest_file_name =
1116 concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
1118 if (write_temp_file (tmpdir, conftest_file_name,
1119 "public class conftestlib {\n"
1120 " public static void main (String[] args) {\n"
1123 gcj_present = false;
1126 char *compiled_file_name;
1127 const char *java_sources[1];
1129 compiled_file_name =
1130 concatenated_pathname (tmpdir->dir_name,
1131 "conftestlib.class",
1133 register_temp_file (tmpdir, compiled_file_name);
1135 java_sources[0] = conftest_file_name;
1136 if (compile_using_gcj (java_sources, 1, false,
1138 false, false, false, true))
1139 gcj_present = false;
1141 free (compiled_file_name);
1143 free (conftest_file_name);
1145 cleanup_temp_dir (tmpdir);
1154 /* Test gcj can be used for compiling with target_version = 1.4 and
1155 source_version = 1.4.
1156 Return a failure indicator (true upon error). */
1158 is_gcj_14_14_usable (bool *usablep)
1160 static bool gcj_tested;
1161 static bool gcj_usable;
1166 struct temp_dir *tmpdir;
1167 char *conftest_file_name;
1168 char *compiled_file_name;
1169 const char *java_sources[1];
1170 struct stat statbuf;
1172 tmpdir = create_temp_dir ("java", NULL, false);
1176 conftest_file_name =
1177 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1178 if (write_temp_file (tmpdir, conftest_file_name,
1179 get_goodcode_snippet ("1.4")))
1181 free (conftest_file_name);
1182 cleanup_temp_dir (tmpdir);
1186 compiled_file_name =
1187 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1188 register_temp_file (tmpdir, compiled_file_name);
1190 java_sources[0] = conftest_file_name;
1191 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1192 false, false, false, true)
1193 && stat (compiled_file_name, &statbuf) >= 0)
1194 /* Compilation succeeded. */
1197 free (compiled_file_name);
1198 free (conftest_file_name);
1200 cleanup_temp_dir (tmpdir);
1205 *usablep = gcj_usable;
1209 /* Test whether gcj can be used for compiling with target_version = 1.4 and
1210 source_version = 1.3.
1211 Return a failure indicator (true upon error). */
1213 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1215 static bool gcj_tested;
1216 static bool gcj_usable;
1217 static bool gcj_need_no_assert_option;
1221 /* Try gcj and "gcj -fno-assert". But add -fno-assert only if
1222 it works (not gcj < 3.3). */
1223 struct temp_dir *tmpdir;
1224 char *conftest_file_name;
1225 char *compiled_file_name;
1226 const char *java_sources[1];
1227 struct stat statbuf;
1229 tmpdir = create_temp_dir ("java", NULL, false);
1233 conftest_file_name =
1234 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1235 if (write_temp_file (tmpdir, conftest_file_name,
1236 get_goodcode_snippet ("1.3")))
1238 free (conftest_file_name);
1239 cleanup_temp_dir (tmpdir);
1243 compiled_file_name =
1244 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1245 register_temp_file (tmpdir, compiled_file_name);
1247 java_sources[0] = conftest_file_name;
1248 if (!compile_using_gcj (java_sources, 1, true, tmpdir->dir_name,
1249 false, false, false, true)
1250 && stat (compiled_file_name, &statbuf) >= 0)
1251 /* Compilation succeeded. */
1254 gcj_need_no_assert_option = true;
1258 unlink (compiled_file_name);
1260 java_sources[0] = conftest_file_name;
1261 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1262 false, false, false, true)
1263 && stat (compiled_file_name, &statbuf) >= 0)
1264 /* Compilation succeeded. */
1267 gcj_need_no_assert_option = false;
1271 free (compiled_file_name);
1272 free (conftest_file_name);
1274 cleanup_temp_dir (tmpdir);
1279 *usablep = gcj_usable;
1280 *need_no_assert_option_p = gcj_need_no_assert_option;
1285 is_javac_present (void)
1287 static bool javac_tested;
1288 static bool javac_present;
1292 /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
1298 exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1300 javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1301 javac_tested = true;
1304 return javac_present;
1307 /* Test whether javac can be used and whether it needs a -source and/or
1309 Return a failure indicator (true upon error). */
1311 is_javac_usable (const char *source_version, const char *target_version,
1312 bool *usablep, bool *source_option_p, bool *target_option_p)
1314 /* The cache depends on the source_version and target_version. */
1322 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1323 struct result_t *resultp;
1325 resultp = &result_cache[source_version_index (source_version)]
1326 [target_version_index (target_version)];
1327 if (!resultp->tested)
1330 struct temp_dir *tmpdir;
1331 char *conftest_file_name;
1332 char *compiled_file_name;
1333 const char *java_sources[1];
1334 struct stat statbuf;
1336 tmpdir = create_temp_dir ("java", NULL, false);
1340 conftest_file_name =
1341 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1342 if (write_temp_file (tmpdir, conftest_file_name,
1343 get_goodcode_snippet (source_version)))
1345 free (conftest_file_name);
1346 cleanup_temp_dir (tmpdir);
1350 compiled_file_name =
1351 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1352 register_temp_file (tmpdir, compiled_file_name);
1354 java_sources[0] = conftest_file_name;
1355 if (!compile_using_javac (java_sources, 1,
1356 false, source_version,
1357 false, target_version,
1358 tmpdir->dir_name, false, false, false, true)
1359 && stat (compiled_file_name, &statbuf) >= 0
1360 && get_classfile_version (compiled_file_name)
1361 <= corresponding_classfile_version (target_version))
1363 /* javac compiled conftest.java successfully. */
1364 /* Try adding -source option if it is useful. */
1365 unlink (compiled_file_name);
1367 java_sources[0] = conftest_file_name;
1368 if (!compile_using_javac (java_sources, 1,
1369 true, source_version,
1370 false, target_version,
1371 tmpdir->dir_name, false, false, false, true)
1372 && stat (compiled_file_name, &statbuf) >= 0
1373 && get_classfile_version (compiled_file_name)
1374 <= corresponding_classfile_version (target_version))
1376 const char *failcode = get_failcode_snippet (source_version);
1378 if (failcode != NULL)
1380 free (compiled_file_name);
1381 free (conftest_file_name);
1383 conftest_file_name =
1384 concatenated_pathname (tmpdir->dir_name,
1385 "conftestfail.java",
1387 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1389 free (conftest_file_name);
1390 cleanup_temp_dir (tmpdir);
1394 compiled_file_name =
1395 concatenated_pathname (tmpdir->dir_name,
1396 "conftestfail.class",
1398 register_temp_file (tmpdir, compiled_file_name);
1400 java_sources[0] = conftest_file_name;
1401 if (!compile_using_javac (java_sources, 1,
1402 false, source_version,
1403 false, target_version,
1405 false, false, false, true)
1406 && stat (compiled_file_name, &statbuf) >= 0)
1408 unlink (compiled_file_name);
1410 java_sources[0] = conftest_file_name;
1411 if (compile_using_javac (java_sources, 1,
1412 true, source_version,
1413 false, target_version,
1415 false, false, false, true))
1416 /* javac compiled conftestfail.java successfully, and
1417 "javac -source $source_version" rejects it. So the
1418 -source option is useful. */
1419 resultp->source_option = true;
1424 resultp->usable = true;
1428 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1429 option but no -source option.) */
1430 unlink (compiled_file_name);
1432 java_sources[0] = conftest_file_name;
1433 if (!compile_using_javac (java_sources, 1,
1434 false, source_version,
1435 true, target_version,
1437 false, false, false, true)
1438 && stat (compiled_file_name, &statbuf) >= 0
1439 && get_classfile_version (compiled_file_name)
1440 <= corresponding_classfile_version (target_version))
1442 /* "javac -target $target_version" compiled conftest.java
1444 /* Try adding -source option if it is useful. */
1445 unlink (compiled_file_name);
1447 java_sources[0] = conftest_file_name;
1448 if (!compile_using_javac (java_sources, 1,
1449 true, source_version,
1450 true, target_version,
1452 false, false, false, true)
1453 && stat (compiled_file_name, &statbuf) >= 0
1454 && get_classfile_version (compiled_file_name)
1455 <= corresponding_classfile_version (target_version))
1457 const char *failcode = get_failcode_snippet (source_version);
1459 if (failcode != NULL)
1461 free (compiled_file_name);
1462 free (conftest_file_name);
1464 conftest_file_name =
1465 concatenated_pathname (tmpdir->dir_name,
1466 "conftestfail.java",
1468 if (write_temp_file (tmpdir, conftest_file_name,
1471 free (conftest_file_name);
1472 cleanup_temp_dir (tmpdir);
1476 compiled_file_name =
1477 concatenated_pathname (tmpdir->dir_name,
1478 "conftestfail.class",
1480 register_temp_file (tmpdir, compiled_file_name);
1482 java_sources[0] = conftest_file_name;
1483 if (!compile_using_javac (java_sources, 1,
1484 false, source_version,
1485 true, target_version,
1487 false, false, false, true)
1488 && stat (compiled_file_name, &statbuf) >= 0)
1490 unlink (compiled_file_name);
1492 java_sources[0] = conftest_file_name;
1493 if (compile_using_javac (java_sources, 1,
1494 true, source_version,
1495 true, target_version,
1497 false, false, false, true))
1498 /* "javac -target $target_version" compiled
1499 conftestfail.java successfully, and
1500 "javac -target $target_version -source $source_version"
1501 rejects it. So the -source option is useful. */
1502 resultp->source_option = true;
1507 resultp->target_option = true;
1508 resultp->usable = true;
1512 /* Maybe this -target option requires a -source option? Try with
1513 -target and -source options. (Supported by Sun javac 1.4 and
1515 unlink (compiled_file_name);
1517 java_sources[0] = conftest_file_name;
1518 if (!compile_using_javac (java_sources, 1,
1519 true, source_version,
1520 true, target_version,
1522 false, false, false, true)
1523 && stat (compiled_file_name, &statbuf) >= 0
1524 && get_classfile_version (compiled_file_name)
1525 <= corresponding_classfile_version (target_version))
1527 /* "javac -target $target_version -source $source_version"
1528 compiled conftest.java successfully. */
1529 resultp->source_option = true;
1530 resultp->target_option = true;
1531 resultp->usable = true;
1536 free (compiled_file_name);
1537 free (conftest_file_name);
1539 resultp->tested = true;
1542 *usablep = resultp->usable;
1543 *source_option_p = resultp->source_option;
1544 *target_option_p = resultp->target_option;
1549 is_jikes_present (void)
1551 static bool jikes_tested;
1552 static bool jikes_present;
1556 /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1" */
1562 exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
1564 jikes_present = (exitstatus == 0 || exitstatus == 1);
1565 jikes_tested = true;
1568 return jikes_present;
1571 /* ============================= Main function ============================= */
1574 compile_java_class (const char * const *java_sources,
1575 unsigned int java_sources_count,
1576 const char * const *classpaths,
1577 unsigned int classpaths_count,
1578 const char *source_version,
1579 const char *target_version,
1580 const char *directory,
1581 bool optimize, bool debug,
1582 bool use_minimal_classpath,
1586 char *old_JAVA_HOME;
1589 const char *javac = getenv ("JAVAC");
1590 if (javac != NULL && javac[0] != '\0')
1592 bool usable = false;
1593 bool no_assert_option = false;
1594 bool source_option = false;
1595 bool target_option = false;
1597 if (target_version == NULL)
1598 target_version = default_target_version ();
1600 if (is_envjavac_gcj (javac))
1602 /* It's a version of gcj. Ignore the version of the class files
1604 if (strcmp (target_version, "1.4") == 0
1605 && strcmp (source_version, "1.4") == 0)
1607 if (is_envjavac_gcj_14_14_usable (javac, &usable))
1613 else if (strcmp (target_version, "1.4") == 0
1614 && strcmp (source_version, "1.3") == 0)
1616 if (is_envjavac_gcj_14_13_usable (javac,
1617 &usable, &no_assert_option))
1626 /* It's not gcj. Assume the classfile versions are correct. */
1627 if (is_envjavac_nongcj_usable (javac,
1628 source_version, target_version,
1630 &source_option, &target_option))
1639 char *old_classpath;
1640 char *javac_with_options;
1642 /* Set CLASSPATH. */
1644 set_classpath (classpaths, classpaths_count, false, verbose);
1646 javac_with_options =
1648 ? xasprintf ("%s -fno-assert", javac)
1649 : xasprintf ("%s%s%s%s%s",
1651 source_option ? " -source " : "",
1652 source_option ? source_version : "",
1653 target_option ? " -target " : "",
1654 target_option ? target_version : ""));
1656 err = compile_using_envjavac (javac_with_options,
1657 java_sources, java_sources_count,
1658 directory, optimize, debug, verbose,
1661 free (javac_with_options);
1663 /* Reset CLASSPATH. */
1664 reset_classpath (old_classpath);
1671 /* Unset the JAVA_HOME environment variable. */
1672 old_JAVA_HOME = getenv ("JAVA_HOME");
1673 if (old_JAVA_HOME != NULL)
1675 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
1676 unsetenv ("JAVA_HOME");
1679 if (is_gcj_present ())
1681 /* Test whether it supports the desired target-version and
1682 source-version. But ignore the version of the class files that
1684 bool usable = false;
1685 bool no_assert_option = false;
1687 if (target_version == NULL)
1688 target_version = default_target_version ();
1690 if (strcmp (target_version, "1.4") == 0
1691 && strcmp (source_version, "1.4") == 0)
1693 if (is_gcj_14_14_usable (&usable))
1699 else if (strcmp (target_version, "1.4") == 0
1700 && strcmp (source_version, "1.3") == 0)
1702 if (is_gcj_14_13_usable (&usable, &no_assert_option))
1711 char *old_classpath;
1713 /* Set CLASSPATH. We could also use the --CLASSPATH=... option
1714 of gcj. Note that --classpath=... option is different: its
1715 argument should also contain gcj's libgcj.jar, but we don't
1716 know its location. */
1718 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1721 err = compile_using_gcj (java_sources, java_sources_count,
1723 directory, optimize, debug, verbose, false);
1725 /* Reset CLASSPATH. */
1726 reset_classpath (old_classpath);
1732 if (is_javac_present ())
1734 bool usable = false;
1735 bool source_option = false;
1736 bool target_option = false;
1738 if (target_version == NULL)
1739 target_version = default_target_version ();
1741 if (is_javac_usable (source_version, target_version,
1742 &usable, &source_option, &target_option))
1750 char *old_classpath;
1752 /* Set CLASSPATH. We don't use the "-classpath ..." option because
1753 in JDK 1.1.x its argument should also contain the JDK's
1754 classes.zip, but we don't know its location. (In JDK 1.3.0 it
1757 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1760 err = compile_using_javac (java_sources, java_sources_count,
1761 source_option, source_version,
1762 target_option, target_version,
1763 directory, optimize, debug, verbose,
1766 /* Reset CLASSPATH. */
1767 reset_classpath (old_classpath);
1773 if (is_jikes_present ())
1775 /* Test whether it supports the desired target-version and
1777 bool usable = (strcmp (source_version, "1.3") == 0);
1781 char *old_classpath;
1783 /* Set CLASSPATH. We could also use the "-classpath ..." option.
1784 Since jikes doesn't come with its own standard library, it
1785 needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
1786 To increase the chance of success, we reuse the current CLASSPATH
1787 if the user has set it. */
1789 set_classpath (classpaths, classpaths_count, false, verbose);
1791 err = compile_using_jikes (java_sources, java_sources_count,
1792 directory, optimize, debug, verbose,
1795 /* Reset CLASSPATH. */
1796 reset_classpath (old_classpath);
1802 error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
1806 if (old_JAVA_HOME != NULL)
1808 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
1809 free (old_JAVA_HOME);