2010-06-23 11:20:47 -07:00
|
|
|
require 'formula'
|
|
|
|
require 'utils'
|
superenv: build-environments that just work
1. A minimal build environment, we don't set CFLAGS, CPPFLAGS, LDFLAGS, etc. the rationale being, the less that is set, the less variables we are introducing that can break builds.
2. A set of scripts that replace cc, ld, etc. and inject the -I, -L, etc. flags we need into the args passed to the build-tools.
Because we now have complete control over compiler instantiations we do a variety of clean-up tasks, like removing bad flags, enforcing universal builds and ensuring makefiles don't try to change the order of library and include paths from ones that work to ones that don't.
The previous ENV-system is still available when --env=std is specified.
superenv applies to Xcode >= 4.3 only currently.
2012-08-11 12:30:51 -04:00
|
|
|
require 'superenv'
|
2013-07-15 19:29:08 -07:00
|
|
|
require 'formula_cellar_checks'
|
2012-03-17 19:49:49 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
module Homebrew extend self
|
|
|
|
def audit
|
|
|
|
formula_count = 0
|
|
|
|
problem_count = 0
|
|
|
|
|
2013-05-07 18:39:45 -05:00
|
|
|
ENV.setup_build_environment
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
ff = if ARGV.named.empty?
|
2012-08-21 11:39:45 -04:00
|
|
|
Formula
|
2012-08-07 01:37:46 -05:00
|
|
|
else
|
|
|
|
ARGV.formulae
|
|
|
|
end
|
|
|
|
|
|
|
|
ff.each do |f|
|
|
|
|
fa = FormulaAuditor.new f
|
|
|
|
fa.audit
|
|
|
|
|
|
|
|
unless fa.problems.empty?
|
|
|
|
puts "#{f.name}:"
|
|
|
|
fa.problems.each { |p| puts " * #{p}" }
|
|
|
|
puts
|
|
|
|
formula_count += 1
|
|
|
|
problem_count += fa.problems.size
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
unless problem_count.zero?
|
|
|
|
ofail "#{problem_count} problems in #{formula_count} formulae"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2012-03-17 19:49:49 -07:00
|
|
|
|
|
|
|
class Module
|
|
|
|
def redefine_const(name, value)
|
|
|
|
__send__(:remove_const, name) if const_defined?(name)
|
|
|
|
const_set(name, value)
|
|
|
|
end
|
|
|
|
end
|
2010-06-23 11:20:47 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Formula extensions for auditing
|
|
|
|
class Formula
|
|
|
|
def head_only?
|
|
|
|
@head and @stable.nil?
|
2011-06-06 07:25:00 -07:00
|
|
|
end
|
2011-03-21 12:57:35 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def text
|
|
|
|
@text ||= FormulaText.new(@path)
|
2010-07-12 10:34:12 -07:00
|
|
|
end
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-07-12 10:34:12 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
class FormulaText
|
|
|
|
def initialize path
|
|
|
|
@text = path.open('r') { |f| f.read }
|
2010-07-23 21:31:32 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def without_patch
|
|
|
|
@text.split("__END__")[0].strip()
|
2011-11-29 19:37:39 -06:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def has_DATA?
|
|
|
|
/\bDATA\b/ =~ @text
|
2010-08-15 15:19:19 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def has_END?
|
|
|
|
/^__END__$/ =~ @text
|
2010-09-08 09:07:59 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def has_trailing_newline?
|
2013-02-21 09:56:30 +01:00
|
|
|
/\Z\n/ =~ @text
|
2010-09-07 14:34:39 -07:00
|
|
|
end
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-09-07 14:34:39 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
class FormulaAuditor
|
2013-07-15 19:29:08 -07:00
|
|
|
include FormulaCellarChecks
|
|
|
|
|
2013-02-17 22:52:39 -06:00
|
|
|
attr_reader :f, :text, :problems
|
2012-08-07 01:37:46 -05:00
|
|
|
|
|
|
|
BUILD_TIME_DEPS = %W[
|
|
|
|
autoconf
|
|
|
|
automake
|
|
|
|
boost-build
|
|
|
|
bsdmake
|
|
|
|
cmake
|
|
|
|
imake
|
2013-02-03 14:41:00 -06:00
|
|
|
intltool
|
2012-08-07 01:37:46 -05:00
|
|
|
libtool
|
|
|
|
pkg-config
|
|
|
|
scons
|
|
|
|
smake
|
2012-09-05 21:12:08 -07:00
|
|
|
swig
|
2012-08-07 01:37:46 -05:00
|
|
|
]
|
|
|
|
|
|
|
|
def initialize f
|
|
|
|
@f = f
|
|
|
|
@problems = []
|
|
|
|
@text = f.text.without_patch
|
2013-04-06 22:10:33 -05:00
|
|
|
@specs = %w{stable devel head}.map { |s| f.send(s) }.compact
|
2012-08-07 01:37:46 -05:00
|
|
|
|
|
|
|
# We need to do this in case the formula defines a patch that uses DATA.
|
|
|
|
f.class.redefine_const :DATA, ""
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_file
|
|
|
|
unless f.path.stat.mode.to_s(8) == "100644"
|
|
|
|
problem "Incorrect file permissions: chmod 644 #{f.path}"
|
|
|
|
end
|
2010-06-23 11:20:47 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if f.text.has_DATA? and not f.text.has_END?
|
|
|
|
problem "'DATA' was found, but no '__END__'"
|
|
|
|
end
|
2010-08-08 10:17:53 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if f.text.has_END? and not f.text.has_DATA?
|
|
|
|
problem "'__END__' was found, but 'DATA' is not used"
|
|
|
|
end
|
2010-08-15 11:32:45 -07:00
|
|
|
|
2013-02-21 09:56:30 +01:00
|
|
|
unless f.text.has_trailing_newline?
|
2012-08-07 01:37:46 -05:00
|
|
|
problem "File should end with a newline"
|
|
|
|
end
|
2010-08-08 10:17:53 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def audit_deps
|
|
|
|
# Don't depend_on aliases; use full name
|
2013-05-07 18:40:14 -05:00
|
|
|
@@aliases ||= Formula.aliases
|
|
|
|
f.deps.select { |d| @@aliases.include? d.name }.each do |d|
|
2012-08-07 01:37:46 -05:00
|
|
|
problem "Dependency #{d} is an alias; use the canonical name."
|
|
|
|
end
|
2010-09-08 09:22:48 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Check for things we don't like to depend on.
|
|
|
|
# We allow non-Homebrew installs whenever possible.
|
2013-01-23 00:26:31 -06:00
|
|
|
f.deps.each do |dep|
|
2012-08-07 01:37:46 -05:00
|
|
|
begin
|
2013-01-23 00:26:31 -06:00
|
|
|
dep_f = dep.to_formula
|
2013-04-06 22:10:43 -05:00
|
|
|
rescue FormulaUnavailableError
|
2013-02-02 00:54:32 -06:00
|
|
|
problem "Can't find dependency #{dep.name.inspect}."
|
2013-06-26 18:08:54 -05:00
|
|
|
next
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-08-08 10:17:53 -07:00
|
|
|
|
2013-01-23 00:26:31 -06:00
|
|
|
dep.options.reject do |opt|
|
|
|
|
dep_f.build.has_option?(opt.name)
|
|
|
|
end.each do |opt|
|
|
|
|
problem "Dependency #{dep} does not define option #{opt.name.inspect}"
|
|
|
|
end
|
|
|
|
|
|
|
|
case dep.name
|
2013-04-06 22:11:02 -05:00
|
|
|
when *BUILD_TIME_DEPS
|
2013-07-16 15:49:11 -05:00
|
|
|
next if dep.build?
|
|
|
|
next if dep.name == 'autoconf' && f.name =~ /automake/
|
|
|
|
next if dep.name == 'libtool' && %w{imagemagick libgphoto2 libp11}.any? { |n| f.name == n }
|
|
|
|
next if dep.name =~ /autoconf|pkg-config/ && f.name == 'ruby-build'
|
|
|
|
|
2013-06-27 21:11:16 -05:00
|
|
|
problem %{#{dep} dependency should be "depends_on '#{dep}' => :build"}
|
2013-06-04 11:15:30 -05:00
|
|
|
when "git", "ruby", "emacs", "mercurial"
|
2012-08-07 01:37:46 -05:00
|
|
|
problem <<-EOS.undent
|
2013-01-23 00:26:31 -06:00
|
|
|
Don't use #{dep} as a dependency. We allow non-Homebrew
|
|
|
|
#{dep} installations.
|
2012-08-07 01:37:46 -05:00
|
|
|
EOS
|
2013-01-21 10:33:56 +01:00
|
|
|
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
|
2012-08-07 01:37:46 -05:00
|
|
|
when 'gfortran'
|
2013-06-23 20:40:00 -07:00
|
|
|
problem "Use `depends_on :fortran` instead of `depends_on 'gfortran'`"
|
2012-08-07 01:37:46 -05:00
|
|
|
when 'open-mpi', 'mpich2'
|
|
|
|
problem <<-EOS.undent
|
|
|
|
There are multiple conflicting ways to install MPI. Use an MPIDependency:
|
2013-06-26 22:08:54 -05:00
|
|
|
depends_on :mpi => [<lang list>]
|
2012-08-07 01:37:46 -05:00
|
|
|
Where <lang list> is a comma delimited list that can include:
|
2013-06-26 22:08:54 -05:00
|
|
|
:cc, :cxx, :f77, :f90
|
2012-08-07 01:37:46 -05:00
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end
|
2010-09-13 15:16:09 -07:00
|
|
|
end
|
|
|
|
|
2013-01-03 11:22:31 -08:00
|
|
|
def audit_conflicts
|
2013-06-09 13:44:59 -05:00
|
|
|
f.conflicts.each do |c|
|
2013-01-03 11:22:31 -08:00
|
|
|
begin
|
2013-06-09 13:44:59 -05:00
|
|
|
Formula.factory(c.name)
|
2013-02-17 22:54:27 -06:00
|
|
|
rescue FormulaUnavailableError
|
2013-06-09 13:44:59 -05:00
|
|
|
problem "Can't find conflicting formula #{c.name.inspect}."
|
2013-01-03 11:22:31 -08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2010-08-07 15:23:13 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def audit_urls
|
|
|
|
unless f.homepage =~ %r[^https?://]
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "The homepage should start with http or https (url is #{f.homepage})."
|
|
|
|
end
|
|
|
|
|
|
|
|
# Check for http:// GitHub homepage urls, https:// is preferred.
|
|
|
|
# Note: only check homepages that are repo pages, not *.github.com hosts
|
|
|
|
if f.homepage =~ %r[^http://github\.com/]
|
|
|
|
problem "Use https:// URLs for homepages on GitHub (url is #{f.homepage})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-10-08 20:11:40 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Google Code homepages should end in a slash
|
|
|
|
if f.homepage =~ %r[^https?://code\.google\.com/p/[^/]+[^/]$]
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "Google Code homepage should end with a slash (url is #{f.homepage})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-10-30 08:26:36 -07:00
|
|
|
|
2013-04-06 22:10:33 -05:00
|
|
|
urls = @specs.map(&:url)
|
2010-10-21 07:51:47 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Check GNU urls; doesn't apply to mirrors
|
2013-05-27 18:31:18 -05:00
|
|
|
urls.grep(%r[^(?:https?|ftp)://(?!alpha).+/gnu/]) do |u|
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "\"ftpmirror.gnu.org\" is preferred for GNU software (url is #{u})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2011-03-21 12:54:00 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# the rest of the checks apply to mirrors as well
|
2013-04-06 22:10:33 -05:00
|
|
|
urls.concat(@specs.map(&:mirrors).flatten)
|
2011-11-18 15:22:04 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Check SourceForge urls
|
|
|
|
urls.each do |p|
|
2013-07-12 23:00:08 -07:00
|
|
|
# Skip if the URL looks like a SVN repo
|
2012-08-07 01:37:46 -05:00
|
|
|
next if p =~ %r[/svnroot/]
|
|
|
|
next if p =~ %r[svn\.sourceforge]
|
2011-12-08 22:16:19 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Is it a sourceforge http(s) URL?
|
|
|
|
next unless p =~ %r[^https?://.*\bsourceforge\.]
|
2011-12-08 22:16:19 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if p =~ /(\?|&)use_mirror=/
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "Don't use #{$1}use_mirror in SourceForge urls (url is #{p})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2012-06-17 17:59:30 -05:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if p =~ /\/download$/
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "Don't use /download in SourceForge urls (url is #{p})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2012-06-17 18:01:22 -05:00
|
|
|
|
2013-06-30 12:07:18 -07:00
|
|
|
if p =~ %r[^https?://sourceforge\.]
|
|
|
|
problem "Use http://downloads.sourceforge.net to get geolocation (url is #{p})."
|
|
|
|
end
|
|
|
|
|
|
|
|
if p =~ %r[^https?://prdownloads\.]
|
2013-01-21 10:33:56 +01:00
|
|
|
problem "Don't use prdownloads in SourceForge urls (url is #{p}).\n" +
|
2013-03-10 10:46:46 +01:00
|
|
|
"\tSee: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/"
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-08-09 11:59:16 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if p =~ %r[^http://\w+\.dl\.]
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "Don't use specific dl mirrors in SourceForge urls (url is #{p})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2012-03-17 19:49:49 -07:00
|
|
|
end
|
2010-09-09 14:16:05 -07:00
|
|
|
|
2013-03-10 10:46:46 +01:00
|
|
|
# Check for git:// GitHub repo urls, https:// is preferred.
|
2013-05-27 18:31:18 -05:00
|
|
|
urls.grep(%r[^git://[^/]*github\.com/]) do |u|
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "Use https:// URLs for accessing GitHub repositories (url is #{u})."
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2013-02-03 14:54:18 -06:00
|
|
|
|
2013-03-10 10:46:46 +01:00
|
|
|
# Check for http:// GitHub repo urls, https:// is preferred.
|
2013-05-27 18:31:18 -05:00
|
|
|
urls.grep(%r[^http://github\.com/.*\.git$]) do |u|
|
2013-03-10 10:46:46 +01:00
|
|
|
problem "Use https:// URLs for accessing GitHub repositories (url is #{u})."
|
2013-01-21 10:33:56 +01:00
|
|
|
end
|
2013-03-10 10:46:46 +01:00
|
|
|
|
2013-03-15 15:18:22 -07:00
|
|
|
# Use new-style archive downloads
|
2013-04-05 22:00:36 -05:00
|
|
|
urls.select { |u| u =~ %r[https://.*/(?:tar|zip)ball/] and not u =~ %r[\.git$] }.each do |u|
|
2013-03-15 15:18:22 -07:00
|
|
|
problem "Use /archive/ URLs for GitHub tarballs (url is #{u})."
|
|
|
|
end
|
|
|
|
|
2013-02-03 14:54:18 -06:00
|
|
|
if urls.any? { |u| u =~ /\.xz/ } && !f.deps.any? { |d| d.name == "xz" }
|
|
|
|
problem "Missing a build-time dependency on 'xz'"
|
|
|
|
end
|
2011-03-15 21:40:09 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def audit_specs
|
|
|
|
problem "Head-only (no stable download)" if f.head_only?
|
2012-01-25 22:41:53 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
[:stable, :devel].each do |spec|
|
|
|
|
s = f.send(spec)
|
|
|
|
next if s.nil?
|
|
|
|
|
|
|
|
if s.version.to_s.empty?
|
|
|
|
problem "Invalid or missing #{spec} version"
|
|
|
|
else
|
2012-07-10 16:10:16 -05:00
|
|
|
version_text = s.version unless s.version.detected_from_url?
|
2013-06-28 21:17:12 -05:00
|
|
|
version_url = Version.detect(s.url, s.specs)
|
2013-05-14 20:13:49 -05:00
|
|
|
if version_url.to_s == version_text.to_s && s.version.instance_of?(Version)
|
2012-08-07 01:37:46 -05:00
|
|
|
problem "#{spec} version #{version_text} is redundant with version scanned from URL"
|
|
|
|
end
|
|
|
|
end
|
2010-09-29 10:21:44 -07:00
|
|
|
|
2013-07-04 08:48:49 -07:00
|
|
|
if s.version.to_s =~ /^v/
|
|
|
|
problem "#{spec} version #{s.version} should not have a leading 'v'"
|
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
cksum = s.checksum
|
|
|
|
next if cksum.nil?
|
|
|
|
|
2013-06-29 15:36:14 -05:00
|
|
|
case cksum.hash_type
|
|
|
|
when :md5
|
|
|
|
problem "md5 checksums are deprecated, please use sha1 or sha256"
|
|
|
|
next
|
|
|
|
when :sha1 then len = 40
|
|
|
|
when :sha256 then len = 64
|
|
|
|
end
|
2012-08-07 01:37:46 -05:00
|
|
|
|
|
|
|
if cksum.empty?
|
|
|
|
problem "#{cksum.hash_type} is empty"
|
|
|
|
else
|
|
|
|
problem "#{cksum.hash_type} should be #{len} characters" unless cksum.hexdigest.length == len
|
|
|
|
problem "#{cksum.hash_type} contains invalid characters" unless cksum.hexdigest =~ /^[a-fA-F0-9]+$/
|
|
|
|
problem "#{cksum.hash_type} should be lowercase" unless cksum.hexdigest == cksum.hexdigest.downcase
|
|
|
|
end
|
2011-11-30 13:14:24 -06:00
|
|
|
end
|
2013-05-27 22:24:22 -07:00
|
|
|
|
|
|
|
# Check for :using that is already detected from the url
|
|
|
|
@specs.each do |s|
|
|
|
|
next if s.using.nil?
|
|
|
|
|
|
|
|
url_strategy = DownloadStrategyDetector.detect(s.url)
|
|
|
|
using_strategy = DownloadStrategyDetector.detect('', s.using)
|
|
|
|
|
|
|
|
problem "redundant :using specification in url or head" if url_strategy == using_strategy
|
|
|
|
end
|
2011-11-30 13:14:24 -06:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def audit_patches
|
2013-05-07 18:39:45 -05:00
|
|
|
Patches.new(f.patches).select(&:external?).each do |p|
|
|
|
|
case p.url
|
|
|
|
when %r[raw\.github\.com], %r[gist\.github\.com/raw]
|
|
|
|
unless p.url =~ /[a-fA-F0-9]{40}/
|
|
|
|
problem "GitHub/Gist patches should specify a revision:\n#{p.url}"
|
2012-09-06 20:47:05 -05:00
|
|
|
end
|
2013-05-07 18:39:45 -05:00
|
|
|
when %r[macports/trunk]
|
|
|
|
problem "MacPorts patches should specify a revision instead of trunk:\n#{p.url}"
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-09-09 14:16:05 -07:00
|
|
|
end
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-09-09 14:16:05 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def audit_text
|
|
|
|
if text =~ /<(Formula|AmazonWebServicesFormula|ScriptFileFormula|GithubGistFormula)/
|
|
|
|
problem "Use a space in class inheritance: class Foo < #{$1}"
|
2010-09-09 14:16:05 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Commented-out cmake support from default template
|
2013-01-04 09:30:57 -08:00
|
|
|
if (text =~ /# system "cmake/)
|
|
|
|
problem "Commented cmake call found"
|
2010-09-09 14:16:05 -07:00
|
|
|
end
|
|
|
|
|
2013-07-03 09:20:41 -07:00
|
|
|
# Comments from default template
|
2013-07-05 12:05:29 -07:00
|
|
|
if (text =~ /# PLEASE REMOVE/)
|
|
|
|
problem "Please remove default template comments"
|
|
|
|
end
|
2013-07-03 09:20:41 -07:00
|
|
|
if (text =~ /# if this fails, try separate make\/make install steps/)
|
|
|
|
problem "Please remove default template comments"
|
|
|
|
end
|
2013-07-05 12:05:29 -07:00
|
|
|
if (text =~ /# if your formula requires any X11\/XQuartz components/)
|
|
|
|
problem "Please remove default template comments"
|
|
|
|
end
|
|
|
|
if (text =~ /# if your formula's build system can't parallelize/)
|
2013-07-03 09:20:41 -07:00
|
|
|
problem "Please remove default template comments"
|
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# FileUtils is included in Formula
|
|
|
|
if text =~ /FileUtils\.(\w+)/
|
|
|
|
problem "Don't need 'FileUtils.' before #{$1}."
|
|
|
|
end
|
2010-09-09 14:16:05 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Check for long inreplace block vars
|
|
|
|
if text =~ /inreplace .* do \|(.{2,})\|/
|
|
|
|
problem "\"inreplace <filenames> do |s|\" is preferred over \"|#{$1}|\"."
|
|
|
|
end
|
2012-01-25 22:41:53 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Check for string interpolation of single values.
|
2013-04-26 19:18:12 -05:00
|
|
|
if text =~ /(system|inreplace|gsub!|change_make_var!).*[ ,]"#\{([\w.]+)\}"/
|
2012-08-07 01:37:46 -05:00
|
|
|
problem "Don't need to interpolate \"#{$2}\" with #{$1}"
|
|
|
|
end
|
2012-01-25 22:41:53 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Check for string concatenation; prefer interpolation
|
|
|
|
if text =~ /(#\{\w+\s*\+\s*['"][^}]+\})/
|
|
|
|
problem "Try not to concatenate paths in string interpolation:\n #{$1}"
|
2012-04-05 21:12:02 -05:00
|
|
|
end
|
2012-01-25 22:41:53 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Prefer formula path shortcuts in Pathname+
|
|
|
|
if text =~ %r{\(\s*(prefix\s*\+\s*(['"])(bin|include|libexec|lib|sbin|share)[/'"])}
|
|
|
|
problem "\"(#{$1}...#{$2})\" should be \"(#{$3}+...)\""
|
|
|
|
end
|
2012-06-18 19:58:35 -05:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ %r[((man)\s*\+\s*(['"])(man[1-8])(['"]))]
|
|
|
|
problem "\"#{$1}\" should be \"#{$4}\""
|
|
|
|
end
|
2012-04-05 21:12:02 -05:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Prefer formula path shortcuts in strings
|
|
|
|
if text =~ %r[(\#\{prefix\}/(bin|include|libexec|lib|sbin|share))]
|
|
|
|
problem "\"#{$1}\" should be \"\#{#{$2}}\""
|
2012-04-05 21:12:02 -05:00
|
|
|
end
|
2012-01-25 22:41:53 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ %r[((\#\{prefix\}/share/man/|\#\{man\}/)(man[1-8]))]
|
|
|
|
problem "\"#{$1}\" should be \"\#{#{$3}}\""
|
|
|
|
end
|
2012-01-25 22:41:53 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ %r[((\#\{share\}/(man)))[/'"]]
|
|
|
|
problem "\"#{$1}\" should be \"\#{#{$3}}\""
|
|
|
|
end
|
2010-08-21 11:55:57 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ %r[(\#\{prefix\}/share/(info|man))]
|
|
|
|
problem "\"#{$1}\" should be \"\#{#{$2}}\""
|
|
|
|
end
|
2010-08-21 11:55:57 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Commented-out depends_on
|
|
|
|
if text =~ /#\s*depends_on\s+(.+)\s*$/
|
2012-09-13 07:14:45 -07:00
|
|
|
problem "Commented-out dep #{$1}"
|
2010-09-07 09:23:29 -07:00
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# No trailing whitespace, please
|
2013-04-05 22:00:36 -05:00
|
|
|
if text =~ /[\t ]+$/
|
2012-09-13 07:14:45 -07:00
|
|
|
problem "Trailing whitespace was found"
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-08-21 11:55:57 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ /if\s+ARGV\.include\?\s+'--(HEAD|devel)'/
|
|
|
|
problem "Use \"if ARGV.build_#{$1.downcase}?\" instead"
|
|
|
|
end
|
2012-03-17 19:49:49 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ /make && make/
|
2012-09-13 07:14:45 -07:00
|
|
|
problem "Use separate make calls"
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2012-03-17 19:49:49 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ /^[ ]*\t/
|
|
|
|
problem "Use spaces instead of tabs for indentation"
|
|
|
|
end
|
2011-05-31 13:23:42 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# xcodebuild should specify SYMROOT
|
|
|
|
if text =~ /system\s+['"]xcodebuild/ and not text =~ /SYMROOT=/
|
|
|
|
problem "xcodebuild should be passed an explicit \"SYMROOT\""
|
|
|
|
end
|
2012-02-16 20:27:08 -08:00
|
|
|
|
2012-09-03 19:18:58 -07:00
|
|
|
if text =~ /ENV\.x11/
|
|
|
|
problem "Use \"depends_on :x11\" instead of \"ENV.x11\""
|
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
# Avoid hard-coding compilers
|
2013-02-17 23:09:43 -06:00
|
|
|
if text =~ %r{(system|ENV\[.+\]\s?=)\s?['"](/usr/bin/)?(gcc|llvm-gcc|clang)['" ]}
|
2012-08-07 01:37:46 -05:00
|
|
|
problem "Use \"\#{ENV.cc}\" instead of hard-coding \"#{$3}\""
|
|
|
|
end
|
2010-08-10 11:52:03 -07:00
|
|
|
|
2013-02-17 23:09:43 -06:00
|
|
|
if text =~ %r{(system|ENV\[.+\]\s?=)\s?['"](/usr/bin/)?((g|llvm-g|clang)\+\+)['" ]}
|
2012-08-07 01:37:46 -05:00
|
|
|
problem "Use \"\#{ENV.cxx}\" instead of hard-coding \"#{$3}\""
|
|
|
|
end
|
2011-02-20 15:03:15 -08:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ /system\s+['"](env|export)/
|
|
|
|
problem "Use ENV instead of invoking '#{$1}' to modify the environment"
|
|
|
|
end
|
2012-01-22 22:32:15 -06:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
if text =~ /version == ['"]HEAD['"]/
|
2012-08-22 20:59:43 -07:00
|
|
|
problem "Use 'build.head?' instead of inspecting 'version'"
|
|
|
|
end
|
|
|
|
|
2012-08-25 09:36:01 -07:00
|
|
|
if text =~ /build\.include\?\s+['"]\-\-(.*)['"]/
|
2012-09-13 07:14:45 -07:00
|
|
|
problem "Reference '#{$1}' without dashes"
|
2012-08-25 09:36:01 -07:00
|
|
|
end
|
|
|
|
|
2013-01-21 10:33:56 +01:00
|
|
|
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
|
|
|
|
|
2013-03-10 09:37:05 +01:00
|
|
|
if text =~ /ARGV\.(?!(debug\?|verbose\?|find[\(\s]))/
|
2012-09-13 07:14:45 -07:00
|
|
|
problem "Use build instead of ARGV to check options"
|
2012-08-22 20:59:43 -07:00
|
|
|
end
|
|
|
|
|
|
|
|
if text =~ /def options/
|
2012-09-13 07:14:45 -07:00
|
|
|
problem "Use new-style option definitions"
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2012-09-04 18:18:14 -05:00
|
|
|
|
|
|
|
if text =~ /MACOS_VERSION/
|
|
|
|
problem "Use MacOS.version instead of MACOS_VERSION"
|
|
|
|
end
|
2012-09-10 16:40:13 -05:00
|
|
|
|
2013-04-06 22:11:26 -05:00
|
|
|
cats = %w{leopard snow_leopard lion mountain_lion}.join("|")
|
|
|
|
if text =~ /MacOS\.(?:#{cats})\?/
|
|
|
|
problem "\"#{$&}\" is deprecated, use a comparison to MacOS.version instead"
|
2012-09-10 16:40:13 -05:00
|
|
|
end
|
2012-09-13 07:14:45 -07:00
|
|
|
|
|
|
|
if text =~ /skip_clean\s+:all/
|
|
|
|
problem "`skip_clean :all` is deprecated; brew no longer strips symbols"
|
|
|
|
end
|
2013-01-27 14:27:32 -08:00
|
|
|
|
2013-04-06 22:11:26 -05:00
|
|
|
if text =~ /depends_on [A-Z][\w:]+\.new$/
|
|
|
|
problem "`depends_on` can take requirement classes instead of instances"
|
2013-01-27 14:27:32 -08:00
|
|
|
end
|
2013-04-22 15:06:42 -05:00
|
|
|
|
|
|
|
if text =~ /^def (\w+).*$/
|
|
|
|
problem "Define method #{$1.inspect} in the class body, not at the top-level"
|
|
|
|
end
|
2013-06-23 20:40:00 -07:00
|
|
|
|
|
|
|
if text =~ /ENV.fortran/
|
|
|
|
problem "Use `depends_on :fortran` instead of `ENV.fortran`"
|
|
|
|
end
|
2013-07-16 20:38:50 -05:00
|
|
|
|
|
|
|
if text =~ /depends_on :(.+) (if.+|unless.+)$/
|
|
|
|
audit_conditional_dep($1.to_sym, $2, $&)
|
|
|
|
end
|
|
|
|
|
|
|
|
if text =~ /depends_on ['"](.+)['"] (if.+|unless.+)$/
|
|
|
|
audit_conditional_dep($1, $2, $&)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_conditional_dep(dep, condition, line)
|
|
|
|
case condition
|
|
|
|
when /if build\.include\? ['"]with-#{dep}['"]$/, /if build\.with\? ['"]#{dep}['"]$/
|
|
|
|
problem %{Replace #{line.inspect} with "depends_on #{quote_dep(dep)} => :optional"}
|
|
|
|
when /unless build\.include\? ['"]without-#{dep}['"]$/, /unless build\.without\? ['"]#{dep}['"]$/
|
|
|
|
problem %{Replace #{line.inspect} with "depends_on #{quote_dep(dep)} => :recommended"}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def quote_dep(dep)
|
|
|
|
Symbol === dep ? dep.inspect : "'#{dep}'"
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-08-09 11:59:16 -07:00
|
|
|
|
2013-01-21 10:33:56 +01:00
|
|
|
def audit_python
|
|
|
|
if text =~ /(def\s*)?which_python/
|
2013-07-16 11:10:14 +02:00
|
|
|
problem "Replace `which_python` by `python.xy`, which returns e.g. 'python2.7'"
|
2013-01-21 10:33:56 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
if text =~ /which\(?["']python/
|
|
|
|
problem "Don't locate python with `which 'python'`, use `python.binary` instead"
|
|
|
|
end
|
|
|
|
|
2013-07-16 11:10:14 +02:00
|
|
|
if text =~ /LanguageModuleDependency.new\s?\(\s?:python/
|
|
|
|
problem <<-EOS.undent
|
|
|
|
Python: Replace `LanguageModuleDependency.new(:python,'PyPi-name','module')`
|
|
|
|
by the new `depends_on :python => ['module' => 'PyPi-name']`
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
|
2013-06-06 16:41:14 +02:00
|
|
|
# Checks that apply only to code in def install
|
|
|
|
if text =~ /(\s*)def\s+install((.*\n)*?)(\1end)/
|
|
|
|
install_body = $2
|
|
|
|
|
|
|
|
if install_body =~ /system\(?\s*['"]python/
|
2013-07-16 11:10:14 +02:00
|
|
|
problem "Instead of `system 'python', ...`, call `system python, ...`"
|
2013-06-06 16:41:14 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
if text =~ /system\(?\s*python\.binary/
|
2013-07-16 11:10:14 +02:00
|
|
|
problem "Instead of `system python.binary, ...`, call `system python, ...`"
|
2013-01-21 10:33:56 +01:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-06-06 16:41:14 +02:00
|
|
|
# Checks that apply only to code in def caveats
|
2013-01-21 10:33:56 +01:00
|
|
|
if text =~ /(\s*)def\s+caveats((.*\n)*?)(\1end)/ || /(\s*)def\s+caveats;(.*?)end/
|
|
|
|
caveats_body = $2
|
2013-06-06 16:41:14 +02:00
|
|
|
if caveats_body =~ /[ \{=](python[23]?)\.(.*\w)/
|
2013-01-21 10:33:56 +01:00
|
|
|
# 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
|
|
|
|
|
2013-06-06 16:41:14 +02:00
|
|
|
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*=[^=]/
|
2013-07-16 11:10:14 +02:00
|
|
|
problem "Don't set the PYTHONPATH, instead declare `depends_on :python`"
|
2013-06-06 16:41:14 +02:00
|
|
|
end
|
|
|
|
else
|
|
|
|
# So if there is no PythonInstalled requirement, we can check if the
|
|
|
|
# formula still uses python and should add a `depends_on :python`
|
|
|
|
unless f.name.to_s =~ /(pypy[0-9]*)|(python[0-9]*)/
|
|
|
|
if text =~ /system.["' ]?python([0-9"'])?/
|
|
|
|
problem "If the formula uses Python, it should declare so by `depends_on :python#{$1}`"
|
|
|
|
end
|
|
|
|
if text =~ /setup\.py/
|
|
|
|
problem <<-EOS.undent
|
|
|
|
If the formula installs Python bindings you should declare `depends_on :python[3]`"
|
|
|
|
EOS
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2013-01-21 10:33:56 +01:00
|
|
|
# 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
|
|
|
|
|
2013-07-15 19:29:08 -07:00
|
|
|
def audit_check_output warning_and_description
|
|
|
|
return unless warning_and_description
|
|
|
|
warning, _ = *warning_and_description
|
|
|
|
problem warning
|
|
|
|
end
|
|
|
|
|
|
|
|
def audit_installed
|
|
|
|
audit_check_output(check_manpages)
|
|
|
|
audit_check_output(check_infopages)
|
|
|
|
audit_check_output(check_jars)
|
|
|
|
audit_check_output(check_non_libraries)
|
|
|
|
audit_check_output(check_non_executables(f.bin))
|
|
|
|
audit_check_output(check_non_executables(f.sbin))
|
|
|
|
end
|
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def audit
|
|
|
|
audit_file
|
|
|
|
audit_specs
|
|
|
|
audit_urls
|
|
|
|
audit_deps
|
2013-01-03 11:22:31 -08:00
|
|
|
audit_conflicts
|
2012-08-07 01:37:46 -05:00
|
|
|
audit_patches
|
|
|
|
audit_text
|
2013-01-21 10:33:56 +01:00
|
|
|
audit_python
|
2013-07-15 19:29:08 -07:00
|
|
|
audit_installed
|
2012-08-07 01:37:46 -05:00
|
|
|
end
|
2010-11-09 13:00:33 +00:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
private
|
2011-05-31 13:23:42 -07:00
|
|
|
|
2012-08-07 01:37:46 -05:00
|
|
|
def problem p
|
|
|
|
@problems << p
|
2010-06-23 11:20:47 -07:00
|
|
|
end
|
|
|
|
end
|