| 
									
										
										
										
											2019-04-19 15:38:03 +09:00
										 |  |  | # frozen_string_literal: true | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  | class Keg | 
					
						
							| 
									
										
										
										
											2018-04-07 20:28:56 +01:00
										 |  |  |   class << self | 
					
						
							|  |  |  |     undef file_linked_libraries | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def file_linked_libraries(file, string) | 
					
						
							|  |  |  |       # Check dynamic library linkage. Importantly, do not perform for static | 
					
						
							|  |  |  |       # libraries, which will falsely report "linkage" to themselves. | 
					
						
							|  |  |  |       if file.mach_o_executable? || file.dylib? || file.mach_o_bundle? | 
					
						
							|  |  |  |         file.dynamically_linked_libraries.select { |lib| lib.include? string } | 
					
						
							|  |  |  |       else | 
					
						
							|  |  |  |         [] | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-07 20:28:56 +01:00
										 |  |  |   undef relocate_dynamic_linkage, detect_cxx_stdlibs | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-23 23:48:07 -04:00
										 |  |  |   def relocate_dynamic_linkage(relocation) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     mach_o_files.each do |file| | 
					
						
							|  |  |  |       file.ensure_writable do | 
					
						
							|  |  |  |         if file.dylib? | 
					
						
							| 
									
										
										
										
											2016-10-23 23:48:07 -04:00
										 |  |  |           id = dylib_id_for(file).sub(relocation.old_prefix, relocation.new_prefix) | 
					
						
							| 
									
										
										
										
											2017-09-27 02:08:23 -07:00
										 |  |  |           change_dylib_id(id, file) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |         end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         each_install_name_for(file) do |old_name| | 
					
						
							| 
									
										
										
										
											2016-10-23 23:48:07 -04:00
										 |  |  |           if old_name.start_with? relocation.old_cellar | 
					
						
							|  |  |  |             new_name = old_name.sub(relocation.old_cellar, relocation.new_cellar) | 
					
						
							|  |  |  |           elsif old_name.start_with? relocation.old_prefix | 
					
						
							|  |  |  |             new_name = old_name.sub(relocation.old_prefix, relocation.new_prefix) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |           end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-27 02:08:23 -07:00
										 |  |  |           change_install_name(old_name, new_name, file) if new_name | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |         end | 
					
						
							|  |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Detects the C++ dynamic libraries in place, scanning the dynamic links | 
					
						
							|  |  |  |   # of the files within the keg. | 
					
						
							|  |  |  |   # Note that this doesn't attempt to distinguish between libstdc++ versions, | 
					
						
							|  |  |  |   # for instance between Apple libstdc++ and GNU libstdc++ | 
					
						
							|  |  |  |   def detect_cxx_stdlibs(options = {}) | 
					
						
							|  |  |  |     skip_executables = options.fetch(:skip_executables, false) | 
					
						
							|  |  |  |     results = Set.new | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mach_o_files.each do |file| | 
					
						
							|  |  |  |       next if file.mach_o_executable? && skip_executables | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |       dylibs = file.dynamically_linked_libraries | 
					
						
							|  |  |  |       results << :libcxx unless dylibs.grep(/libc\+\+.+\.dylib/).empty? | 
					
						
							|  |  |  |       results << :libstdcxx unless dylibs.grep(/libstdc\+\+.+\.dylib/).empty? | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     results.to_a | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-07 20:28:56 +01:00
										 |  |  |   def fix_dynamic_linkage | 
					
						
							|  |  |  |     mach_o_files.each do |file| | 
					
						
							|  |  |  |       file.ensure_writable do | 
					
						
							|  |  |  |         change_dylib_id(dylib_id_for(file), file) if file.dylib? | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         each_install_name_for(file) do |bad_name| | 
					
						
							|  |  |  |           # Don't fix absolute paths unless they are rooted in the build directory | 
					
						
							|  |  |  |           next if bad_name.start_with?("/") && | 
					
						
							|  |  |  |                   !bad_name.start_with?(HOMEBREW_TEMP.to_s) && | 
					
						
							|  |  |  |                   !bad_name.start_with?(HOMEBREW_TEMP.realpath.to_s) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           new_name = fixed_name(file, bad_name) | 
					
						
							|  |  |  |           change_install_name(bad_name, new_name, file) unless new_name == bad_name | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-12-27 13:30:16 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # If none of the install names reference RPATH(s), then we can safely | 
					
						
							|  |  |  |         # remove all RPATHs from the file. | 
					
						
							| 
									
										
										
										
											2019-03-19 07:40:27 -07:00
										 |  |  |         if ENV["HOMEBREW_RELOCATE_METAVARS"] && | 
					
						
							|  |  |  |            file.dynamically_linked_libraries.none? { |lib| lib.start_with?("@rpath") } | 
					
						
							| 
									
										
										
										
											2018-12-27 13:30:16 -05:00
										 |  |  |           # Note: This could probably be made more efficient by reverse-sorting | 
					
						
							|  |  |  |           # the RPATHs by offset and calling MachOFile#delete_command | 
					
						
							|  |  |  |           # with repopulate: false. | 
					
						
							|  |  |  |           file.rpaths.each { |r| file.delete_rpath(r) } | 
					
						
							|  |  |  |         end | 
					
						
							| 
									
										
										
										
											2018-04-07 20:28:56 +01:00
										 |  |  |       end | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     generic_fix_dynamic_linkage | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 19:55:21 -05:00
										 |  |  |   def expand_rpath(file, bad_name) | 
					
						
							|  |  |  |     suffix = bad_name.sub(/^@rpath/, "") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     file.rpaths.each do |rpath| | 
					
						
							| 
									
										
										
										
											2018-12-19 20:23:02 -05:00
										 |  |  |       return rpath/suffix if (rpath/suffix).exist? | 
					
						
							| 
									
										
										
										
											2018-12-19 19:55:21 -05:00
										 |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-19 20:12:28 -05:00
										 |  |  |     opoo "Could not find library #{bad_name} for #{file}" | 
					
						
							| 
									
										
										
										
											2018-12-19 19:55:21 -05:00
										 |  |  |     bad_name | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |   # If file is a dylib or bundle itself, look for the dylib named by | 
					
						
							|  |  |  |   # bad_name relative to the lib directory, so that we can skip the more | 
					
						
							|  |  |  |   # expensive recursive search if possible. | 
					
						
							|  |  |  |   def fixed_name(file, bad_name) | 
					
						
							|  |  |  |     if bad_name.start_with? PREFIX_PLACEHOLDER | 
					
						
							| 
									
										
										
										
											2017-04-27 20:17:06 +02:00
										 |  |  |       bad_name.sub(PREFIX_PLACEHOLDER, HOMEBREW_PREFIX) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     elsif bad_name.start_with? CELLAR_PLACEHOLDER | 
					
						
							| 
									
										
										
										
											2017-04-27 20:17:06 +02:00
										 |  |  |       bad_name.sub(CELLAR_PLACEHOLDER, HOMEBREW_CELLAR) | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     elsif (file.dylib? || file.mach_o_bundle?) && (file.parent + bad_name).exist? | 
					
						
							| 
									
										
										
										
											2019-03-11 20:58:04 -04:00
										 |  |  |       "@loader_path/#{bad_name}" | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     elsif file.mach_o_executable? && (lib + bad_name).exist? | 
					
						
							|  |  |  |       "#{lib}/#{bad_name}" | 
					
						
							| 
									
										
										
										
											2019-03-10 21:29:59 -04:00
										 |  |  |     elsif bad_name.start_with?("@rpath") && ENV["HOMEBREW_RELOCATE_METAVARS"] | 
					
						
							|  |  |  |       expand_rpath file, bad_name | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     elsif (abs_name = find_dylib(bad_name)) && abs_name.exist? | 
					
						
							|  |  |  |       abs_name.to_s | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       opoo "Could not fix #{bad_name} in #{file}" | 
					
						
							|  |  |  |       bad_name | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def each_install_name_for(file, &block) | 
					
						
							|  |  |  |     dylibs = file.dynamically_linked_libraries | 
					
						
							| 
									
										
										
										
											2019-03-11 20:58:04 -04:00
										 |  |  |     dylibs.reject! { |fn| fn =~ /^@(loader|executable)_path/ } | 
					
						
							| 
									
										
										
										
											2019-03-19 07:40:27 -07:00
										 |  |  |     dylibs.reject! { |fn| fn =~ /^@rpath/ } unless ENV["HOMEBREW_RELOCATE_METAVARS"] | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     dylibs.each(&block) | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def dylib_id_for(file) | 
					
						
							|  |  |  |     # The new dylib ID should have the same basename as the old dylib ID, not | 
					
						
							|  |  |  |     # the basename of the file itself. | 
					
						
							|  |  |  |     basename = File.basename(file.dylib_id) | 
					
						
							|  |  |  |     relative_dirname = file.dirname.relative_path_from(path) | 
					
						
							| 
									
										
										
										
											2017-06-01 16:06:51 +02:00
										 |  |  |     (opt_record/relative_dirname/basename).to_s | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   # Matches framework references like `XXX.framework/Versions/YYY/XXX` and | 
					
						
							|  |  |  |   # `XXX.framework/XXX`, both with or without a slash-delimited prefix. | 
					
						
							| 
									
										
										
										
											2018-11-02 17:18:07 +00:00
										 |  |  |   FRAMEWORK_RX = %r{(?:^|/)(([^/]+)\.framework/(?:Versions/[^/]+/)?\2)$}.freeze | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |   def find_dylib_suffix_from(bad_name) | 
					
						
							|  |  |  |     if (framework = bad_name.match(FRAMEWORK_RX)) | 
					
						
							|  |  |  |       framework[1] | 
					
						
							|  |  |  |     else | 
					
						
							|  |  |  |       File.basename(bad_name) | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def find_dylib(bad_name) | 
					
						
							|  |  |  |     return unless lib.directory? | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |     suffix = "/#{find_dylib_suffix_from(bad_name)}" | 
					
						
							|  |  |  |     lib.find { |pn| break pn if pn.to_s.end_with?(suffix) } | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   def mach_o_files | 
					
						
							|  |  |  |     hardlinks = Set.new | 
					
						
							|  |  |  |     mach_o_files = [] | 
					
						
							|  |  |  |     path.find do |pn| | 
					
						
							|  |  |  |       next if pn.symlink? || pn.directory? | 
					
						
							|  |  |  |       next unless pn.dylib? || pn.mach_o_bundle? || pn.mach_o_executable? | 
					
						
							|  |  |  |       # if we've already processed a file, ignore its hardlinks (which have the same dev ID and inode) | 
					
						
							|  |  |  |       # this prevents relocations from being performed on a binary more than once | 
					
						
							|  |  |  |       next unless hardlinks.add? [pn.stat.dev, pn.stat.ino] | 
					
						
							| 
									
										
										
										
											2018-09-17 02:45:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  |       mach_o_files << pn | 
					
						
							|  |  |  |     end | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mach_o_files | 
					
						
							|  |  |  |   end | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-04-02 09:04:49 -07:00
										 |  |  |   def recursive_fgrep_args | 
					
						
							|  |  |  |     # Don't recurse into symlinks; the man page says this is the default, but | 
					
						
							|  |  |  |     # it's wrong. -O is a BSD-grep-only option. | 
					
						
							|  |  |  |     "-lrO" | 
					
						
							|  |  |  |   end | 
					
						
							| 
									
										
										
										
											2016-07-09 13:52:05 +01:00
										 |  |  | end |