627 lines
19 KiB
Ruby
Raw Normal View History

require 'formula'
require 'utils'
2013-08-19 12:32:57 -05:00
require 'extend/ENV'
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-08-19 13:03:41 -05:00
ENV.activate_extensions!
ENV.setup_build_environment
2012-08-07 01:37:46 -05:00
ff = if ARGV.named.empty?
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
2012-08-07 01:37:46 -05:00
# Formula extensions for auditing
class Formula
def head_only?
@head and @stable.nil?
end
2012-08-07 01:37:46 -05:00
def text
@text ||= FormulaText.new(@path)
end
2012-08-07 01:37:46 -05:00
end
2012-08-07 01:37:46 -05:00
class FormulaText
def initialize path
@text = path.open('r') { |f| f.read }
end
2012-08-07 01:37:46 -05:00
def without_patch
@text.split("__END__")[0].strip()
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
end
2012-08-07 01:37:46 -05:00
def has_trailing_newline?
/\Z\n/ =~ @text
end
2012-08-07 01:37:46 -05:00
end
2012-08-07 01:37:46 -05:00
class FormulaAuditor
include FormulaCellarChecks
attr_reader :f, :text, :problems
2012-08-07 01:37:46 -05:00
BUILD_TIME_DEPS = %W[
autoconf
automake
boost-build
bsdmake
cmake
imake
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
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
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
unless f.text.has_trailing_newline?
2012-08-07 01:37:46 -05:00
problem "File should end with a newline"
end
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|
2013-08-14 21:30:23 -07:00
real_name = d.to_formula.name
problem "Dependency '#{d}' is an alias; use the canonical name '#{real_name}'."
2012-08-07 01:37:46 -05:00
end
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.
f.deps.each do |dep|
2012-08-07 01:37:46 -05:00
begin
dep_f = dep.to_formula
rescue FormulaUnavailableError
problem "Can't find dependency #{dep.name.inspect}."
next
2012-08-07 01:37:46 -05:00
end
dep.options.reject do |opt|
2013-07-23 10:51:07 -07:00
# TODO -- fix for :recommended, should still allow --with-xyz
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
when *BUILD_TIME_DEPS
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'
problem %{#{dep} dependency should be "depends_on '#{dep}' => :build"}
when "git", "ruby", "emacs", "mercurial"
2012-08-07 01:37:46 -05:00
problem <<-EOS.undent
Don't use #{dep} as a dependency. We allow non-Homebrew
#{dep} installations.
2012-08-07 01:37:46 -05:00
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
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:
depends_on :mpi => [<lang list>]
2012-08-07 01:37:46 -05:00
Where <lang list> is a comma delimited list that can include:
:cc, :cxx, :f77, :f90
2012-08-07 01:37:46 -05:00
EOS
end
end
end
2013-01-03 11:22:31 -08:00
def audit_conflicts
f.conflicts.each do |c|
2013-01-03 11:22:31 -08:00
begin
Formula.factory(c.name)
2013-02-17 22:54:27 -06:00
rescue FormulaUnavailableError
problem "Can't find conflicting formula #{c.name.inspect}."
2013-01-03 11:22:31 -08:00
end
end
end
2012-08-07 01:37:46 -05:00
def audit_urls
unless f.homepage =~ %r[^https?://]
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/[^/]+[^/]$]
problem "Google Code homepage should end with a slash (url is #{f.homepage})."
2012-08-07 01:37:46 -05:00
end
2013-04-06 22:10:33 -05:00
urls = @specs.map(&:url)
2012-08-07 01:37:46 -05:00
# Check GNU urls; doesn't apply to mirrors
urls.grep(%r[^(?:https?|ftp)://(?!alpha).+/gnu/]) do |u|
problem "\"ftpmirror.gnu.org\" is preferred for GNU software (url is #{u})."
2012-08-07 01:37:46 -05:00
end
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)
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]
2012-08-07 01:37:46 -05:00
# Is it a sourceforge http(s) URL?
next unless p =~ %r[^https?://.*\bsourceforge\.]
2012-08-07 01:37:46 -05:00
if p =~ /(\?|&)use_mirror=/
problem "Don't use #{$1}use_mirror in SourceForge urls (url is #{p})."
2012-08-07 01:37:46 -05:00
end
2012-08-07 01:37:46 -05:00
if p =~ /\/download$/
problem "Don't use /download in SourceForge urls (url is #{p})."
2012-08-07 01:37:46 -05:00
end
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\.]
problem "Don't use prdownloads in SourceForge urls (url is #{p}).\n" +
"\tSee: http://librelist.com/browser/homebrew/2011/1/12/prdownloads-is-bad/"
2012-08-07 01:37:46 -05:00
end
2012-08-07 01:37:46 -05:00
if p =~ %r[^http://\w+\.dl\.]
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
# Check for git:// GitHub repo urls, https:// is preferred.
urls.grep(%r[^git://[^/]*github\.com/]) do |u|
problem "Use https:// URLs for accessing GitHub repositories (url is #{u})."
2012-08-07 01:37:46 -05:00
end
# 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
# Use new-style archive downloads
2013-07-23 11:21:37 -05:00
urls.select { |u| u =~ %r[https://.*/(?:tar|zip)ball/] && u !~ %r[\.git$] }.each do |u|
problem "Use /archive/ URLs for GitHub tarballs (url is #{u})."
end
if urls.any? { |u| u =~ /\.xz/ } && !f.deps.any? { |d| d.name == "xz" }
problem "Missing a build-time dependency on 'xz'"
end
end
2012-08-07 01:37:46 -05:00
def audit_specs
problem "Head-only (no stable download)" if f.head_only?
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?
version_url = Version.detect(s.url, s.specs)
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
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?
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
end
# 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
end
2012-08-07 01:37:46 -05:00
def audit_patches
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}"
end
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
2013-07-16 23:15:22 -05:00
def audit_text
2013-07-23 11:21:37 -05:00
if text =~ /system\s+['"]xcodebuild/ && text !~ /SYMROOT=/
2013-07-16 23:15:22 -05:00
problem "xcodebuild should be passed an explicit \"SYMROOT\""
end
end
def audit_line(line)
2013-07-16 21:25:02 -05:00
if line =~ /<(Formula|AmazonWebServicesFormula|ScriptFileFormula|GithubGistFormula)/
2012-08-07 01:37:46 -05:00
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-07-16 21:39:46 -05:00
if line =~ /# 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-16 21:39:46 -05:00
if line =~ /# PLEASE REMOVE/
2013-07-05 12:05:29 -07:00
problem "Please remove default template comments"
end
2013-07-16 21:39:46 -05:00
if line =~ /# if this fails, try separate make\/make install steps/
2013-07-03 09:20:41 -07:00
problem "Please remove default template comments"
end
2013-07-16 21:39:46 -05:00
if line =~ /# if your formula requires any X11\/XQuartz components/
2013-07-05 12:05:29 -07:00
problem "Please remove default template comments"
end
2013-07-16 21:39:46 -05:00
if line =~ /# 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
2013-07-16 21:25:02 -05:00
if line =~ /FileUtils\.(\w+)/
2012-08-07 01:37:46 -05:00
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
2013-07-16 21:25:02 -05:00
if line =~ /inreplace .* do \|(.{2,})\|/
2012-08-07 01:37:46 -05:00
problem "\"inreplace <filenames> do |s|\" is preferred over \"|#{$1}|\"."
end
2012-08-07 01:37:46 -05:00
# Check for string interpolation of single values.
2013-07-16 21:25:02 -05:00
if line =~ /(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-08-07 01:37:46 -05:00
# Check for string concatenation; prefer interpolation
2013-07-16 21:25:02 -05:00
if line =~ /(#\{\w+\s*\+\s*['"][^}]+\})/
2012-08-07 01:37:46 -05:00
problem "Try not to concatenate paths in string interpolation:\n #{$1}"
2012-04-05 21:12:02 -05:00
end
2012-08-07 01:37:46 -05:00
# Prefer formula path shortcuts in Pathname+
2013-07-16 21:25:02 -05:00
if line =~ %r{\(\s*(prefix\s*\+\s*(['"])(bin|include|libexec|lib|sbin|share)[/'"])}
2012-08-07 01:37:46 -05:00
problem "\"(#{$1}...#{$2})\" should be \"(#{$3}+...)\""
end
2013-07-16 21:25:02 -05:00
if line =~ %r[((man)\s*\+\s*(['"])(man[1-8])(['"]))]
2012-08-07 01:37:46 -05:00
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
2013-07-16 21:25:02 -05:00
if line =~ %r[(\#\{prefix\}/(bin|include|libexec|lib|sbin|share))]
2012-08-07 01:37:46 -05:00
problem "\"#{$1}\" should be \"\#{#{$2}}\""
2012-04-05 21:12:02 -05:00
end
2013-07-16 21:25:02 -05:00
if line =~ %r[((\#\{prefix\}/share/man/|\#\{man\}/)(man[1-8]))]
2012-08-07 01:37:46 -05:00
problem "\"#{$1}\" should be \"\#{#{$3}}\""
end
2013-07-16 21:25:02 -05:00
if line =~ %r[((\#\{share\}/(man)))[/'"]]
2012-08-07 01:37:46 -05:00
problem "\"#{$1}\" should be \"\#{#{$3}}\""
end
2013-07-16 21:25:02 -05:00
if line =~ %r[(\#\{prefix\}/share/(info|man))]
2012-08-07 01:37:46 -05:00
problem "\"#{$1}\" should be \"\#{#{$2}}\""
end
2012-08-07 01:37:46 -05:00
# Commented-out depends_on
2013-07-16 21:25:02 -05:00
if line =~ /#\s*depends_on\s+(.+)\s*$/
2012-09-13 07:14:45 -07:00
problem "Commented-out dep #{$1}"
end
2012-08-07 01:37:46 -05:00
# No trailing whitespace, please
2013-07-16 21:25:02 -05:00
if line =~ /[\t ]+$/
2012-09-13 07:14:45 -07:00
problem "Trailing whitespace was found"
2012-08-07 01:37:46 -05:00
end
2013-07-16 21:25:02 -05:00
if line =~ /if\s+ARGV\.include\?\s+'--(HEAD|devel)'/
2012-08-07 01:37:46 -05:00
problem "Use \"if ARGV.build_#{$1.downcase}?\" instead"
end
2012-03-17 19:49:49 -07:00
2013-07-16 21:25:02 -05:00
if line =~ /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
2013-07-16 21:25:02 -05:00
if line =~ /^[ ]*\t/
2012-08-07 01:37:46 -05:00
problem "Use spaces instead of tabs for indentation"
end
2013-07-16 21:25:02 -05:00
if line =~ /ENV\.x11/
2012-09-03 19:18:58 -07:00
problem "Use \"depends_on :x11\" instead of \"ENV.x11\""
end
2012-08-07 01:37:46 -05:00
# Avoid hard-coding compilers
2013-07-16 21:25:02 -05:00
if line =~ %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
2013-07-16 21:25:02 -05:00
if line =~ %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
2013-07-16 21:25:02 -05:00
if line =~ /system\s+['"](env|export)/
2012-08-07 01:37:46 -05:00
problem "Use ENV instead of invoking '#{$1}' to modify the environment"
end
2013-07-16 21:25:02 -05:00
if line =~ /version == ['"]HEAD['"]/
problem "Use 'build.head?' instead of inspecting 'version'"
end
2013-07-16 21:25:02 -05:00
if line =~ /build\.include\?\s+['"]\-\-(.*)['"]/
2012-09-13 07:14:45 -07:00
problem "Reference '#{$1}' without dashes"
end
2013-07-16 21:25:02 -05:00
if line =~ /build\.with\?\s+['"]-?-?with-(.*)['"]/
problem "No double 'with': Use `build.with? '#{$1}'` to check for \"--with-#{$1}\""
end
2013-07-16 21:25:02 -05:00
if line =~ /build\.without\?\s+['"]-?-?without-(.*)['"]/
problem "No double 'without': Use `build.without? '#{$1}'` to check for \"--without-#{$1}\""
end
2013-07-07 11:56:11 -07:00
unless f.name == 'mongodb' # Mongo writes out a Ruby script that uses ARGV
if line =~ /ARGV\.(?!(debug\?|verbose\?|value[\(\s]))/
problem "Use build instead of ARGV to check options"
end
end
2013-07-16 21:25:02 -05:00
if line =~ /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
2013-07-16 21:25:02 -05:00
if line =~ /MACOS_VERSION/
problem "Use MacOS.version instead of MACOS_VERSION"
end
2013-04-06 22:11:26 -05:00
cats = %w{leopard snow_leopard lion mountain_lion}.join("|")
2013-07-16 21:25:02 -05:00
if line =~ /MacOS\.(?:#{cats})\?/
2013-04-06 22:11:26 -05:00
problem "\"#{$&}\" is deprecated, use a comparison to MacOS.version instead"
end
2012-09-13 07:14:45 -07:00
2013-07-16 21:25:02 -05:00
if line =~ /skip_clean\s+:all/
2012-09-13 07:14:45 -07:00
problem "`skip_clean :all` is deprecated; brew no longer strips symbols"
end
2013-01-27 14:27:32 -08:00
2013-07-16 21:25:02 -05:00
if line =~ /depends_on [A-Z][\w:]+\.new$/
2013-04-06 22:11:26 -05:00
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
2013-07-16 21:25:02 -05:00
if line =~ /^def (\w+).*$/
2013-04-22 15:06:42 -05:00
problem "Define method #{$1.inspect} in the class body, not at the top-level"
end
2013-06-23 20:40:00 -07:00
2013-07-16 21:25:02 -05:00
if line =~ /ENV.fortran/
2013-06-23 20:40:00 -07:00
problem "Use `depends_on :fortran` instead of `ENV.fortran`"
end
2013-07-16 21:25:02 -05:00
if line =~ /depends_on :(.+) (if.+|unless.+)$/
audit_conditional_dep($1.to_sym, $2, $&)
end
2013-07-16 21:25:02 -05:00
if line =~ /depends_on ['"](.+)['"] (if.+|unless.+)$/
audit_conditional_dep($1, $2, $&)
end
end
def audit_conditional_dep(dep, condition, line)
2013-07-23 11:21:37 -05:00
quoted_dep = quote_dep(dep)
dep = Regexp.escape(dep.to_s)
case condition
when /if build\.include\? ['"]with-#{dep}['"]$/, /if build\.with\? ['"]#{dep}['"]$/
2013-07-23 11:21:37 -05:00
problem %{Replace #{line.inspect} with "depends_on #{quoted_dep} => :optional"}
when /unless build\.include\? ['"]without-#{dep}['"]$/, /unless build\.without\? ['"]#{dep}['"]$/
2013-07-23 11:21:37 -05:00
problem %{Replace #{line.inspect} with "depends_on #{quoted_dep} => :recommended"}
end
end
def quote_dep(dep)
Symbol === dep ? dep.inspect : "'#{dep}'"
2012-08-07 01:37:46 -05:00
end
def audit_python
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 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/
problem "Instead of `system 'python', ...`, call `system python, ...`"
2013-06-06 16:41:14 +02:00
end
if text =~ /system\(?\s*python\.binary/
problem "Instead of `system python.binary, ...`, call `system python, ...`"
end
end
2013-06-06 16:41:14 +02:00
# Checks that apply only to code in def caveats
2013-07-22 22:53:11 -05:00
if text =~ /(\s*)def\s+caveats((.*\n)*?)(\1end)/ || text =~ /(\s*)def\s+caveats;(.*?)end/
caveats_body = $2
2013-07-22 22:53:22 -05:00
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
unless f.requirements.any?{ |r| r.kind_of?(PythonInstalled) }
2013-06-06 16:41:14 +02:00
# 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
# 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_check_output warning_and_description
return unless warning_and_description
2013-09-07 17:38:15 +01:00
warning, description = *warning_and_description
problem "#{warning}\n#{description}"
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
2013-07-16 23:15:22 -05:00
audit_text
text.each_line { |line| audit_line(line) }
audit_python
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
2012-08-07 01:37:46 -05:00
def problem p
@problems << p
end
end