From 66f9edfa59dc1ae55a189fb9b647b7319b2a1b02 Mon Sep 17 00:00:00 2001 From: Bruno Haible Date: Sun, 11 Jun 2023 03:13:29 +0200 Subject: [PATCH] javacomp-script, javacomp: Add support for javac versions > 11. Reported by Adrian Bunk in . * m4/javacomp.m4 (gt_JAVACOMP): When the java version is > 11, use that version as target_version, not 11. When the java version is > 17, don't warn. Remove goodcode, failcode variables. Don't bail out if the source-version argument or the target-version argument is in the range 12..99. Use a heuristic that works with javac versions 11..20 at least. Update comments. * lib/javacomp.h (compile_java_class): State that source-version and target-version may be larger than 11. * lib/javacomp.c (default_target_version): Accommodate versions up to 99. (SOURCE_VERSION_BOUND): Increase to 94. (source_version_index): Accept versions up to 99. (get_goodcode_snippet, get_failcode_snippet): Remove functions. (TARGET_VERSION_BOUND): Increase to 94. (target_version_index): Accept versions up to 99. (corresponding_classfile_version): Remove function. (execute_and_read_line): New function, based on lib/javaversion.c. (get_compiler_version): New function. (is_envjavac_usable): Add parameters source_option_out, target_option_out. Use a heuristic that works with javac versions 11..20 at least. Call cleanup_temp_dir. (is_javac_usable): Likewise. (compile_java_class): Update. --- ChangeLog | 29 ++ lib/javacomp.c | 814 +++++++++++++++++++++++++++++-------------------- lib/javacomp.h | 2 + m4/javacomp.m4 | 355 +++++++++++++-------- 4 files changed, 740 insertions(+), 460 deletions(-) diff --git a/ChangeLog b/ChangeLog index 9f69453a99..b008ba17f6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2023-06-10 Bruno Haible + + javacomp-script, javacomp: Add support for javac versions > 11. + Reported by Adrian Bunk in + . + * m4/javacomp.m4 (gt_JAVACOMP): When the java version is > 11, use that + version as target_version, not 11. When the java version is > 17, don't + warn. Remove goodcode, failcode variables. Don't bail out if the + source-version argument or the target-version argument is in the range + 12..99. Use a heuristic that works with javac versions 11..20 at least. + Update comments. + * lib/javacomp.h (compile_java_class): State that source-version and + target-version may be larger than 11. + * lib/javacomp.c (default_target_version): Accommodate versions up to + 99. + (SOURCE_VERSION_BOUND): Increase to 94. + (source_version_index): Accept versions up to 99. + (get_goodcode_snippet, get_failcode_snippet): Remove functions. + (TARGET_VERSION_BOUND): Increase to 94. + (target_version_index): Accept versions up to 99. + (corresponding_classfile_version): Remove function. + (execute_and_read_line): New function, based on lib/javaversion.c. + (get_compiler_version): New function. + (is_envjavac_usable): Add parameters source_option_out, + target_option_out. Use a heuristic that works with javac versions 11..20 + at least. Call cleanup_temp_dir. + (is_javac_usable): Likewise. + (compile_java_class): Update. + 2023-06-10 Bruno Haible javacomp-script, javacomp: Remove support for javac versions < 1.6. diff --git a/lib/javacomp.c b/lib/javacomp.c index 19de05d1fd..d23820537f 100644 --- a/lib/javacomp.c +++ b/lib/javacomp.c @@ -109,19 +109,16 @@ default_target_version (void) && java_version_cache[3] == '\0') || (java_version_cache[0] == '9' && java_version_cache[1] == '\0') - || (java_version_cache[0] == '1' + || ((java_version_cache[0] >= '1' + && java_version_cache[0] <= '9') && (java_version_cache[1] >= '0' - && java_version_cache[1] <= '1') + && java_version_cache[1] <= '9') && java_version_cache[2] == '\0')) - /* It's one of the valid target version values. */ + /* Here we could choose any target_version between source_version and + the java_version_cache. (If it is too small, it will be incremented + below until it works.) Since we documented in javacomp.h that it is + determined from the JVM, we do that. */ ; - else if (java_version_cache[0] == '1' - && (java_version_cache[1] >= '2' - && java_version_cache[1] <= '7') - && java_version_cache[2] == '\0') - /* Assume that these (not yet released) Java versions will behave - like the preceding ones. */ - java_version_cache = "11"; else java_version_cache = "1.6"; } @@ -131,7 +128,7 @@ default_target_version (void) /* ======================= Source version dependent ======================= */ /* Convert a source version to an index. */ -#define SOURCE_VERSION_BOUND 6 /* exclusive upper bound */ +#define SOURCE_VERSION_BOUND 94 /* exclusive upper bound */ static unsigned int source_version_index (const char *source_version) { @@ -143,60 +140,18 @@ source_version_index (const char *source_version) } else if (source_version[0] == '9' && source_version[1] == '\0') return 3; - else if (source_version[0] == '1' - && (source_version[1] >= '0' && source_version[1] <= '1') + else if ((source_version[0] >= '1' && source_version[0] <= '9') + && (source_version[1] >= '0' && source_version[1] <= '9') && source_version[2] == '\0') - return source_version[1] - '0' + 4; + return (source_version[0] - '1') * 10 + source_version[1] - '0' + 4; 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.6") == 0) - return "class conftest { T foo() { return null; } }\n"; - if (strcmp (source_version, "1.7") == 0) - return "class conftest { void foo () { switch (\"A\") {} } }\n"; - if (strcmp (source_version, "1.8") == 0) - return "class conftest { void foo () { Runnable r = () -> {}; } }\n"; - if (strcmp (source_version, "9") == 0) - return "interface conftest { private void foo () {} }\n"; - if (strcmp (source_version, "10") == 0) - return "class conftest { public void m() { var i = new Integer(0); } }\n"; - if (strcmp (source_version, "11") == 0) - return "class conftest { Readable r = (var b) -> 0; }\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.6") == 0) - return "class conftestfail { void foo () { switch (\"A\") {} } }\n"; - if (strcmp (source_version, "1.7") == 0) - return "class conftestfail { void foo () { Runnable r = () -> {}; } }\n"; - if (strcmp (source_version, "1.8") == 0) - return "interface conftestfail { private void foo () {} }\n"; - if (strcmp (source_version, "9") == 0) - return "class conftestfail { public void m() { var i = new Integer(0); } }\n"; - if (strcmp (source_version, "10") == 0) - return "class conftestfail { Readable r = (var b) -> 0; }\n"; - if (strcmp (source_version, "11") == 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 */ +#define TARGET_VERSION_BOUND 94 /* exclusive upper bound */ static unsigned int target_version_index (const char *target_version) { @@ -206,31 +161,10 @@ target_version_index (const char *target_version) return target_version[2] - '6'; else if (target_version[0] == '9' && target_version[1] == '\0') return 3; - else if (target_version[0] == '1' - && (target_version[1] >= '0' && target_version[1] <= '1') + else if ((target_version[0] >= '1' && target_version[0] <= '9') + && (target_version[1] >= '0' && target_version[1] <= '9') && target_version[2] == '\0') - return target_version[1] - '0' + 4; - 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.6") == 0) - return 50; - if (strcmp (target_version, "1.7") == 0) - return 51; - if (strcmp (target_version, "1.8") == 0) - return 52; - if (strcmp (target_version, "9") == 0) - return 53; - if (strcmp (target_version, "10") == 0) - return 54; - if (strcmp (target_version, "11") == 0) - return 55; + return (target_version[0] - '1') * 10 + target_version[1] - '0' + 4; error (EXIT_FAILURE, 0, _("invalid target_version argument to compile_java_class")); return 0; } @@ -389,6 +323,121 @@ compile_using_javac (const char * const *java_sources, /* ====================== Usability test subroutines ====================== */ +/* Executes a program. + Returns the first line of its output, as a freshly allocated string, or + NULL. */ +static char * +execute_and_read_line (const char *progname, + const char *prog_path, const char * const *prog_argv) +{ + pid_t child; + int fd[1]; + FILE *fp; + char *line; + size_t linesize; + size_t linelen; + int exitstatus; + + /* Open a pipe to the program. */ + child = create_pipe_in (progname, prog_path, prog_argv, NULL, + DEV_NULL, false, true, false, fd); + + if (child == -1) + return NULL; + + /* Retrieve its result. */ + fp = fdopen (fd[0], "r"); + if (fp == NULL) + { + error (0, errno, _("fdopen() failed")); + return NULL; + } + + line = NULL; linesize = 0; + linelen = getline (&line, &linesize, fp); + if (linelen == (size_t)(-1)) + { + error (0, 0, _("%s subprocess I/O error"), progname); + return NULL; + } + if (linelen > 0 && line[linelen - 1] == '\n') + line[linelen - 1] = '\0'; + + /* Read until EOF (otherwise the child process may get a SIGPIPE signal). */ + while (getc (fp) != EOF) + ; + + fclose (fp); + + /* Remove zombie process from process list, and retrieve exit status. */ + exitstatus = + wait_subprocess (child, progname, true, false, true, false, NULL); + if (exitstatus != 0) + { + free (line); + return NULL; + } + + return line; +} + +/* Executes a program, assumed to be a Java compiler with '-version' option. + Returns the version number. */ +static unsigned int +get_compiler_version (const char *progname, + const char *prog_path, const char * const *prog_argv) +{ + char *line = execute_and_read_line (progname, prog_path, prog_argv); + if (line == NULL) + return 0; + + /* Search the first digit in line. */ + char *version_start = line; + for (version_start = line; ; version_start++) + { + if (*version_start == '\0') + { + /* No digits found. */ + free (line); + return 0; + } + if (*version_start >= '0' && *version_start <= '9') + break; + } + + /* Search the end of the version string. */ + char *version_end = version_start; + while ((*version_end >= '0' && *version_end <= '9') || *version_end == '.') + version_end++; + *version_end = '\0'; + + /* Map 1.6.0_85 to 6, 1.8.0_151 to 8. Map 9.0.4 to 9, 10.0.2 to 10, etc. */ + if (version_start[0] == '1' && version_start[1] == '.') + version_start += 2; + version_end = strchr (version_start, '.'); + if (version_end != NULL) + *version_end = '\0'; + + /* Convert number to 'unsigned int'. */ + unsigned int result; + switch (strlen (version_start)) + { + case 1: + result = version_start[0] - '0'; + break; + + case 2: + result = (version_start[0] - '0') * 10 + (version_start[1] - '0'); + break; + + default: + result = 0; + } + + free (line); + return result; +} + /* 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. @@ -452,15 +501,15 @@ static bool is_envjavac_usable (const char *javac, const char *source_version, const char *target_version, bool *usablep, - bool *source_option_p, bool *target_option_p) + char source_option_out[30], char target_option_out[30]) { /* The cache depends on the source_version and target_version. */ struct result_t { - bool tested; - bool usable; - bool source_option; - bool target_option; + /*bool*/ unsigned int tested : 1; + /*bool*/ unsigned int usable : 1; + unsigned int source_option : 7; + unsigned int target_option : 7; }; static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND]; struct result_t *resultp; @@ -469,144 +518,210 @@ is_envjavac_usable (const char *javac, [target_version_index (target_version)]; if (!resultp->tested) { - /* Try $JAVAC. */ - struct temp_dir *tmpdir; - char *conftest_file_name; - char *compiled_file_name; - const char *java_sources[1]; - struct stat statbuf; - - tmpdir = create_temp_dir ("java", NULL, false); - if (tmpdir == NULL) - return true; - - conftest_file_name = - xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL); - if (write_temp_file (tmpdir, conftest_file_name, - get_goodcode_snippet (source_version))) - { - free (conftest_file_name); - cleanup_temp_dir (tmpdir); - return true; - } - - compiled_file_name = - xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL); - register_temp_file (tmpdir, compiled_file_name); - - java_sources[0] = conftest_file_name; - if (!compile_using_envjavac (javac, - java_sources, 1, tmpdir->dir_name, - false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0 - && get_classfile_version (compiled_file_name) - <= corresponding_classfile_version (target_version)) + /* Canonicalize source_version and target_version, for easier + arithmetic. */ + int try_source_version = 6 + source_version_index (source_version); + int try_target_version = 6 + target_version_index (target_version); + /* Sanity check. */ + if (try_source_version <= try_target_version) { - /* $JAVAC compiled conftest.java successfully. */ - /* Try adding -source option if it is useful. */ - char *javac_source = - xasprintf ("%s -source %s", javac, source_version); - assume (javac_source != NULL); + /* Try $JAVAC. */ + struct temp_dir *tmpdir; + char *conftest_file_name; + char *compiled_file_name; + const char *java_sources[1]; + struct stat statbuf; + + tmpdir = create_temp_dir ("java", NULL, false); + if (tmpdir == NULL) + return true; + + conftest_file_name = + xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL); + if (write_temp_file (tmpdir, conftest_file_name, "class conftest {}")) + { + free (conftest_file_name); + cleanup_temp_dir (tmpdir); + return true; + } - unlink (compiled_file_name); + compiled_file_name = + xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL); + register_temp_file (tmpdir, compiled_file_name); java_sources[0] = conftest_file_name; - if (!compile_using_envjavac (javac_source, + if (!compile_using_envjavac (javac, java_sources, 1, tmpdir->dir_name, false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0 - && get_classfile_version (compiled_file_name) - <= corresponding_classfile_version (target_version)) + && stat (compiled_file_name, &statbuf) >= 0) { - const char *failcode = get_failcode_snippet (source_version); - - if (failcode != NULL) + /* $JAVAC compiled conftest.java successfully. */ + int compiler_cfversion = + get_classfile_version (compiled_file_name); + int compiler_target_version = compiler_cfversion - 44; + + /* It is hard to determine the compiler_source_version. This + would require a list of code snippets that can be compiled only + with a specific '-source' option and up, and this list would + need to grow every 6 months. + Also, $JAVAC may already include a '-source' option. + Therefore, pass a '-source' option always. */ + char source_option[30]; + sprintf (source_option, " -source %s%d", + try_source_version <= 8 ? "1." : "", + try_source_version); + + /* And pass a '-target' option as well, if needed. + (All supported javac versions support both, see the table in + javacomp.m4.) */ + char target_option[30]; + if (try_target_version == compiler_target_version) + target_option[0] = '\0'; + else + sprintf (target_option, " -target %s%d", + try_target_version <= 8 ? "1." : "", + try_target_version); + + char *javac_source_target = + xasprintf ("%s%s%s", javac, source_option, target_option); + assume (javac_source_target != NULL); + + unlink (compiled_file_name); + + java_sources[0] = conftest_file_name; + if (!compile_using_envjavac (javac_source_target, + java_sources, 1, tmpdir->dir_name, + false, false, false, true) + && stat (compiled_file_name, &statbuf) >= 0) { - free (compiled_file_name); - free (conftest_file_name); - - conftest_file_name = - xconcatenated_filename (tmpdir->dir_name, - "conftestfail.java", - NULL); - if (write_temp_file (tmpdir, conftest_file_name, failcode)) + /* The compiler directly supports the desired source_version + and target_version. Perfect. */ + free (javac_source_target); + + resultp->source_option = try_source_version; + resultp->target_option = + (try_target_version == compiler_target_version ? 0 : + try_target_version); + resultp->usable = true; + } + else + { + /* If the desired source_version or target_version were too + large for the compiler, there's nothing else we can do. */ + unsigned int compiler_version; + + free (javac_source_target); + + { + size_t command_length; + char *command; + const char *argv[4]; + + command_length = strlen (javac) + 9 + 1; + + command = (char *) xmalloca (command_length); { - free (conftest_file_name); - free (javac_source); - cleanup_temp_dir (tmpdir); - return true; + char *p = command; + p = stpcpy (p, javac); + p = stpcpy (p, " -version"); + *p++ = '\0'; + /* Ensure command_length was correctly calculated. */ + if (p - command > command_length) + abort (); } - compiled_file_name = - xconcatenated_filename (tmpdir->dir_name, - "conftestfail.class", - NULL); - register_temp_file (tmpdir, compiled_file_name); - - java_sources[0] = conftest_file_name; - if (!compile_using_envjavac (javac, - java_sources, 1, - tmpdir->dir_name, - false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0) + argv[0] = BOURNE_SHELL; + argv[1] = "-c"; + argv[2] = command; + argv[3] = NULL; + compiler_version = + get_compiler_version (javac, BOURNE_SHELL, argv); + + freea (command); + } + + if (try_source_version <= compiler_version + && try_target_version <= compiler_version) { - unlink (compiled_file_name); - - java_sources[0] = conftest_file_name; - if (compile_using_envjavac (javac_source, - java_sources, 1, - tmpdir->dir_name, - false, false, false, true)) - /* $JAVAC compiled conftestfail.java successfully, and - "$JAVAC -source $source_version" rejects it. - So the -source option is useful. */ - resultp->source_option = true; + /* Increase try_source_version and compiler_version until + the compiler accepts these values. This is necessary + to make e.g. try_source_version = 6 work with Java 12 + or newer, or try_source_version = 7 work with Java 20 + or newer. */ + for (;;) + { + /* Invariant: + try_source_version <= try_target_version. */ + if (try_source_version == try_target_version) + try_target_version++; + try_source_version++; + if (try_source_version > compiler_version) + break; + + sprintf (source_option, " -source %s%d", + try_source_version <= 8 ? "1." : "", + try_source_version); + + if (try_target_version == compiler_target_version) + target_option[0] = '\0'; + else + sprintf (target_option, " -target %s%d", + try_target_version <= 8 ? "1." : "", + try_target_version); + + javac_source_target = + xasprintf ("%s%s%s", javac, + source_option, target_option); + assume (javac_source_target != NULL); + + unlink (compiled_file_name); + + java_sources[0] = conftest_file_name; + if (!compile_using_envjavac (javac_source_target, + java_sources, 1, + tmpdir->dir_name, + false, false, + false, true) + && stat (compiled_file_name, &statbuf) >= 0) + { + /* The compiler supports the try_source_version + and try_target_version. It's better than + nothing. */ + free (javac_source_target); + + resultp->source_option = try_source_version; + resultp->target_option = + (try_target_version == compiler_target_version ? 0 : + try_target_version); + resultp->usable = true; + break; + } + + free (javac_source_target); + } } } } - free (javac_source); - - resultp->usable = true; - } - else - { - /* Try with -target and -source options. (All supported javac - versions support both, see the table in javacomp.m4.) */ - char *javac_target_source = - xasprintf ("%s -target %s -source %s", javac, - target_version, source_version); - assume (javac_target_source != NULL); - - unlink (compiled_file_name); - - java_sources[0] = conftest_file_name; - if (!compile_using_envjavac (javac_target_source, - java_sources, 1, tmpdir->dir_name, - false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0 - && get_classfile_version (compiled_file_name) - <= corresponding_classfile_version (target_version)) - { - /* "$JAVAC -target $target_version -source $source_version" - compiled conftest.java successfully. */ - resultp->source_option = true; - resultp->target_option = true; - resultp->usable = true; - } + cleanup_temp_dir (tmpdir); - free (javac_target_source); + free (compiled_file_name); + free (conftest_file_name); } - free (compiled_file_name); - free (conftest_file_name); - resultp->tested = true; } *usablep = resultp->usable; - *source_option_p = resultp->source_option; - *target_option_p = resultp->target_option; + sprintf (source_option_out, " -source %s%d", + resultp->source_option <= 8 ? "1." : "", + resultp->source_option); + if (resultp->target_option == 0) + target_option_out[0] = '\0'; + else + sprintf (target_option_out, " -target %s%d", + resultp->target_option <= 8 ? "1." : "", + resultp->target_option); return false; } @@ -639,15 +754,16 @@ is_javac_present (void) Return a failure indicator (true upon error). */ static bool is_javac_usable (const char *source_version, const char *target_version, - bool *usablep, bool *source_option_p, bool *target_option_p) + bool *usablep, + char source_option_out[20], char target_option_out[20]) { /* The cache depends on the source_version and target_version. */ struct result_t { - bool tested; - bool usable; - bool source_option; - bool target_option; + /*bool*/ unsigned int tested : 1; + /*bool*/ unsigned int usable : 1; + unsigned int source_option : 7; + unsigned int target_option : 7; }; static struct result_t result_cache[SOURCE_VERSION_BOUND][TARGET_VERSION_BOUND]; struct result_t *resultp; @@ -656,136 +772,178 @@ is_javac_usable (const char *source_version, const char *target_version, [target_version_index (target_version)]; if (!resultp->tested) { - /* Try javac. */ - struct temp_dir *tmpdir; - char *conftest_file_name; - char *compiled_file_name; - const char *java_sources[1]; - struct stat statbuf; - - tmpdir = create_temp_dir ("java", NULL, false); - if (tmpdir == NULL) - return true; - - conftest_file_name = - xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL); - if (write_temp_file (tmpdir, conftest_file_name, - get_goodcode_snippet (source_version))) + /* Canonicalize source_version and target_version, for easier + arithmetic. */ + int try_source_version = 6 + source_version_index (source_version); + int try_target_version = 6 + target_version_index (target_version); + /* Sanity check. */ + if (try_source_version <= try_target_version) { - free (conftest_file_name); - cleanup_temp_dir (tmpdir); - return true; - } + /* Try javac. */ + struct temp_dir *tmpdir; + char *conftest_file_name; + char *compiled_file_name; + const char *java_sources[1]; + struct stat statbuf; + + tmpdir = create_temp_dir ("java", NULL, false); + if (tmpdir == NULL) + return true; + + conftest_file_name = + xconcatenated_filename (tmpdir->dir_name, "conftest.java", NULL); + if (write_temp_file (tmpdir, conftest_file_name, "class conftest {}")) + { + free (conftest_file_name); + cleanup_temp_dir (tmpdir); + return true; + } - compiled_file_name = - xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL); - register_temp_file (tmpdir, compiled_file_name); - - java_sources[0] = conftest_file_name; - if (!compile_using_javac (java_sources, 1, - false, source_version, - false, target_version, - tmpdir->dir_name, false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0 - && get_classfile_version (compiled_file_name) - <= corresponding_classfile_version (target_version)) - { - /* javac compiled conftest.java successfully. */ - /* Try adding -source option if it is useful. */ - unlink (compiled_file_name); + compiled_file_name = + xconcatenated_filename (tmpdir->dir_name, "conftest.class", NULL); + register_temp_file (tmpdir, compiled_file_name); java_sources[0] = conftest_file_name; if (!compile_using_javac (java_sources, 1, - true, source_version, + false, source_version, false, target_version, - tmpdir->dir_name, false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0 - && get_classfile_version (compiled_file_name) - <= corresponding_classfile_version (target_version)) + tmpdir->dir_name, + false, false, false, true) + && stat (compiled_file_name, &statbuf) >= 0) { - const char *failcode = get_failcode_snippet (source_version); - - if (failcode != NULL) + /* javac compiled conftest.java successfully. */ + int compiler_cfversion = + get_classfile_version (compiled_file_name); + int compiler_target_version = compiler_cfversion - 44; + + /* It is hard to determine the compiler_source_version. This + would require a list of code snippets that can be compiled only + with a specific '-source' option and up, and this list would + need to grow every 6 months. + Also, javac may point to a shell script that already includes a + '-source' option. + Therefore, pass a '-source' option always. */ + char source_option[20]; + sprintf (source_option, "%s%d", + try_source_version <= 8 ? "1." : "", + try_source_version); + + /* And pass a '-target' option as well, if needed. + (All supported javac versions support both, see the table in + javacomp.m4.) */ + char target_option[20]; + sprintf (target_option, "%s%d", + try_target_version <= 8 ? "1." : "", + try_target_version); + + unlink (compiled_file_name); + + java_sources[0] = conftest_file_name; + if (!compile_using_javac (java_sources, 1, + true, + source_option, + try_target_version != compiler_target_version, + target_option, + tmpdir->dir_name, + false, false, false, true) + && stat (compiled_file_name, &statbuf) >= 0) { - free (compiled_file_name); - free (conftest_file_name); - - conftest_file_name = - xconcatenated_filename (tmpdir->dir_name, - "conftestfail.java", - NULL); - if (write_temp_file (tmpdir, conftest_file_name, failcode)) - { - free (conftest_file_name); - cleanup_temp_dir (tmpdir); - return true; - } - - compiled_file_name = - xconcatenated_filename (tmpdir->dir_name, - "conftestfail.class", - NULL); - register_temp_file (tmpdir, compiled_file_name); - - java_sources[0] = conftest_file_name; - if (!compile_using_javac (java_sources, 1, - false, source_version, - false, target_version, - tmpdir->dir_name, - false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0) + /* The compiler directly supports the desired source_version + and target_version. Perfect. */ + resultp->source_option = try_source_version; + resultp->target_option = + (try_target_version == compiler_target_version ? 0 : + try_target_version); + resultp->usable = true; + } + else + { + /* If the desired source_version or target_version were too + large for the compiler, there's nothing else we can do. */ + unsigned int compiler_version; + + { + const char *argv[3]; + + argv[0] = "javac"; + argv[1] = "-version"; + argv[2] = NULL; + compiler_version = + get_compiler_version ("javac", argv[0], argv); + } + + if (try_source_version <= compiler_version + && try_target_version <= compiler_version) { - unlink (compiled_file_name); - - java_sources[0] = conftest_file_name; - if (compile_using_javac (java_sources, 1, - true, source_version, - false, target_version, - tmpdir->dir_name, - false, false, false, true)) - /* javac compiled conftestfail.java successfully, and - "javac -source $source_version" rejects it. - So the -source option is useful. */ - resultp->source_option = true; + /* Increase try_source_version and compiler_version until + the compiler accepts these values. This is necessary + to make e.g. try_source_version = 6 work with Java 12 + or newer, or try_source_version = 7 work with Java 20 + or newer. */ + for (;;) + { + /* Invariant: + try_source_version <= try_target_version. */ + if (try_source_version == try_target_version) + try_target_version++; + try_source_version++; + if (try_source_version > compiler_version) + break; + + sprintf (source_option, "%s%d", + try_source_version <= 8 ? "1." : "", + try_source_version); + + sprintf (target_option, "%s%d", + try_target_version <= 8 ? "1." : "", + try_target_version); + + unlink (compiled_file_name); + + java_sources[0] = conftest_file_name; + if (!compile_using_javac (java_sources, 1, + true, + source_option, + try_target_version != compiler_target_version, + target_option, + tmpdir->dir_name, + false, false, false, true) + && stat (compiled_file_name, &statbuf) >= 0) + { + /* The compiler supports the try_source_version + and try_target_version. It's better than + nothing. */ + resultp->source_option = try_source_version; + resultp->target_option = + (try_target_version == compiler_target_version ? 0 : + try_target_version); + resultp->usable = true; + break; + } + } } } } - resultp->usable = true; - } - else - { - /* Try with -target and -source options. (All supported javac - versions support both, see the table in javacomp.m4.) */ - unlink (compiled_file_name); + cleanup_temp_dir (tmpdir); - java_sources[0] = conftest_file_name; - if (!compile_using_javac (java_sources, 1, - true, source_version, - true, target_version, - tmpdir->dir_name, - false, false, false, true) - && stat (compiled_file_name, &statbuf) >= 0 - && get_classfile_version (compiled_file_name) - <= corresponding_classfile_version (target_version)) - { - /* "javac -target $target_version -source $source_version" - compiled conftest.java successfully. */ - resultp->source_option = true; - resultp->target_option = true; - resultp->usable = true; - } + free (compiled_file_name); + free (conftest_file_name); } - free (compiled_file_name); - free (conftest_file_name); - resultp->tested = true; } *usablep = resultp->usable; - *source_option_p = resultp->source_option; - *target_option_p = resultp->target_option; + sprintf (source_option_out, "%s%d", + resultp->source_option <= 8 ? "1." : "", + resultp->source_option); + if (resultp->target_option == 0) + target_option_out[0] = '\0'; + else + sprintf (target_option_out, "%s%d", + resultp->target_option <= 8 ? "1." : "", + resultp->target_option); return false; } @@ -824,8 +982,8 @@ compile_java_class (const char * const *java_sources, if (javac != NULL && javac[0] != '\0') { bool usable = false; - bool source_option = false; - bool target_option = false; + char source_option[30]; + char target_option[30]; if (target_version == NULL) target_version = default_target_version (); @@ -833,7 +991,7 @@ compile_java_class (const char * const *java_sources, if (is_envjavac_usable (javac, source_version, target_version, &usable, - &source_option, &target_option)) + source_option, target_option)) { err = true; goto done1; @@ -849,12 +1007,7 @@ compile_java_class (const char * const *java_sources, set_classpath (classpaths, classpaths_count, false, verbose); javac_with_options = - xasprintf ("%s%s%s%s%s", - javac, - source_option ? " -source " : "", - source_option ? source_version : "", - target_option ? " -target " : "", - target_option ? target_version : ""); + xasprintf ("%s%s%s", javac, source_option, target_option); assume (javac_with_options != NULL); err = compile_using_envjavac (javac_with_options, @@ -883,14 +1036,15 @@ compile_java_class (const char * const *java_sources, if (is_javac_present ()) { bool usable = false; - bool source_option = false; - bool target_option = false; + char source_option[20]; + char target_option[20]; if (target_version == NULL) target_version = default_target_version (); if (is_javac_usable (source_version, target_version, - &usable, &source_option, &target_option)) + &usable, + source_option, target_option)) { err = true; goto done1; @@ -909,8 +1063,8 @@ compile_java_class (const char * const *java_sources, verbose); err = compile_using_javac (java_sources, java_sources_count, - source_option, source_version, - target_option, target_version, + true, source_option, + target_option[0] != '\0', target_option, directory, optimize, debug, verbose, false); diff --git a/lib/javacomp.h b/lib/javacomp.h index e6e5d622e6..2c1ae811a4 100644 --- a/lib/javacomp.h +++ b/lib/javacomp.h @@ -29,6 +29,7 @@ 9 private interface methods 10 type inference for local variables 11 'var' in parameters of lambda expressions + ... If source-version 1.3 or 1.4 or 1.5 is requested, it gets mapped to 1.6, for backward compatibility. (Currently the minimum Java and javac version we need to support is Java 1.6, since that's the default Java version on Solaris 10.) @@ -40,6 +41,7 @@ 9 53.0 10 54.0 11 55.0 + ... ... If a target-version below 1.6 is requested, it gets mapped to 1.6, for backward compatibility. (Currently the minimum Java and javac version we need to support is Java 1.6, since that's the default Java version on Solaris 10.) diff --git a/m4/javacomp.m4 b/m4/javacomp.m4 index e20b309f3a..009c788cb6 100644 --- a/m4/javacomp.m4 +++ b/m4/javacomp.m4 @@ -1,4 +1,4 @@ -# javacomp.m4 serial 24 +# javacomp.m4 serial 25 dnl Copyright (C) 2001-2003, 2006-2007, 2009-2023 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation @@ -18,6 +18,8 @@ dnl with or without modifications, as long as this notice is preserved. # 9 private interface methods # 10 type inference for local variables # 11 'var' in parameters of lambda expressions +# ... +# (For reference, see .) # If source-version 1.3 or 1.4 or 1.5 is requested, it gets mapped to 1.6, for # backward compatibility. (Currently the minimum Java and javac version we need # to support is Java 1.6, since that's the default Java version on Solaris 10.) @@ -29,6 +31,7 @@ dnl with or without modifications, as long as this notice is preserved. # 9 53.0 # 10 54.0 # 11 55.0 +# ... ... # The classfile version of a .class file can be determined through the "file" # command. More portably, the classfile major version can be determined through # "od -A n -t d1 -j 7 -N 1 classfile". @@ -45,6 +48,7 @@ dnl with or without modifications, as long as this notice is preserved. # 9 JDK/JRE 9 # 10 JDK/JRE 10 # 11 JDK/JRE 11 +# ... ... # # Specifying target-version is useful when building a library (.jar) that is # useful outside the given package. Omitting target-version is useful when @@ -106,25 +110,28 @@ changequote([,])dnl | tr -d '\012\015' \ | tr '!"#$%&()*+,./0123456789:;<=>?@ABCDEFGHJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyzI' '\000\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032\033\034\035\036\037\040\041\046\050\051\052\056\057\073\074\076\103\106\114\116\117\120\123\124\126\133\141\142\143\144\145\146\147\151\152\154\155\156\157\160\162\163\164\165\166\171\261\262\266\267\270\272\276\312\376\055' \ > conftestver.class - target_version=`{ + java_exec_version=`{ unset JAVA_HOME echo "$as_me:__oline__: CLASSPATH=.${CLASSPATH:+$CLASSPATH_SEPARATOR$CLASSPATH} $CONF_JAVA conftestver" >&AS_MESSAGE_LOG_FD CLASSPATH=.${CLASSPATH:+$CLASSPATH_SEPARATOR$CLASSPATH} $CONF_JAVA conftestver 2>&AS_MESSAGE_LOG_FD }` - case "$target_version" in + case "$java_exec_version" in null) dnl JDK 1.1.X returns null. - target_version=1.1 ;; + java_exec_version=1.1 ;; esac - case "$target_version" in + case "$java_exec_version" in 1.1 | 1.2 | 1.3 | 1.4 | 1.5) AC_MSG_WARN([$CONF_JAVA is too old, cannot compile Java code for this old version any more]) target_version=1.6 ;; - 1.6 | 1.7 | 1.8 | 9 | 10 | 11) ;; - 12 | 13 | 14 | 15 | 16 | 17) - dnl Assume that these (not yet released) Java versions will behave - dnl like the preceding ones. - target_version=11 ;; +changequote(,)dnl + 1.6 | 1.7 | 1.8 | 9 | [1-9][0-9]) +changequote([,])dnl + dnl Here we could choose any target_version between $source_version + dnl and the $java_exec_version. (If it is too small, it will be + dnl incremented below until it works.) Since we documented above that + dnl it is determined from the JVM, we do that: + target_version="$java_exec_version" ;; *) AC_MSG_WARN([unknown target-version $target_version, please update gt_@&t@JAVACOMP macro]) target_version=1.6 ;; esac @@ -138,27 +145,15 @@ changequote([,])dnl esac ]) case "$source_version" in - 1.6) goodcode='class conftest { T foo() { return null; } }' - failcode='class conftestfail { void foo () { switch ("A") {} } }' ;; - 1.7) goodcode='class conftest { void foo () { switch ("A") {} } }' - failcode='class conftestfail { void foo () { Runnable r = () -> {}; } }' ;; - 1.8) goodcode='class conftest { void foo () { Runnable r = () -> {}; } }' - failcode='interface conftestfail { private void foo () {} }' ;; - 9) goodcode='interface conftest { private void foo () {} }' - failcode='class conftestfail { public void m() { var i = new Integer(0); } }' ;; - 10) goodcode='class conftest { public void m() { var i = new Integer(0); } }' - failcode='class conftestfail { Readable r = (var b) -> 0; }' ;; - 11) goodcode='class conftest { Readable r = (var b) -> 0; }' - failcode='class conftestfail syntax error' ;; +changequote(,)dnl + 1.6 | 1.7 | 1.8 | 9 | [1-9][0-9]) ;; +changequote([,])dnl *) AC_MSG_ERROR([invalid source-version argument to gt_@&t@JAVACOMP: $source_version]) ;; esac case "$target_version" in - 1.6) cfversion=50 ;; - 1.7) cfversion=51 ;; - 1.8) cfversion=52 ;; - 9) cfversion=53 ;; - 10) cfversion=54 ;; - 11) cfversion=55 ;; +changequote(,)dnl + 1.6 | 1.7 | 1.8 | 9 | [1-9][0-9]) ;; +changequote([,])dnl *) AC_MSG_ERROR([invalid target-version argument to gt_@&t@JAVACOMP: $target_version]) ;; esac # Function to output the classfile version of a file (8th byte) in decimal. @@ -214,131 +209,231 @@ changequote([,])dnl dnl -target 1.8 only possible with -source 1.6/1.7/1.8 dnl -target 9 only possible with -source 1.6/1.7/1.8/9 dnl - dnl javac 11: -target 1.6 1.7 1.8 9 10 11 default: 11 - dnl -source 1.6 1.7 1.8 9 10 11 default: 11 - dnl -target 1.6 only possible with -source 1.6 - dnl -target 1.7 only possible with -source 1.6/1.7 - dnl -target 1.8 only possible with -source 1.6/1.7/1.8 - dnl -target 9 only possible with -source 1.6/1.7/1.8/9 - dnl -target 10 only possible with -source 1.6/1.7/1.8/9/10 + dnl and so on. + dnl This can be summarized in this table: dnl + dnl javac classfile valid -source and obsolete -source + dnl version default version -target values and -target values + dnl ------- --------------- ----------------- ------------------ + dnl 1.6 50.0 1.3 .. 1.6 + dnl 1.7 51.0 1.3 .. 1.7 + dnl 1.8 52.0 1.3 .. 1.8 1.3 .. 1.5 + dnl 9 53.0 1.6 .. 9 1.6 + dnl 10 54.0 1.6 .. 10 1.6 + dnl 11 55.0 1.6 .. 11 1.6 + dnl 12 56.0 1.7 .. 12 1.7 + dnl 13 57.0 1.7 .. 13 1.7 + dnl 14 58.0 1.7 .. 14 1.7 + dnl 15 59.0 1.7 .. 15 1.7 + dnl 16 60.0 1.7 .. 16 1.7 + dnl 17 61.0 1.7 .. 17 1.7 + dnl 18 62.0 1.7 .. 18 1.7 + dnl 19 63.0 1.7 .. 19 1.7 + dnl 20 64.0 1.8 .. 20 1.8 + dnl + dnl The -source option value must be <= the -target option value. + dnl The minimal -source and -target option value produces an "is obsolete" + dnl warning. + dnl + dnl Canonicalize source_version and target_version, for easier arithmetic. + case "$source_version" in + 1.*) source_version=`echo "$source_version" | sed -e 's/^1\.//'` ;; + esac + case "$target_version" in + 1.*) target_version=`echo "$target_version" | sed -e 's/^1\.//'` ;; + esac CONF_JAVAC= HAVE_JAVAC_ENVVAR= HAVE_JAVAC= HAVE_JAVACOMP= - echo "$goodcode" > conftest.java - echo "$failcode" > conftestfail.java - dnl If the user has set the JAVAC environment variable, use that, if it - dnl satisfies the constraints (possibly after adding -target and -source - dnl options). - if test -n "$JAVAC"; then - dnl Try $JAVAC. - rm -f conftest.class - if { echo "$as_me:__oline__: $JAVAC -d . conftest.java" >&AS_MESSAGE_LOG_FD - $JAVAC -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 - } \ - && test -f conftest.class \ - && expr `func_classfile_version conftest.class` '<=' $cfversion >/dev/null 2>&AS_MESSAGE_LOG_FD; then - dnl Try adding -source option if it is useful. - rm -f conftest.class - rm -f conftestfail.class - if { echo "$as_me:__oline__: $JAVAC -source $source_version -d . conftest.java" >&AS_MESSAGE_LOG_FD - $JAVAC -source "$source_version" -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 - } \ - && test -f conftest.class \ - && expr `func_classfile_version conftest.class` '<=' $cfversion >/dev/null 2>&AS_MESSAGE_LOG_FD \ - && { echo "$as_me:__oline__: $JAVAC -d . conftestfail.java" >&AS_MESSAGE_LOG_FD - $JAVAC -d . conftestfail.java >&AS_MESSAGE_LOG_FD 2>&1 - } \ - && test -f conftestfail.class \ - && ! { echo "$as_me:__oline__: $JAVAC -source $source_version -d . conftestfail.java" >&AS_MESSAGE_LOG_FD - $JAVAC -source "$source_version" -d . conftestfail.java >&AS_MESSAGE_LOG_FD 2>&1 - }; then - CONF_JAVAC="$JAVAC -source $source_version" - HAVE_JAVAC_ENVVAR=1 - HAVE_JAVACOMP=1 - else - CONF_JAVAC="$JAVAC" - HAVE_JAVAC_ENVVAR=1 - HAVE_JAVACOMP=1 - fi - else - dnl Try with -target and -source options. (All supported javac - dnl versions support both, see the table above.) + dnl Sanity check. + if expr $source_version '<=' $target_version >/dev/null; then + echo 'class conftest {}' > conftest.java + dnl If the user has set the JAVAC environment variable, use that, if it + dnl satisfies the constraints (possibly after adding -target and -source + dnl options). + if test -n "$JAVAC"; then + dnl Test whether $JAVAC is usable. rm -f conftest.class - if { echo "$as_me:__oline__: $JAVAC -target $target_version -source $source_version -d . conftest.java" >&AS_MESSAGE_LOG_FD - $JAVAC -target "$target_version" -source "$source_version" -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 + if { echo "$as_me:__oline__: $JAVAC -d . conftest.java" >&AS_MESSAGE_LOG_FD + $JAVAC -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 } \ - && test -f conftest.class \ - && expr `func_classfile_version conftest.class` '<=' $cfversion >/dev/null 2>&AS_MESSAGE_LOG_FD; then - CONF_JAVAC="$JAVAC -target $target_version -source $source_version" - HAVE_JAVAC_ENVVAR=1 - HAVE_JAVACOMP=1 + && test -f conftest.class; then + compiler_cfversion=`func_classfile_version conftest.class` + compiler_target_version=`expr $compiler_cfversion - 44` + dnl It is hard to determine the compiler_source_version. This would + dnl require a list of code snippets that can be compiled only with a + dnl specific '-source' option and up, and this list would need to grow + dnl every 6 months. + dnl Also, $JAVAC may already include a '-source' option. + dnl Therefore, pass a '-source' option always. + source_option=' -source '`case "$source_version" in 6|7|8) echo 1. ;; esac`"$source_version" + dnl And pass a '-target' option as well, if needed. + dnl (All supported javac versions support both, see the table above.) + if expr $target_version = $compiler_target_version >/dev/null; then + target_option= + else + target_option=' -target '`case "$target_version" in 6|7|8) echo 1. ;; esac`"$target_version" + fi + if { echo "$as_me:__oline__: $JAVAC$source_option$target_option -d . conftest.java" >&AS_MESSAGE_LOG_FD + $JAVAC$source_option$target_option -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 + } \ + && test -f conftest.class; then + dnl The compiler directly supports the desired source_version and + dnl target_version. Perfect. + CONF_JAVAC="$JAVAC$source_option$target_option" + HAVE_JAVAC_ENVVAR=1 + HAVE_JAVACOMP=1 + else + dnl If the desired source_version or target_version were too large + dnl for the compiler, there's nothing else we can do. + compiler_version=`echo "$as_me:__oline__: $JAVAC -version | sed -e 1q" >&AS_MESSAGE_LOG_FD + $JAVAC -version | sed -e 1q` +changequote(,)dnl + compiler_version=`echo "$compiler_version" | sed -e 's/^[^0-9]*\([0-9][0-9.]*\).*/\1/'` +changequote([,])dnl + case "$compiler_version" in + 1.*) dnl Map 1.6.0_85 to 6, 1.8.0_151 to 8. + compiler_version=`echo "$compiler_version" | sed -e 's/^1\.//' -e 's/\..*//'` + ;; + *) dnl Map 9.0.4 to 9, 10.0.2 to 10, etc. + compiler_version=`echo "$compiler_version" | sed -e 's/\..*//'` + ;; + esac + if expr $source_version '<=' "$compiler_version" >/dev/null \ + && expr $target_version '<=' "$compiler_version" >/dev/null; then + dnl Increase $source_version and $compiler_version until the + dnl compiler accepts these values. This is necessary to make + dnl e.g. $source_version = 6 work with Java 12 or newer, or + dnl $source_version = 7 work with Java 20 or newer. + try_source_version="$source_version" + try_target_version="$target_version" + while true; do + dnl Invariant: $try_source_version <= $try_target_version. + if expr $try_source_version = $try_target_version >/dev/null; then + try_target_version=`expr $try_target_version + 1` + fi + try_source_version=`expr $try_source_version + 1` + expr $try_source_version '<=' $compiler_version >/dev/null || break + source_option=' -source '`case "$try_source_version" in 6|7|8) echo 1. ;; esac`"$try_source_version" + if expr $try_target_version = $compiler_target_version >/dev/null; then + target_option= + else + target_option=' -target '`case "$try_target_version" in 6|7|8) echo 1. ;; esac`"$try_target_version" + fi + if { echo "$as_me:__oline__: $JAVAC$source_option$target_option -d . conftest.java" >&AS_MESSAGE_LOG_FD + $JAVAC$source_option$target_option -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 + } \ + && test -f conftest.class; then + dnl The compiler supports the try_source_version and + dnl try_target_version. It's better than nothing. + CONF_JAVAC="$JAVAC$source_option$target_option" + HAVE_JAVAC_ENVVAR=1 + HAVE_JAVACOMP=1 + break + fi + done + fi + fi fi fi - fi - if test -z "$HAVE_JAVACOMP"; then - pushdef([AC_MSG_CHECKING],[:])dnl - pushdef([AC_CHECKING],[:])dnl - pushdef([AC_MSG_RESULT],[:])dnl - AC_CHECK_PROG([HAVE_JAVAC_IN_PATH], [javac], [yes]) - popdef([AC_MSG_RESULT])dnl - popdef([AC_CHECKING])dnl - popdef([AC_MSG_CHECKING])dnl - if test -z "$HAVE_JAVACOMP" && test -n "$HAVE_JAVAC_IN_PATH"; then - dnl Test whether javac is usable. - if { javac -version >/dev/null 2>/dev/null || test $? -le 2; } \ - && ( if javac -help 2>&1 >/dev/null | grep at.dms.kjc.Main >/dev/null && javac -help 2>/dev/null | grep 'released.*2000' >/dev/null ; then exit 1; else exit 0; fi ); then - dnl OK, javac works. - dnl Now test whether it supports the desired target-version and - dnl source-version. + if test -z "$HAVE_JAVACOMP"; then + pushdef([AC_MSG_CHECKING],[:])dnl + pushdef([AC_CHECKING],[:])dnl + pushdef([AC_MSG_RESULT],[:])dnl + AC_CHECK_PROG([HAVE_JAVAC_IN_PATH], [javac], [yes]) + popdef([AC_MSG_RESULT])dnl + popdef([AC_CHECKING])dnl + popdef([AC_MSG_CHECKING])dnl + if test -z "$HAVE_JAVACOMP" && test -n "$HAVE_JAVAC_IN_PATH"; then + dnl Test whether javac is usable. rm -f conftest.class if { echo "$as_me:__oline__: javac -d . conftest.java" >&AS_MESSAGE_LOG_FD javac -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 } \ - && test -f conftest.class \ - && expr `func_classfile_version conftest.class` '<=' $cfversion >/dev/null 2>&AS_MESSAGE_LOG_FD; then - dnl Try adding -source option if it is useful. - rm -f conftest.class - rm -f conftestfail.class - if { echo "$as_me:__oline__: javac -source $source_version -d . conftest.java" >&AS_MESSAGE_LOG_FD - javac -source "$source_version" -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 - } \ - && test -f conftest.class \ - && expr `func_classfile_version conftest.class` '<=' $cfversion >/dev/null 2>&AS_MESSAGE_LOG_FD \ - && { echo "$as_me:__oline__: javac -d . conftestfail.java" >&AS_MESSAGE_LOG_FD - javac -d . conftestfail.java >&AS_MESSAGE_LOG_FD 2>&1 - } \ - && test -f conftestfail.class \ - && ! { echo "$as_me:__oline__: javac -source $source_version -d . conftestfail.java" >&AS_MESSAGE_LOG_FD - javac -source "$source_version" -d . conftestfail.java >&AS_MESSAGE_LOG_FD 2>&1 - }; then - CONF_JAVAC="javac -source $source_version" - HAVE_JAVAC=1 - HAVE_JAVACOMP=1 + && test -f conftest.class; then + compiler_cfversion=`func_classfile_version conftest.class` + compiler_target_version=`expr $compiler_cfversion - 44` + dnl It is hard to determine the compiler_source_version. This would + dnl require a list of code snippets that can be compiled only with a + dnl specific '-source' option and up, and this list would need to grow + dnl every 6 months. + dnl Also, javac may point to a shell script that already includes a + dnl '-source' option. + dnl Therefore, pass a '-source' option always. + source_option=' -source '`case "$source_version" in 6|7|8) echo 1. ;; esac`"$source_version" + dnl And pass a '-target' option as well, if needed. + dnl (All supported javac versions support both, see the table above.) + if expr $target_version = $compiler_target_version >/dev/null; then + target_option= else - CONF_JAVAC="javac" - HAVE_JAVAC=1 - HAVE_JAVACOMP=1 + target_option=' -target '`case "$target_version" in 6|7|8) echo 1. ;; esac`"$target_version" fi - else - dnl Try with -target and -source options. (All supported javac - dnl versions support both, see the table above.) - rm -f conftest.class - if { echo "$as_me:__oline__: javac -target $target_version -source $source_version -d . conftest.java" >&AS_MESSAGE_LOG_FD - javac -target "$target_version" -source "$source_version" -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 + if { echo "$as_me:__oline__: javac$source_option$target_option -d . conftest.java" >&AS_MESSAGE_LOG_FD + javac$source_option$target_option -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 } \ - && test -f conftest.class \ - && expr `func_classfile_version conftest.class` '<=' $cfversion >/dev/null 2>&AS_MESSAGE_LOG_FD; then - CONF_JAVAC="javac -target $target_version -source $source_version" + && test -f conftest.class; then + dnl The compiler directly supports the desired source_version and + dnl target_version. Perfect. + CONF_JAVAC="javac$source_option$target_option" HAVE_JAVAC=1 HAVE_JAVACOMP=1 + else + dnl If the desired source_version or target_version were too large + dnl for the compiler, there's nothing else we can do. + compiler_version=`echo "$as_me:__oline__: javac -version | sed -e 1q" >&AS_MESSAGE_LOG_FD + javac -version | sed -e 1q` +changequote(,)dnl + compiler_version=`echo "$compiler_version" | sed -e 's/^[^0-9]*\([0-9][0-9.]*\).*/\1/'` +changequote([,])dnl + case "$compiler_version" in + 1.*) dnl Map 1.6.0_85 to 6, 1.8.0_151 to 8. + compiler_version=`echo "$compiler_version" | sed -e 's/^1\.//' -e 's/\..*//'` + ;; + *) dnl Map 9.0.4 to 9, 10.0.2 to 10, etc. + compiler_version=`echo "$compiler_version" | sed -e 's/\..*//'` + ;; + esac + if expr $source_version '<=' "$compiler_version" >/dev/null \ + && expr $target_version '<=' "$compiler_version" >/dev/null; then + dnl Increase $source_version and $compiler_version until the + dnl compiler accepts these values. This is necessary to make + dnl e.g. $source_version = 6 work with Java 12 or newer, or + dnl $source_version = 7 work with Java 20 or newer. + try_source_version="$source_version" + try_target_version="$target_version" + while true; do + dnl Invariant: $try_source_version <= $try_target_version. + if expr $try_source_version = $try_target_version >/dev/null; then + try_target_version=`expr $try_target_version + 1` + fi + try_source_version=`expr $try_source_version + 1` + expr $try_source_version '<=' $compiler_version >/dev/null || break + source_option=' -source '`case "$try_source_version" in 6|7|8) echo 1. ;; esac`"$try_source_version" + if expr $try_target_version = $compiler_target_version >/dev/null; then + target_option= + else + target_option=' -target '`case "$try_target_version" in 6|7|8) echo 1. ;; esac`"$try_target_version" + fi + if { echo "$as_me:__oline__: javac$source_option$target_option -d . conftest.java" >&AS_MESSAGE_LOG_FD + javac$source_option$target_option -d . conftest.java >&AS_MESSAGE_LOG_FD 2>&1 + } \ + && test -f conftest.class; then + dnl The compiler supports the try_source_version and + dnl try_target_version. It's better than nothing. + CONF_JAVAC="javac$source_option$target_option" + HAVE_JAVAC=1 + HAVE_JAVACOMP=1 + break + fi + done + fi fi fi fi fi + rm -f conftest*.java conftest*.class fi - rm -f conftest*.java conftest*.class if test -n "$HAVE_JAVACOMP"; then ac_result="$CONF_JAVAC" else -- 2.39.5