Giovanni Bajo wrote:
> On ven, 2009-01-23 at 17:02 +0100, Lorenzo Mancini wrote:
>> Hello guys,
>>
>> from Leopard on, the special name @executable_path cannot be used 
>> anymore in library paths for runtime substitution; two new special names 
>> (@rpath and @library_path) were introduced for similar purposes.
> 
> The link you posted:
> http://www.codeshorts.ca/2007/nov/01/leopard-linking-making-relocatable-libraries-movin
> 
> says that the ld flag "-executable_path" is gone, not the
> @executable_path special symbol within binaries.

True.  I was misled to my original conclusion by erroneously asserting 
that one-file PyInstaller deploys were actually working in Mac OS X < 
10.5; I have since realized that I was just scratching the surface of 
the real problem.

After some experiments, this is my understanding so far about the 
current state of PyInstaller under Mac OS X.

  * PyInstaller is able to produce one-dir deploys by prepending library 
dependency paths the special name @executable_path (see bindepend.py, 
function fixOsxPaths), which will be expanded during runtime to the 
current one-dir workpath.

  * the same trick doesn't work for one-file deploys, because the 
executable first extracts the binary content in a temporary dir (see 
source/linux/main.c), then fork()s itself and the child starts running; 
later, the runtime will expand @executable_path during the execution of 
the child, to the directory of the original executable, *not* the 
temporary dir.  As a consequence, PyInstaller at present is not able to 
produce working one-file deploys under Mac OS X.

  * it's not possible to set an arbitrary path for runtime substitution 
of @executable_path; _NSGetExecutablePath exists for getting that value 
during runtime, but I didn't find a setter.


A possible solution which doesn't use the @executable_path special name 
neither @rpath consists in leaving the dependency path names as they are 
(no more install_name_tool magic), therefore leaving the paths absolute, 
and specifying the temporary dir path in DYLD_LIBRARY_PATH.

See section "The Library Search Process" in document [1], which details 
the searching process for a dynamic library when the pathname contains a 
directory name: it specifies that DYLD_LIBRARY_PATH is the first place 
where the runtime will search for libraries.  Therefore, changing the 
DYLD_LIBRARY_PATH during loader's execution will make sure that the 
temporary dir libraries will be picked up.

The major drawback of this approach is that the libraries in both 
one-dir and one-file deploys will contain absolute paths, so analyzing 
them with otool would erroneously suggest that they rely on system 
libraries.

The proposed patch is attached.  It basically removes the 
install_name_tool magic and inserts the workpath at the beginning of 
DYLD_LIBRARY_PATH if we are running under Mac OS X.  It should work for 
both one-file and one-dir deploys.

[1] 
http://developer.apple.com/documentation/DeveloperTools/Conceptual/DynamicLibraries/DynamicLibraries.pdf

-- 
Lorenzo Mancini

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"PyInstaller" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/PyInstaller?hl=en
-~----------~----~----~----~------~----~------~--~---

Index: bindepend.py
===================================================================
--- bindepend.py        (revision 21247)
+++ bindepend.py        (working copy)
@@ -392,12 +392,6 @@
         _bpath.extend(string.split(os.environ.get('PATH', ''), os.pathsep))
     return _bpath
 
-def fixOsxPaths(moduleName):
-    for name, lib in selectImports(moduleName):
-        dest = os.path.join("@executable_path", name)
-        cmd = "install_name_tool -change %s %s %s" % (lib, dest, moduleName)
-        os.system(cmd)
-
 def findLibrary(name):
     """Look for a library in the system.
 
Index: Build.py
===================================================================
--- Build.py    (revision 21247)
+++ Build.py    (working copy)
@@ -435,7 +435,7 @@
     digest = md5.new(data).digest()
     return digest
 
-def checkCache(fnm, strip, upx, fix_paths=1):
+def checkCache(fnm, strip, upx):
     # On darwin a cache is required anyway to keep the libaries
     # with relative install names
     if not strip and not upx and sys.platform != 'darwin':
@@ -471,7 +471,7 @@
             return cachedfile
     if upx:
         if strip:
-            fnm = checkCache(fnm, 1, 0, fix_paths=0)
+            fnm = checkCache(fnm, 1, 0)
         cmd = "upx --best -q \"%s\"" % cachedfile
     else:
         if strip:
@@ -480,9 +480,6 @@
     os.chmod(cachedfile, 0755)
     if cmd: os.system(cmd)
 
-    if sys.platform == 'darwin' and fix_paths:
-        bindepend.fixOsxPaths(cachedfile)
-
     # update cache index
     cache_index[basenm] = digest
     _save_data(cacheindexfn, cache_index)
Index: source/linux/main.c
===================================================================
--- source/linux/main.c (revision 21247)
+++ source/linux/main.c (working copy)
@@ -36,14 +36,31 @@
     {0, 0, 0}
 };
 #endif
+
+void exportWorkpath(char *workpath, char *envvar_name)
+{
+    char envvar[_MAX_PATH * 4 + 12];
+    char *old_envvar;
+
+    strcpy(envvar, envvar_name);
+    strcat(envvar, "=");
+    strcat(envvar, workpath);
+    envvar[strlen(envvar)-1] = '\0';
+    old_envvar = getenv(envvar_name);
+    if (old_envvar) {
+        strcat(envvar, ":");
+        strcat(envvar, old_envvar);
+    }
+    putenv(envvar);
+    VS("%s\n", envvar);
+}
+
 int main(int argc, char* argv[])
 {
     char thisfile[_MAX_PATH];
     char homepath[_MAX_PATH];
     char magic_envvar[_MAX_PATH + 12];
-    char ldlib_envvar[_MAX_PATH * 4 + 12];
     char archivefile[_MAX_PATH + 5];
-    char *oldldlib;
     TOC *ptoc = NULL;
     int rc = 0;
     int pid;
@@ -109,17 +126,14 @@
             strcpy(magic_envvar, "_MEIPASS2=");
             strcat(magic_envvar, workpath);
             putenv(magic_envvar);
-            /* now LD_LIBRARY_PATH */
-            strcpy(ldlib_envvar, "LD_LIBRARY_PATH=");
-            strcat(ldlib_envvar, workpath);
-            ldlib_envvar[strlen(ldlib_envvar)-1] = '\0';
-            oldldlib = getenv("LD_LIBRARY_PATH");
-            if (oldldlib) {
-                strcat(ldlib_envvar, ":");
-                strcat(ldlib_envvar, oldldlib);
-            }
-            putenv(ldlib_envvar);
-            VS("%s\n", ldlib_envvar);
+
+            /* add workpath to LD_LIBRARY_PATH */
+            exportWorkpath(workpath, "LD_LIBRARY_PATH");
+#ifdef __APPLE__
+            /* add workpath to DYLD_LIBRARY_PATH */
+            exportWorkpath(workpath, "DYLD_LIBRARY_PATH");
+#endif
+
             pid = fork();
             if (pid == 0)
                 execvp(thisfile, argv);

Reply via email to