+ /* Use a cache. Assumes that the PATH environment variable doesn't change
+ during the lifetime of the program. */
+ static const char *java_version_cache;
+ if (java_version_cache == NULL)
+ {
+ /* Determine the version from the found JVM. */
+ java_version_cache = javaexec_version ();
+ if (java_version_cache == NULL
+ || !(java_version_cache[0] == '1' && java_version_cache[1] == '.'
+ && (java_version_cache[2] >= '1' && java_version_cache[2] <= '6')
+ && java_version_cache[3] == '\0'))
+ java_version_cache = "1.1";
+ }
+ return java_version_cache;
+}
+
+/* ======================= Source version dependent ======================= */
+
+/* Convert a source version to an index. */
+#define SOURCE_VERSION_BOUND 3 /* exclusive upper bound */
+static unsigned int
+source_version_index (const char *source_version)
+{
+ if (source_version[0] == '1' && source_version[1] == '.'
+ && (source_version[2] >= '3' && source_version[2] <= '5')
+ && source_version[3] == '\0')
+ return source_version[2] - '3';
+ error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
+ return 0;
+}
+
+/* Return a snippet of code that should compile in the given source version. */
+static const char *
+get_goodcode_snippet (const char *source_version)
+{
+ if (strcmp (source_version, "1.3") == 0)
+ return "class conftest {}\n";
+ if (strcmp (source_version, "1.4") == 0)
+ return "class conftest { static { assert(true); } }\n";
+ if (strcmp (source_version, "1.5") == 0)
+ return "class conftest<T> { T foo() { return null; } }\n";
+ error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
+ return NULL;
+}
+
+/* Return a snippet of code that should fail to compile in the given source
+ version, or NULL (standing for a snippet that would fail to compile with
+ any compiler). */
+static const char *
+get_failcode_snippet (const char *source_version)
+{
+ if (strcmp (source_version, "1.3") == 0)
+ return "class conftestfail { static { assert(true); } }\n";
+ if (strcmp (source_version, "1.4") == 0)
+ return "class conftestfail<T> { T foo() { return null; } }\n";
+ if (strcmp (source_version, "1.5") == 0)
+ return NULL;
+ error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
+ return NULL;
+}
+
+/* ======================= Target version dependent ======================= */
+
+/* Convert a target version to an index. */
+#define TARGET_VERSION_BOUND 6 /* exclusive upper bound */
+static unsigned int
+target_version_index (const char *target_version)
+{
+ if (target_version[0] == '1' && target_version[1] == '.'
+ && (target_version[2] >= '1' && target_version[2] <= '6')
+ && target_version[3] == '\0')
+ return target_version[2] - '1';
+ error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
+ return 0;
+}
+
+/* Return the class file version number corresponding to a given target
+ version. */
+static int
+corresponding_classfile_version (const char *target_version)
+{
+ if (strcmp (target_version, "1.1") == 0)
+ return 45;
+ if (strcmp (target_version, "1.2") == 0)
+ return 46;
+ if (strcmp (target_version, "1.3") == 0)
+ return 47;
+ if (strcmp (target_version, "1.4") == 0)
+ return 48;
+ if (strcmp (target_version, "1.5") == 0)
+ return 49;
+ if (strcmp (target_version, "1.6") == 0)
+ return 50;
+ error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class"));
+ return 0;
+}
+
+/* ======================== Compilation subroutines ======================== */
+
+/* Try to compile a set of Java sources with $JAVAC.
+ Return a failure indicator (true upon error). */
+static bool
+compile_using_envjavac (const char *javac,
+ const char * const *java_sources,
+ unsigned int java_sources_count,
+ const char *directory,
+ bool optimize, bool debug,
+ bool verbose, bool null_stderr)
+{
+ /* Because $JAVAC may consist of a command and options, we use the
+ shell. Because $JAVAC has been set by the user, we leave all
+ environment variables in place, including JAVA_HOME, and we don't
+ erase the user's CLASSPATH. */
+ bool err;
+ unsigned int command_length;
+ char *command;
+ char *argv[4];
+ int exitstatus;
+ unsigned int i;
+ char *p;
+
+ command_length = strlen (javac);
+ if (optimize)
+ command_length += 3;
+ if (debug)
+ command_length += 3;
+ if (directory != NULL)
+ command_length += 4 + shell_quote_length (directory);
+ for (i = 0; i < java_sources_count; i++)
+ command_length += 1 + shell_quote_length (java_sources[i]);
+ command_length += 1;
+
+ command = (char *) xmalloca (command_length);
+ p = command;
+ /* Don't shell_quote $JAVAC, because it may consist of a command
+ and options. */
+ memcpy (p, javac, strlen (javac));
+ p += strlen (javac);
+ if (optimize)
+ {
+ memcpy (p, " -O", 3);
+ p += 3;
+ }
+ if (debug)
+ {
+ memcpy (p, " -g", 3);
+ p += 3;
+ }
+ if (directory != NULL)
+ {
+ memcpy (p, " -d ", 4);
+ p += 4;
+ p = shell_quote_copy (p, directory);
+ }
+ for (i = 0; i < java_sources_count; i++)
+ {
+ *p++ = ' ';
+ p = shell_quote_copy (p, java_sources[i]);
+ }
+ *p++ = '\0';
+ /* Ensure command_length was correctly calculated. */
+ if (p - command > command_length)
+ abort ();
+
+ if (verbose)
+ printf ("%s\n", command);
+
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = command;
+ argv[3] = NULL;
+ exitstatus = execute (javac, "/bin/sh", argv, false, false, false,
+ null_stderr, true, true, NULL);
+ err = (exitstatus != 0);
+
+ freea (command);
+
+ return err;
+}
+
+/* Try to compile a set of Java sources with gcj.
+ Return a failure indicator (true upon error). */
+static bool
+compile_using_gcj (const char * const *java_sources,
+ unsigned int java_sources_count,
+ bool no_assert_option,
+ bool fsource_option, const char *source_version,
+ bool ftarget_option, const char *target_version,
+ const char *directory,
+ bool optimize, bool debug,
+ bool verbose, bool null_stderr)
+{
+ bool err;
+ unsigned int argc;
+ char **argv;
+ char **argp;
+ char *fsource_arg;
+ char *ftarget_arg;
+ int exitstatus;
+ unsigned int i;
+
+ argc =
+ 2 + (no_assert_option ? 1 : 0) + (fsource_option ? 1 : 0)
+ + (ftarget_option ? 1 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
+ + (directory != NULL ? 2 : 0) + java_sources_count;
+ argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
+
+ argp = argv;
+ *argp++ = "gcj";
+ *argp++ = "-C";
+ if (no_assert_option)
+ *argp++ = "-fno-assert";
+ if (fsource_option)
+ {
+ fsource_arg = (char *) xmalloca (9 + strlen (source_version) + 1);
+ memcpy (fsource_arg, "-fsource=", 9);
+ strcpy (fsource_arg + 9, source_version);
+ *argp++ = fsource_arg;
+ }
+ else
+ fsource_arg = NULL;
+ if (ftarget_option)
+ {
+ ftarget_arg = (char *) xmalloca (9 + strlen (target_version) + 1);
+ memcpy (ftarget_arg, "-ftarget=", 9);
+ strcpy (ftarget_arg + 9, target_version);
+ *argp++ = ftarget_arg;
+ }
+ else
+ ftarget_arg = NULL;
+ if (optimize)
+ *argp++ = "-O";
+ if (debug)
+ *argp++ = "-g";
+ if (directory != NULL)
+ {
+ *argp++ = "-d";
+ *argp++ = (char *) directory;
+ }
+ for (i = 0; i < java_sources_count; i++)
+ *argp++ = (char *) java_sources[i];
+ *argp = NULL;
+ /* Ensure argv length was correctly calculated. */
+ if (argp - argv != argc)
+ abort ();
+
+ if (verbose)
+ {
+ char *command = shell_quote_argv (argv);
+ printf ("%s\n", command);
+ free (command);
+ }
+
+ exitstatus = execute ("gcj", "gcj", argv, false, false, false, null_stderr,
+ true, true, NULL);
+ err = (exitstatus != 0);
+
+ if (ftarget_arg != NULL)
+ freea (ftarget_arg);
+ if (fsource_arg != NULL)
+ freea (fsource_arg);
+ freea (argv);
+
+ return err;
+}
+
+/* Try to compile a set of Java sources with javac.
+ Return a failure indicator (true upon error). */
+static bool
+compile_using_javac (const char * const *java_sources,
+ unsigned int java_sources_count,
+ bool source_option, const char *source_version,
+ bool target_option, const char *target_version,
+ const char *directory,
+ bool optimize, bool debug,
+ bool verbose, bool null_stderr)
+{
+ bool err;
+ unsigned int argc;
+ char **argv;
+ char **argp;
+ int exitstatus;
+ unsigned int i;
+
+ argc =
+ 1 + (source_option ? 2 : 0) + (target_option ? 2 : 0) + (optimize ? 1 : 0)
+ + (debug ? 1 : 0) + (directory != NULL ? 2 : 0) + java_sources_count;
+ argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
+
+ argp = argv;
+ *argp++ = "javac";
+ if (source_option)
+ {
+ *argp++ = "-source";
+ *argp++ = (char *) source_version;
+ }
+ if (target_option)
+ {
+ *argp++ = "-target";
+ *argp++ = (char *) target_version;
+ }
+ if (optimize)
+ *argp++ = "-O";
+ if (debug)
+ *argp++ = "-g";
+ if (directory != NULL)
+ {
+ *argp++ = "-d";
+ *argp++ = (char *) directory;
+ }
+ for (i = 0; i < java_sources_count; i++)
+ *argp++ = (char *) java_sources[i];
+ *argp = NULL;
+ /* Ensure argv length was correctly calculated. */
+ if (argp - argv != argc)
+ abort ();
+
+ if (verbose)
+ {
+ char *command = shell_quote_argv (argv);
+ printf ("%s\n", command);
+ free (command);
+ }
+
+ exitstatus = execute ("javac", "javac", argv, false, false, false,
+ null_stderr, true, true, NULL);
+ err = (exitstatus != 0);
+
+ freea (argv);
+
+ return err;
+}
+
+/* Try to compile a set of Java sources with jikes.
+ Return a failure indicator (true upon error). */
+static bool
+compile_using_jikes (const char * const *java_sources,
+ unsigned int java_sources_count,
+ const char *directory,
+ bool optimize, bool debug,
+ bool verbose, bool null_stderr)
+{
+ bool err;
+ unsigned int argc;
+ char **argv;
+ char **argp;
+ int exitstatus;
+ unsigned int i;
+
+ argc =
+ 1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
+ + java_sources_count;
+ argv = (char **) xmalloca ((argc + 1) * sizeof (char *));
+
+ argp = argv;
+ *argp++ = "jikes";
+ if (optimize)
+ *argp++ = "-O";
+ if (debug)
+ *argp++ = "-g";
+ if (directory != NULL)
+ {
+ *argp++ = "-d";
+ *argp++ = (char *) directory;
+ }
+ for (i = 0; i < java_sources_count; i++)
+ *argp++ = (char *) java_sources[i];
+ *argp = NULL;
+ /* Ensure argv length was correctly calculated. */
+ if (argp - argv != argc)
+ abort ();
+
+ if (verbose)
+ {
+ char *command = shell_quote_argv (argv);
+ printf ("%s\n", command);
+ free (command);
+ }
+
+ exitstatus = execute ("jikes", "jikes", argv, false, false, false,
+ null_stderr, true, true, NULL);
+ err = (exitstatus != 0);
+
+ freea (argv);
+
+ return err;
+}
+
+/* ====================== Usability test subroutines ====================== */
+
+/* Write a given contents to a temporary file.
+ FILE_NAME is the name of a file inside TMPDIR that is known not to exist
+ yet.
+ Return a failure indicator (true upon error). */
+static bool
+write_temp_file (struct temp_dir *tmpdir, const char *file_name,
+ const char *contents)
+{
+ FILE *fp;
+
+ register_temp_file (tmpdir, file_name);
+ fp = fopen_temp (file_name, "w");
+ if (fp == NULL)
+ {
+ error (0, errno, _("failed to create \"%s\""), file_name);
+ unregister_temp_file (tmpdir, file_name);
+ return true;
+ }
+ fputs (contents, fp);
+ if (fwriteerror_temp (fp))
+ {
+ error (0, errno, _("error while writing \"%s\" file"), file_name);
+ return true;
+ }
+ return false;
+}
+
+/* Return the class file version number of a class file on disk. */
+static int
+get_classfile_version (const char *compiled_file_name)
+{
+ unsigned char header[8];
+ int fd;
+
+ /* Open the class file. */
+ fd = open (compiled_file_name, O_RDONLY | O_BINARY, 0);
+ if (fd >= 0)
+ {
+ /* Read its first 8 bytes. */
+ if (safe_read (fd, header, 8) == 8)
+ {
+ /* Verify the class file signature. */
+ if (header[0] == 0xCA && header[1] == 0xFE
+ && header[2] == 0xBA && header[3] == 0xBE)
+ return header[7];
+ }
+ close (fd);
+ }
+
+ /* Could not get the class file version. Return a very large one. */
+ return INT_MAX;
+}
+
+/* Return true if $JAVAC is a version of gcj. */
+static bool
+is_envjavac_gcj (const char *javac)
+{
+ static bool envjavac_tested;
+ static bool envjavac_gcj;
+
+ if (!envjavac_tested)
+ {
+ /* Test whether $JAVAC is gcj:
+ "$JAVAC --version 2>/dev/null | sed -e 1q | grep gcj > /dev/null" */
+ unsigned int command_length;
+ char *command;
+ char *argv[4];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ char *line;
+ size_t linesize;
+ size_t linelen;
+ int exitstatus;
+ char *p;
+
+ /* Setup the command "$JAVAC --version". */
+ command_length = strlen (javac) + 1 + 9 + 1;
+ command = (char *) xmalloca (command_length);
+ p = command;
+ /* Don't shell_quote $JAVAC, because it may consist of a command
+ and options. */
+ memcpy (p, javac, strlen (javac));
+ p += strlen (javac);
+ memcpy (p, " --version", 1 + 9 + 1);
+ p += 1 + 9 + 1;
+ /* Ensure command_length was correctly calculated. */
+ if (p - command > command_length)
+ abort ();
+
+ /* Call $JAVAC --version 2>/dev/null. */
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = command;
+ argv[3] = NULL;
+ child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
+ false, fd);
+ if (child == -1)
+ goto failed;
+
+ /* Retrieve its result. */
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ goto failed;
+
+ line = NULL; linesize = 0;
+ linelen = getline (&line, &linesize, fp);
+ if (linelen == (size_t)(-1))
+ {
+ fclose (fp);
+ goto failed;
+ }
+ /* It is safe to call c_strstr() instead of strstr() here; see the
+ comments in c-strstr.h. */
+ envjavac_gcj = (c_strstr (line, "gcj") != NULL);
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus =
+ wait_subprocess (child, javac, true, true, true, false, NULL);
+ if (exitstatus != 0)
+ envjavac_gcj = false;
+
+ failed:
+ freea (command);
+
+ envjavac_tested = true;
+ }
+
+ return envjavac_gcj;
+}
+
+/* Return true if $JAVAC, known to be a version of gcj, is a version >= 4.3
+ of gcj. */
+static bool
+is_envjavac_gcj43 (const char *javac)
+{
+ static bool envjavac_tested;
+ static bool envjavac_gcj43;
+
+ if (!envjavac_tested)
+ {
+ /* Test whether $JAVAC is gcj:
+ "$JAVAC --version 2>/dev/null | sed -e 's,^[^0-9]*,,' -e 1q \
+ | sed -e '/^4\.[012]/d' | grep '^[4-9]' >/dev/null" */
+ unsigned int command_length;
+ char *command;
+ char *argv[4];
+ pid_t child;
+ int fd[1];
+ FILE *fp;
+ char *line;
+ size_t linesize;
+ size_t linelen;
+ int exitstatus;
+ char *p;
+
+ /* Setup the command "$JAVAC --version". */
+ command_length = strlen (javac) + 1 + 9 + 1;
+ command = (char *) xmalloca (command_length);
+ p = command;
+ /* Don't shell_quote $JAVAC, because it may consist of a command
+ and options. */
+ memcpy (p, javac, strlen (javac));
+ p += strlen (javac);
+ memcpy (p, " --version", 1 + 9 + 1);
+ p += 1 + 9 + 1;
+ /* Ensure command_length was correctly calculated. */
+ if (p - command > command_length)
+ abort ();
+
+ /* Call $JAVAC --version 2>/dev/null. */
+ argv[0] = "/bin/sh";
+ argv[1] = "-c";
+ argv[2] = command;
+ argv[3] = NULL;
+ child = create_pipe_in (javac, "/bin/sh", argv, DEV_NULL, true, true,
+ false, fd);
+ if (child == -1)
+ goto failed;
+
+ /* Retrieve its result. */
+ fp = fdopen (fd[0], "r");
+ if (fp == NULL)
+ goto failed;
+
+ line = NULL; linesize = 0;
+ linelen = getline (&line, &linesize, fp);
+ if (linelen == (size_t)(-1))
+ {
+ fclose (fp);
+ goto failed;
+ }
+ p = line;
+ while (*p != '\0' && !(*p >= '0' && *p <= '9'))
+ p++;
+ envjavac_gcj43 =
+ !(*p == '4' && p[1] == '.' && p[2] >= '0' && p[2] <= '2')
+ && (*p >= '4' && *p <= '9');
+
+ fclose (fp);
+
+ /* Remove zombie process from process list, and retrieve exit status. */
+ exitstatus =
+ wait_subprocess (child, javac, true, true, true, false, NULL);
+ if (exitstatus != 0)
+ envjavac_gcj43 = false;
+
+ failed:
+ freea (command);
+
+ envjavac_tested = true;
+ }
+
+ return envjavac_gcj43;
+}