727 lines
21 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
module Homebrew
2012-08-07 01:37:46 -05:00
def audit
formula_count = 0
problem_count = 0
strict = ARGV.include? "--strict"
if strict && ARGV.formulae.any? && MacOS.version >= :mavericks
require "cmd/style"
ohai "brew style #{ARGV.formulae.join " "}"
style
end
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
output_header = !strict
2014-12-27 12:38:04 +00:00
2012-08-07 01:37:46 -05:00
ff.each do |f|
2014-12-27 12:38:04 +00:00
fa = FormulaAuditor.new(f, :strict => strict)
2012-08-07 01:37:46 -05:00
fa.audit
unless fa.problems.empty?
unless output_header
puts
ohai "audit problems"
output_header = true
end
2012-08-07 01:37:46 -05:00
formula_count += 1
problem_count += fa.problems.size
puts "#{f.name}:", fa.problems.map { |p| " * #{p}" }, ""
2012-08-07 01:37:46 -05:00
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
2012-08-07 01:37:46 -05:00
class FormulaText
def initialize path
2014-06-04 15:37:36 -05:00
@text = path.open("rb", &:read)
end
2012-08-07 01:37:46 -05:00
def without_patch
@text.split("\n__END__").first
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 :formula, :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
]
FILEUTILS_METHODS = FileUtils.singleton_methods(false).join "|"
2014-12-27 12:38:04 +00:00
def initialize(formula, options={})
@formula = formula
2014-12-27 12:38:04 +00:00
@strict = !!options[:strict]
2012-08-07 01:37:46 -05:00
@problems = []
@text = FormulaText.new(formula.path)
@specs = %w{stable devel head}.map { |s| formula.send(s) }.compact
2012-08-07 01:37:46 -05:00
end
def audit_file
2014-10-29 22:47:17 -05:00
unless formula.path.stat.mode == 0100644
problem "Incorrect file permissions: chmod 644 #{formula.path}"
2012-08-07 01:37:46 -05:00
end
if text.has_DATA? and not text.has_END?
2012-08-07 01:37:46 -05:00
problem "'DATA' was found, but no '__END__'"
end
if text.has_END? and not text.has_DATA?
2012-08-07 01:37:46 -05:00
problem "'__END__' was found, but 'DATA' is not used"
end
2010-08-15 11:32:45 -07:00
unless text.has_trailing_newline?
2012-08-07 01:37:46 -05:00
problem "File should end with a newline"
end
2014-12-27 20:46:01 +00:00
end
2014-12-27 20:46:01 +00:00
def audit_class
if @strict
2014-12-27 20:46:01 +00:00
unless formula.test_defined?
problem "A `test do` test block should be added"
end
end
end
2014-10-17 00:11:46 -05:00
@@aliases ||= Formula.aliases
2014-10-17 00:11:46 -05:00
def audit_deps
2014-10-17 00:07:35 -05:00
@specs.each do |spec|
# Check for things we don't like to depend on.
# We allow non-Homebrew installs whenever possible.
spec.deps.each do |dep|
begin
dep_f = dep.to_formula
rescue TapFormulaUnavailableError
# Don't complain about missing cross-tap dependencies
next
rescue FormulaUnavailableError
problem "Can't find dependency #{dep.name.inspect}."
next
end
2014-10-17 00:11:46 -05:00
if @@aliases.include?(dep.name)
problem "Dependency '#{dep.name}' is an alias; use the canonical name '#{dep.to_formula.name}'."
end
2014-10-17 00:07:35 -05:00
dep.options.reject do |opt|
next true if dep_f.option_defined?(opt)
dep_f.requirements.detect do |r|
if r.recommended?
opt.name == "with-#{r.name}"
elsif r.optional?
opt.name == "without-#{r.name}"
end
end
2014-10-17 00:07:35 -05:00
end.each do |opt|
problem "Dependency #{dep} does not define option #{opt.name.inspect}"
end
2014-10-17 00:07:35 -05:00
case dep.name
when *BUILD_TIME_DEPS
next if dep.build? or dep.run?
problem %{#{dep} dependency should be "depends_on '#{dep}' => :build"}
when "git", "ruby", "mercurial"
problem "Don't use #{dep} as a dependency. We allow non-Homebrew #{dep} installations."
2014-10-17 00:07:35 -05:00
when 'gfortran'
problem "Use `depends_on :fortran` instead of `depends_on 'gfortran'`"
when 'open-mpi', 'mpich2'
problem <<-EOS.undent
There are multiple conflicting ways to install MPI. Use an MPIDependency:
depends_on :mpi => [<lang list>]
Where <lang list> is a comma delimited list that can include:
:cc, :cxx, :f77, :f90
EOS
end
2012-08-07 01:37:46 -05:00
end
end
end
2013-01-03 11:22:31 -08:00
def audit_conflicts
formula.conflicts.each do |c|
2013-01-03 11:22:31 -08:00
begin
2014-02-24 20:23:21 -08:00
Formulary.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
def audit_options
formula.options.each do |o|
next unless @strict
if o.name !~ /with(out)?-/ && o.name != "c++11" && o.name != "universal" && o.name != "32-bit"
problem "Options should begin with with/without. Migrate '--#{o.name}' with `deprecated_option`."
end
end
end
2012-08-07 01:37:46 -05:00
def audit_urls
homepage = formula.homepage
unless homepage =~ %r[^https?://]
problem "The homepage should start with http or https (URL is #{homepage})."
end
# Check for http:// GitHub homepage urls, https:// is preferred.
# Note: only check homepages that are repo pages, not *.github.com hosts
if homepage =~ %r[^http://github\.com/]
problem "Use https:// URLs for homepages on GitHub (URL is #{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 homepage =~ %r[^https?://code\.google\.com/p/[^/]+[^/]$]
problem "Google Code homepage should end with a slash (URL is #{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?://.*\b(sourceforge|sf)\.(com|net)]
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
if p.start_with? "http://downloads"
problem "Use https:// URLs for downloads from SourceForge (url is #{p})."
end
2012-03-17 19:49:49 -07:00
end
2010-09-09 14:16:05 -07:00
# Check for Google Code download urls, https:// is preferred
urls.grep(%r[^http://.*\.googlecode\.com/files.*]) do |u|
problem "Use https:// URLs for downloads from Google Code (url is #{u})."
end
# 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
urls.select { |u| u =~ %r[https://.*github.*/(?:tar|zip)ball/] && u !~ %r[\.git$] }.each do |u|
problem "Use /archive/ URLs for GitHub tarballs (url is #{u})."
end
# Don't use GitHub .zip files
urls.select { |u| u =~ %r[https://.*github.*/(archive|releases)/.*\.zip$] && u !~ %r[releases/download] }.each do |u|
problem "Use GitHub tarballs rather than zipballs (url is #{u})."
end
end
2012-08-07 01:37:46 -05:00
def audit_specs
2014-12-27 20:55:21 +00:00
if head_only?(formula) && formula.tap != "homebrew/homebrew-head-only"
problem "Head-only (no stable download)"
end
2013-09-18 18:50:23 -05:00
%w[Stable Devel HEAD].each do |name|
next unless spec = formula.send(name.downcase)
2013-09-18 18:50:23 -05:00
ra = ResourceAuditor.new(spec).audit
2013-09-18 18:50:23 -05:00
problems.concat ra.problems.map { |problem| "#{name}: #{problem}" }
2013-09-18 18:22:00 -05:00
spec.resources.each_value do |resource|
2013-09-18 18:50:23 -05:00
ra = ResourceAuditor.new(resource).audit
problems.concat ra.problems.map { |problem|
"#{name} resource #{resource.name.inspect}: #{problem}"
}
2013-09-18 18:22:00 -05:00
end
spec.patches.select(&:external?).each { |p| audit_patch(p) }
end
2014-09-23 13:04:55 -05:00
if formula.stable && formula.devel
if formula.devel.version < formula.stable.version
problem "devel version #{formula.devel.version} is older than stable version #{formula.stable.version}"
elsif formula.devel.version == formula.stable.version
2014-09-23 13:04:55 -05:00
problem "stable and devel versions are identical"
end
end
end
2012-08-07 01:37:46 -05:00
def audit_patches
legacy_patches = Patch.normalize_legacy_patches(formula.patches).grep(LegacyPatch)
if legacy_patches.any?
problem "Use the patch DSL instead of defining a 'patches' method"
legacy_patches.each { |p| audit_patch(p) }
end
end
def audit_patch(patch)
case patch.url
2014-03-19 18:21:14 -05:00
when %r[raw\.github\.com], %r[gist\.github\.com/raw], %r[gist\.github\.com/.+/raw],
%r[gist\.githubusercontent\.com/.+/raw]
unless patch.url =~ /[a-fA-F0-9]{40}/
problem "GitHub/Gist patches should specify a revision:\n#{patch.url}"
2012-08-07 01:37:46 -05:00
end
when %r[macports/trunk]
problem "MacPorts patches should specify a revision instead of trunk:\n#{patch.url}"
when %r[^https?://github\.com/.*commit.*\.patch$]
problem "GitHub appends a git version to patches; use .diff instead."
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
2014-02-25 07:36:47 -08:00
if text =~ /system\s+['"]scons/
2014-02-25 20:51:16 -08:00
problem "use \"scons *args\" instead of \"system 'scons', *args\""
2014-02-25 07:36:47 -08:00
end
if text =~ /system\s+['"]xcodebuild/
problem %{use "xcodebuild *args" instead of "system 'xcodebuild', *args"}
end
if text =~ /xcodebuild[ (]["'*]/ && text !~ /SYMROOT=/
problem %{xcodebuild should be passed an explicit "SYMROOT"}
2013-07-16 23:15:22 -05:00
end
2014-02-24 20:23:21 -08:00
if text =~ /Formula\.factory\(/
problem "\"Formula.factory(name)\" is deprecated in favor of \"Formula[name]\""
end
2013-07-16 23:15:22 -05:00
end
def audit_line(line, lineno)
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
2014-12-22 01:12:44 -05:00
if line =~ /# if your formula fails when building in parallel/
problem "Please remove default template comments"
end
if line =~ /# Remove unrecognized options if warned by configure/
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-12-04 20:07:27 -08:00
# encfs modifies a file with this name, so check for some leading characters
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+
if line =~ %r{\(\s*(prefix\s*\+\s*(['"])(bin|include|libexec|lib|sbin|share|Frameworks)[/'"])}
problem "\"(#{$1}...#{$2})\" should be \"(#{$3.downcase}+...)\""
2012-08-07 01:37:46 -05:00
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
if line =~ %r[(\#\{prefix\}/(bin|include|libexec|lib|sbin|share|Frameworks))]
problem "\"#{$1}\" should be \"\#{#{$2.downcase}}\""
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 ]+$/
problem "#{lineno}: 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)'/
2014-06-15 23:26:07 -05:00
problem "Use \"if build.#{$1.downcase}?\" instead"
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 =~ /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
if line =~ %r{(system|ENV\[.+\]\s?=)\s?['"](/usr/bin/)?(gcc|llvm-gcc|clang)['" ]}
problem "Use \"\#{ENV.cc}\" instead of hard-coding \"#{$3}\""
2012-08-07 01:37:46 -05:00
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
2014-03-05 07:53:53 -08:00
if line =~ /system\s+['"](env|export)(\s+|['"])/
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
if line =~ /build\.include\?[\s\(]+['"]\-\-(.*)['"]/
2012-09-13 07:14:45 -07:00
problem "Reference '#{$1}' without dashes"
end
if line =~ /build\.include\?[\s\(]+['"]with(out)?-(.*)['"]/
problem "Use build.with#{$1}? \"#{$2}\" instead of build.include? 'with#{$1}-#{$2}'"
end
if line =~ /build\.with\?[\s\(]+['"]-?-?with-(.*)['"]/
problem "Don't duplicate 'with': Use `build.with? \"#{$1}\"` to check for \"--with-#{$1}\""
end
if line =~ /build\.without\?[\s\(]+['"]-?-?without-(.*)['"]/
problem "Don't duplicate 'without': Use `build.without? \"#{$1}\"` to check for \"--without-#{$1}\""
end
if line =~ /unless build\.with\?(.*)/
problem "Use if build.without?#{$1} instead of unless build.with?#{$1}"
end
if line =~ /unless build\.without\?(.*)/
problem "Use if build.with?#{$1} instead of unless build.without?#{$1}"
end
if line =~ /(not\s|!)\s*build\.with?\?/
problem "Don't negate 'build.without?': use 'build.with?'"
end
if line =~ /(not\s|!)\s*build\.without?\?/
problem "Don't negate 'build.with?': use 'build.without?'"
end
if line =~ /ARGV\.(?!(debug\?|verbose\?|value[\(\s]))/
problem "Use build instead of ARGV to check options"
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
2014-08-21 15:13:13 -05:00
if line =~ /def test$/
problem "Use new-style test definitions (test do)"
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/
2014-02-23 12:09:28 -08:00
problem "`skip_clean :all` is deprecated; brew no longer strips symbols\n" +
"\tPass explicit paths to prevent Homebrew from removing empty folders."
2012-09-13 07:14:45 -07:00
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
if line =~ /(Dir\[("[^\*{},]+")\])/
problem "#{$1} is unnecessary; just use #{$2}"
end
if line =~ /system (["'](#{FILEUTILS_METHODS})["' ])/o
system = $1
method = $2
problem "Use the `#{method}` Ruby method instead of `system #{system}`"
end
if @strict
if line =~ /system (["'][^"' ]*(?:\s[^"' ]*)+["'])/
bad_system = $1
good_system = bad_system.gsub(" ", "\", \"")
problem "Use `system #{good_system}` instead of `system #{bad_system}` "
end
if line =~ /(require ["']formula["'])/
problem "`#{$1}` is now unnecessary"
end
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
2014-10-13 23:13:00 -05:00
def audit_check_output(output)
problem(output) if output
end
2012-08-07 01:37:46 -05:00
def audit
audit_file
2014-12-27 20:46:01 +00:00
audit_class
2012-08-07 01:37:46 -05:00
audit_specs
audit_urls
audit_deps
2013-01-03 11:22:31 -08:00
audit_conflicts
audit_options
2012-08-07 01:37:46 -05:00
audit_patches
2013-07-16 23:15:22 -05:00
audit_text
text.without_patch.split("\n").each_with_index { |line, lineno| audit_line(line, lineno+1) }
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
def problem p
2012-08-07 01:37:46 -05:00
@problems << p
end
def head_only?(formula)
formula.head && formula.stable.nil?
end
end
class ResourceAuditor
attr_reader :problems
attr_reader :version, :checksum, :using, :specs, :url, :name
def initialize(resource)
@name = resource.name
@version = resource.version
@checksum = resource.checksum
@url = resource.url
@using = resource.using
@specs = resource.specs
@problems = []
end
def audit
audit_version
audit_checksum
audit_download_strategy
self
end
def audit_version
if version.nil?
problem "missing version"
elsif version.to_s.empty?
problem "version is set to an empty string"
elsif not version.detected_from_url?
version_text = version
version_url = Version.detect(url, specs)
if version_url.to_s == version_text.to_s && version.instance_of?(Version)
problem "version #{version_text} is redundant with version scanned from URL"
end
end
if version.to_s =~ /^v/
problem "version #{version} should not have a leading 'v'"
end
end
def audit_checksum
return unless checksum
case checksum.hash_type
when :md5
problem "MD5 checksums are deprecated, please use SHA1 or SHA256"
return
when :sha1 then len = 40
when :sha256 then len = 64
end
if checksum.empty?
problem "#{checksum.hash_type} is empty"
else
problem "#{checksum.hash_type} should be #{len} characters" unless checksum.hexdigest.length == len
problem "#{checksum.hash_type} contains invalid characters" unless checksum.hexdigest =~ /^[a-fA-F0-9]+$/
problem "#{checksum.hash_type} should be lowercase" unless checksum.hexdigest == checksum.hexdigest.downcase
end
end
def audit_download_strategy
if url =~ %r[^(cvs|bzr|hg|fossil)://] || url =~ %r[^(svn)\+http://]
problem "Use of the #{$&} scheme is deprecated, pass `:using => :#{$1}` instead"
end
return unless using
2014-10-18 17:39:53 -05:00
if using == :ssl3 || using == CurlSSL3DownloadStrategy
problem "The SSL3 download strategy is deprecated, please choose a different URL"
elsif using == CurlUnsafeDownloadStrategy || using == UnsafeSubversionDownloadStrategy
2014-10-18 17:39:53 -05:00
problem "#{using.name} is deprecated, please choose a different URL"
end
if using == :cvs
mod = specs[:module]
if mod == name
problem "Redundant :module value in URL"
end
if url =~ %r[:[^/]+$]
mod = url.split(":").last
if mod == name
problem "Redundant CVS module appended to URL"
else
problem "Specify CVS module as `:module => \"#{mod}\"` instead of appending it to the URL"
end
end
end
url_strategy = DownloadStrategyDetector.detect(url)
using_strategy = DownloadStrategyDetector.detect('', using)
if url_strategy == using_strategy
problem "Redundant :using value in URL"
end
end
def problem text
@problems << text
end
end