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:
Misty De Meo 2013-10-06 01:51:31 -07:00
parent 9c53a1b8b7
commit 74ab023422
5 changed files with 43 additions and 4 deletions

View File

@ -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?

View File

@ -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+.

View File

@ -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?

View File

@ -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+\)/

View File

@ -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