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. */
33 #include <sys/types.h>
36 #include "javaversion.h"
39 #include "wait-process.h"
40 #include "classpath.h"
43 #include "binary-io.h"
44 #include "safe-read.h"
49 #include "fwriteerror.h"
50 #include "clean-temp.h"
52 #include "xvasprintf.h"
56 #define _(str) gettext (str)
59 /* Survey of Java compilers.
61 A = does it work without CLASSPATH being set
62 C = option to set CLASSPATH, other than setting it in the environment
63 O = option for optimizing
64 g = option for debugging
67 Program from A C O g T
69 $JAVAC unknown N n/a -O -g true
70 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
71 javac JDK 1.1.8 Y -classpath P -O -g javac 2>/dev/null; test $? = 1
72 javac JDK 1.3.0 Y -classpath P -O -g javac 2>/dev/null; test $? -le 2
73 jikes Jikes 1.14 N -classpath P -O -g jikes 2>/dev/null; test $? = 1
75 All compilers support the option "-d DIRECTORY" for the base directory
76 of the classes to be written.
78 The CLASSPATH is a colon separated list of pathnames. (On Windows: a
79 semicolon separated list of pathnames.)
81 We try the Java compilers in the following order:
82 1. getenv ("JAVAC"), because the user must be able to override our
84 2. "gcj -C", because it is a completely free compiler,
85 3. "javac", because it is a standard compiler,
86 4. "jikes", comes last because it has some deviating interpretation
87 of the Java Language Specification and because it requires a
88 CLASSPATH environment variable.
90 We unset the JAVA_HOME environment variable, because a wrong setting of
91 this variable can confuse the JDK's javac.
94 /* Return the default target_version. */
96 default_target_version (void)
98 /* Use a cache. Assumes that the PATH environment variable doesn't change
99 during the lifetime of the program. */
100 static const char *java_version_cache;
101 if (java_version_cache == NULL)
103 /* Determine the version from the found JVM. */
104 java_version_cache = javaexec_version ();
105 if (java_version_cache == NULL
106 || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
107 && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
108 && java_version_cache[3] == '\0'))
109 java_version_cache = "1.1";
111 return java_version_cache;
114 /* ======================= Source version dependent ======================= */
116 /* Convert a source version to an index. */
117 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
119 source_version_index (const char *source_version)
121 if (source_version[0] == '1' && source_version[1] == '.'
122 && (source_version[2] >= '3' && source_version[2] <= '5')
123 && source_version[3] == '\0')
124 return source_version[2] - '3';
125 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
129 /* Return a snippet of code that should compile in the given source version. */
131 get_goodcode_snippet (const char *source_version)
133 if (strcmp (source_version, "1.3") == 0)
134 return "class conftest {}\n";
135 if (strcmp (source_version, "1.4") == 0)
136 return "class conftest { static { assert(true); } }\n";
137 if (strcmp (source_version, "1.5") == 0)
138 return "class conftest<T> { T foo() { return null; } }\n";
139 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
143 /* Return a snippet of code that should fail to compile in the given source
144 version, or NULL (standing for a snippet that would fail to compile with
147 get_failcode_snippet (const char *source_version)
149 if (strcmp (source_version, "1.3") == 0)
150 return "class conftestfail { static { assert(true); } }\n";
151 if (strcmp (source_version, "1.4") == 0)
152 return "class conftestfail<T> { T foo() { return null; } }\n";
153 if (strcmp (source_version, "1.5") == 0)
155 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
159 /* ======================= Target version dependent ======================= */
161 /* Convert a target version to an index. */
162 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
164 target_version_index (const char *target_version)
166 if (target_version[0] == '1' && target_version[1] == '.'
167 && (target_version[2] >= '1' && target_version[2] <= '6')
168 && target_version[3] == '\0')
169 return target_version[2] - '1';
170 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
174 /* Return the class file version number corresponding to a given target
177 corresponding_classfile_version (const char *target_version)
179 if (strcmp (target_version, "1.1") == 0)
181 if (strcmp (target_version, "1.2") == 0)
183 if (strcmp (target_version, "1.3") == 0)
185 if (strcmp (target_version, "1.4") == 0)
187 if (strcmp (target_version, "1.5") == 0)
189 if (strcmp (target_version, "1.6") == 0)
191 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
195 /* ======================== Compilation subroutines ======================== */
197 /* Try to compile a set of Java sources with $JAVAC.
198 Return a failure indicator (true upon error). */
200 compile_using_envjavac (const char *javac,
201 const char * const *java_sources,
202 unsigned int java_sources_count,
203 const char *directory,
204 bool optimize, bool debug,
205 bool verbose, bool null_stderr)
207 /* Because $JAVAC may consist of a command and options, we use the
208 shell. Because $JAVAC has been set by the user, we leave all
209 environment variables in place, including JAVA_HOME, and we don't
210 erase the user's CLASSPATH. */
212 unsigned int command_length;
219 command_length = strlen (javac);
224 if (directory != NULL)
225 command_length += 4 + shell_quote_length (directory);
226 for (i = 0; i < java_sources_count; i++)
227 command_length += 1 + shell_quote_length (java_sources[i]);
230 command = (char *) xallocsa (command_length);
232 /* Don't shell_quote $JAVAC, because it may consist of a command
234 memcpy (p, javac, strlen (javac));
238 memcpy (p, " -O", 3);
243 memcpy (p, " -g", 3);
246 if (directory != NULL)
248 memcpy (p, " -d ", 4);
250 p = shell_quote_copy (p, directory);
252 for (i = 0; i < java_sources_count; i++)
255 p = shell_quote_copy (p, java_sources[i]);
258 /* Ensure command_length was correctly calculated. */
259 if (p - command > command_length)
263 printf ("%s\n", command);
269 exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
270 null_stderr, true, true);
271 err = (exitstatus != 0);
278 /* Try to compile a set of Java sources with gcj.
279 Return a failure indicator (true upon error). */
281 compile_using_gcj (const char * const *java_sources,
282 unsigned int java_sources_count,
283 bool no_assert_option,
284 const char *directory,
285 bool optimize, bool debug,
286 bool verbose, bool null_stderr)
296 2 + (no_assert_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
297 + (directory != NULL ? 2 : 0) + java_sources_count;
298 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
303 if (no_assert_option)
304 *argp++ = "-fno-assert";
309 if (directory != NULL)
312 *argp++ = (char *) directory;
314 for (i = 0; i < java_sources_count; i++)
315 *argp++ = (char *) java_sources[i];
317 /* Ensure argv length was correctly calculated. */
318 if (argp - argv != argc)
323 char *command = shell_quote_argv (argv);
324 printf ("%s\n", command);
328 exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
330 err = (exitstatus != 0);
337 /* Try to compile a set of Java sources with javac.
338 Return a failure indicator (true upon error). */
340 compile_using_javac (const char * const *java_sources,
341 unsigned int java_sources_count,
342 bool source_option, const char *source_version,
343 bool target_option, const char *target_version,
344 const char *directory,
345 bool optimize, bool debug,
346 bool verbose, bool null_stderr)
356 1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
357 + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
358 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
365 *argp++ = (char *) source_version;
370 *argp++ = (char *) target_version;
376 if (directory != NULL)
379 *argp++ = (char *) directory;
381 for (i = 0; i < java_sources_count; i++)
382 *argp++ = (char *) java_sources[i];
384 /* Ensure argv length was correctly calculated. */
385 if (argp - argv != argc)
390 char *command = shell_quote_argv (argv);
391 printf ("%s\n", command);
395 exitstatus = execute ("javac", "javac", argv, false, false, false,
396 null_stderr, true, true);
397 err = (exitstatus != 0);
404 /* Try to compile a set of Java sources with jikes.
405 Return a failure indicator (true upon error). */
407 compile_using_jikes (const char * const *java_sources,
408 unsigned int java_sources_count,
409 const char *directory,
410 bool optimize, bool debug,
411 bool verbose, bool null_stderr)
421 1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
422 + java_sources_count;
423 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
431 if (directory != NULL)
434 *argp++ = (char *) directory;
436 for (i = 0; i < java_sources_count; i++)
437 *argp++ = (char *) java_sources[i];
439 /* Ensure argv length was correctly calculated. */
440 if (argp - argv != argc)
445 char *command = shell_quote_argv (argv);
446 printf ("%s\n", command);
450 exitstatus = execute ("jikes", "jikes", argv, false, false, false,
451 null_stderr, true, true);
452 err = (exitstatus != 0);
459 /* ====================== Usability test subroutines ====================== */
461 /* Write a given contents to a temporary file.
462 FILE_NAME is the name of a file inside TMPDIR that is known not to exist
464 Return a failure indicator (true upon error). */
466 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
467 const char *contents)
471 register_temp_file (tmpdir, file_name);
472 fp = fopen (file_name, "w");
475 error (0, errno, _("failed to create \"%s\""), file_name);
476 unregister_temp_file (tmpdir, file_name);
479 fputs (contents, fp);
480 if (fwriteerror (fp))
482 error (0, errno, _("error while writing \"%s\" file"), file_name);
488 /* Return the class file version number of a class file on disk. */
490 get_classfile_version (const char *compiled_file_name)
492 unsigned char header[8];
495 /* Open the class file. */
496 fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
499 /* Read its first 8 bytes. */
500 if (safe_read (fd, header, 8) == 8)
502 /* Verify the class file signature. */
503 if (header[0] == 0xCA && header[1] == 0xFE
504 && header[2] == 0xBA && header[3] == 0xBE)
510 /* Could not get the class file version. Return a very large one. */
514 /* Return true if $JAVAC is a version of gcj. */
516 is_envjavac_gcj (const char *javac)
518 static bool envjavac_tested;
519 static bool envjavac_gcj;
521 if (!envjavac_tested)
523 /* Test whether $JAVAC is gcj:
524 "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null" */
525 unsigned int command_length;
537 /* Setup the command "$JAVAC --version". */
538 command_length = strlen (javac) + 1 + 9 + 1;
539 command = (char *) xallocsa (command_length);
541 /* Don't shell_quote $JAVAC, because it may consist of a command
543 memcpy (p, javac, strlen (javac));
545 memcpy (p, " --version", 1 + 9 + 1);
547 /* Ensure command_length was correctly calculated. */
548 if (p - command > command_length)
551 /* Call $JAVAC --version 2>/dev/null. */
556 child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
561 /* Retrieve its result. */
562 fp = fdopen (fd[0], "r");
566 line = NULL; linesize = 0;
567 linelen = getline (&line, &linesize, fp);
568 if (linelen == (size_t)(-1))
573 envjavac_gcj = (strstr (line, "gcj") != NULL);
577 /* Remove zombie process from process list, and retrieve exit status. */
578 exitstatus = wait_subprocess (child, javac, true, true, true, false);
580 envjavac_gcj = false;
585 envjavac_tested = true;
591 /* Test whether $JAVAC, known to be a version of gcj, can be used for
592 compiling with target_version = 1.4 and source_version = 1.4.
593 Return a failure indicator (true upon error). */
595 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
597 static bool envjavac_tested;
598 static bool envjavac_usable;
600 if (!envjavac_tested)
603 struct temp_dir *tmpdir;
604 char *conftest_file_name;
605 char *compiled_file_name;
606 const char *java_sources[1];
609 tmpdir = create_temp_dir ("java", NULL, false);
614 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
615 if (write_temp_file (tmpdir, conftest_file_name,
616 get_goodcode_snippet ("1.4")))
618 free (conftest_file_name);
619 cleanup_temp_dir (tmpdir);
624 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
625 register_temp_file (tmpdir, compiled_file_name);
627 java_sources[0] = conftest_file_name;
628 if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
629 false, false, false, true)
630 && stat (compiled_file_name, &statbuf) >= 0)
631 /* Compilation succeeded. */
632 envjavac_usable = true;
634 free (compiled_file_name);
635 free (conftest_file_name);
637 cleanup_temp_dir (tmpdir);
639 envjavac_tested = true;
642 *usablep = envjavac_usable;
646 /* Test whether $JAVAC, known to be a version of gcj, can be used for
647 compiling with target_version = 1.4 and source_version = 1.3.
648 Return a failure indicator (true upon error). */
650 is_envjavac_gcj_14_13_usable (const char *javac,
651 bool *usablep, bool *need_no_assert_option_p)
653 static bool envjavac_tested;
654 static bool envjavac_usable;
655 static bool envjavac_need_no_assert_option;
657 if (!envjavac_tested)
659 /* Try $JAVAC and "$JAVAC -fno-assert". But add -fno-assert only if
660 it makes a difference. (It could already be part of $JAVAC.) */
661 struct temp_dir *tmpdir;
662 char *conftest_file_name;
663 char *compiled_file_name;
664 const char *java_sources[1];
667 char *javac_noassert;
668 bool javac_noassert_works;
670 tmpdir = create_temp_dir ("java", NULL, false);
675 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
676 if (write_temp_file (tmpdir, conftest_file_name,
677 get_goodcode_snippet ("1.3")))
679 free (conftest_file_name);
680 cleanup_temp_dir (tmpdir);
685 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
686 register_temp_file (tmpdir, compiled_file_name);
688 java_sources[0] = conftest_file_name;
689 if (!compile_using_envjavac (javac,
690 java_sources, 1, tmpdir->dir_name,
691 false, false, false, true)
692 && stat (compiled_file_name, &statbuf) >= 0)
693 /* Compilation succeeded. */
698 unlink (compiled_file_name);
700 javac_noassert = xasprintf ("%s -fno-assert", javac);
702 java_sources[0] = conftest_file_name;
703 if (!compile_using_envjavac (javac_noassert,
704 java_sources, 1, tmpdir->dir_name,
705 false, false, false, true)
706 && stat (compiled_file_name, &statbuf) >= 0)
707 /* Compilation succeeded. */
708 javac_noassert_works = true;
710 javac_noassert_works = false;
712 free (compiled_file_name);
713 free (conftest_file_name);
715 if (javac_works && javac_noassert_works)
718 concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
720 if (write_temp_file (tmpdir, conftest_file_name,
721 get_failcode_snippet ("1.3")))
723 free (conftest_file_name);
724 free (javac_noassert);
725 cleanup_temp_dir (tmpdir);
730 concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
732 register_temp_file (tmpdir, compiled_file_name);
734 java_sources[0] = conftest_file_name;
735 if (!compile_using_envjavac (javac,
736 java_sources, 1, tmpdir->dir_name,
737 false, false, false, true)
738 && stat (compiled_file_name, &statbuf) >= 0)
740 /* Compilation succeeded. */
741 unlink (compiled_file_name);
743 java_sources[0] = conftest_file_name;
744 if (!(!compile_using_envjavac (javac_noassert,
745 java_sources, 1, tmpdir->dir_name,
746 false, false, false, true)
747 && stat (compiled_file_name, &statbuf) >= 0))
748 /* Compilation failed. */
749 /* "$JAVAC -fno-assert" works better than $JAVAC. */
753 free (compiled_file_name);
754 free (conftest_file_name);
757 cleanup_temp_dir (tmpdir);
761 envjavac_usable = true;
762 envjavac_need_no_assert_option = false;
764 else if (javac_noassert_works)
766 envjavac_usable = true;
767 envjavac_need_no_assert_option = true;
770 envjavac_tested = true;
773 *usablep = envjavac_usable;
774 *need_no_assert_option_p = envjavac_need_no_assert_option;
778 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
779 whether it needs a -source and/or -target option.
780 Return a failure indicator (true upon error). */
782 is_envjavac_nongcj_usable (const char *javac,
783 const char *source_version,
784 const char *target_version,
786 bool *source_option_p, bool *target_option_p)
788 /* The cache depends on the source_version and target_version. */
796 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
797 struct result_t *resultp;
799 resultp = &result_cache[source_version_index (source_version)]
800 [target_version_index (target_version)];
801 if (!resultp->tested)
804 struct temp_dir *tmpdir;
805 char *conftest_file_name;
806 char *compiled_file_name;
807 const char *java_sources[1];
810 tmpdir = create_temp_dir ("java", NULL, false);
815 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
816 if (write_temp_file (tmpdir, conftest_file_name,
817 get_goodcode_snippet (source_version)))
819 free (conftest_file_name);
820 cleanup_temp_dir (tmpdir);
825 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
826 register_temp_file (tmpdir, compiled_file_name);
828 java_sources[0] = conftest_file_name;
829 if (!compile_using_envjavac (javac,
830 java_sources, 1, tmpdir->dir_name,
831 false, false, false, true)
832 && stat (compiled_file_name, &statbuf) >= 0
833 && get_classfile_version (compiled_file_name)
834 <= corresponding_classfile_version (target_version))
836 /* $JAVAC compiled conftest.java successfully. */
837 /* Try adding -source option if it is useful. */
839 xasprintf ("%s -source %s", javac, source_version);
841 unlink (compiled_file_name);
843 java_sources[0] = conftest_file_name;
844 if (!compile_using_envjavac (javac_source,
845 java_sources, 1, tmpdir->dir_name,
846 false, false, false, true)
847 && stat (compiled_file_name, &statbuf) >= 0
848 && get_classfile_version (compiled_file_name)
849 <= corresponding_classfile_version (target_version))
851 const char *failcode = get_failcode_snippet (source_version);
853 if (failcode != NULL)
855 free (compiled_file_name);
856 free (conftest_file_name);
859 concatenated_pathname (tmpdir->dir_name,
862 if (write_temp_file (tmpdir, conftest_file_name, failcode))
864 free (conftest_file_name);
866 cleanup_temp_dir (tmpdir);
871 concatenated_pathname (tmpdir->dir_name,
872 "conftestfail.class",
874 register_temp_file (tmpdir, compiled_file_name);
876 java_sources[0] = conftest_file_name;
877 if (!compile_using_envjavac (javac,
880 false, false, false, true)
881 && stat (compiled_file_name, &statbuf) >= 0)
883 unlink (compiled_file_name);
885 java_sources[0] = conftest_file_name;
886 if (compile_using_envjavac (javac_source,
889 false, false, false, true))
890 /* $JAVAC compiled conftestfail.java successfully, and
891 "$JAVAC -source $source_version" rejects it. So the
892 -source option is useful. */
893 resultp->source_option = true;
900 resultp->usable = true;
904 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
905 option but no -source option.) */
907 xasprintf ("%s -target %s", javac, target_version);
909 unlink (compiled_file_name);
911 java_sources[0] = conftest_file_name;
912 if (!compile_using_envjavac (javac_target,
913 java_sources, 1, tmpdir->dir_name,
914 false, false, false, true)
915 && stat (compiled_file_name, &statbuf) >= 0
916 && get_classfile_version (compiled_file_name)
917 <= corresponding_classfile_version (target_version))
919 /* "$JAVAC -target $target_version" compiled conftest.java
921 /* Try adding -source option if it is useful. */
922 char *javac_target_source =
923 xasprintf ("%s -source %s", javac_target, source_version);
925 unlink (compiled_file_name);
927 java_sources[0] = conftest_file_name;
928 if (!compile_using_envjavac (javac_target_source,
929 java_sources, 1, tmpdir->dir_name,
930 false, false, false, true)
931 && stat (compiled_file_name, &statbuf) >= 0
932 && get_classfile_version (compiled_file_name)
933 <= corresponding_classfile_version (target_version))
935 const char *failcode = get_failcode_snippet (source_version);
937 if (failcode != NULL)
939 free (compiled_file_name);
940 free (conftest_file_name);
943 concatenated_pathname (tmpdir->dir_name,
946 if (write_temp_file (tmpdir, conftest_file_name,
949 free (conftest_file_name);
950 free (javac_target_source);
952 cleanup_temp_dir (tmpdir);
957 concatenated_pathname (tmpdir->dir_name,
958 "conftestfail.class",
960 register_temp_file (tmpdir, compiled_file_name);
962 java_sources[0] = conftest_file_name;
963 if (!compile_using_envjavac (javac_target,
966 false, false, false, true)
967 && stat (compiled_file_name, &statbuf) >= 0)
969 unlink (compiled_file_name);
971 java_sources[0] = conftest_file_name;
972 if (compile_using_envjavac (javac_target_source,
977 /* "$JAVAC -target $target_version" compiled
978 conftestfail.java successfully, and
979 "$JAVAC -target $target_version -source $source_version"
980 rejects it. So the -source option is useful. */
981 resultp->source_option = true;
986 free (javac_target_source);
988 resultp->target_option = true;
989 resultp->usable = true;
993 /* Maybe this -target option requires a -source option? Try with
994 -target and -source options. (Supported by Sun javac 1.4 and
996 char *javac_target_source =
997 xasprintf ("%s -source %s", javac_target, source_version);
999 unlink (compiled_file_name);
1001 java_sources[0] = conftest_file_name;
1002 if (!compile_using_envjavac (javac_target_source,
1003 java_sources, 1, tmpdir->dir_name,
1004 false, false, false, true)
1005 && stat (compiled_file_name, &statbuf) >= 0
1006 && get_classfile_version (compiled_file_name)
1007 <= corresponding_classfile_version (target_version))
1009 /* "$JAVAC -target $target_version -source $source_version"
1010 compiled conftest.java successfully. */
1011 resultp->source_option = true;
1012 resultp->target_option = true;
1013 resultp->usable = true;
1016 free (javac_target_source);
1019 free (javac_target);
1022 free (compiled_file_name);
1023 free (conftest_file_name);
1025 resultp->tested = true;
1028 *usablep = resultp->usable;
1029 *source_option_p = resultp->source_option;
1030 *target_option_p = resultp->target_option;
1035 is_gcj_present (void)
1037 static bool gcj_tested;
1038 static bool gcj_present;
1042 /* Test for presence of gcj:
1043 "gcj --version 2> /dev/null | \
1044 sed -e 's,^[^0-9]*,,' -e 1q | \
1045 sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null" */
1052 argv[1] = "--version";
1054 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1056 gcj_present = false;
1059 /* Read the subprocess output, drop all lines except the first,
1060 drop all characters before the first digit, and test whether
1061 the remaining string starts with a digit >= 3, but not with
1066 while (safe_read (fd[0], &c[count], 1) > 0)
1068 if (c[count] == '\n')
1072 if (!(c[0] >= '0' && c[0] <= '9'))
1074 gcj_present = (c[0] >= '3');
1079 if (c[0] == '3' && c[1] == '.'
1080 && (c[2] == '0' || c[2] == '1'))
1081 gcj_present = false;
1085 while (safe_read (fd[0], &c[0], 1) > 0)
1090 /* Remove zombie process from process list, and retrieve exit
1093 wait_subprocess (child, "gcj", false, true, true, false);
1094 if (exitstatus != 0)
1095 gcj_present = false;
1100 /* See if libgcj.jar is well installed. */
1101 struct temp_dir *tmpdir;
1103 tmpdir = create_temp_dir ("java", NULL, false);
1105 gcj_present = false;
1108 char *conftest_file_name;
1110 conftest_file_name =
1111 concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
1113 if (write_temp_file (tmpdir, conftest_file_name,
1114 "public class conftestlib {\n"
1115 " public static void main (String[] args) {\n"
1118 gcj_present = false;
1121 char *compiled_file_name;
1122 const char *java_sources[1];
1124 compiled_file_name =
1125 concatenated_pathname (tmpdir->dir_name,
1126 "conftestlib.class",
1128 register_temp_file (tmpdir, compiled_file_name);
1130 java_sources[0] = conftest_file_name;
1131 if (compile_using_gcj (java_sources, 1, false,
1133 false, false, false, true))
1134 gcj_present = false;
1136 free (compiled_file_name);
1138 free (conftest_file_name);
1140 cleanup_temp_dir (tmpdir);
1149 /* Test gcj can be used for compiling with target_version = 1.4 and
1150 source_version = 1.4.
1151 Return a failure indicator (true upon error). */
1153 is_gcj_14_14_usable (bool *usablep)
1155 static bool gcj_tested;
1156 static bool gcj_usable;
1161 struct temp_dir *tmpdir;
1162 char *conftest_file_name;
1163 char *compiled_file_name;
1164 const char *java_sources[1];
1165 struct stat statbuf;
1167 tmpdir = create_temp_dir ("java", NULL, false);
1171 conftest_file_name =
1172 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1173 if (write_temp_file (tmpdir, conftest_file_name,
1174 get_goodcode_snippet ("1.4")))
1176 free (conftest_file_name);
1177 cleanup_temp_dir (tmpdir);
1181 compiled_file_name =
1182 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1183 register_temp_file (tmpdir, compiled_file_name);
1185 java_sources[0] = conftest_file_name;
1186 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1187 false, false, false, true)
1188 && stat (compiled_file_name, &statbuf) >= 0)
1189 /* Compilation succeeded. */
1192 free (compiled_file_name);
1193 free (conftest_file_name);
1195 cleanup_temp_dir (tmpdir);
1200 *usablep = gcj_usable;
1204 /* Test whether gcj can be used for compiling with target_version = 1.4 and
1205 source_version = 1.3.
1206 Return a failure indicator (true upon error). */
1208 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1210 static bool gcj_tested;
1211 static bool gcj_usable;
1212 static bool gcj_need_no_assert_option;
1216 /* Try gcj and "gcj -fno-assert". But add -fno-assert only if
1217 it works (not gcj < 3.3). */
1218 struct temp_dir *tmpdir;
1219 char *conftest_file_name;
1220 char *compiled_file_name;
1221 const char *java_sources[1];
1222 struct stat statbuf;
1224 tmpdir = create_temp_dir ("java", NULL, false);
1228 conftest_file_name =
1229 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1230 if (write_temp_file (tmpdir, conftest_file_name,
1231 get_goodcode_snippet ("1.3")))
1233 free (conftest_file_name);
1234 cleanup_temp_dir (tmpdir);
1238 compiled_file_name =
1239 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1240 register_temp_file (tmpdir, compiled_file_name);
1242 java_sources[0] = conftest_file_name;
1243 if (!compile_using_gcj (java_sources, 1, true, tmpdir->dir_name,
1244 false, false, false, true)
1245 && stat (compiled_file_name, &statbuf) >= 0)
1246 /* Compilation succeeded. */
1249 gcj_need_no_assert_option = true;
1253 unlink (compiled_file_name);
1255 java_sources[0] = conftest_file_name;
1256 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1257 false, false, false, true)
1258 && stat (compiled_file_name, &statbuf) >= 0)
1259 /* Compilation succeeded. */
1262 gcj_need_no_assert_option = false;
1266 free (compiled_file_name);
1267 free (conftest_file_name);
1269 cleanup_temp_dir (tmpdir);
1274 *usablep = gcj_usable;
1275 *need_no_assert_option_p = gcj_need_no_assert_option;
1280 is_javac_present (void)
1282 static bool javac_tested;
1283 static bool javac_present;
1287 /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
1293 exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1295 javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1296 javac_tested = true;
1299 return javac_present;
1302 /* Test whether javac can be used and whether it needs a -source and/or
1304 Return a failure indicator (true upon error). */
1306 is_javac_usable (const char *source_version, const char *target_version,
1307 bool *usablep, bool *source_option_p, bool *target_option_p)
1309 /* The cache depends on the source_version and target_version. */
1317 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1318 struct result_t *resultp;
1320 resultp = &result_cache[source_version_index (source_version)]
1321 [target_version_index (target_version)];
1322 if (!resultp->tested)
1325 struct temp_dir *tmpdir;
1326 char *conftest_file_name;
1327 char *compiled_file_name;
1328 const char *java_sources[1];
1329 struct stat statbuf;
1331 tmpdir = create_temp_dir ("java", NULL, false);
1335 conftest_file_name =
1336 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1337 if (write_temp_file (tmpdir, conftest_file_name,
1338 get_goodcode_snippet (source_version)))
1340 free (conftest_file_name);
1341 cleanup_temp_dir (tmpdir);
1345 compiled_file_name =
1346 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1347 register_temp_file (tmpdir, compiled_file_name);
1349 java_sources[0] = conftest_file_name;
1350 if (!compile_using_javac (java_sources, 1,
1351 false, source_version,
1352 false, target_version,
1353 tmpdir->dir_name, false, false, false, true)
1354 && stat (compiled_file_name, &statbuf) >= 0
1355 && get_classfile_version (compiled_file_name)
1356 <= corresponding_classfile_version (target_version))
1358 /* javac compiled conftest.java successfully. */
1359 /* Try adding -source option if it is useful. */
1360 unlink (compiled_file_name);
1362 java_sources[0] = conftest_file_name;
1363 if (!compile_using_javac (java_sources, 1,
1364 true, source_version,
1365 false, target_version,
1366 tmpdir->dir_name, false, false, false, true)
1367 && stat (compiled_file_name, &statbuf) >= 0
1368 && get_classfile_version (compiled_file_name)
1369 <= corresponding_classfile_version (target_version))
1371 const char *failcode = get_failcode_snippet (source_version);
1373 if (failcode != NULL)
1375 free (compiled_file_name);
1376 free (conftest_file_name);
1378 conftest_file_name =
1379 concatenated_pathname (tmpdir->dir_name,
1380 "conftestfail.java",
1382 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1384 free (conftest_file_name);
1385 cleanup_temp_dir (tmpdir);
1389 compiled_file_name =
1390 concatenated_pathname (tmpdir->dir_name,
1391 "conftestfail.class",
1393 register_temp_file (tmpdir, compiled_file_name);
1395 java_sources[0] = conftest_file_name;
1396 if (!compile_using_javac (java_sources, 1,
1397 false, source_version,
1398 false, target_version,
1400 false, false, false, true)
1401 && stat (compiled_file_name, &statbuf) >= 0)
1403 unlink (compiled_file_name);
1405 java_sources[0] = conftest_file_name;
1406 if (compile_using_javac (java_sources, 1,
1407 true, source_version,
1408 false, target_version,
1410 false, false, false, true))
1411 /* javac compiled conftestfail.java successfully, and
1412 "javac -source $source_version" rejects it. So the
1413 -source option is useful. */
1414 resultp->source_option = true;
1419 resultp->usable = true;
1423 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1424 option but no -source option.) */
1425 unlink (compiled_file_name);
1427 java_sources[0] = conftest_file_name;
1428 if (!compile_using_javac (java_sources, 1,
1429 false, source_version,
1430 true, target_version,
1432 false, false, false, true)
1433 && stat (compiled_file_name, &statbuf) >= 0
1434 && get_classfile_version (compiled_file_name)
1435 <= corresponding_classfile_version (target_version))
1437 /* "javac -target $target_version" compiled conftest.java
1439 /* Try adding -source option if it is useful. */
1440 unlink (compiled_file_name);
1442 java_sources[0] = conftest_file_name;
1443 if (!compile_using_javac (java_sources, 1,
1444 true, source_version,
1445 true, target_version,
1447 false, false, false, true)
1448 && stat (compiled_file_name, &statbuf) >= 0
1449 && get_classfile_version (compiled_file_name)
1450 <= corresponding_classfile_version (target_version))
1452 const char *failcode = get_failcode_snippet (source_version);
1454 if (failcode != NULL)
1456 free (compiled_file_name);
1457 free (conftest_file_name);
1459 conftest_file_name =
1460 concatenated_pathname (tmpdir->dir_name,
1461 "conftestfail.java",
1463 if (write_temp_file (tmpdir, conftest_file_name,
1466 free (conftest_file_name);
1467 cleanup_temp_dir (tmpdir);
1471 compiled_file_name =
1472 concatenated_pathname (tmpdir->dir_name,
1473 "conftestfail.class",
1475 register_temp_file (tmpdir, compiled_file_name);
1477 java_sources[0] = conftest_file_name;
1478 if (!compile_using_javac (java_sources, 1,
1479 false, source_version,
1480 true, target_version,
1482 false, false, false, true)
1483 && stat (compiled_file_name, &statbuf) >= 0)
1485 unlink (compiled_file_name);
1487 java_sources[0] = conftest_file_name;
1488 if (compile_using_javac (java_sources, 1,
1489 true, source_version,
1490 true, target_version,
1492 false, false, false, true))
1493 /* "javac -target $target_version" compiled
1494 conftestfail.java successfully, and
1495 "javac -target $target_version -source $source_version"
1496 rejects it. So the -source option is useful. */
1497 resultp->source_option = true;
1502 resultp->target_option = true;
1503 resultp->usable = true;
1507 /* Maybe this -target option requires a -source option? Try with
1508 -target and -source options. (Supported by Sun javac 1.4 and
1510 unlink (compiled_file_name);
1512 java_sources[0] = conftest_file_name;
1513 if (!compile_using_javac (java_sources, 1,
1514 true, source_version,
1515 true, target_version,
1517 false, false, false, true)
1518 && stat (compiled_file_name, &statbuf) >= 0
1519 && get_classfile_version (compiled_file_name)
1520 <= corresponding_classfile_version (target_version))
1522 /* "javac -target $target_version -source $source_version"
1523 compiled conftest.java successfully. */
1524 resultp->source_option = true;
1525 resultp->target_option = true;
1526 resultp->usable = true;
1531 free (compiled_file_name);
1532 free (conftest_file_name);
1534 resultp->tested = true;
1537 *usablep = resultp->usable;
1538 *source_option_p = resultp->source_option;
1539 *target_option_p = resultp->target_option;
1544 is_jikes_present (void)
1546 static bool jikes_tested;
1547 static bool jikes_present;
1551 /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1" */
1557 exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
1559 jikes_present = (exitstatus == 0 || exitstatus == 1);
1560 jikes_tested = true;
1563 return jikes_present;
1566 /* ============================= Main function ============================= */
1569 compile_java_class (const char * const *java_sources,
1570 unsigned int java_sources_count,
1571 const char * const *classpaths,
1572 unsigned int classpaths_count,
1573 const char *source_version,
1574 const char *target_version,
1575 const char *directory,
1576 bool optimize, bool debug,
1577 bool use_minimal_classpath,
1581 char *old_JAVA_HOME;
1584 const char *javac = getenv ("JAVAC");
1585 if (javac != NULL && javac[0] != '\0')
1587 bool usable = false;
1588 bool no_assert_option = false;
1589 bool source_option = false;
1590 bool target_option = false;
1592 if (target_version == NULL)
1593 target_version = default_target_version ();
1595 if (is_envjavac_gcj (javac))
1597 /* It's a version of gcj. Ignore the version of the class files
1599 if (strcmp (target_version, "1.4") == 0
1600 && strcmp (source_version, "1.4") == 0)
1602 if (is_envjavac_gcj_14_14_usable (javac, &usable))
1608 else if (strcmp (target_version, "1.4") == 0
1609 && strcmp (source_version, "1.3") == 0)
1611 if (is_envjavac_gcj_14_13_usable (javac,
1612 &usable, &no_assert_option))
1621 /* It's not gcj. Assume the classfile versions are correct. */
1622 if (is_envjavac_nongcj_usable (javac,
1623 source_version, target_version,
1625 &source_option, &target_option))
1634 char *old_classpath;
1635 char *javac_with_options;
1637 /* Set CLASSPATH. */
1639 set_classpath (classpaths, classpaths_count, false, verbose);
1641 javac_with_options =
1643 ? xasprintf ("%s -fno-assert", javac)
1644 : xasprintf ("%s%s%s%s%s",
1646 source_option ? " -source " : "",
1647 source_option ? source_version : "",
1648 target_option ? " -target " : "",
1649 target_option ? target_version : ""));
1651 err = compile_using_envjavac (javac_with_options,
1652 java_sources, java_sources_count,
1653 directory, optimize, debug, verbose,
1656 free (javac_with_options);
1658 /* Reset CLASSPATH. */
1659 reset_classpath (old_classpath);
1666 /* Unset the JAVA_HOME environment variable. */
1667 old_JAVA_HOME = getenv ("JAVA_HOME");
1668 if (old_JAVA_HOME != NULL)
1670 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
1671 unsetenv ("JAVA_HOME");
1674 if (is_gcj_present ())
1676 /* Test whether it supports the desired target-version and
1677 source-version. But ignore the version of the class files that
1679 bool usable = false;
1680 bool no_assert_option = false;
1682 if (target_version == NULL)
1683 target_version = default_target_version ();
1685 if (strcmp (target_version, "1.4") == 0
1686 && strcmp (source_version, "1.4") == 0)
1688 if (is_gcj_14_14_usable (&usable))
1694 else if (strcmp (target_version, "1.4") == 0
1695 && strcmp (source_version, "1.3") == 0)
1697 if (is_gcj_14_13_usable (&usable, &no_assert_option))
1706 char *old_classpath;
1708 /* Set CLASSPATH. We could also use the --CLASSPATH=... option
1709 of gcj. Note that --classpath=... option is different: its
1710 argument should also contain gcj's libgcj.jar, but we don't
1711 know its location. */
1713 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1716 err = compile_using_gcj (java_sources, java_sources_count,
1718 directory, optimize, debug, verbose, false);
1720 /* Reset CLASSPATH. */
1721 reset_classpath (old_classpath);
1727 if (is_javac_present ())
1729 bool usable = false;
1730 bool source_option = false;
1731 bool target_option = false;
1733 if (target_version == NULL)
1734 target_version = default_target_version ();
1736 if (is_javac_usable (source_version, target_version,
1737 &usable, &source_option, &target_option))
1745 char *old_classpath;
1747 /* Set CLASSPATH. We don't use the "-classpath ..." option because
1748 in JDK 1.1.x its argument should also contain the JDK's
1749 classes.zip, but we don't know its location. (In JDK 1.3.0 it
1752 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1755 err = compile_using_javac (java_sources, java_sources_count,
1756 source_option, source_version,
1757 target_option, target_version,
1758 directory, optimize, debug, verbose,
1761 /* Reset CLASSPATH. */
1762 reset_classpath (old_classpath);
1768 if (is_jikes_present ())
1770 /* Test whether it supports the desired target-version and
1772 bool usable = (strcmp (source_version, "1.3") == 0);
1776 char *old_classpath;
1778 /* Set CLASSPATH. We could also use the "-classpath ..." option.
1779 Since jikes doesn't come with its own standard library, it
1780 needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
1781 To increase the chance of success, we reuse the current CLASSPATH
1782 if the user has set it. */
1784 set_classpath (classpaths, classpaths_count, false, verbose);
1786 err = compile_using_jikes (java_sources, java_sources_count,
1787 directory, optimize, debug, verbose,
1790 /* Reset CLASSPATH. */
1791 reset_classpath (old_classpath);
1797 error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
1801 if (old_JAVA_HOME != NULL)
1803 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
1804 free (old_JAVA_HOME);