Only track C++ stdlibs for C++ code
After a formula is built, scan all mach-o files for dynamic links to see if any of them point to a C++ stdlib (libc++ or libstdc++). If one of them is linked, record that information in the formula's tab. This replaces the old behaviour where all files were assumed to be C++ code, and stdlibs were always tracked regardless of whether they were actually linked against. This also modifies the way that tabs are written - now tabs are written with the stdlib field null, and values are only written if an stdlib is detected.
This commit is contained in:
		
							parent
							
								
									9c53a1b8b7
								
							
						
					
					
						commit
						74ab023422
					
				@ -171,7 +171,7 @@ class Build
 | 
			
		||||
 | 
			
		||||
        begin
 | 
			
		||||
          f.install
 | 
			
		||||
          Tab.create(f, :libstdcxx, ENV.compiler,
 | 
			
		||||
          Tab.create(f, ENV.compiler,
 | 
			
		||||
            Options.coerce(ARGV.options_only)).write
 | 
			
		||||
        rescue Exception => e
 | 
			
		||||
          if ARGV.debug?
 | 
			
		||||
 | 
			
		||||
@ -390,6 +390,17 @@ class Pathname
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # Returns an array containing all dynamically-linked libraries, based on the
 | 
			
		||||
  # output of otool. This returns the install names, so these are not guaranteed
 | 
			
		||||
  # to be absolute paths.
 | 
			
		||||
  # Returns an empty array both for software that links against no libraries,
 | 
			
		||||
  # and for non-mach objects.
 | 
			
		||||
  def dynamically_linked_libraries
 | 
			
		||||
    `otool -L "#{expand_path}"`.chomp.split("\n")[1..-1].map do |line|
 | 
			
		||||
      line[/\t(.+) \([^(]+\)/, 1]
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  # We redefine these private methods in order to add the /o modifier to
 | 
			
		||||
  # the Regexp literals, which forces string interpolation to happen only
 | 
			
		||||
  # once instead of each time the method is called. This is fixed in 1.9+.
 | 
			
		||||
 | 
			
		||||
@ -349,6 +349,8 @@ class FormulaInstaller
 | 
			
		||||
 | 
			
		||||
    fix_install_names
 | 
			
		||||
 | 
			
		||||
    record_cxx_stdlib
 | 
			
		||||
 | 
			
		||||
    ohai "Summary" if ARGV.verbose? or show_summary_heading
 | 
			
		||||
    unless ENV['HOMEBREW_NO_EMOJI']
 | 
			
		||||
      print "\xf0\x9f\x8d\xba  " if MacOS.version >= :lion
 | 
			
		||||
@ -490,6 +492,18 @@ class FormulaInstaller
 | 
			
		||||
    @show_summary_heading = true
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def record_cxx_stdlib
 | 
			
		||||
    stdlibs = Keg.new(f.prefix).detect_cxx_stdlibs
 | 
			
		||||
    return if stdlibs.empty?
 | 
			
		||||
 | 
			
		||||
    tab = Tab.for_formula(f)
 | 
			
		||||
    tab.tabfile.unlink
 | 
			
		||||
    # It's technically possible for the same lib to link to multiple C++ stdlibs,
 | 
			
		||||
    # but very bad news. Right now we don't track this woeful scenario.
 | 
			
		||||
    tab.stdlib = stdlibs.first
 | 
			
		||||
    tab.write
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def clean
 | 
			
		||||
    ohai "Cleaning" if ARGV.verbose?
 | 
			
		||||
    if f.class.skip_clean_all?
 | 
			
		||||
 | 
			
		||||
@ -42,6 +42,21 @@ class Keg
 | 
			
		||||
    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
 | 
			
		||||
    results = Set.new
 | 
			
		||||
    mach_o_files.each do |file|
 | 
			
		||||
      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
 | 
			
		||||
 | 
			
		||||
  private
 | 
			
		||||
 | 
			
		||||
  OTOOL_RX = /\t(.*) \(compatibility version (\d+\.)*\d+, current version (\d+\.)*\d+\)/
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,7 @@ require 'utils/json'
 | 
			
		||||
class Tab < OpenStruct
 | 
			
		||||
  FILENAME = 'INSTALL_RECEIPT.json'
 | 
			
		||||
 | 
			
		||||
  def self.create f, stdlib, compiler, args
 | 
			
		||||
  def self.create f, compiler, args
 | 
			
		||||
    f.build.args = args
 | 
			
		||||
 | 
			
		||||
    sha = HOMEBREW_REPOSITORY.cd do
 | 
			
		||||
@ -25,8 +25,7 @@ class Tab < OpenStruct
 | 
			
		||||
            :tapped_from => f.tap,
 | 
			
		||||
            :time => Time.now.to_i, # to_s would be better but Ruby has no from_s function :P
 | 
			
		||||
            :HEAD => sha,
 | 
			
		||||
            :compiler => compiler,
 | 
			
		||||
            :stdlib => stdlib
 | 
			
		||||
            :compiler => compiler
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.from_file path
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user