Files
RedBear-OS/recipes/tools/gettext/source/gettext-tools/gnulib-lib/javacomp.c
T
vasilito facf0c92e0 feat: track all source trees in git — full fork offline-first model
Red Bear OS is a full fork. All sources must be available from git clone
with zero network access. Removed gitignore rules that excluded fetched
source trees under recipes/*/source/, local/recipes/kde/*/source/,
local/recipes/qt/*/source/, and vendor source trees.

Build artifacts (target/, build/, source.tar, *.o, *.so) remain excluded.

127291 files added — kernel, relibc, base, bootloader, pkgar, all KDE/Qt
frameworks, mesa, wayland, DRM drivers, and every other recipe source.
2026-05-14 10:55:53 +01:00

1147 lines
40 KiB
C

/* Compile a Java program.
Copyright (C) 2001-2003, 2006-2024 Free Software Foundation, Inc.
Written by Bruno Haible <haible@clisp.cons.org>, 2001.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include <alloca.h>
/* Specification. */
#include "javacomp.h"
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "javaversion.h"
#include "execute.h"
#include "spawn-pipe.h"
#include "wait-process.h"
#include "classpath.h"
#include "xsetenv.h"
#include "sh-quote.h"
#include "binary-io.h"
#include "safe-read.h"
#include "xalloc.h"
#include "xmalloca.h"
#include "concat-filename.h"
#include "fwriteerror.h"
#include "clean-temp.h"
#include <error.h>
#include "xvasprintf.h"
#include "verify.h"
#include "c-strstr.h"
#include "gettext.h"
#define _(str) gettext (str)
/* Survey of Java compilers.
A = does it work without CLASSPATH being set
C = option to set CLASSPATH, other than setting it in the environment
O = option for optimizing
g = option for debugging
T = test for presence
Program from A C O g T
$JAVAC unknown N n/a -O -g true
javac JDK 1.1.8 Y -classpath P -O -g javac 2>/dev/null; test $? = 1
javac JDK 1.3.0 Y -classpath P -O -g javac 2>/dev/null; test $? -le 2
All compilers support the option "-d DIRECTORY" for the base directory
of the classes to be written.
The CLASSPATH is a colon separated list of pathnames. (On Windows: a
semicolon separated list of pathnames.)
We try the Java compilers in the following order:
1. getenv ("JAVAC"), because the user must be able to override our
preferences,
2. "javac", because it is a standard compiler.
We unset the JAVA_HOME environment variable, because a wrong setting of
this variable can confuse the JDK's javac.
*/
/* Return the default target_version. */
static const char *
default_target_version (void)
{
/* 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 = "1.6";
else if (java_version_cache[0] == '1'
&& java_version_cache[1] == '.'
&& java_version_cache[2] >= '1' && java_version_cache[2] <= '5'
&& java_version_cache[3] == '\0')
{
error (0, 0, _("The java program is too old. Cannot compile Java code for this old version any more."));
java_version_cache = "1.6";
}
else if ((java_version_cache[0] == '1'
&& java_version_cache[1] == '.'
&& java_version_cache[2] >= '6' && java_version_cache[2] <= '8'
&& java_version_cache[3] == '\0')
|| (java_version_cache[0] == '9'
&& java_version_cache[1] == '\0')
|| ((java_version_cache[0] >= '1'
&& java_version_cache[0] <= '9')
&& (java_version_cache[1] >= '0'
&& java_version_cache[1] <= '9')
&& java_version_cache[2] == '\0'))
/* 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
java_version_cache = "1.6";
}
return java_version_cache;
}
/* ======================= Source version dependent ======================= */
/* Convert a source version to an index. */
#define SOURCE_VERSION_BOUND 94 /* exclusive upper bound */
static unsigned int
source_version_index (const char *source_version)
{
if (source_version[0] == '1' && source_version[1] == '.')
{
if ((source_version[2] >= '6' && source_version[2] <= '8')
&& source_version[3] == '\0')
return source_version[2] - '6';
}
else if (source_version[0] == '9' && source_version[1] == '\0')
return 3;
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[0] - '1') * 10 + source_version[1] - '0' + 4;
error (EXIT_FAILURE, 0, _("invalid source_version argument to compile_java_class"));
return 0;
}
/* ======================= Target version dependent ======================= */
/* Convert a target version to an index. */
#define TARGET_VERSION_BOUND 94 /* exclusive upper bound */
static unsigned int
target_version_index (const char *target_version)
{
if (target_version[0] == '1' && target_version[1] == '.'
&& (target_version[2] >= '6' && target_version[2] <= '8')
&& target_version[3] == '\0')
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[0] <= '9')
&& (target_version[1] >= '0' && target_version[1] <= '9')
&& target_version[2] == '\0')
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;
}
/* ======================== 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;
const 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] = BOURNE_SHELL;
argv[1] = "-c";
argv[2] = command;
argv[3] = NULL;
exitstatus = execute (javac, BOURNE_SHELL, argv, NULL,
false, false, false, null_stderr,
true, true, NULL);
err = (exitstatus != 0);
freea (command);
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,
const char *nowarn_option,
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;
const char **argv;
const char **argp;
int exitstatus;
unsigned int i;
argc =
1 + (nowarn_option != NULL ? 1 : 0) + (source_option ? 2 : 0)
+ (target_option ? 2 : 0) + (optimize ? 1 : 0) + (debug ? 1 : 0)
+ (directory != NULL ? 2 : 0) + java_sources_count;
argv = (const char **) xmalloca ((argc + 1) * sizeof (const char *));
argp = argv;
*argp++ = "javac";
if (nowarn_option != NULL)
*argp++ = nowarn_option;
if (source_option)
{
*argp++ = "-source";
*argp++ = source_version;
}
if (target_option)
{
*argp++ = "-target";
*argp++ = target_version;
}
if (optimize)
*argp++ = "-O";
if (debug)
*argp++ = "-g";
if (directory != NULL)
{
*argp++ = "-d";
*argp++ = directory;
}
for (i = 0; i < java_sources_count; i++)
*argp++ = 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, NULL,
false, false, false,
null_stderr, true, true, NULL);
err = (exitstatus != 0);
freea (argv);
return err;
}
/* ====================== 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.
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, "we", false);
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 | O_CLOEXEC, 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)
{
close (fd);
return header[7];
}
}
close (fd);
}
/* Could not get the class file version. Return a very large one. */
return INT_MAX;
}
/* Test whether $JAVAC can be used, and whether it needs a -source and/or
-target option, as well as an option to inhibit warnings.
Return a failure indicator (true upon error). */
static bool
is_envjavac_usable (const char *javac,
const char *source_version, const char *target_version,
bool *usablep,
char nowarn_option_out[17],
char source_option_out[30], char target_option_out[30])
{
/* The cache depends on the source_version and target_version. */
struct result_t
{
/*bool*/ unsigned int tested : 1;
/*bool*/ unsigned int usable : 1;
/*bool*/ unsigned int nowarn_option : 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;
resultp = &result_cache[source_version_index (source_version)]
[target_version_index (target_version)];
if (!resultp->tested)
{
/* 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)
{
/* Try $JAVAC. */
struct temp_dir *tmpdir;
char *conftest_file_name;
char *compiled_file_name;
const char *java_sources[1];
const char *nowarn_option;
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);
/* See the discussion in javacomp.m4. */
nowarn_option = " -Xlint:-options";
char *javac_nowarn = xasprintf ("%s%s", javac, nowarn_option);
assume (javac_nowarn != NULL);
java_sources[0] = conftest_file_name;
if ((!compile_using_envjavac (javac_nowarn,
java_sources, 1, tmpdir->dir_name,
false, false, false, true)
&& stat (compiled_file_name, &statbuf) >= 0)
|| (nowarn_option = "",
unlink (compiled_file_name),
(!compile_using_envjavac (javac,
java_sources, 1, tmpdir->dir_name,
false, false, false, true)
&& stat (compiled_file_name, &statbuf) >= 0)))
{
/* $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%s", javac, nowarn_option,
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 directly supports the desired source_version
and target_version. Perfect. */
free (javac_source_target);
resultp->nowarn_option = (nowarn_option[0] != '\0');
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);
{
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 ();
}
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)
{
/* 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%s", javac, nowarn_option,
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->nowarn_option = (nowarn_option[0] != '\0');
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);
}
}
}
}
cleanup_temp_dir (tmpdir);
free (javac_nowarn);
free (compiled_file_name);
free (conftest_file_name);
}
resultp->tested = true;
}
*usablep = resultp->usable;
if (resultp->nowarn_option)
strcpy (nowarn_option_out, " -Xlint:-options");
else
nowarn_option_out[0] = '\0';
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;
}
static bool
is_javac_present (void)
{
static bool javac_tested;
static bool javac_present;
if (!javac_tested)
{
/* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2" */
const char *argv[2];
int exitstatus;
argv[0] = "javac";
argv[1] = NULL;
exitstatus = execute ("javac", "javac", argv, NULL,
false, false, true, true,
true, false, NULL);
javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
javac_tested = true;
}
return javac_present;
}
/* Test whether javac can be used and whether it needs a -source and/or
-target option, as well as an option to inhibit warnings.
Return a failure indicator (true upon error). */
static bool
is_javac_usable (const char *source_version, const char *target_version,
bool *usablep,
char nowarn_option_out[17],
char source_option_out[20], char target_option_out[20])
{
/* The cache depends on the source_version and target_version. */
struct result_t
{
/*bool*/ unsigned int tested : 1;
/*bool*/ unsigned int usable : 1;
/*bool*/ unsigned int nowarn_option : 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;
resultp = &result_cache[source_version_index (source_version)]
[target_version_index (target_version)];
if (!resultp->tested)
{
/* 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)
{
/* Try javac. */
struct temp_dir *tmpdir;
char *conftest_file_name;
char *compiled_file_name;
const char *java_sources[1];
const char *nowarn_option;
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);
/* See the discussion in javacomp.m4. */
nowarn_option = "-Xlint:-options";
java_sources[0] = conftest_file_name;
if ((!compile_using_javac (java_sources, 1,
nowarn_option,
false, source_version,
false, target_version,
tmpdir->dir_name,
false, false, false, true)
&& stat (compiled_file_name, &statbuf) >= 0)
|| (nowarn_option = NULL,
unlink (compiled_file_name),
(!compile_using_javac (java_sources, 1,
nowarn_option,
false, source_version,
false, target_version,
tmpdir->dir_name,
false, false, false, true)
&& stat (compiled_file_name, &statbuf) >= 0)))
{
/* 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,
nowarn_option,
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 directly supports the desired source_version
and target_version. Perfect. */
resultp->nowarn_option = (nowarn_option != NULL);
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)
{
/* 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,
nowarn_option,
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->nowarn_option = (nowarn_option != NULL);
resultp->source_option = try_source_version;
resultp->target_option =
(try_target_version == compiler_target_version ? 0 :
try_target_version);
resultp->usable = true;
break;
}
}
}
}
}
cleanup_temp_dir (tmpdir);
free (compiled_file_name);
free (conftest_file_name);
}
resultp->tested = true;
}
*usablep = resultp->usable;
if (resultp->nowarn_option)
strcpy (nowarn_option_out, "-Xlint:-options");
else
nowarn_option_out[0] = '\0';
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;
}
/* ============================= Main function ============================= */
bool
compile_java_class (const char * const *java_sources,
unsigned int java_sources_count,
const char * const *classpaths,
unsigned int classpaths_count,
const char *source_version,
const char *target_version,
const char *directory,
bool optimize, bool debug,
bool use_minimal_classpath,
bool verbose)
{
bool err = false;
char *old_JAVA_HOME;
/* Map source_version 1.1 ... 1.5 to 1.6. */
if (source_version[0] == '1' && source_version[1] == '.'
&& (source_version[2] >= '1' && source_version[2] <= '5')
&& source_version[3] == '\0')
source_version = "1.6";
/* Map target_version 1.1 ... 1.5 to 1.6. */
if (target_version != NULL
&& target_version[0] == '1' && target_version[1] == '.'
&& (target_version[2] >= '1' && target_version[2] <= '5')
&& target_version[3] == '\0')
target_version = "1.6";
{
const char *javac = getenv ("JAVAC");
if (javac != NULL && javac[0] != '\0')
{
bool usable = false;
char nowarn_option[17];
char source_option[30];
char target_option[30];
if (target_version == NULL)
target_version = default_target_version ();
if (is_envjavac_usable (javac,
source_version, target_version,
&usable,
nowarn_option,
source_option, target_option))
{
err = true;
goto done1;
}
if (usable)
{
char *old_classpath;
char *javac_with_options;
/* Set CLASSPATH. */
old_classpath =
set_classpath (classpaths, classpaths_count, false, verbose);
javac_with_options =
xasprintf ("%s%s%s%s", javac,
nowarn_option, source_option, target_option);
assume (javac_with_options != NULL);
err = compile_using_envjavac (javac_with_options,
java_sources, java_sources_count,
directory, optimize, debug, verbose,
false);
free (javac_with_options);
/* Reset CLASSPATH. */
reset_classpath (old_classpath);
goto done1;
}
}
}
/* Unset the JAVA_HOME environment variable. */
old_JAVA_HOME = getenv ("JAVA_HOME");
if (old_JAVA_HOME != NULL)
{
old_JAVA_HOME = xstrdup (old_JAVA_HOME);
unsetenv ("JAVA_HOME");
}
if (is_javac_present ())
{
bool usable = false;
char nowarn_option[17];
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,
nowarn_option,
source_option, target_option))
{
err = true;
goto done1;
}
if (usable)
{
char *old_classpath;
/* Set CLASSPATH. We don't use the "-classpath ..." option because
in JDK 1.1.x its argument should also contain the JDK's
classes.zip, but we don't know its location. (In JDK 1.3.0 it
would work.) */
old_classpath =
set_classpath (classpaths, classpaths_count, use_minimal_classpath,
verbose);
err = compile_using_javac (java_sources, java_sources_count,
nowarn_option[0] != '\0' ? nowarn_option : NULL,
true, source_option,
target_option[0] != '\0', target_option,
directory, optimize, debug, verbose,
false);
/* Reset CLASSPATH. */
reset_classpath (old_classpath);
goto done2;
}
}
error (0, 0, _("Java compiler not found, try setting $JAVAC"));
err = true;
done2:
if (old_JAVA_HOME != NULL)
{
xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
free (old_JAVA_HOME);
}
done1:
return err;
}