Mercurial > hg > mxe-octave-anirudha
changeset 3213:a4692f0e6932
[Darwin] M-files for fixing "dyld: Library not loaded" Errors
author | Anirudha Bose <ani07nov@gmail.com> |
---|---|
date | Sat, 21 Sep 2013 15:10:25 +0530 |
parents | 5fc65ca6f7c9 |
children | 0e32c67b78ca |
files | darwin_files/dylibs_find.m darwin_files/dylibs_fix.m darwin_files/dylibs_get_deps.m darwin_files/dylibs_isdylib.m |
diffstat | 4 files changed, 512 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/darwin_files/dylibs_find.m @@ -0,0 +1,110 @@ +## Copyright (C) 2012 Ben Abbott +## +## 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} dylibs = dylibs_find (@var{directory}) +## +## Recursively finds all dylibs in @var{directory}, and returns a structure +## with fields @code{name}, @code{location}, and @code{dependents}. The +## @var{directory} defaults to the present working directory. +## +## @table +## @item @code{name} +## Is the name of a dynamic library. +## @item @code{location} +## Is the path to the named dynamic library. +## @item @code{dependents} +## Is a cellstr array listing the dynamic libraries the named library +## depends upon. +## @item @code{islink} +## Logical scalar indicating whether the file is a symbolic link to a +## dynamically loadable library. +## @end table +## +## @seealso {dylibs_fix, dylibs_get_deps, dylibs_isdylib} +## @end deftypefn + +## Author: Ben Abbott <bpabbott@mac.com> +## Created: 2012-06-27 + +function dylibs = dylibs_find (directory = ".") + + persistent root_dir={"/opt/local/lib/", "@executable_path"} + + remove_symbolic_links = false; + + cwd = pwd (); + + unwind_protect + cd (directory); + f = dir (); + ## Remove ".", "..", and ".hidden_files" + f = f(! strncmp ({f.name}, ".", 1)); + ## Separate directories + d = [f.isdir]; + dirs = {f(d).name}; + dirs = dirs (! strcmpi (dirs, "share")); + f(d) = []; + str = sprintf ("Working in directory -> ""%s"" ...", directory); + nstr = numel (str); + printf ("%s", str); + ## Find dylibs + if (numel (f)) + names = {f.name}; + [status, output] = system ("find . -maxdepth 1 -type l -print"); + symlinks = strtrim (strrep (strsplit (output, char (10)), "./", "")); + symlinks(cellfun (@isempty, symlinks)) = []; + ## Remove symbolic links + if (remove_symbolic_links) + names = setdiff (names, symlinks); + endif + names = names(dylibs_isdylib(names)); + if (numel (names)) + deps = dylibs_get_deps (names, {"@", root_dir{:}}); + if (numel (names) == 1) + deps = {deps}; + endif + islink = ismember (names, symlinks); + dylibs = cell2struct ([names; repmat({pwd()}, size (names)); deps; mat2cell(islink,1,ones(size(islink)))], + {"name", "location", "depdendents", "islink"}); + str = sprintf (" %d dynamic libraries.", numel (dylibs)); + else + dylibs = struct ("name", "", "location", "", ... + "dependents", {}, "islink", logical([])); + str = " NO dynamic libraries."; + endif + else + dylibs = struct ("name", "", "location", ... + "", "dependents", {}, "islink", logical([])); + str = " NO dynamic libraries."; + endif + printf ("%s %s\n", repmat (".", [1, (80 - nstr - numel (str))]), str); + for n = 1:numel(dirs) + new_dylibs = dylibs_find (dirs{n}); + if (numel (new_dylibs)) + if (numel (dylibs)) + dylibs = [dylibs; new_dylibs]; + else + dylibs = new_dylibs; + endif + endif + endfor + + unwind_protect_cleanup + cd (cwd); + end_unwind_protect + +endfunction
new file mode 100644 --- /dev/null +++ b/darwin_files/dylibs_fix.m @@ -0,0 +1,284 @@ +## Copyright (C) 2012 Ben Abbott +## +## 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} fix_dylibs (@var{exebin}, @var{libdir}, @var{dryrun}) +## Make the executable and dynamic libraries relocatable. The inputs are; +## +## @table +## @item @var{exebin} +## The full file name of the executable binary for the App bundle. +## The default is @code{bin/Octave-3.7.0+}; +## @item @var{libdir} +## The full path to the directory containing the App bundle's dynamic +## libraries. The default is @code{lib}. +## @item @var{dryrun} +## If @var{true}, the @code{install_name_tool} commands are printed to +## the command line and are not executed (i.e. the install names and paths +## to the dependent libraries are not changed). The default is @var{true}. +## @end table +## +## Using @code{install_name_tool} the portion of the built in +## dynamic library paths external to the App bundles are replaced with +## the token @code{@@executable_path}. +## Ths will modify the paths in both the @var{exebin} and the the dynamic +## libraries and allow the application to be relocated with no risk +## that the binary executable and libraries are unable to locate the +## dynamic libraries they depend upon. +## +## In addition to correcting the location information for the dependent's +## of @var{exebin}, the @var{libdir} is searched recursively to locate all +## dynamic libraries present in the App bundle. Both their install names +## and the locations of their dependents will also be fixed. +## +## Using the default, this script expects to find a @code{bin} and +## @code{lib} directory in the current working directory. +## +## @center BACKGROUND INFORMATION BELOW @end center +## +## MacOS X supports the use of tokens to permit executable files and dynamic +## libraries to be relocated to a different path. This is an essential +## feature for MacOS X's App bundles. The three tokens supporte are below; +## +## @table @code +## @item @@executable_path +## This token expands to the path of the executable application that is +## loading the dynamic library. Using this toek requires the location of +## the library remains fixed relative to the binary executable. +## @item @@loader_path +## This token expands to the path of binary loading the dynamic library. +## The loader may be the binary application, a library, or a framework. +## Using this token requires the location of the library remains fixed +## relative to the loader. +## @item @@rpath +## Searches a list of locations for the dynamic library. @rpath is the +## most flexible of these tokens. When set in the install name of a +## dynamic library, that library can be used in any applications, +## frameworks or any other type of project without needing further +## modification. The @var{rpath} may be modified for a binary by using +## the @code{otool} utility. +## +## @example +## otool -add_rpath @var{path_to_binary} @var{name_of_binary} +## @end example +## +## The @var{path_to_binary} may use either the @code{@@executable_path} +## or @code{@@loader_path} tokens. +## +## As it isn't clear how to check the entires of the @code{@@rpath} token. +## Is it possible that dynamic libraries built by package managers +## such as MacPorts or Fink have @code{@@rpath} entries to +## @code{/opt/loca/lib/..} or @code{/sw/lib/...}? As a precaution, this +## token can be avoided. +## @end table +## +## A hypothetical bundle, @var{my_exe.app}, will be used for demonstration. +## In this example, @var{my_exe} depends upon @var{libfoo.dyld} and +## @var{libbar.dyld}, and @var{libfoo.dyld} also depends upon @var{libbar.dyld}. +## +## @example +## ex.app/Contents/Resources/bin/@var{my_exe} +## ex.app/Contents/Resources/lib/@var{libfoo.dyld} +## ex.app/Contents/Resources/lib/@var{libbar.dyld} +## @end example +## +## The @code{@@executable_path} token represents the path to the executable, @var{my_exe}. +## +## @example +## @@executable_path = @code{/Applications/ex.app/Contents/Resources/bin} +## @end example +## +## To properly relocate the dynamic libraries the install name ID for each +## dynamic library must be set correctly, and the binary files which depend +## upon dynamic libraries need to be told where to find them. +## +## For this hypothetical example, and using the @code{@@executable_path} token, +## the commands below will properly relocate the dynamic libraries and fix their +## install name IDs. +## +## @example +## install_name_tool -change /Applications/ex.app/Contents/Resources/lib/@var{libfoo.dyld} \ +## @@executable_path/../lib/@var{libfoo.dyld} \ +## bin/@var{my_exe} +## install_name_tool -change /Applications/ex.app/Contents/Resources/lib/@var{libbar.dyld} \ +## @@executable_path/../lib/@var{libbar.dyld} \ +## bin/@var{my_exe} +## install_name_tool -change /Applications/ex.app/Contents/Resources/lib/@var{libbar.dyld} \ +## @@executable_path/../lib/@var{libbar.dyld} \ +## ../lib/@var{libfoo.dyld} +## install_name_tool -id @@executable_path/../lib/@var{libfoo.dyld} lib/@var{libfoo.dyld} +## install_name_tool -id @@executable_path/../lib/@var{libbar.dyld} lib/@var{libbar.dyld} +## @end example +## +## The @code{otool} utility may be used check the install name of a binary +## application. Replace @var{binary_name} with the actual path and name of +## the binary. +## +## @example +## otool -D @var{binary_name} +## @end example +## +## The @code{otool} utility may be also be used check the locations of its +## dependent libraries. Again, replace @var{binary_name} with the actual path +## and name of the binary. The first entry is the binary's install name. +## +## @example +## otool -L @var{binary_name} +## @end example +## +## @seealso {dylibs__find, dylibs_isdylib, dylibs_get_deps} +## @end deftypefn + +## Author: Ben Abbott <bpabbott@mac.com> +## Created: 2012-06-26 + +## NOTE - It is possible to avoid a recursive search down the directory +## tree, but this would require that the bundle's directory tree +## accurately represent the part above the @executable_path. With +## the approach used, the stored dylib locations are not relevant. +## Thus, their paths can be mangled in any way and this script will +## fix it. + +function dylibs_fix (BINARY = "bin/octave-3.7.0+", LIBDIR = "lib", dryrun = true) + if (ischar (BINARY)) + BINARY = {BINARY}; + endif + only_binary = false; + pager_state(1) = page_output_immediately (true); + pager_state(2) = page_screen_output (false); + unwind_protect + dylibs = dylibs_find (LIBDIR); + [~, n] = sort ({dylibs.name}); + dylibs = dylibs (n); + printf ("%d dynamic libraries found.\n", numel (dylibs)); + executable_path = file_location (BINARY{1}); + for b = 1:numel(BINARY) + cmds = fix_deps (executable_path, BINARY{b}, dylibs); + for c = 1:numel(cmds) + printf ("%s", cmds{c}) + [status, output] = system (cmds{c}); + if (! status == 0) + error (output); + endif + endfor + endfor + if (! only_binary) + for d = 1:numel(dylibs) + full_name = strcat (dylibs(d).location, filesep (), dylibs(d).name); + if (! dylibs(d).islink) + ## Don't fix symbolic links + cmd = fix_id (executable_path, full_name); + cmds = fix_deps (executable_path, dylibs(d).name, dylibs); + cmds = [cmd;cmds]; + if (dryrun) + printf ("%s", cmds{:}) + else + for c = 1:numel(cmds) + printf ("%s", cmds{c}) + [status, output] = system (cmds{c}); + if (! status == 0) + error (output); + endif + endfor + endif + endif + endfor + endif + unwind_protect_cleanup + page_output_immediately (pager_state(1)); + page_screen_output (pager_state(2)); + end_unwind_protect +endfunction + +function cmds = fix_deps (executable_path, binary, dylibs) + [~, name, ext] = fileparts (binary); + binary = strcat (name, ext); + m = find (strcmp ({dylibs.name}, binary)); + if (isempty (m)) + ## If not in dylib list, it must be an executable + full_binary = strcat (executable_path, filesep (), binary); + is_link = false; + else + full_binary = strcat (dylibs(m).location, filesep (), binary); + is_link = dylibs(m).islink; + endif + if (! is_link) + [deps, full_deps] = dylibs_get_deps (full_binary); + cmds = cell (numel (deps), 1); + for n = 1:numel(deps) + m = find (strcmp ({dylibs.name}, deps{n}), 1); + if (! isempty (m)) + ## Don't run install_tool_name on symbolic links + full_name = strcat (dylibs(m).location, filesep (), dylibs(m).name); + tokenized_dep = tokenized_path (executable_path, full_name); + cmds{n} = sprintf ("install_name_tool -change '%s' '%s' '%s'\n\n", + full_deps{n}, + tokenized_dep, + full_binary); + else + warning ('dylibs_fix: Missing dynamic library "%s" needed by "%s"', + deps{n}, binary) + endif + endfor + ## Missing dynamic libraries result in empty cmds + cmds(cellfun(@isempty,cmds,"UniforOutput",true)) = []; + else + cmds = {}; + endif +endfunction + +function cmd = fix_id (executable_path, full_binary) + try + if (dylibs_isdylib (full_binary)) + tokenized_binary = tokenized_path (executable_path, full_binary); + cmd = sprintf ("install_name_tool -id '%s' '%s'\n", + tokenized_binary, + full_binary); + endif + catch + save fix_id.mat + rethrow (lasterror ()) + end_try_catch +endfunction + +function tpath = tokenized_path (executable_path, binary) + binary_path = file_location (binary); + [~, name, ext] = fileparts (binary); + name = strcat (name, ext); + binary = strcat (binary_path, filesep (), name); + n = min (numel (binary_path), numel (executable_path)); + n = binary_path(1:n) == executable_path(1:n); + n = find (n == 0, 1, "first") - 1; + shared_path = binary_path(1:n); + extra_dirs = numel (findstr (executable_path(n+1:end), filesep ())); + exe2bin = repmat (strcat ("..", filesep ()), [1, extra_dirs+1]); + tpath = strcat ("@executable_path", filesep (), exe2bin, binary_path(n+1:end), + filesep (), name); +endfunction + +function loc = file_location (file) + path2file = fileparts (file); + cwd = pwd (); + unwind_protect + if (! isempty (path2file)) + cd (path2file) + endif + loc = pwd (); + unwind_protect_cleanup + cd (cwd); + end_unwind_protect +endfunction +
new file mode 100644 --- /dev/null +++ b/darwin_files/dylibs_get_deps.m @@ -0,0 +1,75 @@ +## Copyright (C) 2012 Ben Abbott +## +## 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} dylibs = dylibs_get_deps (@var{name}) +## @deftypefnx {Function File} = dylibs_get_deps (@var{name}, @var{root}) +## +## Extracts the dependent libary names from the named binary, @var{name}. +## If @var{root} is specified, the only libraries returned will have paths +## which begin with one of the cell-strings contained by @var{root}. +## +## The defaults for @var{root} are @code{@{"/opt/local/", "/sw/", "/usr/local/", +## "@@executable_path"@}}. These root paths are intended to match all the +## relocatable libraries which should be included in an App bundle. The first +## and second entries correspond to the MacPorts and Fink installation +## directories, respectively. +## +## @seealso {dylibs_find, dylibs_fix, dylibs_isdylib} +## @end deftypefn + +## Author: Ben Abbott <bpabbott@mac.com> +## Created: 2012-06-27 + +function [deps, full_deps] = dylibs_get_deps (names, root_dir = {"/opt/local/", "/sw/", "/usr/local", "@executable_path"}) + if (ischar (root_dir)) + root_dir = {root_dir}; + endif + strip_back = @(str) str(1:find(str==" ", 1)-1); + strip_path = @(str) str(find(str=="/", 1, "last")+1:end); + if (ischar (names)) + names = {names}; + endif + deps = cell (size (names)); + full_deps = cell (size (names)); + for n = 1:numel(names) + [status, output] = system (sprintf ('otool -L "%s"', names{n})); + if (status) + error (output) + endif + output = strtrim (strsplit (output, char (10)) (1:end)); + is_dependent = @(str) ! isempty (str) && isempty (findstr (str, names{n})); + full_output = output (cellfun (is_dependent, output, "UniformOutput", true)); + if (! isempty (root_dir)) + keep = false (size (full_output)); + for r = 1:numel(root_dir) + if (numel (root_dir{r}) && ischar (root_dir{r})) + keep = keep | strncmp (full_output, root_dir{r}, numel (root_dir{r})); + endif + endfor + full_output = full_output (keep); + endif + full_output = cellfun(strip_back, full_output, "UniformOutput", false); + output = cellfun(strip_path, full_output, "UniformOutput", false); + deps(n) = {output}; + full_deps(n) = {full_output}; + endfor + if (numel (names) == 1) + deps = deps{1}; + full_deps = full_deps{1}; + endif +endfunction +
new file mode 100644 --- /dev/null +++ b/darwin_files/dylibs_isdylib.m @@ -0,0 +1,43 @@ +## Copyright (C) 2012 Ben Abbott +## +## 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 Octave; see the file COPYING. If not, see +## <http://www.gnu.org/licenses/>. + +## -*- texinfo -*- +## @deftypefn {Function File} result = dylibs_isdylib (@var{filename}) +## Determines if the filename qualifies as a dynamically loaded library. +## The @var{result} is @var{true} for dylib-files, oct-files, and mex-files. +## The result is @var{false} for all other files. +## +## This function isn't sophisticated. It only examines the file extension. +## @seealso {dylibs__find, dylibs_fix, dylibs_get_deps} +## @end deftypefn + +## Author: Ben Abbott <bpabbott@Bens-MacBook-Pro.local> +## Created: 2012-06-29 + +function result = dylibs_isdylib (cstr) + persistent flip=@(str)str(end:-1:1) + if (ischar (cstr)) + cstr = {cstr}; + endif + suffix = {".dylib", ".oct", ".mex"}; + result = false (size (cstr)); + for n = 1:numel(suffix) + fun = @(str) strncmp (cellfun (flip, str, "UniformOutput", false), + flip (suffix{n}), numel (suffix{n})); + result = result | fun (cstr); + endfor +endfunction +