1+ import os
2+
13from . import tool
24from .. import file_types , options as opts
35from .common import SimpleCommand
4- from ..iterutils import flatten , listify
6+ from ..iterutils import flatten
7+ from ..path import BasePath , Path , Root , InstallRoot
58
69
710@tool ('install_name_tool' )
@@ -14,37 +17,100 @@ def __init__(self, env):
1417 default = 'install_name_tool'
1518 )
1619
17- def _call (self , cmd , file , * , id = None , changes = []):
20+ def _call (self , cmd , file , * , id = None , changes = [], rpaths = [] ):
1821 args = []
1922 if id :
2023 args += ['-id' , id ]
21- args += flatten (['-change' ] + listify (i ) for i in changes )
24+ args += flatten (['-change' , old , new ] for old , new in changes )
25+ for old , new in rpaths :
26+ if old and new :
27+ args += ['-rpath' , old , new ]
28+ elif new :
29+ args += ['-add_rpath' , new ]
30+ elif old :
31+ args += ['-delete_rpath' , old ]
2232
2333 if args :
2434 return cmd + args + [file ]
2535
2636
27- def darwin_install_name (library , env , strict = True ):
28- while isinstance (library , file_types .LinkLibrary ):
29- library = library .library
37+ def install_name (env , library , * , strict = True ):
38+ if library .runtime_file :
39+ path = library .runtime_file .path
40+ if path .root in InstallRoot :
41+ return path .cross (env )
42+ else :
43+ return os .path .join ('@rpath' , path .suffix )
44+ elif not strict :
45+ return None
46+
47+ raise TypeError ('unable to create install_name' ) # pragma: no cover
48+
49+
50+ def local_rpath (env , library , output ):
51+ if not library .runtime_file :
52+ return None
53+
54+ rpath = library .runtime_file .path .parent ().cross (env )
55+ if rpath .root != Root .absolute and rpath .root not in InstallRoot :
56+ if not output :
57+ raise ValueError ('unable to construct runtime search path' )
58+ # This should almost always be true, except for when linking to a
59+ # shared library stored in the srcdir.
60+ if rpath .root == output .path .root :
61+ rpath = Path ('.' , rpath .root ).relpath (output .path .parent (),
62+ prefix = '@loader_path' )
63+ return rpath
64+
3065
31- if isinstance (library , file_types .VersionedSharedLibrary ):
32- return library .soname .path .string (env .base_dirs )
33- elif isinstance (library , file_types .SharedLibrary ):
34- return library .path .string (env .base_dirs )
35- elif strict : # pragma: no cover
36- raise TypeError ('unable to create darwin install_name' )
37- else :
66+ def installed_rpath (env , library , install_db ):
67+ if not library .runtime_file :
3868 return None
69+ try :
70+ return install_db .target [library .runtime_file ].path .parent ()
71+ except KeyError :
72+ if library .runtime_file .path .root == Root .absolute :
73+ return library .runtime_file .path .parent ()
74+ raise
3975
4076
4177def post_install (env , options , output , install_db , * , is_library = False ):
78+ new_id = None
79+ if is_library :
80+ installed_id = install_name (env , install_db .host [output ])
81+ if installed_id != install_name (env , output ):
82+ new_id = installed_id
83+
4284 change_opts = options .filter (opts .install_name_change )
4385 changes = ([(i .old , i .new ) for i in change_opts ] +
44- [(darwin_install_name ( i , env ) , install_db .target [i ]. path )
86+ [(install_name ( env , i ), install_name ( env , install_db .target [i ]) )
4587 for i in output .runtime_deps ])
4688
89+ # Compute all the changed rpaths, preserving the original order. To do
90+ # this, add all the entries into a dict with the key being the old rpath;
91+ # for new entries, set the key to a sentinel value for uniqueness, which
92+ # we'll remove at the end.
93+ new_rpaths = {}
94+ sentinel = 1
95+ for i in options :
96+ if isinstance (i , opts .lib ):
97+ if not isinstance (i .library , file_types .Library ):
98+ continue
99+ local = local_rpath (env , i .library , output )
100+ if local is not None :
101+ installed = installed_rpath (env , i .library , install_db )
102+ if not isinstance (local , BasePath ) or local != installed :
103+ new_rpaths [local ] = installed
104+ elif isinstance (i , opts .rpath_dir ):
105+ if i .when == opts .RpathWhen .installed :
106+ new_rpaths [sentinel ] = i .path
107+ sentinel += 1
108+ elif i .when == opts .RpathWhen .uninstalled :
109+ new_rpaths [i .path ] = None
110+ new_rpaths = ((k if not isinstance (k , int ) else None , v )
111+ for k , v in new_rpaths .items ())
112+
47113 path = install_db .host [output ].path
48114 return env .tool ('install_name_tool' )(
49- path , id = path . cross ( env ) if is_library else None , changes = changes
115+ path , id = new_id , changes = changes , rpaths = new_rpaths
50116 )
0 commit comments