libtool-patches
[Top][All Lists]
Advanced

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

[cygwin] cwrapper emits wrapper script


From: Charles Wilson
Subject: [cygwin] cwrapper emits wrapper script
Date: Fri, 20 Apr 2007 21:03:02 -0400
User-agent: Thunderbird 1.5.0.10 (Windows/20070221)

This patch depends on this one: http://lists.gnu.org/archive/html/libtool-patches/2007-04/msg00048.html

With this patch, on cygwin/mingw the wrapper script created by libtool in '.' is used only for reading back in via func_source(); THIS copy of the wrapper script is not used during the "run the uninstalled target" sequence *at all*.

In existing libtool, on cygwin/mingw, the following files are created:
   foo.exe   -- a wrapper executable, that launches...
   foo       -- a wrapper scripts, which sets env vars and launches...
   .libs/foo.exe -- the actual target executable
However, the presence of both foo and foo.exe in the same directory causes some problems with coreutils/shells' "autodetect .exe's" logic. And it's downright unusable when the cygwin "transparent_exe" option is active. So, eventually we want to eliminate ./foo entirely on these platforms, and rely entirely on ./foo.exe and .libs/foo.exe (with perhaps some other files in .libs/)

With this patch, after a successful build the following files are created:
   foo.exe       -- the wrapper executable. It does NOT launch the
                    wrapper script 'foo' in thisdir (.)
   foo           -- a wrapper script. It could be used to launch the
                    target executable, but isn't normally used for that.
                    This wrapper is sourced by func_source() when
                    necessary.
   .libs/foo.exe -- the target executable

When the wrapper foo.exe is launched, it generates a new wrapper script
   .libs/foo_ltshwrapper
which is identical [1] to the one in the "real" directory (./foo). Then, the executable wrapper execs THAT new wrapper script, which sets the env vars and execs the real target.

At present, the .libs/foo_ltwrappersh is recreated every single time the wrapper executable is run; later, timestamps could be used to avoid this, but that's an optimization.

Furthermore, the wrapper executable can be invoked with the '-ltdumpscript' option, which will emit the script on *stdout*. This is intended for future progress toward eliminating the ./foo wrapper script entirely, on cygwin/mingw.

[1] This patch changes the *all* wrapper scripts on all platforms by adding the following:
    WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
    if test "$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR" = "yes"; then
       ... remove "/.libs" from $thisdir ...
    fi
The ./foo wrapper (and all wrappers on non-cygwin/mingw) looks just like the above, so the additional code is permanently disabled. However, the script emitted by the wrapper executable on cygwin/mingw is modified so that WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=yes, activating the additional code. This is because the script needs to compute "$thisdir" as being the main directory, in which .libs/* exists.

Also: as it happens, currently under cygwin AND mingw, the executable wrapper is *not used* unless explicitly invoked with the .exe ending. Since the tests do not do so, the executable wrappers have not been exercised on either platform for a while. They do still work on cygwin, but not mingw: calling the msvcrt.dll version of execv("/bin/sh", ...) doesn't work. (The testsuite on mingw has been passing because the MSYS shell launches the wrapper *script* directly, instead of the wrapper executable). This patch ALSO fixes the wrapper executable on mingw, by DOS-izing "/bin/sh" when emitting those lines of the executable wrapper's source code.

POSSIBLE LIMITATION:
assumes that if S_ISLNK is defined, then $host has lstat() and realpath(). Since the only place this matters (at present) is cygwin and mingw, this assumption is safe: cygwin has all three, mingw has none. The new code [chase_symlinks()] which makes this assumption was added so that symlinks to the wrapper executable would work as expected (on systems where symlinks exist) just like symlinks to the wrapper script work.

TESTING:
___________________________________________________________
Tested on cygwin (full testsuite) with expected results:
======================
All 115 tests passed
(9 tests were not run) [no gfortran]
======================
 14: Java convenience archives       FAILED (convenience.at:273)
 16: Link order of deplibs.          FAILED (link-order2.at:129)
 49: Run tests with low max_cmd_len  FAILED (cmdline_wrap.at:42)
This is the expected output, for now.

___________________________________________________________
Linux, full testsuite:
======================
All 105 tests passed
(1 tests were not run)
======================
 37: compiling softlinked libltdl  FAILED (nonrecursive.at:90)
 38: compiling copied libltdl      FAILED (nonrecursive.at:114)
 39: installable libltdl           FAILED (nonrecursive.at:140)
 40: compiling softlinked libltdl  FAILED (recursive.at:67)
 41: compiling copied libltdl      FAILED (recursive.at:87)
 42: installable libltdl           FAILED (recursive.at:109)
Each of these looks like:

configure:10874: error: possibly undefined macro: AS_CASE
      If this token and others are legitimate, please use m4_pattern_allow.
      See the Autoconf documentation.
configure:10881: error: possibly undefined macro: AS_IF
stdout:
../../libtool-2.1a/tests/nonrecursive.at:90: exit code was 1, expected 0
37. nonrecursive.at:78: 37. compiling softlinked libltdl (nonrecursive.at:78): FAILED (nonrecursive.at:90)

I don't think this error is germane; my linux box has autoconf-2.59 and automake-1.9.4 but I was using a dist-bzip2 package created from cygwin with autoconf-2.61 and automake-1.10. I do not know why, on linux, these tests complain about AS_CASE and AS_IF: those macros *are* defined in /usr/share/autoconf/m4sugar/m4sh.m4. Speculation: conflict between older versions on target system, and newer version used to configure the package. Judging by the old-tests, and the fact that the only part that could affect linux is the additions to the wrapper script -- and those are permanently disabled thanks to WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no, I surmise this patch has no deleterious impact on any non-cygwin/mingw system.
___________________________________________________________
mingw (old tests only because no dejagnu)
====================================
1 of 114 tests failed
(10 tests were not run)
Please report to address@hidden
====================================
Failed test was: tests/mdemo-dryrun.test -- false alarm:

$ diff before after
66c66
< drwxr-xr-x    2 cwilson  Administ        0 Apr 20  2007 bin
---
> drwxr-xr-x    2 cwilson  Administ        0 Apr 20 20:29 bin

Probably need another 'sleep 1' somewhere, but that's outside the scope of this patch.
___________________________________________________________


ChangeLog:

2007-04-20  Charles Wilson  <address@hidden>

        * ltmain.m4sh (func_emit_libtool_wrapper_script): add
        code block to handle cases when wrapper script is in $objdir.
        (func_emit_libtool_cwrapperexe_source): replace DEBUG() macro
        with namespace-safe LTWRAPPER_DEBUGPRINTF(). Call
        func_emit_libtool_wrapper_script() with appropriate filters to
        embed script text in C char* static variable.
        (f_e_l_c_s: main): add new option -ltdumpscript, parse argv[]
        for it, and take appropriate action.  Call chase_symlinks()
        on argv[0], in case this.exe was launched via one.  Use chased
        value to determine full absolute pathname of wrapper script, in
        $objdir. Unconditionally write script out to this pathname and
        set permission bits.
        (f_e_l_c_s: main) [mingw]: DOS-ize $SHELL before populating     
        newargv[0]. Ensure newargv[1] (wrapper script absolute path)
        uses only '/', not '\'. Make sure to call execv() with DOS-ized
        $SHELL.
        (f_e_l_c_s: make_executable): new function
        (f_e_l_c_s: chase_symlinks): new function (no-op if !S_ISLNK)

--
Chuck




Index: libltdl/config/ltmain.m4sh
===================================================================
RCS file: /cvsroot/libtool/libtool/libltdl/config/ltmain.m4sh,v
retrieving revision 1.72
diff -u -r1.72 ltmain.m4sh
--- libltdl/config/ltmain.m4sh  2007-04-19 19:54:53.500000000 -0400
+++ libltdl/config/ltmain.m4sh  2007-04-20 03:20:46.281250000 -0400
@@ -2301,6 +2301,20 @@
     file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\`
   done
 
+  # cygwin/mingw cwrapper will rewrite this line:
+  WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=no
+  if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
+    # special case for '.'
+    if test \"\$thisdir\" = \".\"; then
+      thisdir=\`pwd\`
+    fi
+    # remove .libs from thisdir
+    case \"\$thisdir\" in
+    *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 
's%[\\\\/][^\\\\/]*$%%'\` ;;
+    $objdir )   thisdir=. ;;
+    esac
+  fi
+
   # Try to get the absolute directory name.
   absdir=\`cd \"\$thisdir\" && pwd\`
   test -n \"\$absdir\" && thisdir=\"\$absdir\"
@@ -2424,7 +2438,7 @@
    This wrapper executable should never be moved out of the build directory.
    If it is, it will not operate correctly.
 
-   Currently, it simply execs the wrapper *script* "/bin/sh $output",
+   Currently, it simply execs the wrapper *script* "$SHELL $output",
    but could eventually absorb all of the scripts functionality and
    exec $objdir/$outputname directly.
 */
@@ -2438,6 +2452,7 @@
 #include <assert.h>
 #include <string.h>
 #include <ctype.h>
+#include <errno.h>
 #include <sys/stat.h>
 
 #if defined(PATH_MAX)
@@ -2482,12 +2497,11 @@
   if (stale) { free ((void *) stale); stale = 0; } \
 } while (0)
 
-/* -DDEBUG is fairly common in CFLAGS.  */
-#undef DEBUG
+#undef LTWRAPPER_DEBUGPRINTF
 #if defined DEBUGWRAPPER
-# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__)
+# define LTWRAPPER_DEBUGPRINTF(format, ...) fprintf(stderr, format, 
__VA_ARGS__)
 #else
-# define DEBUG(format, ...)
+# define LTWRAPPER_DEBUGPRINTF(format, ...)
 #endif
 
 const char *program_name = NULL;
@@ -2496,41 +2510,156 @@
 char * xstrdup (const char *string);
 const char * base_name (const char *name);
 char * find_executable(const char *wrapper);
+char * chase_symlinks(const char *pathspec);
+int    make_executable(const char *path);
 int    check_executable(const char *path);
 char * strendzap(char *str, const char *pat);
 void lt_fatal (const char *message, ...);
 
+static const char* script_text = 
+EOF
+
+           func_emit_libtool_wrapper_script |\
+               $SED -e 's/\([\\"]\)/\\\1/g' |\
+               $SED -e 's/\(WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\)=.*/\1=yes/' |\
+               $SED -e 's/^/"/' -e 's/$/\\n"/' 
+           $ECHO ";"
+
+           cat <<EOF
 int
 main (int argc, char *argv[])
 {
   char **newargz;
+  char *tmp_pathspec;
+  char *actual_cwrapper_path;
+  char *shwrapper_name;
+  FILE *shwrapperFILE;
+
+  const char* dumpscript_opt = "-ltdumpscript";
+  size_t dumpscript_len = strlen(dumpscript_opt);
   int i;
 
   program_name = (char *) xstrdup (base_name (argv[0]));
-  DEBUG("(main) argv[0]      : %s\n",argv[0]);
-  DEBUG("(main) program_name : %s\n",program_name);
+  LTWRAPPER_DEBUGPRINTF("(main) argv[0]      : %s\n",argv[0]);
+  LTWRAPPER_DEBUGPRINTF("(main) program_name : %s\n",program_name);
+
+  /* very simple arg parsing; don't want to rely on getopt */
+  for (i=1; i<argc; i++)
+  {
+    if (strncmp(argv[i], dumpscript_opt, dumpscript_len) == 0)
+    {
+      printf("%s", script_text);
+      return 0;
+    }
+  }
+
   newargz = XMALLOC(char *, argc+2);
 EOF
 
-           cat <<EOF
+           case $host_os in
+             mingw*)
+               # such a shame msys has no cygpath-like program...
+               lt_mingwSHELL_prog=`basename $SHELL`;
+               lt_mingwSHELL_prog_noEXE=`$ECHO "X${lt_mingwSHELL_prog}" | 
$Xsed -e 's%.exe$%%'`
+               if test -n "$lt_mingwSHELL_prog_noEXE" ; then
+                 lt_mingwSHELL_progEXE="${lt_mingwSHELL_prog_noEXE}.exe"
+               else
+                 # best guess
+                  lt_mingwSHELL_progEXE="sh.exe"
+                fi
+
+               lt_mingwSHELL_dir=`dirname $SHELL`;
+               lt_mingwSHELL_wdir=`cd $lt_mingwSHELL_dir && pwd -W`
+               if test -z "$lt_mingwSHELL_wdir"; then
+                  # best guess
+                 lt_mingwSHELL_wdir=`cd /bin && pwd -W`
+               fi
+               lt_mingwSHELL_mdir=`$ECHO "X${lt_mingwSHELL_wdir}" | $Xsed -e 
's%\\\\%/%g'`
+               lt_mingwSHELL="$lt_mingwSHELL_mdir/$lt_mingwSHELL_progEXE"
+
+               cat <<EOF
+  newargz[0] = (char *) xstrdup("$lt_mingwSHELL");
+EOF
+             ;;
+             *)
+               cat <<EOF
   newargz[0] = (char *) xstrdup("$SHELL");
 EOF
+             ;;
+           esac
 
            cat <<"EOF"
-  newargz[1] = find_executable(argv[0]);
-  if (newargz[1] == NULL)
+  tmp_pathspec = find_executable (argv[0]);
+  if (tmp_pathspec == NULL)
     lt_fatal("Couldn't find %s", argv[0]);
-  DEBUG("(main) found exe at : %s\n",newargz[1]);
-  /* we know the script has the same name, without the .exe */
-  /* so make sure newargz[1] doesn't end in .exe */
-  strendzap(newargz[1],".exe");
+  LTWRAPPER_DEBUGPRINTF("(main) found exe (before symlink chase) at : 
%s\n",tmp_pathspec);
+
+  actual_cwrapper_path = chase_symlinks (tmp_pathspec);
+  LTWRAPPER_DEBUGPRINTF("(main) found exe (after symlink chase) at : 
%s\n",actual_cwrapper_path);
+  XFREE(tmp_pathspec);
+
+  shwrapper_name = (char *) xstrdup (base_name (actual_cwrapper_path));
+  strendzap( actual_cwrapper_path, shwrapper_name );
+
+  /* shwrapper_name transforms */
+  strendzap(shwrapper_name,".exe");
+  tmp_pathspec = XMALLOC( char, strlen ( shwrapper_name ) +
+                                    strlen ( "_ltshwrapper" ) + 1 );
+  strcpy ( tmp_pathspec, shwrapper_name );
+  strcat ( tmp_pathspec, "_ltshwrapper" );
+  XFREE( shwrapper_name );
+  shwrapper_name = tmp_pathspec;
+  tmp_pathspec = 0;
+  LTWRAPPER_DEBUGPRINTF("(main) libtool shell wrapper name: 
%s\n",shwrapper_name);
+EOF
+
+           cat <<EOF
+  newargz[1] = XMALLOC( char, strlen ( actual_cwrapper_path ) +
+                              strlen ( "$objdir" ) + 1 +
+                              strlen ( shwrapper_name ) + 1 );
+  strcpy ( newargz[1], actual_cwrapper_path );
+  strcat ( newargz[1], "$objdir" );
+  strcat ( newargz[1], "/" );
+  strcat ( newargz[1], shwrapper_name );
+EOF
+
+
+           case $host_os in
+             mingw*)
+           cat <<"EOF"
+  {
+     char* p = newargz[1];
+     while(*p!='\0') {
+       if (*p == '\\') {
+         *p = '/';
+       }
+       p++;
+     }
+  }
+EOF
+           ;;
+           esac
+
+           cat <<"EOF"
+  XFREE( shwrapper_name );
+  XFREE( actual_cwrapper_path );
+
+  if ( (shwrapperFILE = fopen (newargz[1], "wb")) == 0 )
+  {
+    lt_fatal("Could not open %s for writing", newargz[1]);
+  }
+  fprintf (shwrapperFILE, "%s", script_text);
+  fclose (shwrapperFILE);
+
+  make_executable( newargz[1] );
+  
   for (i = 1; i < argc; i++)
     newargz[i+1] = xstrdup(argv[i]);
   newargz[argc+1] = NULL;
 
   for (i=0; i<argc+1; i++)
   {
-    DEBUG("(main) newargz[%d]   : %s\n",i,newargz[i]);
+    LTWRAPPER_DEBUGPRINTF("(main) newargz[%d]   : %s\n",i,newargz[i]);
     ;
   }
 
@@ -2539,7 +2668,7 @@
            case $host_os in
              mingw*)
                cat <<EOF
-  execv("$SHELL",(char const **)newargz);
+  execv("$lt_mingwSHELL",(const char * const *)newargz);
 EOF
              ;;
              *)
@@ -2592,7 +2721,7 @@
 {
   struct stat st;
 
-  DEBUG("(check_executable)  : %s\n", path ? (*path ? path : "EMPTY!") : 
"NULL!");
+  LTWRAPPER_DEBUGPRINTF("(check_executable)  : %s\n", path ? (*path ? path : 
"EMPTY!") : "NULL!");
   if ((!path) || (!*path))
     return 0;
 
@@ -2612,8 +2741,37 @@
     return 0;
 }
 
+int
+make_executable(const char * path)
+{
+  int rval = 0;
+  struct stat st;
+
+  /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */
+  int S_XFLAGS = 
+#if defined (S_IXOTH)
+       S_IXOTH ||
+#endif
+#if defined (S_IXGRP)
+       S_IXGRP ||
+#endif
+       S_IXUSR;
+
+    LTWRAPPER_DEBUGPRINTF("(make_executable)  : %s\n", path ? (*path ? path : 
"EMPTY!") : "NULL!");
+    if ((!path) || (!*path))
+        return 0;
+
+    if (stat (path, &st) >= 0)
+    {
+        rval = chmod ( path, st.st_mode | S_XFLAGS );
+    }
+    return rval;
+}
+
 /* Searches for the full path of the wrapper.  Returns
-   newly allocated full path name if found, NULL otherwise */
+   newly allocated full path name if found, NULL otherwise
+   Does not chase symlinks, even on platforms that support them.
+*/
 char *
 find_executable (const char* wrapper)
 {
@@ -2625,7 +2783,7 @@
   int tmp_len;
   char* concat_name;
 
-  DEBUG("(find_executable)  : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") 
: "NULL!");
+  LTWRAPPER_DEBUGPRINTF("(find_executable)  : %s\n", wrapper ? (*wrapper ? 
wrapper : "EMPTY!") : "NULL!");
 
   if ((wrapper == NULL) || (*wrapper == '\0'))
     return NULL;
@@ -2715,6 +2873,62 @@
 }
 
 char *
+chase_symlinks(const char *pathspec)
+{
+#ifndef S_ISLNK
+    return xstrdup ( pathspec );
+#else
+    char buf[LT_PATHMAX];
+    struct stat s;
+    int rv = 0;
+    char* tmp_pathspec = xstrdup (pathspec);
+    char* p;
+    int has_symlinks = 0;
+    while (strlen(tmp_pathspec) && !has_symlinks)
+    {
+        LTWRAPPER_DEBUGPRINTF("checking path component for symlinks: %s\n", 
tmp_pathspec);
+        if (lstat (tmp_pathspec, &s) == 0)
+        {
+            if (S_ISLNK(s.st_mode) != 0)
+            {
+                has_symlinks = 1;
+                break;
+            }
+
+            /* search backwards for last DIR_SEPARATOR */
+            p = tmp_pathspec + strlen(tmp_pathspec) - 1;
+            while ( (p > tmp_pathspec) && (! IS_DIR_SEPARATOR(*p)) )
+                p--;
+            if ( (p == tmp_pathspec) && (! IS_DIR_SEPARATOR(*p)) )
+            {
+                /* no more DIR_SEPARATORS left */
+                break;
+            }
+            *p = '\0';
+        }
+        else
+        {
+            char* errstr = strerror(errno);
+            lt_fatal("Error accessing file %s (%s)", tmp_pathspec, errstr);
+        }
+    }
+    XFREE(tmp_pathspec);
+
+    if (!has_symlinks)
+    {
+       return xstrdup ( pathspec );
+    } 
+
+    tmp_pathspec = realpath ( pathspec, buf );
+    if (tmp_pathspec == 0)
+    {
+        lt_fatal("Could not follow symlinks for %s", pathspec);
+    }
+    return xstrdup ( tmp_pathspec );
+#endif
+}
+
+char *
 strendzap(char *str, const char *pat)
 {
   size_t len, patlen;

reply via email to

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