bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[bug-gnulib] Re: new modules for Java interoperability


From: Bruno Haible
Subject: [bug-gnulib] Re: new modules for Java interoperability
Date: Wed, 26 Jan 2005 18:01:19 +0100
User-agent: KMail/1.5

Hey, someone still proofreading these modules? :-)

There were no comments, so I committed the javaexec module.

The next one is for compiling Java programs. For the same reason that
some GNU programs (like gcl) require gcc to be in the PATH at runtime
and that dlopen() is in libc, the ability to compile Java programs at
runtime is crucial.

In particular, GNU gettext uses it to compile the "plurals" formula of
a message catalog.

============================= modules/javacomp =============================
Description:
Compile a Java program.

Files:
lib/javacomp.h
lib/javacomp.c
lib/javacomp.sh.in
m4/javacomp.m4

Depends-on:
stdbool
xallocsa
execute
pipe
wait-process
classpath
xsetenv
sh-quote
safe-read
xalloc
error
gettext

configure.ac:
gt_JAVACOMP

Makefile.am:
lib_SOURCES += javacomp.h javacomp.c
EXTRA_DIST += javacomp.sh.in

Include:
"javacomp.h"

License:
GPL

Maintainer:
Bruno Haible

============================= lib/javacomp.sh.in =============================
#!/bin/sh
# Compile a Java program.

# Copyright (C) 2001-2002 Free Software Foundation, Inc.
# Written by Bruno Haible <address@hidden>, 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 2, 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, write to the Free Software Foundation,
# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

# This uses the same choices as javacomp.c, but instead of relying on the
# environment settings at run time, it uses the environment variables
# present at configuration time.
#
# This is a separate shell script, because it must be able to unset JAVA_HOME
# in some cases, which a simple shell command cannot do.
#
# The extra CLASSPATH must have been set prior to calling this script.
# Options that can be passed are -O, -g and "-d DIRECTORY".

CONF_JAVAC='@JAVAC@'
CONF_CLASSPATH='@CLASSPATH@'
if test -n "$CONF_JAVAC"; then
  # Combine given CLASSPATH and configured CLASSPATH.
  if test -n "$CLASSPATH"; then
    CLASSPATH="$CLASSPATH${CONF_CLASSPATH:address@hidden@$CONF_CLASSPATH}"
  else
    CLASSPATH="$CONF_CLASSPATH"
  fi
  export CLASSPATH
  test -z "$JAVA_VERBOSE" || echo "$CONF_JAVAC $@"
  exec $CONF_JAVAC "$@"
else
  unset JAVA_HOME
  if test -n "@HAVE_GCJ_C@"; then
    CLASSPATH="$CLASSPATH"
    export CLASSPATH
    test -z "$JAVA_VERBOSE" || echo gcj -C "$@"
    exec gcj -C "$@"
  else
    if test -n "@HAVE_JAVAC@"; then
      CLASSPATH="$CLASSPATH"
      export CLASSPATH
      test -z "$JAVA_VERBOSE" || echo javac "$@"
      exec javac "$@"
    else
      if test -n "@HAVE_JIKES@"; then
        # Combine given CLASSPATH and configured CLASSPATH.
        if test -n "$CLASSPATH"; then
          CLASSPATH="$CLASSPATH${CONF_CLASSPATH:address@hidden@$CONF_CLASSPATH}"
        else
          CLASSPATH="$CONF_CLASSPATH"
        fi
        export CLASSPATH
        test -z "$JAVA_VERBOSE" || echo jikes "$@"
        exec jikes "$@"
      else
        echo 'Java compiler not found, try installing gcj or set $JAVAC, then 
reconfigure' 1>&2
        exit 1
      fi
    fi
  fi
fi
============================= lib/javacomp.h =============================
/* Compile a Java program.
   Copyright (C) 2001-2002 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 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 2, 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, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifndef _JAVACOMP_H
#define _JAVACOMP_H

#include <stdbool.h>

/* Compile a Java source file to bytecode.
   java_sources is an array of source file names.
   classpaths is a list of pathnames to be prepended to the CLASSPATH.
   directory is the target directory. The .class file for class X.Y.Z is
   written at directory/X/Y/Z.class. If directory is NULL, the .class
   file is written in the source's directory.
   use_minimal_classpath = true means to ignore the user's CLASSPATH and
   use a minimal one. This is likely to reduce possible problems if the
   user's CLASSPATH contains garbage or a classes.zip file of the wrong
   Java version.
   If verbose, the command to be executed will be printed.
   Return false if OK, true on error.  */
extern bool compile_java_class (const char * const *java_sources,
                                unsigned int java_sources_count,
                                const char * const *classpaths,
                                unsigned int classpaths_count,
                                const char *directory,
                                bool optimize, bool debug,
                                bool use_minimal_classpath,
                                bool verbose);

#endif /* _JAVACOMP_H */
============================= lib/javacomp.c =============================
/* Compile a Java program.
   Copyright (C) 2001-2003 Free Software Foundation, Inc.
   Written by Bruno Haible <address@hidden>, 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 2, 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, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <alloca.h>

/* Specification.  */
#include "javacomp.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "execute.h"
#include "pipe.h"
#include "wait-process.h"
#include "classpath.h"
#include "xsetenv.h"
#include "sh-quote.h"
#include "safe-read.h"
#include "xalloc.h"
#include "xallocsa.h"
#include "error.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
   gcj -C   GCC 3.2     Y  --classpath=P  -O -g  gcj --version | sed -e 
's,^[^0-9]*,,' -e 1q | sed -e '/^3\.[01]/d' | grep '^[3-9]' >/dev/null
   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
   jikes    Jikes 1.14  N  -classpath P   -O -g  jikes 2>/dev/null; test $? = 1

   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. "gcj -C", because it is a completely free compiler,
     3. "javac", because it is a standard compiler,
     4. "jikes", comes last because it has some deviating interpretation
        of the Java Language Specification and because it requires a
        CLASSPATH environment variable.

   We unset the JAVA_HOME environment variable, because a wrong setting of
   this variable can confuse the JDK's javac.
 */

bool
compile_java_class (const char * const *java_sources,
                    unsigned int java_sources_count,
                    const char * const *classpaths,
                    unsigned int classpaths_count,
                    const char *directory,
                    bool optimize, bool debug,
                    bool use_minimal_classpath,
                    bool verbose)
{
  bool err = false;
  char *old_JAVA_HOME;

  {
    const char *javac = getenv ("JAVAC");
    if (javac != NULL && javac[0] != '\0')
      {
        /* Because $JAVAC may consist of a command and options, we use the
           shell.  Because $JAVAC has been set by the user, we leave all
           all environment variables in place, including JAVA_HOME, and
           we don't erase the user's CLASSPATH.  */
        char *old_classpath;
        unsigned int command_length;
        char *command;
        char *argv[4];
        int exitstatus;
        unsigned int i;
        char *p;

        /* Set CLASSPATH.  */
        old_classpath =
          set_classpath (classpaths, classpaths_count, false,
                         verbose);

        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 *) xallocsa (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,
                              false, true, true);
        err = (exitstatus != 0);

        freesa (command);

        /* 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");
    }

  {
    static bool gcj_tested;
    static bool gcj_present;

    if (!gcj_tested)
      {
        /* Test for presence of gcj:
           "gcj --version 2> /dev/null | \
            sed -e 's,^[^0-9]*,,' -e 1q | \
            sed -e '/^3\.[01]/d' | grep '^[3-9]' > /dev/null"  */
        char *argv[3];
        pid_t child;
        int fd[1];
        int exitstatus;

        argv[0] = "gcj";
        argv[1] = "--version";
        argv[2] = NULL;
        child = create_pipe_in ("gcj", "gcj", argv, DEV_NULL, true, true,
                                false, fd);
        gcj_present = false;
        if (child != -1)
          {
            /* Read the subprocess output, drop all lines except the first,
               drop all characters before the first digit, and test whether
               the remaining string starts with a digit >= 3, but not with
               "3.0" or "3.1".  */
            char c[3];
            size_t count = 0;

            while (safe_read (fd[0], &c[count], 1) > 0)
              {
                if (c[count] == '\n')
                  break;
                if (count == 0)
                  {
                    if (!(c[0] >= '0' && c[0] <= '9'))
                      continue;
                    gcj_present = (c[0] >= '3');
                  }
                count++;
                if (count == 3)
                  {
                    if (c[0] == '3' && c[1] == '.'
                        && (c[2] == '0' || c[2] == '1'))
                      gcj_present = false;
                    break;
                  }
              }
            while (safe_read (fd[0], &c[0], 1) > 0)
              ;

            close (fd[0]);

            /* Remove zombie process from process list, and retrieve exit
               status.  */
            exitstatus =
              wait_subprocess (child, "gcj", false, true, true, false);
            if (exitstatus != 0)
              gcj_present = false;
          }
        gcj_tested = true;
      }

    if (gcj_present)
      {
        char *old_classpath;
        unsigned int argc;
        char **argv;
        char **argp;
        int exitstatus;
        unsigned int i;

        /* Set CLASSPATH.  We could also use the --CLASSPATH=... option
           of gcj.  Note that --classpath=... option is different: its
           argument should also contain gcj's libgcj.jar, but we don't
           know its location.  */
        old_classpath =
          set_classpath (classpaths, classpaths_count, use_minimal_classpath,
                         verbose);

        argc =
          2 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
          + java_sources_count;
        argv = (char **) xallocsa ((argc + 1) * sizeof (char *));

        argp = argv;
        *argp++ = "gcj";
        *argp++ = "-C";
        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, false,
                              true, true);
        err = (exitstatus != 0);

        freesa (argv);

        /* Reset CLASSPATH.  */
        reset_classpath (old_classpath);

        goto done2;
      }
  }

  {
    static bool javac_tested;
    static bool javac_present;

    if (!javac_tested)
      {
        /* Test for presence of javac: "javac 2> /dev/null ; test $? -le 2"  */
        char *argv[2];
        int exitstatus;

        argv[0] = "javac";
        argv[1] = NULL;
        exitstatus = execute ("javac", "javac", argv, false, false, true, true,
                              true, false);
        javac_present = (exitstatus == 0 || exitstatus == 1 || exitstatus == 2);
        javac_tested = true;
      }

    if (javac_present)
      {
        char *old_classpath;
        unsigned int argc;
        char **argv;
        char **argp;
        int exitstatus;
        unsigned int i;

        /* 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);

        argc =
          1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
          + java_sources_count;
        argv = (char **) xallocsa ((argc + 1) * sizeof (char *));

        argp = argv;
        *argp++ = "javac";
        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,
                              false, true, true);
        err = (exitstatus != 0);

        freesa (argv);

        /* Reset CLASSPATH.  */
        reset_classpath (old_classpath);

        goto done2;
      }
  }

  {
    static bool jikes_tested;
    static bool jikes_present;

    if (!jikes_tested)
      {
        /* Test for presence of jikes: "jikes 2> /dev/null ; test $? = 1"  */
        char *argv[2];
        int exitstatus;

        argv[0] = "jikes";
        argv[1] = NULL;
        exitstatus = execute ("jikes", "jikes", argv, false, false, true, true,
                              true, false);
        jikes_present = (exitstatus == 0 || exitstatus == 1);
        jikes_tested = true;
      }

    if (jikes_present)
      {
        char *old_classpath;
        unsigned int argc;
        char **argv;
        char **argp;
        int exitstatus;
        unsigned int i;

        /* Set CLASSPATH.  We could also use the "-classpath ..." option.
           Since jikes doesn't come with its own standard library, it
           needs a classes.zip or rt.jar or libgcj.jar in the CLASSPATH.
           To increase the chance of success, we reuse the current CLASSPATH
           if the user has set it.  */
        old_classpath =
          set_classpath (classpaths, classpaths_count, false,
                         verbose);

        argc =
          1 + (optimize ? 1 : 0) + (debug ? 1 : 0) + (directory != NULL ? 2 : 0)
          + java_sources_count;
        argv = (char **) xallocsa ((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,
                              false, true, true);
        err = (exitstatus != 0);

        freesa (argv);

        /* Reset CLASSPATH.  */
        reset_classpath (old_classpath);

        goto done2;
      }
  }

  error (0, 0, _("Java compiler not found, try installing gcj or set $JAVAC"));
  err = true;

 done2:
  if (old_JAVA_HOME != NULL)
    {
      xsetenv ("JAVA_HOME", old_JAVA_HOME, 1);
      free (old_JAVA_HOME);
    }

 done1:
  return err;
}
============================= m4/javacomp.m4 =============================
# javacomp.m4 serial 6 (gettext-0.13)
dnl Copyright (C) 2001-2003 Free Software Foundation, Inc.
dnl This file is free software; the Free Software Foundation
dnl gives unlimited permission to copy and/or distribute it,
dnl with or without modifications, as long as this notice is preserved.

# Prerequisites of javacomp.sh.
# Sets HAVE_JAVACOMP to nonempty if javacomp.sh will work.

AC_DEFUN([gt_JAVACOMP],
[
  AC_MSG_CHECKING([for Java compiler])
  AC_EGREP_CPP(yes, [
#if defined _WIN32 || defined __WIN32__ || defined __EMX__ || defined __DJGPP__
  yes
#endif
], CLASSPATH_SEPARATOR=';', CLASSPATH_SEPARATOR=':')
  HAVE_JAVACOMP=1
  if test -n "$JAVAC"; then
    ac_result="$JAVAC"
  else
    pushdef([AC_MSG_CHECKING],[:])dnl
    pushdef([AC_CHECKING],[:])dnl
    pushdef([AC_MSG_RESULT],[:])dnl
    AC_CHECK_PROG(HAVE_GCJ_IN_PATH, gcj, yes)
    AC_CHECK_PROG(HAVE_JAVAC_IN_PATH, javac, yes)
    AC_CHECK_PROG(HAVE_JIKES_IN_PATH, jikes, yes)
    popdef([AC_MSG_RESULT])dnl
    popdef([AC_CHECKING])dnl
    popdef([AC_MSG_CHECKING])dnl
changequote(,)dnl
    # Test for a good gcj version (>= 3.0).
    # Exclude some versions of gcj: gcj 3.0.4 compiles GetURL.java to invalid
    # bytecode, that crashes with an IllegalAccessError when executed by
    # gij 3.0.4 or with a VerifyError when executed by Sun Java. Likewise for
    # gcj 3.1.
    # I also exclude gcj 3.2, 3.3 etc. because I have no idea when this bug
    # will be fixed. The bug is registered as java/7066, see
    # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=7066
    # FIXME: Check new versions of gcj as they come out.
    if test -n "$HAVE_GCJ_IN_PATH" \
       && gcj --version 2>/dev/null | sed -e 's,^[^0-9]*,,' -e 1q | sed -e 
'/^3\.[0123456789]/d' | grep '^[3-9]' >/dev/null \
       && (
        # See if libgcj.jar is well installed.
        cat > conftest.java <<EOF
public class conftest {
  public static void main (String[] args) {
  }
}
EOF
        gcj -C -d . conftest.java 2>/dev/null
        error=$?
        rm -f conftest.java conftest.class
        exit $error
       ); then
      HAVE_GCJ_C=1
      ac_result="gcj -C"
    else
      if test -n "$HAVE_JAVAC_IN_PATH" \
         && (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
        HAVE_JAVAC=1
        ac_result="javac"
      else
        if test -n "$HAVE_JIKES_IN_PATH" \
           && (jikes >/dev/null 2>/dev/null || test $? = 1) \
           && (
            # See if the existing CLASSPATH is sufficient to make jikes work.
            cat > conftest.java <<EOF
public class conftest {
  public static void main (String[] args) {
  }
}
EOF
            unset JAVA_HOME
            jikes conftest.java 2>/dev/null
            error=$?
            rm -f conftest.java conftest.class
            exit $error
           ); then
          HAVE_JIKES=1
          ac_result="jikes"
        else
          HAVE_JAVACOMP=
          ac_result="no"
        fi
      fi
    fi
changequote([,])dnl
  fi
  AC_MSG_RESULT([$ac_result])
  AC_SUBST(JAVAC)
  AC_SUBST(CLASSPATH)
  AC_SUBST(CLASSPATH_SEPARATOR)
  AC_SUBST(HAVE_GCJ_C)
  AC_SUBST(HAVE_JAVAC)
  AC_SUBST(HAVE_JIKES)
])





reply via email to

[Prev in Thread] Current Thread [Next in Thread]