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"
53 #define _(str) gettext (str)
55 /* The results of open() in this file are not used with fchdir,
56 therefore save some unnecessary work in fchdir.c. */
61 /* Survey of Java compilers.
63 A = does it work without CLASSPATH being set
64 C = option to set CLASSPATH, other than setting it in the environment
65 O = option for optimizing
66 g = option for debugging
69 Program from A C O g T
71 $JAVAC unknown N n/a -O -g true
72 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
73 javac JDK 1.1.8 Y -classpath P -O -g javac 2>/dev/null; test $? = 1
74 javac JDK 1.3.0 Y -classpath P -O -g javac 2>/dev/null; test $? -le 2
75 jikes Jikes 1.14 N -classpath P -O -g jikes 2>/dev/null; test $? = 1
77 All compilers support the option "-d DIRECTORY" for the base directory
78 of the classes to be written.
80 The CLASSPATH is a colon separated list of pathnames. (On Windows: a
81 semicolon separated list of pathnames.)
83 We try the Java compilers in the following order:
84 1. getenv ("JAVAC"), because the user must be able to override our
86 2. "gcj -C", because it is a completely free compiler,
87 3. "javac", because it is a standard compiler,
88 4. "jikes", comes last because it has some deviating interpretation
89 of the Java Language Specification and because it requires a
90 CLASSPATH environment variable.
92 We unset the JAVA_HOME environment variable, because a wrong setting of
93 this variable can confuse the JDK's javac.
96 /* Return the default target_version. */
98 default_target_version (void)
100 /* Use a cache. Assumes that the PATH environment variable doesn't change
101 during the lifetime of the program. */
102 static const char *java_version_cache;
103 if (java_version_cache == NULL)
105 /* Determine the version from the found JVM. */
106 java_version_cache = javaexec_version ();
107 if (java_version_cache == NULL
108 || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
109 && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
110 && java_version_cache[3] == '\0'))
111 java_version_cache = "1.1";
113 return java_version_cache;
116 /* ======================= Source version dependent ======================= */
118 /* Convert a source version to an index. */
119 #define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
121 source_version_index (const char *source_version)
123 if (source_version[0] == '1' && source_version[1] == '.'
124 && (source_version[2] >= '3' && source_version[2] <= '5')
125 && source_version[3] == '\0')
126 return source_version[2] - '3';
127 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
131 /* Return a snippet of code that should compile in the given source version. */
133 get_goodcode_snippet (const char *source_version)
135 if (strcmp (source_version, "1.3") == 0)
136 return "class conftest {}\n";
137 if (strcmp (source_version, "1.4") == 0)
138 return "class conftest { static { assert(true); } }\n";
139 if (strcmp (source_version, "1.5") == 0)
140 return "class conftest<T> { T foo() { return null; } }\n";
141 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
145 /* Return a snippet of code that should fail to compile in the given source
146 version, or NULL (standing for a snippet that would fail to compile with
149 get_failcode_snippet (const char *source_version)
151 if (strcmp (source_version, "1.3") == 0)
152 return "class conftestfail { static { assert(true); } }\n";
153 if (strcmp (source_version, "1.4") == 0)
154 return "class conftestfail<T> { T foo() { return null; } }\n";
155 if (strcmp (source_version, "1.5") == 0)
157 error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
161 /* ======================= Target version dependent ======================= */
163 /* Convert a target version to an index. */
164 #define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
166 target_version_index (const char *target_version)
168 if (target_version[0] == '1' && target_version[1] == '.'
169 && (target_version[2] >= '1' && target_version[2] <= '6')
170 && target_version[3] == '\0')
171 return target_version[2] - '1';
172 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
176 /* Return the class file version number corresponding to a given target
179 corresponding_classfile_version (const char *target_version)
181 if (strcmp (target_version, "1.1") == 0)
183 if (strcmp (target_version, "1.2") == 0)
185 if (strcmp (target_version, "1.3") == 0)
187 if (strcmp (target_version, "1.4") == 0)
189 if (strcmp (target_version, "1.5") == 0)
191 if (strcmp (target_version, "1.6") == 0)
193 error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
197 /* ======================== Compilation subroutines ======================== */
199 /* Try to compile a set of Java sources with $JAVAC.
200 Return a failure indicator (true upon error). */
202 compile_using_envjavac (const char *javac,
203 const char * const *java_sources,
204 unsigned int java_sources_count,
205 const char *directory,
206 bool optimize, bool debug,
207 bool verbose, bool null_stderr)
209 /* Because $JAVAC may consist of a command and options, we use the
210 shell. Because $JAVAC has been set by the user, we leave all
211 environment variables in place, including JAVA_HOME, and we don't
212 erase the user's CLASSPATH. */
214 unsigned int command_length;
221 command_length = strlen (javac);
226 if (directory != NULL)
227 command_length += 4 + shell_quote_length (directory);
228 for (i = 0; i < java_sources_count; i++)
229 command_length += 1 + shell_quote_length (java_sources[i]);
232 command = (char *) xallocsa (command_length);
234 /* Don't shell_quote $JAVAC, because it may consist of a command
236 memcpy (p, javac, strlen (javac));
240 memcpy (p, " -O", 3);
245 memcpy (p, " -g", 3);
248 if (directory != NULL)
250 memcpy (p, " -d ", 4);
252 p = shell_quote_copy (p, directory);
254 for (i = 0; i < java_sources_count; i++)
257 p = shell_quote_copy (p, java_sources[i]);
260 /* Ensure command_length was correctly calculated. */
261 if (p - command > command_length)
265 printf ("%s\n", command);
271 exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
272 null_stderr, true, true);
273 err = (exitstatus != 0);
280 /* Try to compile a set of Java sources with gcj.
281 Return a failure indicator (true upon error). */
283 compile_using_gcj (const char * const *java_sources,
284 unsigned int java_sources_count,
285 bool no_assert_option,
286 const char *directory,
287 bool optimize, bool debug,
288 bool verbose, bool null_stderr)
298 2 + (no_assert_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
299 + (directory != NULL ? 2 : 0) + java_sources_count;
300 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
305 if (no_assert_option)
306 *argp++ = "-fno-assert";
311 if (directory != NULL)
314 *argp++ = (char *) directory;
316 for (i = 0; i < java_sources_count; i++)
317 *argp++ = (char *) java_sources[i];
319 /* Ensure argv length was correctly calculated. */
320 if (argp - argv != argc)
325 char *command = shell_quote_argv (argv);
326 printf ("%s\n", command);
330 exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
332 err = (exitstatus != 0);
339 /* Try to compile a set of Java sources with javac.
340 Return a failure indicator (true upon error). */
342 compile_using_javac (const char * const *java_sources,
343 unsigned int java_sources_count,
344 bool source_option, const char *source_version,
345 bool target_option, const char *target_version,
346 const char *directory,
347 bool optimize, bool debug,
348 bool verbose, bool null_stderr)
358 1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
359 + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
360 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
367 *argp++ = (char *) source_version;
372 *argp++ = (char *) target_version;
378 if (directory != NULL)
381 *argp++ = (char *) directory;
383 for (i = 0; i < java_sources_count; i++)
384 *argp++ = (char *) java_sources[i];
386 /* Ensure argv length was correctly calculated. */
387 if (argp - argv != argc)
392 char *command = shell_quote_argv (argv);
393 printf ("%s\n", command);
397 exitstatus = execute ("javac", "javac", argv, false, false, false,
398 null_stderr, true, true);
399 err = (exitstatus != 0);
406 /* Try to compile a set of Java sources with jikes.
407 Return a failure indicator (true upon error). */
409 compile_using_jikes (const char * const *java_sources,
410 unsigned int java_sources_count,
411 const char *directory,
412 bool optimize, bool debug,
413 bool verbose, bool null_stderr)
423 1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
424 + java_sources_count;
425 argv = (char **) xallocsa ((argc + 1) * sizeof (char *));
433 if (directory != NULL)
436 *argp++ = (char *) directory;
438 for (i = 0; i < java_sources_count; i++)
439 *argp++ = (char *) java_sources[i];
441 /* Ensure argv length was correctly calculated. */
442 if (argp - argv != argc)
447 char *command = shell_quote_argv (argv);
448 printf ("%s\n", command);
452 exitstatus = execute ("jikes", "jikes", argv, false, false, false,
453 null_stderr, true, true);
454 err = (exitstatus != 0);
461 /* ====================== Usability test subroutines ====================== */
463 /* Write a given contents to a temporary file.
464 FILE_NAME is the name of a file inside TMPDIR that is known not to exist
466 Return a failure indicator (true upon error). */
468 write_temp_file (struct temp_dir *tmpdir, const char *file_name,
469 const char *contents)
473 register_temp_file (tmpdir, file_name);
474 fp = fopen_temp (file_name, "w");
477 error (0, errno, _("failed to create \"%s\""), file_name);
478 unregister_temp_file (tmpdir, file_name);
481 fputs (contents, fp);
482 if (fwriteerror_temp (fp))
484 error (0, errno, _("error while writing \"%s\" file"), file_name);
490 /* Return the class file version number of a class file on disk. */
492 get_classfile_version (const char *compiled_file_name)
494 unsigned char header[8];
497 /* Open the class file. */
498 fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
501 /* Read its first 8 bytes. */
502 if (safe_read (fd, header, 8) == 8)
504 /* Verify the class file signature. */
505 if (header[0] == 0xCA && header[1] == 0xFE
506 && header[2] == 0xBA && header[3] == 0xBE)
512 /* Could not get the class file version. Return a very large one. */
516 /* Return true if $JAVAC is a version of gcj. */
518 is_envjavac_gcj (const char *javac)
520 static bool envjavac_tested;
521 static bool envjavac_gcj;
523 if (!envjavac_tested)
525 /* Test whether $JAVAC is gcj:
526 "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null" */
527 unsigned int command_length;
539 /* Setup the command "$JAVAC --version". */
540 command_length = strlen (javac) + 1 + 9 + 1;
541 command = (char *) xallocsa (command_length);
543 /* Don't shell_quote $JAVAC, because it may consist of a command
545 memcpy (p, javac, strlen (javac));
547 memcpy (p, " --version", 1 + 9 + 1);
549 /* Ensure command_length was correctly calculated. */
550 if (p - command > command_length)
553 /* Call $JAVAC --version 2>/dev/null. */
558 child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
563 /* Retrieve its result. */
564 fp = fdopen (fd[0], "r");
568 line = NULL; linesize = 0;
569 linelen = getline (&line, &linesize, fp);
570 if (linelen == (size_t)(-1))
575 envjavac_gcj = (strstr (line, "gcj") != NULL);
579 /* Remove zombie process from process list, and retrieve exit status. */
580 exitstatus = wait_subprocess (child, javac, true, true, true, false);
582 envjavac_gcj = false;
587 envjavac_tested = true;
593 /* Test whether $JAVAC, known to be a version of gcj, can be used for
594 compiling with target_version = 1.4 and source_version = 1.4.
595 Return a failure indicator (true upon error). */
597 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
599 static bool envjavac_tested;
600 static bool envjavac_usable;
602 if (!envjavac_tested)
605 struct temp_dir *tmpdir;
606 char *conftest_file_name;
607 char *compiled_file_name;
608 const char *java_sources[1];
611 tmpdir = create_temp_dir ("java", NULL, false);
616 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
617 if (write_temp_file (tmpdir, conftest_file_name,
618 get_goodcode_snippet ("1.4")))
620 free (conftest_file_name);
621 cleanup_temp_dir (tmpdir);
626 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
627 register_temp_file (tmpdir, compiled_file_name);
629 java_sources[0] = conftest_file_name;
630 if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
631 false, false, false, true)
632 && stat (compiled_file_name, &statbuf) >= 0)
633 /* Compilation succeeded. */
634 envjavac_usable = true;
636 free (compiled_file_name);
637 free (conftest_file_name);
639 cleanup_temp_dir (tmpdir);
641 envjavac_tested = true;
644 *usablep = envjavac_usable;
648 /* Test whether $JAVAC, known to be a version of gcj, can be used for
649 compiling with target_version = 1.4 and source_version = 1.3.
650 Return a failure indicator (true upon error). */
652 is_envjavac_gcj_14_13_usable (const char *javac,
653 bool *usablep, bool *need_no_assert_option_p)
655 static bool envjavac_tested;
656 static bool envjavac_usable;
657 static bool envjavac_need_no_assert_option;
659 if (!envjavac_tested)
661 /* Try $JAVAC and "$JAVAC -fno-assert". But add -fno-assert only if
662 it makes a difference. (It could already be part of $JAVAC.) */
663 struct temp_dir *tmpdir;
664 char *conftest_file_name;
665 char *compiled_file_name;
666 const char *java_sources[1];
669 char *javac_noassert;
670 bool javac_noassert_works;
672 tmpdir = create_temp_dir ("java", NULL, false);
677 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
678 if (write_temp_file (tmpdir, conftest_file_name,
679 get_goodcode_snippet ("1.3")))
681 free (conftest_file_name);
682 cleanup_temp_dir (tmpdir);
687 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
688 register_temp_file (tmpdir, compiled_file_name);
690 java_sources[0] = conftest_file_name;
691 if (!compile_using_envjavac (javac,
692 java_sources, 1, tmpdir->dir_name,
693 false, false, false, true)
694 && stat (compiled_file_name, &statbuf) >= 0)
695 /* Compilation succeeded. */
700 unlink (compiled_file_name);
702 javac_noassert = xasprintf ("%s -fno-assert", javac);
704 java_sources[0] = conftest_file_name;
705 if (!compile_using_envjavac (javac_noassert,
706 java_sources, 1, tmpdir->dir_name,
707 false, false, false, true)
708 && stat (compiled_file_name, &statbuf) >= 0)
709 /* Compilation succeeded. */
710 javac_noassert_works = true;
712 javac_noassert_works = false;
714 free (compiled_file_name);
715 free (conftest_file_name);
717 if (javac_works && javac_noassert_works)
720 concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
722 if (write_temp_file (tmpdir, conftest_file_name,
723 get_failcode_snippet ("1.3")))
725 free (conftest_file_name);
726 free (javac_noassert);
727 cleanup_temp_dir (tmpdir);
732 concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
734 register_temp_file (tmpdir, compiled_file_name);
736 java_sources[0] = conftest_file_name;
737 if (!compile_using_envjavac (javac,
738 java_sources, 1, tmpdir->dir_name,
739 false, false, false, true)
740 && stat (compiled_file_name, &statbuf) >= 0)
742 /* Compilation succeeded. */
743 unlink (compiled_file_name);
745 java_sources[0] = conftest_file_name;
746 if (!(!compile_using_envjavac (javac_noassert,
747 java_sources, 1, tmpdir->dir_name,
748 false, false, false, true)
749 && stat (compiled_file_name, &statbuf) >= 0))
750 /* Compilation failed. */
751 /* "$JAVAC -fno-assert" works better than $JAVAC. */
755 free (compiled_file_name);
756 free (conftest_file_name);
759 cleanup_temp_dir (tmpdir);
763 envjavac_usable = true;
764 envjavac_need_no_assert_option = false;
766 else if (javac_noassert_works)
768 envjavac_usable = true;
769 envjavac_need_no_assert_option = true;
772 envjavac_tested = true;
775 *usablep = envjavac_usable;
776 *need_no_assert_option_p = envjavac_need_no_assert_option;
780 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
781 whether it needs a -source and/or -target option.
782 Return a failure indicator (true upon error). */
784 is_envjavac_nongcj_usable (const char *javac,
785 const char *source_version,
786 const char *target_version,
788 bool *source_option_p, bool *target_option_p)
790 /* The cache depends on the source_version and target_version. */
798 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
799 struct result_t *resultp;
801 resultp = &result_cache[source_version_index (source_version)]
802 [target_version_index (target_version)];
803 if (!resultp->tested)
806 struct temp_dir *tmpdir;
807 char *conftest_file_name;
808 char *compiled_file_name;
809 const char *java_sources[1];
812 tmpdir = create_temp_dir ("java", NULL, false);
817 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
818 if (write_temp_file (tmpdir, conftest_file_name,
819 get_goodcode_snippet (source_version)))
821 free (conftest_file_name);
822 cleanup_temp_dir (tmpdir);
827 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
828 register_temp_file (tmpdir, compiled_file_name);
830 java_sources[0] = conftest_file_name;
831 if (!compile_using_envjavac (javac,
832 java_sources, 1, tmpdir->dir_name,
833 false, false, false, true)
834 && stat (compiled_file_name, &statbuf) >= 0
835 && get_classfile_version (compiled_file_name)
836 <= corresponding_classfile_version (target_version))
838 /* $JAVAC compiled conftest.java successfully. */
839 /* Try adding -source option if it is useful. */
841 xasprintf ("%s -source %s", javac, source_version);
843 unlink (compiled_file_name);
845 java_sources[0] = conftest_file_name;
846 if (!compile_using_envjavac (javac_source,
847 java_sources, 1, tmpdir->dir_name,
848 false, false, false, true)
849 && stat (compiled_file_name, &statbuf) >= 0
850 && get_classfile_version (compiled_file_name)
851 <= corresponding_classfile_version (target_version))
853 const char *failcode = get_failcode_snippet (source_version);
855 if (failcode != NULL)
857 free (compiled_file_name);
858 free (conftest_file_name);
861 concatenated_pathname (tmpdir->dir_name,
864 if (write_temp_file (tmpdir, conftest_file_name, failcode))
866 free (conftest_file_name);
868 cleanup_temp_dir (tmpdir);
873 concatenated_pathname (tmpdir->dir_name,
874 "conftestfail.class",
876 register_temp_file (tmpdir, compiled_file_name);
878 java_sources[0] = conftest_file_name;
879 if (!compile_using_envjavac (javac,
882 false, false, false, true)
883 && stat (compiled_file_name, &statbuf) >= 0)
885 unlink (compiled_file_name);
887 java_sources[0] = conftest_file_name;
888 if (compile_using_envjavac (javac_source,
891 false, false, false, true))
892 /* $JAVAC compiled conftestfail.java successfully, and
893 "$JAVAC -source $source_version" rejects it. So the
894 -source option is useful. */
895 resultp->source_option = true;
902 resultp->usable = true;
906 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
907 option but no -source option.) */
909 xasprintf ("%s -target %s", javac, target_version);
911 unlink (compiled_file_name);
913 java_sources[0] = conftest_file_name;
914 if (!compile_using_envjavac (javac_target,
915 java_sources, 1, tmpdir->dir_name,
916 false, false, false, true)
917 && stat (compiled_file_name, &statbuf) >= 0
918 && get_classfile_version (compiled_file_name)
919 <= corresponding_classfile_version (target_version))
921 /* "$JAVAC -target $target_version" compiled conftest.java
923 /* Try adding -source option if it is useful. */
924 char *javac_target_source =
925 xasprintf ("%s -source %s", javac_target, source_version);
927 unlink (compiled_file_name);
929 java_sources[0] = conftest_file_name;
930 if (!compile_using_envjavac (javac_target_source,
931 java_sources, 1, tmpdir->dir_name,
932 false, false, false, true)
933 && stat (compiled_file_name, &statbuf) >= 0
934 && get_classfile_version (compiled_file_name)
935 <= corresponding_classfile_version (target_version))
937 const char *failcode = get_failcode_snippet (source_version);
939 if (failcode != NULL)
941 free (compiled_file_name);
942 free (conftest_file_name);
945 concatenated_pathname (tmpdir->dir_name,
948 if (write_temp_file (tmpdir, conftest_file_name,
951 free (conftest_file_name);
952 free (javac_target_source);
954 cleanup_temp_dir (tmpdir);
959 concatenated_pathname (tmpdir->dir_name,
960 "conftestfail.class",
962 register_temp_file (tmpdir, compiled_file_name);
964 java_sources[0] = conftest_file_name;
965 if (!compile_using_envjavac (javac_target,
968 false, false, false, true)
969 && stat (compiled_file_name, &statbuf) >= 0)
971 unlink (compiled_file_name);
973 java_sources[0] = conftest_file_name;
974 if (compile_using_envjavac (javac_target_source,
979 /* "$JAVAC -target $target_version" compiled
980 conftestfail.java successfully, and
981 "$JAVAC -target $target_version -source $source_version"
982 rejects it. So the -source option is useful. */
983 resultp->source_option = true;
988 free (javac_target_source);
990 resultp->target_option = true;
991 resultp->usable = true;
995 /* Maybe this -target option requires a -source option? Try with
996 -target and -source options. (Supported by Sun javac 1.4 and
998 char *javac_target_source =
999 xasprintf ("%s -source %s", javac_target, source_version);
1001 unlink (compiled_file_name);
1003 java_sources[0] = conftest_file_name;
1004 if (!compile_using_envjavac (javac_target_source,
1005 java_sources, 1, tmpdir->dir_name,
1006 false, false, false, true)
1007 && stat (compiled_file_name, &statbuf) >= 0
1008 && get_classfile_version (compiled_file_name)
1009 <= corresponding_classfile_version (target_version))
1011 /* "$JAVAC -target $target_version -source $source_version"
1012 compiled conftest.java successfully. */
1013 resultp->source_option = true;
1014 resultp->target_option = true;
1015 resultp->usable = true;
1018 free (javac_target_source);
1021 free (javac_target);
1024 free (compiled_file_name);
1025 free (conftest_file_name);
1027 resultp->tested = true;
1030 *usablep = resultp->usable;
1031 *source_option_p = resultp->source_option;
1032 *target_option_p = resultp->target_option;
1037 is_gcj_present (void)
1039 static bool gcj_tested;
1040 static bool gcj_present;
1044 /* Test for presence of gcj:
1045 "gcj --version 2> /dev/null | \
1046 sed -e 's,^[^0-9]*,,' -e 1q | \
1047 sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null" */
1054 argv[1] = "--version";
1056 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1058 gcj_present = false;
1061 /* Read the subprocess output, drop all lines except the first,
1062 drop all characters before the first digit, and test whether
1063 the remaining string starts with a digit >= 3, but not with
1068 while (safe_read (fd[0], &c[count], 1) > 0)
1070 if (c[count] == '\n')
1074 if (!(c[0] >= '0' && c[0] <= '9'))
1076 gcj_present = (c[0] >= '3');
1081 if (c[0] == '3' && c[1] == '.'
1082 && (c[2] == '0' || c[2] == '1'))
1083 gcj_present = false;
1087 while (safe_read (fd[0], &c[0], 1) > 0)
1092 /* Remove zombie process from process list, and retrieve exit
1095 wait_subprocess (child, "gcj", false, true, true, false);
1096 if (exitstatus != 0)
1097 gcj_present = false;
1102 /* See if libgcj.jar is well installed. */
1103 struct temp_dir *tmpdir;
1105 tmpdir = create_temp_dir ("java", NULL, false);
1107 gcj_present = false;
1110 char *conftest_file_name;
1112 conftest_file_name =
1113 concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
1115 if (write_temp_file (tmpdir, conftest_file_name,
1116 "public class conftestlib {\n"
1117 " public static void main (String[] args) {\n"
1120 gcj_present = false;
1123 char *compiled_file_name;
1124 const char *java_sources[1];
1126 compiled_file_name =
1127 concatenated_pathname (tmpdir->dir_name,
1128 "conftestlib.class",
1130 register_temp_file (tmpdir, compiled_file_name);
1132 java_sources[0] = conftest_file_name;
1133 if (compile_using_gcj (java_sources, 1, false,
1135 false, false, false, true))
1136 gcj_present = false;
1138 free (compiled_file_name);
1140 free (conftest_file_name);
1142 cleanup_temp_dir (tmpdir);
1151 /* Test gcj can be used for compiling with target_version = 1.4 and
1152 source_version = 1.4.
1153 Return a failure indicator (true upon error). */
1155 is_gcj_14_14_usable (bool *usablep)
1157 static bool gcj_tested;
1158 static bool gcj_usable;
1163 struct temp_dir *tmpdir;
1164 char *conftest_file_name;
1165 char *compiled_file_name;
1166 const char *java_sources[1];
1167 struct stat statbuf;
1169 tmpdir = create_temp_dir ("java", NULL, false);
1173 conftest_file_name =
1174 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1175 if (write_temp_file (tmpdir, conftest_file_name,
1176 get_goodcode_snippet ("1.4")))
1178 free (conftest_file_name);
1179 cleanup_temp_dir (tmpdir);
1183 compiled_file_name =
1184 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1185 register_temp_file (tmpdir, compiled_file_name);
1187 java_sources[0] = conftest_file_name;
1188 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1189 false, false, false, true)
1190 && stat (compiled_file_name, &statbuf) >= 0)
1191 /* Compilation succeeded. */
1194 free (compiled_file_name);
1195 free (conftest_file_name);
1197 cleanup_temp_dir (tmpdir);
1202 *usablep = gcj_usable;
1206 /* Test whether gcj can be used for compiling with target_version = 1.4 and
1207 source_version = 1.3.
1208 Return a failure indicator (true upon error). */
1210 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1212 static bool gcj_tested;
1213 static bool gcj_usable;
1214 static bool gcj_need_no_assert_option;
1218 /* Try gcj and "gcj -fno-assert". But add -fno-assert only if
1219 it works (not gcj < 3.3). */
1220 struct temp_dir *tmpdir;
1221 char *conftest_file_name;
1222 char *compiled_file_name;
1223 const char *java_sources[1];
1224 struct stat statbuf;
1226 tmpdir = create_temp_dir ("java", NULL, false);
1230 conftest_file_name =
1231 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1232 if (write_temp_file (tmpdir, conftest_file_name,
1233 get_goodcode_snippet ("1.3")))
1235 free (conftest_file_name);
1236 cleanup_temp_dir (tmpdir);
1240 compiled_file_name =
1241 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1242 register_temp_file (tmpdir, compiled_file_name);
1244 java_sources[0] = conftest_file_name;
1245 if (!compile_using_gcj (java_sources, 1, true, tmpdir->dir_name,
1246 false, false, false, true)
1247 && stat (compiled_file_name, &statbuf) >= 0)
1248 /* Compilation succeeded. */
1251 gcj_need_no_assert_option = true;
1255 unlink (compiled_file_name);
1257 java_sources[0] = conftest_file_name;
1258 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1259 false, false, false, true)
1260 && stat (compiled_file_name, &statbuf) >= 0)
1261 /* Compilation succeeded. */
1264 gcj_need_no_assert_option = false;
1268 free (compiled_file_name);
1269 free (conftest_file_name);
1271 cleanup_temp_dir (tmpdir);
1276 *usablep = gcj_usable;
1277 *need_no_assert_option_p = gcj_need_no_assert_option;
1282 is_javac_present (void)
1284 static bool javac_tested;
1285 static bool javac_present;
1289 /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
1295 exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1297 javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1298 javac_tested = true;
1301 return javac_present;
1304 /* Test whether javac can be used and whether it needs a -source and/or
1306 Return a failure indicator (true upon error). */
1308 is_javac_usable (const char *source_version, const char *target_version,
1309 bool *usablep, bool *source_option_p, bool *target_option_p)
1311 /* The cache depends on the source_version and target_version. */
1319 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1320 struct result_t *resultp;
1322 resultp = &result_cache[source_version_index (source_version)]
1323 [target_version_index (target_version)];
1324 if (!resultp->tested)
1327 struct temp_dir *tmpdir;
1328 char *conftest_file_name;
1329 char *compiled_file_name;
1330 const char *java_sources[1];
1331 struct stat statbuf;
1333 tmpdir = create_temp_dir ("java", NULL, false);
1337 conftest_file_name =
1338 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1339 if (write_temp_file (tmpdir, conftest_file_name,
1340 get_goodcode_snippet (source_version)))
1342 free (conftest_file_name);
1343 cleanup_temp_dir (tmpdir);
1347 compiled_file_name =
1348 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1349 register_temp_file (tmpdir, compiled_file_name);
1351 java_sources[0] = conftest_file_name;
1352 if (!compile_using_javac (java_sources, 1,
1353 false, source_version,
1354 false, target_version,
1355 tmpdir->dir_name, false, false, false, true)
1356 && stat (compiled_file_name, &statbuf) >= 0
1357 && get_classfile_version (compiled_file_name)
1358 <= corresponding_classfile_version (target_version))
1360 /* javac compiled conftest.java successfully. */
1361 /* Try adding -source option if it is useful. */
1362 unlink (compiled_file_name);
1364 java_sources[0] = conftest_file_name;
1365 if (!compile_using_javac (java_sources, 1,
1366 true, source_version,
1367 false, target_version,
1368 tmpdir->dir_name, false, false, false, true)
1369 && stat (compiled_file_name, &statbuf) >= 0
1370 && get_classfile_version (compiled_file_name)
1371 <= corresponding_classfile_version (target_version))
1373 const char *failcode = get_failcode_snippet (source_version);
1375 if (failcode != NULL)
1377 free (compiled_file_name);
1378 free (conftest_file_name);
1380 conftest_file_name =
1381 concatenated_pathname (tmpdir->dir_name,
1382 "conftestfail.java",
1384 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1386 free (conftest_file_name);
1387 cleanup_temp_dir (tmpdir);
1391 compiled_file_name =
1392 concatenated_pathname (tmpdir->dir_name,
1393 "conftestfail.class",
1395 register_temp_file (tmpdir, compiled_file_name);
1397 java_sources[0] = conftest_file_name;
1398 if (!compile_using_javac (java_sources, 1,
1399 false, source_version,
1400 false, target_version,
1402 false, false, false, true)
1403 && stat (compiled_file_name, &statbuf) >= 0)
1405 unlink (compiled_file_name);
1407 java_sources[0] = conftest_file_name;
1408 if (compile_using_javac (java_sources, 1,
1409 true, source_version,
1410 false, target_version,
1412 false, false, false, true))
1413 /* javac compiled conftestfail.java successfully, and
1414 "javac -source $source_version" rejects it. So the
1415 -source option is useful. */
1416 resultp->source_option = true;
1421 resultp->usable = true;
1425 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1426 option but no -source option.) */
1427 unlink (compiled_file_name);
1429 java_sources[0] = conftest_file_name;
1430 if (!compile_using_javac (java_sources, 1,
1431 false, source_version,
1432 true, target_version,
1434 false, false, false, true)
1435 && stat (compiled_file_name, &statbuf) >= 0
1436 && get_classfile_version (compiled_file_name)
1437 <= corresponding_classfile_version (target_version))
1439 /* "javac -target $target_version" compiled conftest.java
1441 /* Try adding -source option if it is useful. */
1442 unlink (compiled_file_name);
1444 java_sources[0] = conftest_file_name;
1445 if (!compile_using_javac (java_sources, 1,
1446 true, source_version,
1447 true, target_version,
1449 false, false, false, true)
1450 && stat (compiled_file_name, &statbuf) >= 0
1451 && get_classfile_version (compiled_file_name)
1452 <= corresponding_classfile_version (target_version))
1454 const char *failcode = get_failcode_snippet (source_version);
1456 if (failcode != NULL)
1458 free (compiled_file_name);
1459 free (conftest_file_name);
1461 conftest_file_name =
1462 concatenated_pathname (tmpdir->dir_name,
1463 "conftestfail.java",
1465 if (write_temp_file (tmpdir, conftest_file_name,
1468 free (conftest_file_name);
1469 cleanup_temp_dir (tmpdir);
1473 compiled_file_name =
1474 concatenated_pathname (tmpdir->dir_name,
1475 "conftestfail.class",
1477 register_temp_file (tmpdir, compiled_file_name);
1479 java_sources[0] = conftest_file_name;
1480 if (!compile_using_javac (java_sources, 1,
1481 false, source_version,
1482 true, target_version,
1484 false, false, false, true)
1485 && stat (compiled_file_name, &statbuf) >= 0)
1487 unlink (compiled_file_name);
1489 java_sources[0] = conftest_file_name;
1490 if (compile_using_javac (java_sources, 1,
1491 true, source_version,
1492 true, target_version,
1494 false, false, false, true))
1495 /* "javac -target $target_version" compiled
1496 conftestfail.java successfully, and
1497 "javac -target $target_version -source $source_version"
1498 rejects it. So the -source option is useful. */
1499 resultp->source_option = true;
1504 resultp->target_option = true;
1505 resultp->usable = true;
1509 /* Maybe this -target option requires a -source option? Try with
1510 -target and -source options. (Supported by Sun javac 1.4 and
1512 unlink (compiled_file_name);
1514 java_sources[0] = conftest_file_name;
1515 if (!compile_using_javac (java_sources, 1,
1516 true, source_version,
1517 true, target_version,
1519 false, false, false, true)
1520 && stat (compiled_file_name, &statbuf) >= 0
1521 && get_classfile_version (compiled_file_name)
1522 <= corresponding_classfile_version (target_version))
1524 /* "javac -target $target_version -source $source_version"
1525 compiled conftest.java successfully. */
1526 resultp->source_option = true;
1527 resultp->target_option = true;
1528 resultp->usable = true;
1533 free (compiled_file_name);
1534 free (conftest_file_name);
1536 resultp->tested = true;
1539 *usablep = resultp->usable;
1540 *source_option_p = resultp->source_option;
1541 *target_option_p = resultp->target_option;
1546 is_jikes_present (void)
1548 static bool jikes_tested;
1549 static bool jikes_present;
1553 /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1" */
1559 exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
1561 jikes_present = (exitstatus == 0 || exitstatus == 1);
1562 jikes_tested = true;
1565 return jikes_present;
1568 /* ============================= Main function ============================= */
1571 compile_java_class (const char * const *java_sources,
1572 unsigned int java_sources_count,
1573 const char * const *classpaths,
1574 unsigned int classpaths_count,
1575 const char *source_version,
1576 const char *target_version,
1577 const char *directory,
1578 bool optimize, bool debug,
1579 bool use_minimal_classpath,
1583 char *old_JAVA_HOME;
1586 const char *javac = getenv ("JAVAC");
1587 if (javac != NULL && javac[0] != '\0')
1589 bool usable = false;
1590 bool no_assert_option = false;
1591 bool source_option = false;
1592 bool target_option = false;
1594 if (target_version == NULL)
1595 target_version = default_target_version ();
1597 if (is_envjavac_gcj (javac))
1599 /* It's a version of gcj. Ignore the version of the class files
1601 if (strcmp (target_version, "1.4") == 0
1602 && strcmp (source_version, "1.4") == 0)
1604 if (is_envjavac_gcj_14_14_usable (javac, &usable))
1610 else if (strcmp (target_version, "1.4") == 0
1611 && strcmp (source_version, "1.3") == 0)
1613 if (is_envjavac_gcj_14_13_usable (javac,
1614 &usable, &no_assert_option))
1623 /* It's not gcj. Assume the classfile versions are correct. */
1624 if (is_envjavac_nongcj_usable (javac,
1625 source_version, target_version,
1627 &source_option, &target_option))
1636 char *old_classpath;
1637 char *javac_with_options;
1639 /* Set CLASSPATH. */
1641 set_classpath (classpaths, classpaths_count, false, verbose);
1643 javac_with_options =
1645 ? xasprintf ("%s -fno-assert", javac)
1646 : xasprintf ("%s%s%s%s%s",
1648 source_option ? " -source " : "",
1649 source_option ? source_version : "",
1650 target_option ? " -target " : "",
1651 target_option ? target_version : ""));
1653 err = compile_using_envjavac (javac_with_options,
1654 java_sources, java_sources_count,
1655 directory, optimize, debug, verbose,
1658 free (javac_with_options);
1660 /* Reset CLASSPATH. */
1661 reset_classpath (old_classpath);
1668 /* Unset the JAVA_HOME environment variable. */
1669 old_JAVA_HOME = getenv ("JAVA_HOME");
1670 if (old_JAVA_HOME != NULL)
1672 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
1673 unsetenv ("JAVA_HOME");
1676 if (is_gcj_present ())
1678 /* Test whether it supports the desired target-version and
1679 source-version. But ignore the version of the class files that
1681 bool usable = false;
1682 bool no_assert_option = false;
1684 if (target_version == NULL)
1685 target_version = default_target_version ();
1687 if (strcmp (target_version, "1.4") == 0
1688 && strcmp (source_version, "1.4") == 0)
1690 if (is_gcj_14_14_usable (&usable))
1696 else if (strcmp (target_version, "1.4") == 0
1697 && strcmp (source_version, "1.3") == 0)
1699 if (is_gcj_14_13_usable (&usable, &no_assert_option))
1708 char *old_classpath;
1710 /* Set CLASSPATH. We could also use the --CLASSPATH=... option
1711 of gcj. Note that --classpath=... option is different: its
1712 argument should also contain gcj's libgcj.jar, but we don't
1713 know its location. */
1715 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1718 err = compile_using_gcj (java_sources, java_sources_count,
1720 directory, optimize, debug, verbose, false);
1722 /* Reset CLASSPATH. */
1723 reset_classpath (old_classpath);
1729 if (is_javac_present ())
1731 bool usable = false;
1732 bool source_option = false;
1733 bool target_option = false;
1735 if (target_version == NULL)
1736 target_version = default_target_version ();
1738 if (is_javac_usable (source_version, target_version,
1739 &usable, &source_option, &target_option))
1747 char *old_classpath;
1749 /* Set CLASSPATH. We don't use the "-classpath ..." option because
1750 in JDK 1.1.x its argument should also contain the JDK's
1751 classes.zip, but we don't know its location. (In JDK 1.3.0 it
1754 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1757 err = compile_using_javac (java_sources, java_sources_count,
1758 source_option, source_version,
1759 target_option, target_version,
1760 directory, optimize, debug, verbose,
1763 /* Reset CLASSPATH. */
1764 reset_classpath (old_classpath);
1770 if (is_jikes_present ())
1772 /* Test whether it supports the desired target-version and
1774 bool usable = (strcmp (source_version, "1.3") == 0);
1778 char *old_classpath;
1780 /* Set CLASSPATH. We could also use the "-classpath ..." option.
1781 Since jikes doesn't come with its own standard library, it
1782 needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
1783 To increase the chance of success, we reuse the current CLASSPATH
1784 if the user has set it. */
1786 set_classpath (classpaths, classpaths_count, false, verbose);
1788 err = compile_using_jikes (java_sources, java_sources_count,
1789 directory, optimize, debug, verbose,
1792 /* Reset CLASSPATH. */
1793 reset_classpath (old_classpath);
1799 error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
1803 if (old_JAVA_HOME != NULL)
1805 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
1806 free (old_JAVA_HOME);