Skip to content
This repository has been archived by the owner on Jul 4, 2023. It is now read-only.

"brew upgrade" does not check dynamic library links #12190

Closed
MattiHameister opened this issue May 10, 2012 · 39 comments
Closed

"brew upgrade" does not check dynamic library links #12190

MattiHameister opened this issue May 10, 2012 · 39 comments

Comments

@MattiHameister
Copy link

brew -v
0.9

When homebrew upgrade updated formulars it does not check if another program is linked against the old version of this formular.

For example, I installed imagemagick a while ago and last week "brew upgrade" updated libtiff to version 4. But ImageMagick is still linked against libtiff 3 which was deleted (by cleanup) or unlinked (by upgrade).

otool -L /usr/local/bin/convert 
/usr/local/bin/convert:
...
    /usr/local/lib/libtiff.3.dylib (compatibility version 13.0.0, current version 13.5.0)
...

So ImageMagick does not work anymore:

convert
dyld: Library not loaded: /usr/local/lib/libtiff.3.dylib
  Referenced from: /usr/local/bin/convert
  Reason: image not found
Trace/BPT trap: 5

This is a general problem. I had a similar problem with ffmpeg and x264. So brew has to check for broken library links after the upgrade. The only solution is to remove and install the program again to link against the new version.

@matteoipri
Copy link
Contributor

I think this is a bug, too. It is annoying because most of the times I get this issue is with bigger programs that take much more to compile, like octave, for example.
Maybe homebrew should print a list of reverse dependencies for every upgraded formula, so the user is aware that it could be needed to uninstaal-install some other things. Or maybe it should automatically recompile every reverse dependency.

@jacknagel
Copy link
Contributor

Note that this is usually only a problem when libraries cross a major version boundary; otherwise the dylib names remain the same and upgrades can be done underneath other software.

We go to some lengths to make sure we don't have to recompile all reverse dependencies every time a formula is updated, so my suggestion would be that someone write an external command that walks the installed kegs, looking for broken linkages, and then prints a list of formula that need to be recompiled. Note that broken linkage may also indicate that a dependency is just plain missing, so that would need to be treated separately. Once this command is working well the functionality could potentially be ported into brew upgrade.

@MikeMcQuaid
Copy link
Member

@jacknagel We can/should really fix this with install_name_tool if possible I think.

@jacknagel
Copy link
Contributor

install_name_tool hackery is good for making things link via HOMEBREW_PREFIX rather than into the Cellar, but I think that if the actual dylib name changes, i.e. the major library version number is increased, the potential for API incompatibilities is pretty high and we should be looking at recompilation.

@petrushy
Copy link

This is a bit confusing yes, for example: just updated qt but this is not visible within python through pyqt, which still points at previous version.

@jacknagel
Copy link
Contributor

This is a bit confusing yes, for example: just updated qt but this is not visible within python through pyqt, which still points at previous version.

Actually this is not directly related to brew upgrade; we already do some rewriting of install names so things link via /usr/local/lib rather than /usr/local/Cellar/<foo>/<version>/lib. pyqt shouldn't be breaking because of a qt upgrade.

I can see this:

$ otool -L /usr/local/Cellar/pyqt/4.9.1/lib/python2.7/site-packages/PyQt4/QtCore.so
/usr/local/Cellar/pyqt/4.9.1/lib/python2.7/site-packages/PyQt4/QtCore.so:
    /usr/local/Cellar/qt/4.8.1/lib/QtCore.framework/Versions/4/QtCore (compatibility version 4.8.0, current version 4.8.1)
    /usr/lib/libstdc++.6.dylib (compatibility version 7.0.0, current version 7.9.0)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 125.2.11)

Looking at the keg_fix_install_names code, it looks like we only fix files with the .dylib extension, and we don't recurse into subdirectories of lib. So there is room for improvement.

@hmitsch
Copy link

hmitsch commented Aug 2, 2012

@jacknagel thanks for referencing the libmpc issue. You are right this is the correct place to track this issue.

@samueljohn
Copy link
Contributor

Does ist help if we use opt_prefix?

@akerl
Copy link
Contributor

akerl commented Jan 28, 2013

Is there a temporary fix for this while we await a built-in fix? I'm trying to regain the ability to use the SQLite module in python.

@jacknagel
Copy link
Contributor

You'll have to rebuild python against the updated sqlite.

@jacknagel
Copy link
Contributor

For people who come across this issue, note that the problem being discussed here is not related to directory names changing; that has been addressed by linking keg-only things via opt_prefix. The only time this should bite people anymore is when non-keg-only things are made keg-only, which is what happened to sqlite recently.

The issue here is when the actual names of the dynamic libraries change (e.g. libfoo.1.dylib to libfoo.2.dylib). This is a much more difficult problem to solve.

@akerl
Copy link
Contributor

akerl commented Jan 28, 2013

Thanks; brew remove python ; brew install python worked like a charm. For future reference, is that the smoothest way to rebuild a package? I couldn't find 'rebuild' in the brew manpage.

@MikeMcQuaid
Copy link
Member

When the dynamic libraries change we need to relink really as that normally indicates an API (or at least ABI) change.

@rburhum
Copy link
Contributor

rburhum commented Feb 6, 2013

I just ran into #17312 which brought me here. 17 of my virtualenvs do not work anymore :(
Perhaps a command like the one described in in this ticket that walks the dependency and allows to rebuild the projects would be really nice. Nevertheless, for some projects I build with extra flags to enable extra features. Do those get saved anywhere? (e.g I build gdal with "--complete").

@samueljohn
Copy link
Contributor

The options you used to brew software is stored in a a json file in each Cellar.

You can use brew info for that

brew info python
python: stable 2.7.3
http://www.python.org
Depends on: pkg-config, readline, sqlite, gdbm
/homebrew/Cellar/python/2.7.3 (5169 files, 79M) *
  Installed with: --with-brewed-tk

[...]

Is that what you look for?

Sorry for breaking your venvs. Won't happen too often. Making sqlite keg_only was the right decision, though.
Perhaps you can just replace the _sqlite.so in your venvs site-package dir?

@rburhum
Copy link
Contributor

rburhum commented Feb 12, 2013

Hi @samueljohn. Sure, I can write a script to take care of the rest. No worries about the virtualenvs. Already recreated them :)

@mitchellrj
Copy link

This also seems to break builds of versions/python32. Are there any recipe changes required to get this working again? I don't see any pulls or commits referencing this ticket.

Removing & reinstalling both sqlite & python32 fail to solve the problem. The only way I could work around it was by adding the SQLite libs to my DYLD_LIBRARY_PATH...

$ brew install python32
...
Could not find platform dependent libraries <exec_prefix>
Consider setting $PYTHONHOME to <prefix>[:<exec_prefix>]
...
brew: superenv removed: -L/usr/local/lib -L/usr/local/lib
*** WARNING: renaming "_sqlite3" since importing it failed: dlopen(build/lib.macosx-10.8-x86_64-3.2/_sqlite3.so, 2): Symbol not found: _sqlite3_enable_load_extension
  Referenced from: build/lib.macosx-10.8-x86_64-3.2/_sqlite3.so
  Expected in: flat namespace
 in build/lib.macosx-10.8-x86_64-3.2/_sqlite3.so
...
Failed to build these modules:
_sqlite3  

@rburhum
Copy link
Contributor

rburhum commented Mar 5, 2013

@mitchellrj try removing the DYLD_LIBRARY_PATH sqlite entry and instead do a brew link sqlite Does it that make it work?

@mitchellrj
Copy link

@rburhum That works but as stated above, sqlite should be keg only and there are good reasons for keeping it that way.

@rburhum
Copy link
Contributor

rburhum commented Mar 5, 2013

Just wanted to make sure you have exactly the same issue I have :)

@samueljohn
Copy link
Contributor

We probably need to apply the fixes from python3.rb to the 3.2 version, too.

@claudiodsf
Copy link
Contributor

Just to add another element to this discussion, I just brew update'd hdf5 to the latest version (1.8.10-patch1), which, per se, didn't create any problem on netcdf (which is dynamically linked to hdf5).

However, when you run a gmt command which uses netcdf, you get this nice runtime warning:

Warning! ***HDF5 library version mismatched error***
The HDF5 header files used to compile this application do not match
the version used by the HDF5 library to which this application is linked.
Data corruption or segmentation faults may occur if the application continues.
This can happen when an application was compiled by one version of HDF5 but
linked with a different version of static or shared HDF5 library.
You should recompile the application or check your shared library related
settings such as 'LD_LIBRARY_PATH'.
You can, at your own risk, disable this warning by setting the environment
variable 'HDF5_DISABLE_VERSION_CHECK' to a value of '1'.
Setting it to 2 or higher will suppress the warning messages totally.
Headers are 1.8.9, library is 1.8.10
        SUMMARY OF THE HDF5 CONFIGURATION
        =================================

General Information:
-------------------
           HDF5 Version: 1.8.10-patch1
          Configured on: Thu Mar  7 11:07:30 CET 2013
          Configured by: claudio@dhcp-10-1-5-151.ipgp.fr
         Configure mode: production
            Host system: i386-apple-darwin12.2.0
          Uname information: Darwin dhcp-10-1-5-151.ipgp.fr 12.2.0 Darwin Kernel Version 12.2.0: Sat Aug 25 00:48:52 PDT 2012; root:xnu-2050.18.24~1/RELEASE_X86_64 x86_64
               Byte sex: little-endian
              Libraries:
         Installation point: /usr/local/Cellar/hdf5/1.8.10-patch1

Compiling Options:
------------------
               Compilation Mode: production
                     C Compiler: /usr/local/Library/ENV/4.3/cc
                         CFLAGS:
                      H5_CFLAGS:
                      AM_CFLAGS:
                       CPPFLAGS:
                    H5_CPPFLAGS:   -DNDEBUG -UH5_DEBUG_API
                    AM_CPPFLAGS: -I/usr/local/include
               Shared C Library: yes
               Static C Library: yes
  Statically Linked Executables: no
                        LDFLAGS:
                     H5_LDFLAGS:
                     AM_LDFLAGS:  -L/usr/local/lib
        Extra libraries:  -lsz -lz -lm
               Archiver: ar
             Ranlib: ranlib
          Debugged Packages:
            API Tracing: no

Languages:
----------
                        Fortran: no

                            C++: no

Features:
---------
                  Parallel HDF5: no
             High Level library: yes
                   Threadsafety: no
            Default API Mapping: v18
 With Deprecated Public Symbols: yes
         I/O filters (external): deflate(zlib),szip(encoder)
         I/O filters (internal): shuffle,fletcher32,nbit,scaleoffset
                            MPE: no
                     Direct VFD: no
                        dmalloc: no
Clear file buffers before write: yes
           Using memory checker: no
         Function Stack Tracing: no
                           GPFS: no
      Strict File Format Checks: no
   Optimization Instrumentation: no
       Large File Support (LFS): yes
Bye...

Edit: Of course, recompiling netcdf fixes the problem.

@manphiz
Copy link
Contributor

manphiz commented Apr 23, 2013

I think this is a problem that needs to fix. Took a look at how other parties dealt with this situation.

Macports support port version, so they manually bump the ports that got affected and forces user to update, which basically works as a rebuild. There's also some mechanisms to trigger port with broken links to rebuild, at least when a binary package (as bottles in homebrew) is affected when its dependencies has upgraded.

Gentoo's portage system also does this kind of checking for broken links, and will trigger rebuild for reverse-dependencies that are affected.

I guess the mechanism is not hard, just need to check its reverse-dependencies. And also there is brew reinstall already.

@jacknagel
Copy link
Contributor

The more I have thought about it the more I am convinced that we need to go the "formula revision" route rather than trying to check dynamic library links. I the the latter approach is too error-prone (we are bound to miss some things here and there) and will also be horrendously slow, as the lack of a central database means we have to walk the lib and bin directories of every reverse dependency.

@MikeMcQuaid
Copy link
Member

@jacknagel A silly, brew2 level approach I might try would be to have each formula keep all it's dependencies inside it's Cellar.

@jacknagel
Copy link
Contributor

So here's a partial implementation of formula revisions:

https://github.com/jacknagel/homebrew/compare/rev

The presentation-layer things work, and incrementing the revision number of a formula will trigger upgrades, etc. However the main problem is that we cannot upgrade a package "in place", because the prefix contains just the version and not the revision. I'm not quite sure how to address that yet.

@MikeMcQuaid
Copy link
Member

Am I being stupid here or does this only become a problem if you brew cleanup existing linked libraries?

@jacknagel
Copy link
Contributor

With the advent of opt links, any upgrade that changes library versions will break things that depend on that library, since only one thing can occupy the opt symlink at a time.

@MikeMcQuaid
Copy link
Member

Arr, yes. Seems like a good fix then.

@jacknagel
Copy link
Contributor

Made some tangible progress:

https://github.com/jacknagel/homebrew/compare/rev

The revision is now encoded in the name of the keg as _#{revision}, so e.g. /usr/local/Cellar/foo/1.0_1/, except when revision == 0, in which case the trailing _#{revision} is omitted, to retain compatibility.

I chose to do it this way because it is much simpler than trying to temporarily move a keg while a new one with the same version is built, then either removing it or moving it back depending on the outcome of the build. It also keeps all version comparison code working as-is.

It does mean that there can be false positives for version numbers that match /_(\d+)$/, so we might have to (a) use a character other than _, (b) forbid version numbers that end in "_number", or (c) use _rev1 or something similar.

@jacknagel
Copy link
Contributor

A prime example of what this fixes is the recent issues with pango; in that case, icu4c was updated and the library version changed, breaking things linked against it, including harfbuzz, which in turn broke updates to pango.

@mbostock
Copy link

Is this feature still being worked on? It seems like sqlite gets updated frequently, and so whenever I brew update && brew upgrade, it breaks my GDAL installation. For example:

$ ogr2ogr -f GeoJSON …
dyld: Library not loaded: /usr/local/opt/sqlite/lib/libsqlite3.0.8.6.dylib
  Referenced from: /usr/local/lib/libspatialite.5.dylib
  Reason: image not found
Trace/BPT trap: 5

I can of course fix it by reinstalling:

brew reinstall `brew uses --installed sqlite`

But, it’s still a pain and it would be nice if the necessary changes happened automatically.

@macdhuibh
Copy link

My GDAL installation just broke for the same reason:

OSError: dlopen(/usr/local/lib/libgdal.dylib, 6): Library not loaded: /usr/local/opt/sqlite/lib/libsqlite3.0.8.6.dylib
Referenced from: /usr/local/lib/libspatialite.5.dylib
Reason: image not found`

Thanks @mbostock, that little brew command just saved me from a headache.

@jacknagel
Copy link
Contributor

#19920 has been merged.

@truebit
Copy link

truebit commented Apr 9, 2014

nginx also get this problem. After I updated openssl due to the heartbleed bug, I brew uninstall nginx and then brew install nginx, nginx is still using the old openssl( a.k.a 1.0.1f)

brew install nginx did not trigger recompile actions like "configure", "make" and "make install":

==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/nginx-1.4.7
Already downloaded: /Library/Caches/Homebrew/nginx-1.4.7.mountain_lion.bottle.tar.gz
==> Pouring nginx-1.4.7.mountain_lion.bottle.tar.gz
==> Caveats
Docroot is: /usr/local/var/www

The default port has been set in /usr/local/etc/nginx/nginx.conf to 8080 so that
nginx can run without sudo.

To have launchd start nginx at login:
ln -sfv /usr/local/opt/nginx/*.plist ~/Library/LaunchAgents
Then to load nginx now:
launchctl load ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist
Or, if you don't want/need launchctl, you can just run:
nginx
==> Summary
🍺 /usr/local/Cellar/nginx/1.4.7: 7 files, 936K

brew info nginx

nginx version: nginx/1.4.7
TLS SNI support enabled
configure arguments: --prefix=/usr/local/Cellar/nginx/1.4.7 --with-http_ssl_module --with-pcre --with-ipv6 --sbin-path=/usr/local/Cellar/nginx/1.4.7/bin/nginx --with-cc-opt='-I/usr/local/Cellar/pcre/8.34/include -I/usr/local/Cellar/openssl/1.0.1f/include' --with-ld-opt='-L/usr/local/Cellar/pcre/8.34/lib -L/usr/local/Cellar/openssl/1.0.1f/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --pid-path=/usr/local/var/run/nginx.pid --lock-path=/usr/local/var/run/nginx.lock --http-client-body-temp-path=/usr/local/var/run/nginx/client_body_temp --http-proxy-temp-path=/usr/local/var/run/nginx/proxy_temp --http-fastcgi-temp-path=/usr/local/var/run/nginx/fastcgi_temp --http-uwsgi-temp-path=/usr/local/var/run/nginx/uwsgi_temp --http-scgi-temp-path=/usr/local/var/run/nginx/scgi_temp --http-log-path=/usr/local/var/log/nginx/access.log --error-log-path=/usr/local/var/log/nginx/error.log --with-http_gzip_static_module

@MikeMcQuaid
Copy link
Member

@truebit nginx installed from a binary package in that case. We'll need to build a new one.

@truebit
Copy link

truebit commented Apr 9, 2014

@MikeMcQuaid Thanks for your reply. So it's because nginx-1.4.7.mountain_lion.bottle.tar.gz itself as a Bottle: https://github.com/Homebrew/homebrew/wiki/Bottles
I have found how to build from source from link above.
use command below:
brew install nginx --build-from-source

@MikeMcQuaid
Copy link
Member

@truebit The binaries have been updated; brew update; brew upgrade will link against the newer OpenSSL.

@jacknagel
Copy link
Contributor

For future reference, nginx -V is not an accurate way of determining what libraries nginx is linked to. The correct way to determine what libraries nginx is linked to is to look at otool -L $(which nginx).

$ otool -L `which nginx`
/usr/local/bin/nginx:
    /usr/local/lib/libpcre.1.dylib (compatibility version 4.0.0, current version 4.2.0)
    /usr/local/opt/openssl/lib/libssl.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib (compatibility version 1.0.0, current version 1.0.0)
    /usr/lib/libz.1.dylib (compatibility version 1.0.0, current version 1.2.5)
    /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1197.1.1)

This shows that the library is linked dynamically, which means it is not necessary to recompile nginx, only openssl.

@Homebrew Homebrew locked and limited conversation to collaborators Feb 16, 2016
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests