1 /* Compile a Java program.
2 Copyright (C) 2001-2003, 2006 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
31 #include <sys/types.h>
34 #include "javaversion.h"
37 #include "wait-process.h"
38 #include "classpath.h"
41 #include "binary-io.h"
42 #include "safe-read.h"
47 #include "fwriteerror.h"
48 #include "clean-temp.h"
50 #include "xvasprintf.h"
54 #define _(str) gettext (str)
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 envjavac_gcj = (strstr (line, "gcj") != NULL);
580 /* Remove zombie process from process list, and retrieve exit status. */
581 exitstatus = wait_subprocess (child, javac, true, true, true, false);
583 envjavac_gcj = false;
588 envjavac_tested = true;
594 /* Test whether $JAVAC, known to be a version of gcj, can be used for
595 compiling with target_version = 1.4 and source_version = 1.4.
596 Return a failure indicator (true upon error). */
598 is_envjavac_gcj_14_14_usable (const char *javac, bool *usablep)
600 static bool envjavac_tested;
601 static bool envjavac_usable;
603 if (!envjavac_tested)
606 struct temp_dir *tmpdir;
607 char *conftest_file_name;
608 char *compiled_file_name;
609 const char *java_sources[1];
612 tmpdir = create_temp_dir ("java", NULL, false);
617 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
618 if (write_temp_file (tmpdir, conftest_file_name,
619 get_goodcode_snippet ("1.4")))
621 free (conftest_file_name);
622 cleanup_temp_dir (tmpdir);
627 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
628 register_temp_file (tmpdir, compiled_file_name);
630 java_sources[0] = conftest_file_name;
631 if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name,
632 false, false, false, true)
633 && stat (compiled_file_name, &statbuf) >= 0)
634 /* Compilation succeeded. */
635 envjavac_usable = true;
637 free (compiled_file_name);
638 free (conftest_file_name);
640 cleanup_temp_dir (tmpdir);
642 envjavac_tested = true;
645 *usablep = envjavac_usable;
649 /* Test whether $JAVAC, known to be a version of gcj, can be used for
650 compiling with target_version = 1.4 and source_version = 1.3.
651 Return a failure indicator (true upon error). */
653 is_envjavac_gcj_14_13_usable (const char *javac,
654 bool *usablep, bool *need_no_assert_option_p)
656 static bool envjavac_tested;
657 static bool envjavac_usable;
658 static bool envjavac_need_no_assert_option;
660 if (!envjavac_tested)
662 /* Try $JAVAC and "$JAVAC -fno-assert". But add -fno-assert only if
663 it makes a difference. (It could already be part of $JAVAC.) */
664 struct temp_dir *tmpdir;
665 char *conftest_file_name;
666 char *compiled_file_name;
667 const char *java_sources[1];
670 char *javac_noassert;
671 bool javac_noassert_works;
673 tmpdir = create_temp_dir ("java", NULL, false);
678 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
679 if (write_temp_file (tmpdir, conftest_file_name,
680 get_goodcode_snippet ("1.3")))
682 free (conftest_file_name);
683 cleanup_temp_dir (tmpdir);
688 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
689 register_temp_file (tmpdir, compiled_file_name);
691 java_sources[0] = conftest_file_name;
692 if (!compile_using_envjavac (javac,
693 java_sources, 1, tmpdir->dir_name,
694 false, false, false, true)
695 && stat (compiled_file_name, &statbuf) >= 0)
696 /* Compilation succeeded. */
701 unlink (compiled_file_name);
703 javac_noassert = xasprintf ("%s -fno-assert", javac);
705 java_sources[0] = conftest_file_name;
706 if (!compile_using_envjavac (javac_noassert,
707 java_sources, 1, tmpdir->dir_name,
708 false, false, false, true)
709 && stat (compiled_file_name, &statbuf) >= 0)
710 /* Compilation succeeded. */
711 javac_noassert_works = true;
713 javac_noassert_works = false;
715 free (compiled_file_name);
716 free (conftest_file_name);
718 if (javac_works && javac_noassert_works)
721 concatenated_pathname (tmpdir->dir_name, "conftestfail.java",
723 if (write_temp_file (tmpdir, conftest_file_name,
724 get_failcode_snippet ("1.3")))
726 free (conftest_file_name);
727 free (javac_noassert);
728 cleanup_temp_dir (tmpdir);
733 concatenated_pathname (tmpdir->dir_name, "conftestfail.class",
735 register_temp_file (tmpdir, compiled_file_name);
737 java_sources[0] = conftest_file_name;
738 if (!compile_using_envjavac (javac,
739 java_sources, 1, tmpdir->dir_name,
740 false, false, false, true)
741 && stat (compiled_file_name, &statbuf) >= 0)
743 /* Compilation succeeded. */
744 unlink (compiled_file_name);
746 java_sources[0] = conftest_file_name;
747 if (!(!compile_using_envjavac (javac_noassert,
748 java_sources, 1, tmpdir->dir_name,
749 false, false, false, true)
750 && stat (compiled_file_name, &statbuf) >= 0))
751 /* Compilation failed. */
752 /* "$JAVAC -fno-assert" works better than $JAVAC. */
756 free (compiled_file_name);
757 free (conftest_file_name);
760 cleanup_temp_dir (tmpdir);
764 envjavac_usable = true;
765 envjavac_need_no_assert_option = false;
767 else if (javac_noassert_works)
769 envjavac_usable = true;
770 envjavac_need_no_assert_option = true;
773 envjavac_tested = true;
776 *usablep = envjavac_usable;
777 *need_no_assert_option_p = envjavac_need_no_assert_option;
781 /* Test whether $JAVAC, known to be not a version of gcj, can be used, and
782 whether it needs a -source and/or -target option.
783 Return a failure indicator (true upon error). */
785 is_envjavac_nongcj_usable (const char *javac,
786 const char *source_version,
787 const char *target_version,
789 bool *source_option_p, bool *target_option_p)
791 /* The cache depends on the source_version and target_version. */
799 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
800 struct result_t *resultp;
802 resultp = &result_cache[source_version_index (source_version)]
803 [target_version_index (target_version)];
804 if (!resultp->tested)
807 struct temp_dir *tmpdir;
808 char *conftest_file_name;
809 char *compiled_file_name;
810 const char *java_sources[1];
813 tmpdir = create_temp_dir ("java", NULL, false);
818 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
819 if (write_temp_file (tmpdir, conftest_file_name,
820 get_goodcode_snippet (source_version)))
822 free (conftest_file_name);
823 cleanup_temp_dir (tmpdir);
828 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
829 register_temp_file (tmpdir, compiled_file_name);
831 java_sources[0] = conftest_file_name;
832 if (!compile_using_envjavac (javac,
833 java_sources, 1, tmpdir->dir_name,
834 false, false, false, true)
835 && stat (compiled_file_name, &statbuf) >= 0
836 && get_classfile_version (compiled_file_name)
837 <= corresponding_classfile_version (target_version))
839 /* $JAVAC compiled conftest.java successfully. */
840 /* Try adding -source option if it is useful. */
842 xasprintf ("%s -source %s", javac, source_version);
844 unlink (compiled_file_name);
846 java_sources[0] = conftest_file_name;
847 if (!compile_using_envjavac (javac_source,
848 java_sources, 1, tmpdir->dir_name,
849 false, false, false, true)
850 && stat (compiled_file_name, &statbuf) >= 0
851 && get_classfile_version (compiled_file_name)
852 <= corresponding_classfile_version (target_version))
854 const char *failcode = get_failcode_snippet (source_version);
856 if (failcode != NULL)
858 free (compiled_file_name);
859 free (conftest_file_name);
862 concatenated_pathname (tmpdir->dir_name,
865 if (write_temp_file (tmpdir, conftest_file_name, failcode))
867 free (conftest_file_name);
869 cleanup_temp_dir (tmpdir);
874 concatenated_pathname (tmpdir->dir_name,
875 "conftestfail.class",
877 register_temp_file (tmpdir, compiled_file_name);
879 java_sources[0] = conftest_file_name;
880 if (!compile_using_envjavac (javac,
883 false, false, false, true)
884 && stat (compiled_file_name, &statbuf) >= 0)
886 unlink (compiled_file_name);
888 java_sources[0] = conftest_file_name;
889 if (compile_using_envjavac (javac_source,
892 false, false, false, true))
893 /* $JAVAC compiled conftestfail.java successfully, and
894 "$JAVAC -source $source_version" rejects it. So the
895 -source option is useful. */
896 resultp->source_option = true;
903 resultp->usable = true;
907 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
908 option but no -source option.) */
910 xasprintf ("%s -target %s", javac, target_version);
912 unlink (compiled_file_name);
914 java_sources[0] = conftest_file_name;
915 if (!compile_using_envjavac (javac_target,
916 java_sources, 1, tmpdir->dir_name,
917 false, false, false, true)
918 && stat (compiled_file_name, &statbuf) >= 0
919 && get_classfile_version (compiled_file_name)
920 <= corresponding_classfile_version (target_version))
922 /* "$JAVAC -target $target_version" compiled conftest.java
924 /* Try adding -source option if it is useful. */
925 char *javac_target_source =
926 xasprintf ("%s -source %s", javac_target, source_version);
928 unlink (compiled_file_name);
930 java_sources[0] = conftest_file_name;
931 if (!compile_using_envjavac (javac_target_source,
932 java_sources, 1, tmpdir->dir_name,
933 false, false, false, true)
934 && stat (compiled_file_name, &statbuf) >= 0
935 && get_classfile_version (compiled_file_name)
936 <= corresponding_classfile_version (target_version))
938 const char *failcode = get_failcode_snippet (source_version);
940 if (failcode != NULL)
942 free (compiled_file_name);
943 free (conftest_file_name);
946 concatenated_pathname (tmpdir->dir_name,
949 if (write_temp_file (tmpdir, conftest_file_name,
952 free (conftest_file_name);
953 free (javac_target_source);
955 cleanup_temp_dir (tmpdir);
960 concatenated_pathname (tmpdir->dir_name,
961 "conftestfail.class",
963 register_temp_file (tmpdir, compiled_file_name);
965 java_sources[0] = conftest_file_name;
966 if (!compile_using_envjavac (javac_target,
969 false, false, false, true)
970 && stat (compiled_file_name, &statbuf) >= 0)
972 unlink (compiled_file_name);
974 java_sources[0] = conftest_file_name;
975 if (compile_using_envjavac (javac_target_source,
980 /* "$JAVAC -target $target_version" compiled
981 conftestfail.java successfully, and
982 "$JAVAC -target $target_version -source $source_version"
983 rejects it. So the -source option is useful. */
984 resultp->source_option = true;
989 free (javac_target_source);
991 resultp->target_option = true;
992 resultp->usable = true;
996 /* Maybe this -target option requires a -source option? Try with
997 -target and -source options. (Supported by Sun javac 1.4 and
999 char *javac_target_source =
1000 xasprintf ("%s -source %s", javac_target, source_version);
1002 unlink (compiled_file_name);
1004 java_sources[0] = conftest_file_name;
1005 if (!compile_using_envjavac (javac_target_source,
1006 java_sources, 1, tmpdir->dir_name,
1007 false, false, false, true)
1008 && stat (compiled_file_name, &statbuf) >= 0
1009 && get_classfile_version (compiled_file_name)
1010 <= corresponding_classfile_version (target_version))
1012 /* "$JAVAC -target $target_version -source $source_version"
1013 compiled conftest.java successfully. */
1014 resultp->source_option = true;
1015 resultp->target_option = true;
1016 resultp->usable = true;
1019 free (javac_target_source);
1022 free (javac_target);
1025 free (compiled_file_name);
1026 free (conftest_file_name);
1028 resultp->tested = true;
1031 *usablep = resultp->usable;
1032 *source_option_p = resultp->source_option;
1033 *target_option_p = resultp->target_option;
1038 is_gcj_present (void)
1040 static bool gcj_tested;
1041 static bool gcj_present;
1045 /* Test for presence of gcj:
1046 "gcj --version 2> /dev/null | \
1047 sed -e 's,^[^0-9]*,,' -e 1q | \
1048 sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null" */
1055 argv[1] = "--version";
1057 child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
1059 gcj_present = false;
1062 /* Read the subprocess output, drop all lines except the first,
1063 drop all characters before the first digit, and test whether
1064 the remaining string starts with a digit >= 3, but not with
1069 while (safe_read (fd[0], &c[count], 1) > 0)
1071 if (c[count] == '\n')
1075 if (!(c[0] >= '0' && c[0] <= '9'))
1077 gcj_present = (c[0] >= '3');
1082 if (c[0] == '3' && c[1] == '.'
1083 && (c[2] == '0' || c[2] == '1'))
1084 gcj_present = false;
1088 while (safe_read (fd[0], &c[0], 1) > 0)
1093 /* Remove zombie process from process list, and retrieve exit
1096 wait_subprocess (child, "gcj", false, true, true, false);
1097 if (exitstatus != 0)
1098 gcj_present = false;
1103 /* See if libgcj.jar is well installed. */
1104 struct temp_dir *tmpdir;
1106 tmpdir = create_temp_dir ("java", NULL, false);
1108 gcj_present = false;
1111 char *conftest_file_name;
1113 conftest_file_name =
1114 concatenated_pathname (tmpdir->dir_name, "conftestlib.java",
1116 if (write_temp_file (tmpdir, conftest_file_name,
1117 "public class conftestlib {\n"
1118 " public static void main (String[] args) {\n"
1121 gcj_present = false;
1124 char *compiled_file_name;
1125 const char *java_sources[1];
1127 compiled_file_name =
1128 concatenated_pathname (tmpdir->dir_name,
1129 "conftestlib.class",
1131 register_temp_file (tmpdir, compiled_file_name);
1133 java_sources[0] = conftest_file_name;
1134 if (compile_using_gcj (java_sources, 1, false,
1136 false, false, false, true))
1137 gcj_present = false;
1139 free (compiled_file_name);
1141 free (conftest_file_name);
1143 cleanup_temp_dir (tmpdir);
1152 /* Test gcj can be used for compiling with target_version = 1.4 and
1153 source_version = 1.4.
1154 Return a failure indicator (true upon error). */
1156 is_gcj_14_14_usable (bool *usablep)
1158 static bool gcj_tested;
1159 static bool gcj_usable;
1164 struct temp_dir *tmpdir;
1165 char *conftest_file_name;
1166 char *compiled_file_name;
1167 const char *java_sources[1];
1168 struct stat statbuf;
1170 tmpdir = create_temp_dir ("java", NULL, false);
1174 conftest_file_name =
1175 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1176 if (write_temp_file (tmpdir, conftest_file_name,
1177 get_goodcode_snippet ("1.4")))
1179 free (conftest_file_name);
1180 cleanup_temp_dir (tmpdir);
1184 compiled_file_name =
1185 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1186 register_temp_file (tmpdir, compiled_file_name);
1188 java_sources[0] = conftest_file_name;
1189 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1190 false, false, false, true)
1191 && stat (compiled_file_name, &statbuf) >= 0)
1192 /* Compilation succeeded. */
1195 free (compiled_file_name);
1196 free (conftest_file_name);
1198 cleanup_temp_dir (tmpdir);
1203 *usablep = gcj_usable;
1207 /* Test whether gcj can be used for compiling with target_version = 1.4 and
1208 source_version = 1.3.
1209 Return a failure indicator (true upon error). */
1211 is_gcj_14_13_usable (bool *usablep, bool *need_no_assert_option_p)
1213 static bool gcj_tested;
1214 static bool gcj_usable;
1215 static bool gcj_need_no_assert_option;
1219 /* Try gcj and "gcj -fno-assert". But add -fno-assert only if
1220 it works (not gcj < 3.3). */
1221 struct temp_dir *tmpdir;
1222 char *conftest_file_name;
1223 char *compiled_file_name;
1224 const char *java_sources[1];
1225 struct stat statbuf;
1227 tmpdir = create_temp_dir ("java", NULL, false);
1231 conftest_file_name =
1232 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1233 if (write_temp_file (tmpdir, conftest_file_name,
1234 get_goodcode_snippet ("1.3")))
1236 free (conftest_file_name);
1237 cleanup_temp_dir (tmpdir);
1241 compiled_file_name =
1242 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1243 register_temp_file (tmpdir, compiled_file_name);
1245 java_sources[0] = conftest_file_name;
1246 if (!compile_using_gcj (java_sources, 1, true, tmpdir->dir_name,
1247 false, false, false, true)
1248 && stat (compiled_file_name, &statbuf) >= 0)
1249 /* Compilation succeeded. */
1252 gcj_need_no_assert_option = true;
1256 unlink (compiled_file_name);
1258 java_sources[0] = conftest_file_name;
1259 if (!compile_using_gcj (java_sources, 1, false, tmpdir->dir_name,
1260 false, false, false, true)
1261 && stat (compiled_file_name, &statbuf) >= 0)
1262 /* Compilation succeeded. */
1265 gcj_need_no_assert_option = false;
1269 free (compiled_file_name);
1270 free (conftest_file_name);
1272 cleanup_temp_dir (tmpdir);
1277 *usablep = gcj_usable;
1278 *need_no_assert_option_p = gcj_need_no_assert_option;
1283 is_javac_present (void)
1285 static bool javac_tested;
1286 static bool javac_present;
1290 /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
1296 exitstatus = execute ("javac", "javac", argv, false, false, true, true,
1298 javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
1299 javac_tested = true;
1302 return javac_present;
1305 /* Test whether javac can be used and whether it needs a -source and/or
1307 Return a failure indicator (true upon error). */
1309 is_javac_usable (const char *source_version, const char *target_version,
1310 bool *usablep, bool *source_option_p, bool *target_option_p)
1312 /* The cache depends on the source_version and target_version. */
1320 static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND];
1321 struct result_t *resultp;
1323 resultp = &result_cache[source_version_index (source_version)]
1324 [target_version_index (target_version)];
1325 if (!resultp->tested)
1328 struct temp_dir *tmpdir;
1329 char *conftest_file_name;
1330 char *compiled_file_name;
1331 const char *java_sources[1];
1332 struct stat statbuf;
1334 tmpdir = create_temp_dir ("java", NULL, false);
1338 conftest_file_name =
1339 concatenated_pathname (tmpdir->dir_name, "conftest.java", NULL);
1340 if (write_temp_file (tmpdir, conftest_file_name,
1341 get_goodcode_snippet (source_version)))
1343 free (conftest_file_name);
1344 cleanup_temp_dir (tmpdir);
1348 compiled_file_name =
1349 concatenated_pathname (tmpdir->dir_name, "conftest.class", NULL);
1350 register_temp_file (tmpdir, compiled_file_name);
1352 java_sources[0] = conftest_file_name;
1353 if (!compile_using_javac (java_sources, 1,
1354 false, source_version,
1355 false, target_version,
1356 tmpdir->dir_name, false, false, false, true)
1357 && stat (compiled_file_name, &statbuf) >= 0
1358 && get_classfile_version (compiled_file_name)
1359 <= corresponding_classfile_version (target_version))
1361 /* javac compiled conftest.java successfully. */
1362 /* Try adding -source option if it is useful. */
1363 unlink (compiled_file_name);
1365 java_sources[0] = conftest_file_name;
1366 if (!compile_using_javac (java_sources, 1,
1367 true, source_version,
1368 false, target_version,
1369 tmpdir->dir_name, false, false, false, true)
1370 && stat (compiled_file_name, &statbuf) >= 0
1371 && get_classfile_version (compiled_file_name)
1372 <= corresponding_classfile_version (target_version))
1374 const char *failcode = get_failcode_snippet (source_version);
1376 if (failcode != NULL)
1378 free (compiled_file_name);
1379 free (conftest_file_name);
1381 conftest_file_name =
1382 concatenated_pathname (tmpdir->dir_name,
1383 "conftestfail.java",
1385 if (write_temp_file (tmpdir, conftest_file_name, failcode))
1387 free (conftest_file_name);
1388 cleanup_temp_dir (tmpdir);
1392 compiled_file_name =
1393 concatenated_pathname (tmpdir->dir_name,
1394 "conftestfail.class",
1396 register_temp_file (tmpdir, compiled_file_name);
1398 java_sources[0] = conftest_file_name;
1399 if (!compile_using_javac (java_sources, 1,
1400 false, source_version,
1401 false, target_version,
1403 false, false, false, true)
1404 && stat (compiled_file_name, &statbuf) >= 0)
1406 unlink (compiled_file_name);
1408 java_sources[0] = conftest_file_name;
1409 if (compile_using_javac (java_sources, 1,
1410 true, source_version,
1411 false, target_version,
1413 false, false, false, true))
1414 /* javac compiled conftestfail.java successfully, and
1415 "javac -source $source_version" rejects it. So the
1416 -source option is useful. */
1417 resultp->source_option = true;
1422 resultp->usable = true;
1426 /* Try with -target option alone. (Sun javac 1.3.1 has the -target
1427 option but no -source option.) */
1428 unlink (compiled_file_name);
1430 java_sources[0] = conftest_file_name;
1431 if (!compile_using_javac (java_sources, 1,
1432 false, source_version,
1433 true, target_version,
1435 false, false, false, true)
1436 && stat (compiled_file_name, &statbuf) >= 0
1437 && get_classfile_version (compiled_file_name)
1438 <= corresponding_classfile_version (target_version))
1440 /* "javac -target $target_version" compiled conftest.java
1442 /* Try adding -source option if it is useful. */
1443 unlink (compiled_file_name);
1445 java_sources[0] = conftest_file_name;
1446 if (!compile_using_javac (java_sources, 1,
1447 true, source_version,
1448 true, target_version,
1450 false, false, false, true)
1451 && stat (compiled_file_name, &statbuf) >= 0
1452 && get_classfile_version (compiled_file_name)
1453 <= corresponding_classfile_version (target_version))
1455 const char *failcode = get_failcode_snippet (source_version);
1457 if (failcode != NULL)
1459 free (compiled_file_name);
1460 free (conftest_file_name);
1462 conftest_file_name =
1463 concatenated_pathname (tmpdir->dir_name,
1464 "conftestfail.java",
1466 if (write_temp_file (tmpdir, conftest_file_name,
1469 free (conftest_file_name);
1470 cleanup_temp_dir (tmpdir);
1474 compiled_file_name =
1475 concatenated_pathname (tmpdir->dir_name,
1476 "conftestfail.class",
1478 register_temp_file (tmpdir, compiled_file_name);
1480 java_sources[0] = conftest_file_name;
1481 if (!compile_using_javac (java_sources, 1,
1482 false, source_version,
1483 true, target_version,
1485 false, false, false, true)
1486 && stat (compiled_file_name, &statbuf) >= 0)
1488 unlink (compiled_file_name);
1490 java_sources[0] = conftest_file_name;
1491 if (compile_using_javac (java_sources, 1,
1492 true, source_version,
1493 true, target_version,
1495 false, false, false, true))
1496 /* "javac -target $target_version" compiled
1497 conftestfail.java successfully, and
1498 "javac -target $target_version -source $source_version"
1499 rejects it. So the -source option is useful. */
1500 resultp->source_option = true;
1505 resultp->target_option = true;
1506 resultp->usable = true;
1510 /* Maybe this -target option requires a -source option? Try with
1511 -target and -source options. (Supported by Sun javac 1.4 and
1513 unlink (compiled_file_name);
1515 java_sources[0] = conftest_file_name;
1516 if (!compile_using_javac (java_sources, 1,
1517 true, source_version,
1518 true, target_version,
1520 false, false, false, true)
1521 && stat (compiled_file_name, &statbuf) >= 0
1522 && get_classfile_version (compiled_file_name)
1523 <= corresponding_classfile_version (target_version))
1525 /* "javac -target $target_version -source $source_version"
1526 compiled conftest.java successfully. */
1527 resultp->source_option = true;
1528 resultp->target_option = true;
1529 resultp->usable = true;
1534 free (compiled_file_name);
1535 free (conftest_file_name);
1537 resultp->tested = true;
1540 *usablep = resultp->usable;
1541 *source_option_p = resultp->source_option;
1542 *target_option_p = resultp->target_option;
1547 is_jikes_present (void)
1549 static bool jikes_tested;
1550 static bool jikes_present;
1554 /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1" */
1560 exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
1562 jikes_present = (exitstatus == 0 || exitstatus == 1);
1563 jikes_tested = true;
1566 return jikes_present;
1569 /* ============================= Main function ============================= */
1572 compile_java_class (const char * const *java_sources,
1573 unsigned int java_sources_count,
1574 const char * const *classpaths,
1575 unsigned int classpaths_count,
1576 const char *source_version,
1577 const char *target_version,
1578 const char *directory,
1579 bool optimize, bool debug,
1580 bool use_minimal_classpath,
1584 char *old_JAVA_HOME;
1587 const char *javac = getenv ("JAVAC");
1588 if (javac != NULL && javac[0] != '\0')
1590 bool usable = false;
1591 bool no_assert_option = false;
1592 bool source_option = false;
1593 bool target_option = false;
1595 if (target_version == NULL)
1596 target_version = default_target_version ();
1598 if (is_envjavac_gcj (javac))
1600 /* It's a version of gcj. Ignore the version of the class files
1602 if (strcmp (target_version, "1.4") == 0
1603 && strcmp (source_version, "1.4") == 0)
1605 if (is_envjavac_gcj_14_14_usable (javac, &usable))
1611 else if (strcmp (target_version, "1.4") == 0
1612 && strcmp (source_version, "1.3") == 0)
1614 if (is_envjavac_gcj_14_13_usable (javac,
1615 &usable, &no_assert_option))
1624 /* It's not gcj. Assume the classfile versions are correct. */
1625 if (is_envjavac_nongcj_usable (javac,
1626 source_version, target_version,
1628 &source_option, &target_option))
1637 char *old_classpath;
1638 char *javac_with_options;
1640 /* Set CLASSPATH. */
1642 set_classpath (classpaths, classpaths_count, false, verbose);
1644 javac_with_options =
1646 ? xasprintf ("%s -fno-assert", javac)
1647 : xasprintf ("%s%s%s%s%s",
1649 source_option ? " -source " : "",
1650 source_option ? source_version : "",
1651 target_option ? " -target " : "",
1652 target_option ? target_version : ""));
1654 err = compile_using_envjavac (javac_with_options,
1655 java_sources, java_sources_count,
1656 directory, optimize, debug, verbose,
1659 free (javac_with_options);
1661 /* Reset CLASSPATH. */
1662 reset_classpath (old_classpath);
1669 /* Unset the JAVA_HOME environment variable. */
1670 old_JAVA_HOME = getenv ("JAVA_HOME");
1671 if (old_JAVA_HOME != NULL)
1673 old_JAVA_HOME = xstrdup (old_JAVA_HOME);
1674 unsetenv ("JAVA_HOME");
1677 if (is_gcj_present ())
1679 /* Test whether it supports the desired target-version and
1680 source-version. But ignore the version of the class files that
1682 bool usable = false;
1683 bool no_assert_option = false;
1685 if (target_version == NULL)
1686 target_version = default_target_version ();
1688 if (strcmp (target_version, "1.4") == 0
1689 && strcmp (source_version, "1.4") == 0)
1691 if (is_gcj_14_14_usable (&usable))
1697 else if (strcmp (target_version, "1.4") == 0
1698 && strcmp (source_version, "1.3") == 0)
1700 if (is_gcj_14_13_usable (&usable, &no_assert_option))
1709 char *old_classpath;
1711 /* Set CLASSPATH. We could also use the --CLASSPATH=... option
1712 of gcj. Note that --classpath=... option is different: its
1713 argument should also contain gcj's libgcj.jar, but we don't
1714 know its location. */
1716 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1719 err = compile_using_gcj (java_sources, java_sources_count,
1721 directory, optimize, debug, verbose, false);
1723 /* Reset CLASSPATH. */
1724 reset_classpath (old_classpath);
1730 if (is_javac_present ())
1732 bool usable = false;
1733 bool source_option = false;
1734 bool target_option = false;
1736 if (target_version == NULL)
1737 target_version = default_target_version ();
1739 if (is_javac_usable (source_version, target_version,
1740 &usable, &source_option, &target_option))
1748 char *old_classpath;
1750 /* Set CLASSPATH. We don't use the "-classpath ..." option because
1751 in JDK 1.1.x its argument should also contain the JDK's
1752 classes.zip, but we don't know its location. (In JDK 1.3.0 it
1755 set_classpath (classpaths, classpaths_count, use_minimal_classpath,
1758 err = compile_using_javac (java_sources, java_sources_count,
1759 source_option, source_version,
1760 target_option, target_version,
1761 directory, optimize, debug, verbose,
1764 /* Reset CLASSPATH. */
1765 reset_classpath (old_classpath);
1771 if (is_jikes_present ())
1773 /* Test whether it supports the desired target-version and
1775 bool usable = (strcmp (source_version, "1.3") == 0);
1779 char *old_classpath;
1781 /* Set CLASSPATH. We could also use the "-classpath ..." option.
1782 Since jikes doesn't come with its own standard library, it
1783 needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
1784 To increase the chance of success, we reuse the current CLASSPATH
1785 if the user has set it. */
1787 set_classpath (classpaths, classpaths_count, false, verbose);
1789 err = compile_using_jikes (java_sources, java_sources_count,
1790 directory, optimize, debug, verbose,
1793 /* Reset CLASSPATH. */
1794 reset_classpath (old_classpath);
1800 error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
1804 if (old_JAVA_HOME != NULL)
1806 xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
1807 free (old_JAVA_HOME);