This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Solving the "relink exe's" libtool problem [take 2]


Alexandre Duret-Lutz wrote:
 Chuck> Said "stub" executable would have to do ALL of the
 Chuck> things the script does, and then pass that environment
 Chuck> to its exec'ed target in .libs/ --

Maybe it could just exec() something like `/bin/sh .libs/foo.sh',
where `.libs/foo.sh' is the script wrapper.
Good idea. Try this version...

It no longer requires any coordination with automake; it's all implemented in ltmain.sh and doesn't mess with EXEEXT or LT_EXEEXT.

Now, this isn't perfect -- it solves the problem, has no regressions on cygwin, and is probably okay for inclusion in libtool CVS. But there are stylistic warts that could be sanded off, if someone else were to take the effort (hint, hint)

The following behavior is only "active" when $host is cygwin or mingw:

I left the shell wrapper where it was, in the build directory. When creating the shell wrapper, libtool will also create lt-${prog}.c and compile it using
$run $LTCC -s -o ${prog}.exe lt-${prog}.c

So, you end up with:

<builddir>/foo (shell wrapper)
<builddir>/foo.exe (binary wrapper)
<builddir>/lt-foo.c
<builddir>/.libs/foo.exe (the real executable)

Since builddir contains foo.exe, make is happy. ./foo.exe execs "/bin/sh foo", which sets up the environment and calls .libs/foo.exe.

Eventually, the functionality of the shell wrapper could be moved into the source code for the binary wrapper, and we could drop the shell wrapper completely for cygwin/mingw $host.

So the fact that the shell wrapper is still there is a wart (but removing the shell wrapper creates its own difficulties -- see next point).

There are two places in ltmain.sh where the shell wrapper is directly sourced. This doesn't work very well, because when both "foo" and "foo.exe" exist, ". ./foo" ends up sourcing "foo.exe" -- which is bad.

[Note, if the shell wrapper is eliminated, then somehow libtool needs to be able to get the info embedded into the binary wrapper. Maybe the binary wrapper needs a "--shell" option, that emits what is effectively the current shell script? But then, that's just another wart (`binwrap --shell > shellwrap`; . shellwrap ; rm shellwrap), unless there is a way to "source" rather than execute the contents of a variable.]

But, if both the binary wrapper and the shell wrapper exist, there are a few ways to solve the conflict that occurs when libtool tries to source the shell wrapper (and sources the binary wrapper instead):
(1) prior to directly sourcing the shell wrapper into libtool's memory space, make a temporary copy with a name that doesn't conflict with "foo.exe" in the same way that "foo" does. E.g.
cp foo lt-foo.sh
. ./lt-foo.sh
rm -f lt-foo.sh
Yes, it's a race condition, but it should be temporary pending further work...

(2) on the cygwin/mingw platforms, change the name of the shell wrapper itself. So don't create "foo" at all; instead, create lt-foo.sh (or .libs/foo.sh). But this requires catching ALL of the places were $output or $outputname are set, and fixing them with case $host ; *cgywin* | *mingw* ) ... ;; esac blocks. Blech.

I did #1, as it appeared to require fewer modifications. ($output and $outputname are set in SO many different places...) Plus, I anticipate that, at least on cygwin/mingw, we might eventually completely do away with the shell wrapper by incorporating its functionality within the binary wrapper(given the caveats re: --shell above)...an added incentive to avoid disrupting the rest of ltmain.sh beyond the absolute minimum.

Also, there may be some complaints about using $LTCC to build the binary wrapper, especially in the context of a cross compiler. Here's a block of comments from the patch:

# we should really use a build-platform specific compiler
# here, but OTOH, the wrappers (shell script and this C one)
# are only useful if you want to execute the "real" binary.
# Since the "real" binary is built for $host, then this
# wrapper might as well be built for $host, too.
$run $LTCC -s -o $cwrapper $cwrappersource

--------------------------------------

cgywin: if you've already installed my test versions of automake and libtool (automake-devel-1.7.2-2 and libtool-devel-20030103-2), you must revert to the "REAL" cygwin automake-devel-1.7.2-1. Then, update to libtool-devel-20030103-3 which is now available by pointing setup.exe at
http://www.neuro.gatech.edu/users/cwilson/cygutils/testing/

mingw: test reports?

automake: please ignore the previously posted patch

libtool: Hopefully this gives a starting point for further refinement. But IMO it is okay (e.g. functional) for inclusion into CVS as-is, provided that it causes no problems on non-windows, and fixes the relink-exe problem on mingw. It DOES fix the relink-exe problem on cygwin.

---------------------------------------

--Chuck
2003-01-11  Charles Wilson  <cwilson@ece.gatech.edu>

	* ltmain.in: add code for a binary wrapper
	to use with uninstalled executables on cygwin/mingw.
	To work around a feature of the cygwin/MSYS shell,
	temporarily create a copy of the shell wrapper under
	a different name before sourcing it (otherwise,
	the binary wrapper gets sourced, instead...)
Index: ltmain.in
===================================================================
RCS file: /cvsroot/libtool/libtool/ltmain.in,v
retrieving revision 1.318
diff -u -u -r1.318 ltmain.in
--- ltmain.in	1 Jan 2003 01:57:47 -0000	1.318
+++ ltmain.in	11 Jan 2003 20:00:37 -0000
@@ -4284,6 +4284,201 @@
 	    outputname=`echo $outputname|${SED} 's,.exe$,,'` ;;
 	  *) exeext= ;;
 	esac
+	case $host in
+	  *cygwin* | *mingw* )
+	    cwrappersource=`echo lt-$output|${SED} 's,$,.c,'`
+	    cwrapper=`echo $output|${SED} 's,$,.exe,'`
+	    $rm $cwrappersource $cwrapper
+	    trap "$fm $cwrappersource $cwrapper; exit 1" 1 2 15
+
+	    cat > $cwrappersource <<EOF
+
+/* $cwrappersource - temporary wrapper executable for $objdir/$outputname
+   Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP
+
+   The $output program cannot be directly executed until all the libtool
+   libraries that it depends on are installed.
+   
+   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",
+   but could eventually absorb all of the scripts functionality and
+   exec $objdir/$outputname directly.
+*/
+EOF
+	    cat >> $cwrappersource<<"EOF"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <malloc.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#if defined(PATH_MAX)
+# define LT_PATHMAX PATH_MAX
+#elif defined(MAXPATHLEN)
+# define LT_PATHMAX MAXPATHLEN
+#else
+# define LT_PATHMAX 1024
+#endif
+
+#ifndef DIR_SEPARATOR
+#define DIR_SEPARATOR '/'
+#endif
+
+#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \
+  defined (__OS2__)
+#define HAVE_DOS_BASED_FILE_SYSTEM
+#ifndef DIR_SEPARATOR_2 
+#define DIR_SEPARATOR_2 '\\'
+#endif
+#endif
+
+#ifndef DIR_SEPARATOR_2
+# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR)
+#else /* DIR_SEPARATOR_2 */
+# define IS_DIR_SEPARATOR(ch) \
+        (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2))
+#endif /* DIR_SEPARATOR_2 */
+
+#define XMALLOC(type, num)      ((type *) xmalloc ((num) * sizeof(type)))
+#define XFREE(stale) do { \
+  if (stale) { free ((void *) stale); stale = 0; } \
+} while (0)
+
+const char *program_name = NULL;
+
+void * xmalloc (size_t num);
+char * xstrdup (const char *string);
+char * basename (const char *name);
+char * fnqualify(const char *path);
+char * strendzap(char *str, const char *pat);
+void lt_fatal (const char *message, ...);
+
+int
+main (int argc, char *argv[])
+{
+  char *full;
+
+  program_name = (char *) xstrdup ((char *) basename (argv[0]));
+  full = fnqualify(argv[0]);
+  /* we know the script has the same name, without the .exe */
+  /* so make sure full doesn't end in .exe */
+  strendzap(full,".exe"); 
+  execl("/bin/sh","/bin/sh",full,NULL);
+}
+
+void *
+xmalloc (size_t num)
+{
+  void * p = (void *) malloc (num);
+  if (!p)
+    lt_fatal ("Memory exhausted");
+
+  return p;
+}
+
+char * 
+xstrdup (const char *string)
+{
+  return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL
+;
+}
+
+char *
+basename (const char *name)
+{
+  const char *base;
+
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  /* Skip over the disk name in MSDOS pathnames. */
+  if (isalpha (name[0]) && name[1] == ':') 
+    name += 2;
+#endif
+
+  for (base = name; *name; name++)
+    if (IS_DIR_SEPARATOR (*name))
+      base = name + 1;
+  return (char *) base;
+}
+
+char * 
+fnqualify(const char *path)
+{
+  size_t size;
+  char *p;
+  char tmp[LT_PATHMAX + 1];
+
+  assert(path != NULL);
+
+  /* Is it qualified already? */
+#if defined (HAVE_DOS_BASED_FILE_SYSTEM)
+  if (isalpha (path[0]) && path[1] == ':')
+    return xstrdup (path);
+#endif
+  if (IS_DIR_SEPARATOR (path[0]))
+    return xstrdup (path);
+
+  /* prepend the current directory */
+  /* doesn't handle '~' */
+  if (getcwd (tmp, LT_PATHMAX) == NULL)
+    lt_fatal ("getcwd failed");
+  size = strlen(tmp) + 1 + strlen(path) + 1; /* +2 for '/' and '\0' */
+  p = XMALLOC(char, size);
+  sprintf(p, "%s%c%s", tmp, DIR_SEPARATOR, path);
+  return p;
+}
+
+char *
+strendzap(char *str, const char *pat) 
+{
+  size_t len, patlen;
+
+  assert(str != NULL);
+  assert(pat != NULL);
+
+  len = strlen(str);
+  patlen = strlen(pat);
+
+  if (patlen <= len)
+  {
+    str += len - patlen;
+    if (strcmp(str, pat) == 0)
+      *str = '\0';
+  }
+  return str;
+}
+
+static void
+lt_error_core (int exit_status, const char * mode, 
+          const char * message, va_list ap)
+{
+  fprintf (stderr, "%s: %s: ", program_name, mode);
+  vfprintf (stderr, message, ap);
+  fprintf (stderr, ".\n");
+
+  if (exit_status >= 0)
+    exit (exit_status);
+}
+
+void
+lt_fatal (const char *message, ...)
+{
+  va_list ap;
+  va_start (ap, message);
+  lt_error_core (EXIT_FAILURE, "FATAL", message, ap);
+  va_end (ap);
+}
+EOF
+	  # we should really use a build-platform specific compiler
+	  # here, but OTOH, the wrappers (shell script and this C one)
+	  # are only useful if you want to execute the "real" binary.
+	  # Since the "real" binary is built for $host, then this
+	  # wrapper might as well be built for $host, too.
+	  $run $LTCC -s -o $cwrapper $cwrappersource
+	  ;;
+	esac
 	$rm $output
 	trap "$rm $output; exit 1" 1 2 15
 
@@ -5066,8 +5261,12 @@
 
 	  # If there is no directory component, then add one.
 	  case $file in
-	  */* | *\\*) . $wrapper ;;
-	  *) . ./$wrapper ;;
+	  */* | *\\*) cp ${wrapper} ${wrapper}-lt.sh 
+	    . ${wrapper}-lt.sh
+	    $rm -f ${wrapper}-lt.sh ;;
+	  *) cp ./${wrapper} ./${wrapper}-lt.sh 
+	    . ./${wrapper}-lt.sh
+	    $rm -f ${wrapper}-lt.sh ;;
 	  esac
 
 	  # Check the variables that should have been set.
@@ -5097,8 +5296,12 @@
 	  relink_command=
 	  # If there is no directory component, then add one.
 	  case $file in
-	  */* | *\\*) . $file ;;
-	  *) . ./$file ;;
+	  */* | *\\*) cp ${wrapper} ${wrapper}-lt.sh 
+	    . ${wrapper}-lt.sh
+	    $rm -f ${wrapper}-lt.sh ;;
+	  *) cp ./${wrapper} ./${wrapper}-lt.sh 
+	    . ./${wrapper}-lt.sh
+	    $rm -f ${wrapper}-lt.sh ;;
 	  esac
 
 	  outputname=

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]