aboutsummaryrefslogtreecommitdiffstats
path: root/Library
diff options
context:
space:
mode:
authorSamuel John2013-01-21 10:33:56 +0100
committerSamuel John2013-06-03 17:29:43 +0200
commitc5248956662e06094b7c4f7f7475d66771771ff6 (patch)
tree084b05b98d35f7ded16d6f83a8905c4a8a2830ce /Library
parent9647954a603dcb39cc8bb3f5fd0028670c2db0de (diff)
downloadbrew-c5248956662e06094b7c4f7f7475d66771771ff6.tar.bz2
Python 2.x and 3.x support
New `depends_on :python` Dependency. New `depends_on :python3` Dependency. To avoid having multiple formulae with endings -py2 and -py3, we will handle support for different pythons (2.x vs. 3.x) in the same formula. Further brewed vs. external python will be transparently supported. The formula also gets a new object `python`, which is false if no Python is available or the user has disabled it. Otherwise it is defined and provides several support methods: python.site_packages # the site-packages in the formula's Cellar python.global_site_packages python.binary # the full path to the python binary python.prefix python.version python.version.major python.version.minor python.xy # => e.g. "python2.7" python.incdir # includes of python python.libdir # the python dylib library python.pkg_config_path # used internally by brew python.from_osx? python.framework? python.universal? python.pypy? python.standard_caveats # Text to set PYTHONPATH for python.from_osx? python.if3then3 # => "" for 2.x and to "3" for 3.x. Further, to avoid code duplication, `python` takes an optional block that is run twice if the formula defines depends_on :python AND :python3. python do system python, 'setup.py', "--prefix=#{prefix}" end Read more in the Homebrew wiki.
Diffstat (limited to 'Library')
-rw-r--r--Library/Homebrew/build_options.rb15
-rw-r--r--Library/Homebrew/cmd/audit.rb68
-rw-r--r--Library/Homebrew/dependency.rb2
-rw-r--r--Library/Homebrew/dependency_collector.rb5
-rw-r--r--Library/Homebrew/download_strategy.rb1
-rw-r--r--Library/Homebrew/formula.rb19
-rw-r--r--Library/Homebrew/keg.rb8
-rw-r--r--Library/Homebrew/python_helper.rb80
-rw-r--r--Library/Homebrew/requirements.rb1
-rw-r--r--Library/Homebrew/requirements/language_module_dependency.rb4
-rw-r--r--Library/Homebrew/requirements/python_dependency.rb296
-rw-r--r--Library/Homebrew/superenv.rb23
-rw-r--r--Library/Homebrew/test/test_build_options.rb11
-rw-r--r--Library/Homebrew/test/test_python.rb1
-rw-r--r--Library/Homebrew/utils.rb2
15 files changed, 511 insertions, 25 deletions
diff --git a/Library/Homebrew/build_options.rb b/Library/Homebrew/build_options.rb
index 24c49931f..077a8b3d8 100644
--- a/Library/Homebrew/build_options.rb
+++ b/Library/Homebrew/build_options.rb
@@ -85,4 +85,19 @@ class BuildOptions
def unused_options
Options.new(@options - @args)
end
+
+ # Some options are implicitly ON because they are not explictly turned off
+ # by their counterpart option. This applies only to with-/without- options.
+ # implicit_options are needed because `depends_on 'spam' => 'with-stuff'`
+ # complains if 'spam' has stuff as default and only defines `--without-stuff`.
+ def implicit_options
+ implicit = unused_options.map do |o|
+ if o.name =~ /^with-(.+)$/ && without?($1)
+ Option.new("without-#{$1}") # we loose the description, but that's ok
+ elsif o.name =~ /^without-(.+)$/ && with?($1)
+ Option.new("with-#{$1}")
+ end
+ end.compact
+ Options.new(implicit)
+ end
end
diff --git a/Library/Homebrew/cmd/audit.rb b/Library/Homebrew/cmd/audit.rb
index 6bab71095..99aad1392 100644
--- a/Library/Homebrew/cmd/audit.rb
+++ b/Library/Homebrew/cmd/audit.rb
@@ -148,11 +148,18 @@ class FormulaAuditor
problem <<-EOS.undent unless dep.tags.any? || f.name =~ /automake/ && dep.name == 'autoconf'
#{dep} dependency should be "depends_on '#{dep}' => :build"
EOS
- when "git", "python", "ruby", "emacs", "mysql", "mercurial"
+ when "git", "ruby", "emacs", "mysql", "mercurial"
problem <<-EOS.undent
Don't use #{dep} as a dependency. We allow non-Homebrew
#{dep} installations.
EOS
+ when 'python', 'python2', 'python3'
+ problem <<-EOS.undent
+ Don't use #{dep} as a dependency (string).
+ We have special `depends_on :python` (or :python2 or :python3 )
+ that works with brewed and system Python and allows us to support
+ bindings for 2.x and 3.x in parallel and much more.
+ EOS
when "postgresql"
# Postgis specifically requires a Homebrewed postgresql
unless f.name == "postgis"
@@ -232,7 +239,7 @@ class FormulaAuditor
end
if p =~ %r[^http://prdownloads\.]
- problem "Don't use prdownloads in SourceForge urls (url is #{p}).\n" +
+ problem "Don't use prdownloads in SourceForge urls (url is #{p}).\n" +
"\tSee: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/"
end
@@ -249,7 +256,7 @@ class FormulaAuditor
# Check for http:// GitHub repo urls, https:// is preferred.
urls.grep(%r[^http://github\.com/.*\.git$]) do |u|
problem "Use https:// URLs for accessing GitHub repositories (url is #{u})."
- end
+ end
# Use new-style archive downloads
urls.select { |u| u =~ %r[https://.*/(?:tar|zip)ball/] and not u =~ %r[\.git$] }.each do |u|
@@ -427,6 +434,14 @@ class FormulaAuditor
problem "Reference '#{$1}' without dashes"
end
+ if text =~ /build\.with\?\s+['"]-?-?with-(.*)['"]/
+ problem "No double 'with': Use `build.with? '#{$1}'` to check for \"--with-#{$1}\""
+ end
+
+ if text =~ /build\.without\?\s+['"]-?-?without-(.*)['"]/
+ problem "No double 'without': Use `build.without? '#{$1}'` to check for \"--without-#{$1}\""
+ end
+
if text =~ /ARGV\.(?!(debug\?|verbose\?|find[\(\s]))/
problem "Use build instead of ARGV to check options"
end
@@ -457,6 +472,52 @@ class FormulaAuditor
end
end
+ def audit_python
+ if text =~ /system\(?\s*['"]python/
+ # Todo: In `def test` it is okay to do it this way. It's even recommended!
+ problem "Instead of `system 'python', ...`, call `system python, ...`."
+ end
+
+ if text =~ /system\(?\s*python\.binary/
+ problem "Instead of `system python.binary, ...`, call `system python, ...`."
+ end
+
+ if text =~ /(def\s*)?which_python/
+ problem "Replace `which_python` by `python.xy`, which returns e.g. 'python2.7'."
+ end
+
+ if text =~ /which\(?["']python/
+ problem "Don't locate python with `which 'python'`, use `python.binary` instead"
+ end
+
+ if f.requirements.any?{ |r| r.kind_of?(PythonInstalled) }
+ # Don't check this for all formulae, because some are allowed to set the
+ # PYTHONPATH. E.g. python.rb itself needs to set it.
+ if text =~ /ENV\.append.*PYTHONPATH/ || text =~ /ENV\[['"]PYTHONPATH['"]\]\s*=[^=]/
+ problem "Don't set the PYTHONPATH, instead declare `depends_on :python`."
+ end
+ end
+
+ if text =~ /(\s*)def\s+caveats((.*\n)*?)(\1end)/ || /(\s*)def\s+caveats;(.*?)end/
+ caveats_body = $2
+ if caveats_body =~ /(python[23]?)\.(.*\w)/
+ # So if in the body of caveats there is a `python.whatever` called,
+ # check that there is a guard like `if python` or similiar:
+ python = $1
+ method = $2
+ unless caveats_body =~ /(if python[23]?)|(if build\.with\?\s?\(?['"]python)|(unless build.without\?\s?\(?['"]python)/
+ problem "Please guard `#{python}.#{method}` like so `#{python}.#{method} if #{python}`"
+ end
+ end
+ end
+
+ # Todo:
+ # The python do ... end block is possibly executed twice. Once for
+ # python 2.x and once for 3.x. So if a `system 'make'` is called, a
+ # `system 'make clean'` should also be called at the end of the block.
+
+ end
+
def audit
audit_file
audit_specs
@@ -465,6 +526,7 @@ class FormulaAuditor
audit_conflicts
audit_patches
audit_text
+ audit_python
end
private
diff --git a/Library/Homebrew/dependency.rb b/Library/Homebrew/dependency.rb
index cc378b7df..a6905bc37 100644
--- a/Library/Homebrew/dependency.rb
+++ b/Library/Homebrew/dependency.rb
@@ -47,7 +47,7 @@ class Dependency
end
def missing_options
- options - Tab.for_formula(to_formula).used_options
+ options - Tab.for_formula(to_formula).used_options - to_formula.build.implicit_options
end
def universal!
diff --git a/Library/Homebrew/dependency_collector.rb b/Library/Homebrew/dependency_collector.rb
index 3de385fbf..74f94dc63 100644
--- a/Library/Homebrew/dependency_collector.rb
+++ b/Library/Homebrew/dependency_collector.rb
@@ -17,7 +17,7 @@ require 'set'
class DependencyCollector
# Define the languages that we can handle as external dependencies.
LANGUAGE_MODULES = Set[
- :chicken, :jruby, :lua, :node, :ocaml, :perl, :python, :rbx, :ruby
+ :chicken, :jruby, :lua, :node, :ocaml, :perl, :python, :python2, :python3, :rbx, :ruby
].freeze
attr_reader :deps, :requirements
@@ -92,6 +92,9 @@ class DependencyCollector
when :clt then CLTDependency.new(tags)
when :arch then ArchRequirement.new(tags)
when :hg then MercurialDependency.new(tags)
+ when :python then PythonInstalled.new(tags)
+ when :python2 then PythonInstalled.new("2", tags)
+ when :python3 then PythonInstalled.new("3", tags)
else
raise "Unsupported special dependency #{spec}"
end
diff --git a/Library/Homebrew/download_strategy.rb b/Library/Homebrew/download_strategy.rb
index 6745219fb..72a0652fb 100644
--- a/Library/Homebrew/download_strategy.rb
+++ b/Library/Homebrew/download_strategy.rb
@@ -548,6 +548,7 @@ class MercurialDownloadStrategy < AbstractDownloadStrategy
def cached_location; @clone; end
def hgpath
+ # #{HOMEBREW_PREFIX}/share/python/hg is deprecated, but we levae it in for a while
@path ||= %W[
#{which("hg")}
#{HOMEBREW_PREFIX}/bin/hg
diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb
index dab9726ed..bf64044b3 100644
--- a/Library/Homebrew/formula.rb
+++ b/Library/Homebrew/formula.rb
@@ -305,6 +305,21 @@ class Formula
]
end
+ def python(options={:allowed_major_versions => [2, 3]}, &block)
+ require 'python_helper'
+ self.instance_eval{ python_helper(options, &block) }
+ end
+
+ # Explicitly only execute the block for 2.x (if a python 2.x is available)
+ def python2 &block
+ python(:allowed_major_versions => [2], &block)
+ end
+
+ # Explicitly only execute the block for 3.x (if a python 3.x is available)
+ def python3 &block
+ python(:allowed_major_versions => [3], &block)
+ end
+
def self.class_s name
# remove invalid characters and then camelcase it
name.capitalize.gsub(/[-_.\s]([a-zA-Z0-9])/) { $1.upcase } \
@@ -558,7 +573,7 @@ class Formula
@exec_count ||= 0
@exec_count += 1
logd = HOMEBREW_LOGS/name
- logfn = "#{logd}/%02d.%s" % [@exec_count, File.basename(cmd).split(' ').first]
+ logfn = "#{logd}/%02d.%s" % [@exec_count, File.basename(cmd.to_s).split(' ').first]
mkdir_p(logd)
rd, wr = IO.pipe
@@ -567,7 +582,7 @@ class Formula
$stdout.reopen wr
$stderr.reopen wr
args.collect!{|arg| arg.to_s}
- exec(cmd, *args) rescue nil
+ exec(cmd.to_s, *args) rescue nil
puts "Failed to execute: #{cmd}"
exit! 1 # never gets here unless exec threw or failed
end
diff --git a/Library/Homebrew/keg.rb b/Library/Homebrew/keg.rb
index a63af419d..e1df0adb5 100644
--- a/Library/Homebrew/keg.rb
+++ b/Library/Homebrew/keg.rb
@@ -210,15 +210,18 @@ class Keg < Pathname
def link_dir foo, mode=OpenStruct.new
root = self+foo
return unless root.exist?
-
root.find do |src|
next if src == root
-
dst = HOMEBREW_PREFIX+src.relative_path_from(self)
dst.extend ObserverPathnameExtension
if src.file?
Find.prune if File.basename(src) == '.DS_Store'
+ # Don't link pyc files because Python overwrites these cached object
+ # files and next time brew wants to link, the pyc file is in the way.
+ if src.extname.to_s == '.pyc' && src.to_s =~ /site-packages/
+ Find.prune
+ end
case yield src.relative_path_from(root)
when :skip_file, nil
@@ -233,7 +236,6 @@ class Keg < Pathname
elsif src.directory?
# if the dst dir already exists, then great! walk the rest of the tree tho
next if dst.directory? and not dst.symlink?
-
# no need to put .app bundles in the path, the user can just use
# spotlight, or the open command and actual mac apps use an equivalent
Find.prune if src.extname.to_s == '.app'
diff --git a/Library/Homebrew/python_helper.rb b/Library/Homebrew/python_helper.rb
new file mode 100644
index 000000000..ef71b2a30
--- /dev/null
+++ b/Library/Homebrew/python_helper.rb
@@ -0,0 +1,80 @@
+
+# The python_helper is used in the Formula class when the user calls
+# `python`, `python2` or `python3`.
+
+# This method has a dual nature. For one, it takes a &block and sets up
+# the ENV such that a Python, as defined in the requirements, is the default.
+# If there are multiple `PythonInstalled` requirements, the block is evaluated
+# once for each Python. This makes it possible to easily support 2.x and
+# 3.x Python bindings without code duplication in formulae.
+# If you need to special case stuff, set :allowed_major_versions.
+# Second, inside the block, a formula author may call this method to access
+# certain convienience methods for the currently selected Python, e.g.
+# `python.site_packages`.
+def python_helper(options={:allowed_major_versions => [2, 3]}, &block)
+ if !block_given? and !@current_python.nil?
+ # We are already inside of a `python do ... end` block, so just return
+ # the current_python or false if the version.major is not allowed.
+ if options[:allowed_major_versions].include?(@current_python.version.major)
+ @current_python
+ else
+ false
+ end
+ else
+ # Look for PythonInstalled requirements for this formula
+ python_reqs = requirements.select{ |r| r.kind_of?(PythonInstalled) }
+ if python_reqs.empty?
+ raise "If you use python in the formula, you have to add `depends_on :python` (or :python3)!"
+ end
+ # Now select those that are satisfied and matching the version.major
+ python_reqs = python_reqs.select do |p|
+ p.satisfied? &&
+ options[:allowed_major_versions].include?(p.version.major) &&
+ if p.optional? || p.recommended?
+ self.build.with?(p.name)
+ else
+ true
+ end
+ end
+
+ # Allow to use an else-branch like so: `if python do ... end; else ... end`
+ return false if python_reqs.empty?
+
+ # Sort by version, so the older 2.x will be used first and if no
+ # block_given? then 2.x is preferred because it is returned.
+ # Further note, having 3.x last allows us to run `2to3 --write .`
+ # which modifies the sources in-place (for some packages that need this).
+ python_reqs.sort_by{ |py| py.version }.map do |py|
+ # Now is the time to set the site_packages to the correct value
+ py.site_packages = lib/py.xy/'site-packages'
+ if block_given?
+ puts "brew: Python block (#{py.binary})..." if ARGV.verbose?
+ require 'superenv'
+ # Ensure env changes are only temporary by using `with_build_environment`
+ ENV.with_build_environment do
+ # In order to install into the Cellar, the dir must exist and be in the
+ # PYTHONPATH. This will be executed in the context of the formula
+ # so that lib points to the HOMEBREW_PREFIX/Cellar/<formula>/<version>/lib
+ puts "brew: Setting PYTHONPATH=#{py.site_packages}" if ARGV.verbose?
+ mkdir_p py.site_packages
+ ENV.append 'PYTHONPATH', py.site_packages, ':'
+ ENV['PYTHON'] = py.binary
+ ENV.prepend 'CMAKE_INCLUDE_PATH', py.incdir, ':'
+ ENV.prepend 'PKG_CONFIG_PATH', py.pkg_config_path, ':' if py.pkg_config_path
+ ENV.prepend 'PATH', py.binary.dirname, ':' unless py.from_osx?
+ # Track the state of the currently selected python for this block,
+ # so if this python_helper is called again _inside_ the block, we can
+ # just return the right python (see `else`-branch a few lines down):
+ @current_python = py
+ res = instance_eval(&block)
+ @current_python = nil
+ res
+ end
+ else
+ puts "brew: Using #{py.binary}" if ARGV.verbose?
+ # We return here with intention, because no block_given?
+ return py
+ end
+ end
+ end
+end
diff --git a/Library/Homebrew/requirements.rb b/Library/Homebrew/requirements.rb
index ad621867c..c1c7fff7a 100644
--- a/Library/Homebrew/requirements.rb
+++ b/Library/Homebrew/requirements.rb
@@ -3,6 +3,7 @@ require 'requirements/conflict_requirement'
require 'requirements/language_module_dependency'
require 'requirements/x11_dependency'
require 'requirements/mpi_dependency'
+require 'requirements/python_dependency'
class XcodeDependency < Requirement
fatal true
diff --git a/Library/Homebrew/requirements/language_module_dependency.rb b/Library/Homebrew/requirements/language_module_dependency.rb
index 7c293e0c5..e62e61530 100644
--- a/Library/Homebrew/requirements/language_module_dependency.rb
+++ b/Library/Homebrew/requirements/language_module_dependency.rb
@@ -27,7 +27,8 @@ class LanguageModuleDependency < Requirement
when :node then %W{/usr/bin/env node -e require('#{@import_name}');}
when :ocaml then %W{/usr/bin/env opam list #{@import_name} | grep #{@import_name}}
when :perl then %W{/usr/bin/env perl -e use\ #{@import_name}}
- when :python then %W{/usr/bin/env python -c import\ #{@import_name}}
+ when :python then %W{/usr/bin/env python2 -c import\ #{@import_name}}
+ when :python3 then %W{/usr/bin/env python3 -c import\ #{@import_name}}
when :ruby then %W{/usr/bin/env ruby -rubygems -e require\ '#{@import_name}'}
when :rbx then %W{/usr/bin/env rbx -rubygems -e require\ '#{@import_name}'}
end
@@ -42,6 +43,7 @@ class LanguageModuleDependency < Requirement
when :ocaml then "opam install"
when :perl then "cpan -i"
when :python then "pip install"
+ when :python3 then "pip3 install"
when :rbx then "rbx gem install"
when :ruby then "gem install"
end
diff --git a/Library/Homebrew/requirements/python_dependency.rb b/Library/Homebrew/requirements/python_dependency.rb
new file mode 100644
index 000000000..194d434c7
--- /dev/null
+++ b/Library/Homebrew/requirements/python_dependency.rb
@@ -0,0 +1,296 @@
+require 'requirement'
+
+# We support Python 2.x and 3.x, either brewed or external.
+# This requirement locates the correct CPython binary (no PyPy), provides
+# support methods like `site_packages`, and writes our sitecustomize.py file.
+# In `dependency_collector.rb`, special `:python` and `:python3` shortcuts are
+# defined. You can specify a minimum version of the Python that needs to be
+# present, but since not every package is ported to 3.x yet,
+# `PythonInstalled("2")` is not satisfied by 3.x.
+# In a formula that shall provide support for 2.x and 3.x, the idiom is:
+# depends_on :python
+# depends_on :python3 => :optional # or :recommended
+#
+# Todo:
+# - Allow further options that choose: universal, framework?, brewed?...
+class PythonInstalled < Requirement
+ attr_reader :min_version
+ attr_reader :if3then3
+ attr_reader :site_packages
+ attr_accessor :site_packages
+
+ fatal true # you can still make Python optional by `depends_on :python => :optional`
+
+ class PythonVersion < Version
+ def major
+ to_a[0].to_s.to_i # Python's major.minor are always ints.
+ end
+ def minor
+ to_a[1].to_s.to_i
+ end
+ end
+
+ def initialize(*tags)
+ # Extract the min_version if given. Default to python 2.X else
+ tags.flatten!
+ if /(\d+\.)*\d+/ === tags.first
+ @min_version = PythonVersion.new(tags.shift)
+ else
+ @min_version = PythonVersion.new("2.7") # default
+ end
+
+ # often used idiom: e.g. sipdir = "share/sip" + python.if3then3
+ if @min_version.major == 3
+ @if3then3 = "3"
+ else
+ @if3then3 = ""
+ end
+
+ # Set name according to the major version.
+ # The name is used to generate the options like --without-python3
+ @name = "python" + @if3then3
+
+ # will be set later by the python_helper, because it needs the
+ # formula prefix to set site_packages
+ @site_packages = nil
+
+ super tags
+ end
+
+ # Note that during `satisfy` we still have the PATH as the user has set.
+ # We look for a brewed python or an external Python and store the loc of
+ # that binary for later usage. (See Formula#python)
+ satisfy :build_env => false do
+ @unsatisfied_because = "This formula needs #{@name}.\n"
+ if binary.nil?
+ @unsatisfied_because += "But no `#{@name}` found in your PATH! Consider to `brew install #{@name}`."
+ false
+ elsif pypy?
+ @unsatisfied_because += "Your #{@name} executable appears to be a PyPy, which is not supported."
+ false
+ elsif version.major != @min_version.major
+ @unsatisfied_because += "No Python #{@min_version.major}.x found!"
+ false
+ elsif version < @min_version
+ @unsatisfied_because += "Python version #{version} is too old (need at least #{@min_version})."
+ false
+ elsif @min_version.major == 2 && `python -c "import sys; print(sys.version_info.major)"`.strip == "3"
+ @unsatisfied_because += "Your `python` points to a Python 3.x. This is not supported."
+ false
+ else
+ true
+ end
+ end
+
+ # The full path to the python or python3 executable, depending on `version`.
+ def binary
+ if brewed?
+ # If the python is brewed we always prefer it!
+ # Note, we don't support homebrew/versions/pythonXX.rb, though.
+ Formula.factory(@name).opt_prefix/"bin/python#{@min_version.major}"
+ else
+ p = which(@name)
+ raise "PythonInstalled: #{p} is not executable" if !p.nil? && !p.executable?
+ p
+ end
+ end
+
+ # The python prefix (special cased for a brewed python to point into the opt_prefix)
+ def prefix
+ if brewed?
+ # Homebrew since a long while only supports frameworked python
+ HOMEBREW_PREFIX/"opt/#{name}/Frameworks/Python.framework/Versions/#{version.major}.#{version.minor}"
+ elsif from_osx?
+ # Python on OS X has been stripped off its includes (unless you install the CLT), therefore we use the MacOS.sdk.
+ Pathname.new("#{MacOS.sdk_path}/System/Library/Frameworks/Python.framework/Versions/#{version.major}.#{version.minor}")
+ else
+ # What Python knows about itself
+ Pathname.new(`#{binary} -c 'import sys;print(sys.prefix)'`.strip)
+ end
+ end
+
+ # Get the actual x.y.z version by asking python (or python3 if @min_version>=3)
+ def version
+ @version ||= PythonVersion.new(`#{binary} -c 'import sys;print(sys.version[:5])'`.strip)
+ end
+
+ # python.xy => "python2.7" is often used (and many formulae had this as `which_python`).
+ def xy
+ "python#{version.major}.#{version.minor}"
+ end
+
+ # Homebrew's global site-packages. The local ones are populated by the
+ # python_helper method when the `prefix` of a formula is known.
+ def global_site_packages
+ HOMEBREW_PREFIX/"lib/#{xy}/site-packages"
+ end
+
+ # Dir containing Python.h and others.
+ def incdir
+ if (from_osx? || brewed?) && framework?
+ prefix/"Headers"
+ else
+ # For all other we use Python's own standard method (works with a non-framework version, too)
+ Pathname.new(`#{binary} -c 'from distutils import sysconfig; print(sysconfig.get_python_inc())'`.strip)
+ end
+ end
+
+ # Dir containing e.g. libpython2.7.dylib
+ def libdir
+ if brewed? || from_osx?
+ prefix/"lib/#{xy}/config"
+ else
+ Pathname.new(`#{binary} -c "from distutils import sysconfig; print(sysconfig.get_config_var('LIBPL'))"`.strip)
+ end
+ end
+
+ # Pkgconfig (pc) files of python
+ def pkg_config_path
+ if from_osx?
+ # No matter if CLT-only or Xcode-only, the pc file is always here on OS X:
+ path = Pathname.new("/System/Library/Frameworks/Python.framework/Versions/#{version.major}.#{version.minor}/lib/pkgconfig")
+ path if path.exist?
+ else
+ prefix/"lib/pkgconfig"
+ end
+ end
+
+ # Is the Python brewed (and linked)?
+ def brewed?
+ @brewed ||= begin
+ require 'formula'
+ f = Formula.factory(@name)
+ f.installed? && f.linked_keg.exist?
+ end
+ end
+
+ # Is the python the one from OS X?
+ def from_osx?
+ @from_osx ||= begin
+ p = `#{binary} -c "import sys; print(sys.prefix)"`.strip
+ p.start_with?("/System/Library/Frameworks/Python.framework")
+ end
+ end
+
+ # Is the `python` a PyPy?
+ def pypy?
+ @pypy ||= !(`#{binary} -c "import sys; print(sys.version)"`.downcase =~ /.*pypy.*/).nil?
+ end
+
+ # Is this python a framework-style install (OS X only)?
+ def framework?
+ @framework ||= /Python[0-9]*\.framework/ === prefix.to_s
+ end
+
+ def universal?
+ @universal ||= archs_for_command(binary).universal?
+ end
+
+ def standard_caveats
+ if brewed?
+ "" # empty string, so we can concat this
+ else
+ <<-EOS.undent
+ For non-homebrew #{@name} (#{@min_version.major}.x), you need to amend your PYTHONPATH like so:
+ export PYTHONPATH=#{global_site_packages}:$PYTHONPATH
+ EOS
+ end
+ end
+
+ def modify_build_environment
+ # Write our sitecustomize.py
+ file = global_site_packages/"sitecustomize.py"
+ ohai "Writing #{file}" if ARGV.verbose? || ARGV.homebrew_developer?
+ [".pyc", ".pyo", ".py"].map{ |f|
+ global_site_packages/"sitecustomize#{f}"
+ }.each{ |f| f.delete if f.exist? }
+ file.write(sitecustomize)
+
+ # For non-system python's we add the opt_prefix/bin of python to the path.
+ ENV.prepend 'PATH', binary.dirname, ':' unless from_osx?
+
+ ENV['PYTHONHOME'] = nil # to avoid fuck-ups.
+ ENV['PYTHONNOUSERSITE'] = '1'
+ # Python respects the ARCHFLAGS var if set. Shall we set them here?
+ # ENV['ARCHFLAGS'] = ??? # FIXME
+ ENV.append 'CMAKE_INCLUDE_PATH', incdir, ':'
+ ENV.append 'PKG_CONFIG_PATH', pkg_config_path, ':' if pkg_config_path
+
+ # Udpate distutils.cfg (later we can remove this, but people still have
+ # their old brewed pythons and we have to update it here)
+ # Todo: If Jack's formula revisions arrive, we can get rid of this here!
+ if brewed?
+ require 'formula'
+ file = Formula.factory(@name).prefix/"Frameworks/Python.framework/Versions/#{version.major}.#{version.minor}/lib/#{xy}/distutils/distutils.cfg"
+ ohai "Writing #{file}" if ARGV.verbose? || ARGV.homebrew_developer?
+ file.delete if file.exist?
+ file.write <<-EOF.undent
+ [global]
+ verbose=1
+ [install]
+ force=1
+ prefix=#{HOMEBREW_PREFIX}
+ EOF
+ end
+ end
+
+ def sitecustomize
+ <<-EOF.undent
+ # This file is created by Homebrew and is executed on each python startup.
+ # Don't print from here, or else universe will collapse.
+ import sys
+
+ if sys.version_info.major == #{version.major} and sys.version_info.minor == #{version.minor}:
+ if sys.executable.startswith('#{HOMEBREW_PREFIX}'):
+ # Fix 1)
+ # A setuptools.pth and/or easy-install.pth sitting either in
+ # /Library/Python/2.7/site-packages or in
+ # ~/Library/Python/2.7/site-packages can inject the
+ # /System's Python site-packages. People then report
+ # "OSError: [Errno 13] Permission denied" because pip/easy_install
+ # attempts to install into
+ # /System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python
+ # See: https://github.com/mxcl/homebrew/issues/14712
+ # Fix 2)
+ # Remove brewed Python's hard-coded Cellar-site-packages
+ sys.path = [ p for p in sys.path
+ if not (p.startswith('/System') or
+ p.startswith('#{HOMEBREW_PREFIX}/Cellar/python') and p.endswith('site-packages')) ]
+ # Fix 3)
+ # Set the sys.executable to use the opt_prefix
+ sys.executable = '#{HOMEBREW_PREFIX}/opt/#{name}/bin/python#{version.major}.#{version.minor}'
+ # Fix 4)
+ # Make LINKFORSHARED (and python-confing --ldflags) return the
+ # full path to the lib (yes, "Python" is actually the lib, not a
+ # dir) so that third-party software does not need to add the
+ # -F/#{HOMEBREW_PREFIX}/Frameworks switch.
+ # Assume Framework style build (default since months in brew)
+ try:
+ from _sysconfigdata import build_time_vars
+ build_time_vars['LINKFORSHARED'] = '-u _PyMac_Error #{HOMEBREW_PREFIX}/opt/#{name}/Frameworks/Python.framework/Versions/#{version.major}.#{version.minor}/Python'
+ except:
+ pass # remember: don't print here. Better to fail silent.
+ # Fix 5)
+ # For all Pythons of the right major.minor version: Tell about homebrew's
+ # site-packages location. This is needed for Python to parse *.pth.
+ import site
+ site.addsitedir('#{global_site_packages}')
+ EOF
+ end
+
+ def message
+ @unsatisfied_because
+ end
+
+ def <=> other
+ version <=> other.version
+ end
+
+ def to_s
+ binary.to_s
+ end
+
+ def hash
+ to_s.hash
+ end
+end
diff --git a/Library/Homebrew/superenv.rb b/Library/Homebrew/superenv.rb
index 18a47ca0d..022517534 100644
--- a/Library/Homebrew/superenv.rb
+++ b/Library/Homebrew/superenv.rb
@@ -30,7 +30,7 @@ class << ENV
alias_method :x11?, :x11
def reset
- %w{CC CXX OBJC OBJCXX CPP MAKE LD
+ %w{CC CXX OBJC OBJCXX CPP MAKE LD LDSHARED
CFLAGS CXXFLAGS OBJCFLAGS OBJCXXFLAGS LDFLAGS CPPFLAGS
MACOS_DEPLOYMENT_TARGET SDKROOT
CMAKE_PREFIX_PATH CMAKE_INCLUDE_PATH CMAKE_FRAMEWORK_PATH
@@ -57,7 +57,7 @@ class << ENV
ENV['HOMEBREW_BREW_FILE'] = HOMEBREW_BREW_FILE
ENV['HOMEBREW_SDKROOT'] = "#{MacOS.sdk_path}" if MacSystem.xcode43_without_clt?
ENV['CMAKE_PREFIX_PATH'] = determine_cmake_prefix_path
- ENV['CMAKE_FRAMEWORK_PATH'] = "#{MacOS.sdk_path}/System/Library/Frameworks" if MacSystem.xcode43_without_clt?
+ ENV['CMAKE_FRAMEWORK_PATH'] = determine_cmake_frameworks_path
ENV['CMAKE_INCLUDE_PATH'] = determine_cmake_include_path
ENV['CMAKE_LIBRARY_PATH'] = determine_cmake_library_path
ENV['ACLOCAL_PATH'] = determine_aclocal_path
@@ -133,7 +133,6 @@ class << ENV
paths << "#{MacOS::Xcode.prefix}/Toolchains/XcodeDefault.xctoolchain/usr/bin"
end
paths += deps.map{|dep| "#{HOMEBREW_PREFIX}/opt/#{dep}/bin" }
- paths << "#{HOMEBREW_PREFIX}/opt/python/bin" if brewed_python?
paths << "#{MacSystem.x11_prefix}/bin" if x11?
paths += %w{/usr/bin /bin /usr/sbin /sbin}
paths.to_path_s
@@ -158,6 +157,14 @@ class << ENV
paths.to_path_s
end
+ def determine_cmake_frameworks_path
+ # XXX: keg_only_deps perhaps? but Qt does not link its Frameworks because of Ruby's Find.find ignoring symlinks!!
+ paths = deps.map{|dep| "#{HOMEBREW_PREFIX}/opt/#{dep}/Frameworks" }
+ paths << "#{HOMEBREW_PREFIX}/Frameworks"
+ paths << "#{MacOS.sdk_path}/System/Library/Frameworks" if MacSystem.xcode43_without_clt?
+ paths.to_path_s
+ end
+
def determine_cmake_include_path
sdk = MacOS.sdk_path if MacSystem.xcode43_without_clt?
paths = []
@@ -165,11 +172,6 @@ class << ENV
paths << "#{sdk}/usr/include/libxml2" unless deps.include? 'libxml2'
if MacSystem.xcode43_without_clt?
paths << "#{sdk}/usr/include/apache2"
- paths << if brewed_python?
- "#{HOMEBREW_PREFIX}/opt/python/Frameworks/Python.framework/Headers"
- else
- "#{sdk}/System/Library/Frameworks/Python.framework/Versions/Current/include/python2.7"
- end
end
paths << "#{sdk}/System/Library/Frameworks/OpenGL.framework/Versions/Current/Headers/" unless x11?
paths << "#{MacSystem.x11_prefix}/include" if x11?
@@ -227,11 +229,6 @@ class << ENV
MacOS::Xcode.prefix || ENV['DEVELOPER_DIR']
end
- def brewed_python?
- require 'formula'
- Formula.factory('python').linked_keg.directory?
- end
-
public
### NO LONGER NECESSARY OR NO LONGER SUPPORTED
diff --git a/Library/Homebrew/test/test_build_options.rb b/Library/Homebrew/test/test_build_options.rb
index daf5ef96b..cc5fd01d7 100644
--- a/Library/Homebrew/test/test_build_options.rb
+++ b/Library/Homebrew/test/test_build_options.rb
@@ -42,4 +42,15 @@ class BuildOptionsTests < Test::Unit::TestCase
def test_unused_options
assert @build.unused_options.include?("--without-baz")
end
+
+ def test_implicit_options
+ # --without-baz is not explicitly specified on the command line
+ # therefore --with-baz should be implicitly assumed:
+ assert @build.implicit_options.include?("--with-baz")
+ # But all these should not be in the implict_options:
+ assert !@build.implicit_options.include?("--without-baz")
+ assert !@build.implicit_options.include?("--with-bar")
+ assert !@build.implicit_options.include?("--without-bar")
+ assert !@build.implicit_options.include?("--with-qux")
+ end
end
diff --git a/Library/Homebrew/test/test_python.rb b/Library/Homebrew/test/test_python.rb
new file mode 100644
index 000000000..7658efa85
--- /dev/null
+++ b/Library/Homebrew/test/test_python.rb
@@ -0,0 +1 @@
+#TODO! \ No newline at end of file
diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb
index e959d9d64..60eead67c 100644
--- a/Library/Homebrew/utils.rb
+++ b/Library/Homebrew/utils.rb
@@ -97,7 +97,7 @@ module Homebrew
fork do
yield if block_given?
args.collect!{|arg| arg.to_s}
- exec(cmd, *args) rescue nil
+ exec(cmd.to_s, *args) rescue nil
exit! 1 # never gets here unless exec failed
end
Process.wait