Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Linter] Cleanup the linter #50

Merged
merged 11 commits into from
Apr 1, 2014
195 changes: 19 additions & 176 deletions lib/cocoapods-core/specification/linter.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
require 'cocoapods-core/specification/linter/result'
require 'cocoapods-core/specification/linter/analyzer'

module Pod
class Specification

Expand All @@ -8,6 +11,8 @@ class Specification
#
class Linter

include ResultHelpers

# @return [Specification] the specification to lint.
#
attr_reader :spec
Expand Down Expand Up @@ -60,10 +65,6 @@ def lint

public

# @return [Array<Result>] all the results generated by the Linter.
#
attr_reader :results

# @return [Array<Result>] all the errors generated by the Linter.
#
def errors
Expand Down Expand Up @@ -134,13 +135,7 @@ def check_required_root_attributes
#
def run_root_validation_hooks
attributes = DSL.attributes.values.select(&:root_only?)
attributes.each do |attr|
validation_hook = "_validate_#{attr.name}"
next unless respond_to?(validation_hook, true)
value = spec.send(attr.name)
next unless value
send(validation_hook, value)
end
run_validation_hooks(attributes, spec)
end

# Run validations for multi-platform attributes activating .
Expand All @@ -153,10 +148,9 @@ def perform_all_specs_analysis
current_spec.available_platforms.each do |platform|
@consumer = Specification::Consumer.new(current_spec, platform)
run_all_specs_validation_hooks
validate_file_patterns
check_tmp_arc_not_nil
check_if_spec_is_empty
check_install_hooks
analyzer = Analyzer.new(@consumer)
analyzer.analyze
add_results(analyzer.results)
@consumer = nil
end
end
Expand All @@ -172,13 +166,7 @@ def perform_all_specs_analysis
#
def run_all_specs_validation_hooks
attributes = DSL.attributes.values.reject(&:root_only?)
attributes.each do |attr|
validation_hook = "_validate_#{attr.name}"
next unless respond_to?(validation_hook, true)
value = consumer.send(attr.name)
next unless value
send(validation_hook, value)
end
run_validation_hooks(attributes, consumer)
end

# Runs the validation hook for each attribute.
Expand All @@ -189,9 +177,15 @@ def run_all_specs_validation_hooks
#
# @return [void]
#
# def run_validation_hooks(attributes)
#
# end
def run_validation_hooks(attributes, target)
attributes.each do |attr|
validation_hook = "_validate_#{attr.name}"
next unless respond_to?(validation_hook, true)
value = target.send(attr.name)
next unless value
send(validation_hook, value)
end
end

#-----------------------------------------------------------------------#

Expand Down Expand Up @@ -368,64 +362,6 @@ def _validate_compiler_flags(flags)
end
end

# Checks the attributes that represent file patterns.
#
# @todo Check the attributes hash directly.
#
def validate_file_patterns
attributes = DSL.attributes.values.select(&:file_patterns?)
attributes.each do |attrb|
patterns = consumer.send(attrb.name)
if patterns.is_a?(Hash)
patterns = patterns.values.flatten(1)
end
patterns.each do |pattern|
if pattern.start_with?('/')
error "File patterns must be relative and cannot start with a " \
"slash (#{attrb.name})."
end
end
end
end

# @todo remove in 0.18 and switch the default to true.
#
def check_tmp_arc_not_nil
if consumer.spec.attributes_hash["requires_arc"].nil?
warning "A value for `requires_arc` should be specified until the " \
"migration to a `true` default."
end
end

# Check empty subspec attributes
#
def check_if_spec_is_empty
methods = %w(source_files resources preserve_paths dependencies vendored_libraries vendored_frameworks)
empty_patterns = methods.all? { |m| consumer.send(m).empty? }
empty = empty_patterns && consumer.spec.subspecs.empty?
if empty
error "The #{consumer.spec} spec is empty (no source files, " \
"resources, preserve paths, vendored libraries, " \
"vendored frameworks, dependencies or subspecs)."
end
end

# Check the hooks
#
def check_install_hooks
unless consumer.spec.pre_install_callback.nil?
warning "The pre install hook of the specification DSL has been " \
"deprecated, use the `resource_bundles` or the " \
"`prepare_command` attributes."
end

unless consumer.spec.post_install_callback.nil?
warning "The post install hook of the specification DSL has been " \
"deprecated, use the `resource_bundles` or the " \
" `prepare_command` attributes."
end
end

# Returns whether the frameworks are valid
#
# @params frameworks [Array<String>]
Expand All @@ -447,99 +383,6 @@ def frameworks_invalid?(frameworks)
def libraries_invalid?(libs)
libs.any? { |lib| lib.end_with?('.a', '.dylib') || lib.start_with?('lib') }
end

#-----------------------------------------------------------------------#

# !@group Result Helpers

private

# Adds an error result with the given message.
#
# @param [String] message
# The message of the result.
#
# @return [void]
#
def error(message)
add_result(:error, message)
end

# Adds an warning result with the given message.
#
# @param [String] message
# The message of the result.
#
# @return [void]
#
def warning(message)
add_result(:warning, message)
end

# Adds a result of the given type with the given message. If there is a
# current platform it is added to the result. If a result with the same
# type and the same message is already available the current platform is
# added to the existing result.
#
# @param [Symbol] type
# The type of the result (`:error`, `:warning`).
#
# @param [String] message
# The message of the result.
#
# @return [void]
#
def add_result(type, message)
result = results.find { |r| r.type == type && r.message == message }
unless result
result = Result.new(type, message)
results << result
end
result.platforms << consumer.platform_name if consumer
end

#-----------------------------------------------------------------------#

class Result

# @return [Symbol] the type of result.
#
attr_reader :type

# @return [String] the message associated with result.
#
attr_reader :message

# @param [Symbol] type @see type
# @param [String] message @see message
#
def initialize(type, message)
@type = type
@message = message
@platforms = []
end

# @return [Array<Platform>] the platforms where this result was
# generated.
#
attr_reader :platforms

# @return [String] a string representation suitable for UI output.
#
def to_s
r = "[#{type.to_s.upcase}] #{message}"
if platforms != Specification::PLATFORMS
platforms_names = platforms.uniq.map do |p|
Platform.string_name(p)
end
r << " [#{platforms_names * ' - '}]" unless platforms.empty?
end
r
end
end

#-----------------------------------------------------------------------#

end
end
end
Expand Down
86 changes: 86 additions & 0 deletions lib/cocoapods-core/specification/linter/analyzer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
require 'cocoapods-core/specification/linter/result'

module Pod
class Specification
class Linter
class Analyzer

include Linter::ResultHelpers

def initialize(consumer)
@consumer = consumer
@results = []
end

def analyze
validate_file_patterns
check_tmp_arc_not_nil
check_if_spec_is_empty
check_install_hooks
end

private

attr_reader :consumer

# Checks the attributes that represent file patterns.
#
# @todo Check the attributes hash directly.
#
def validate_file_patterns
attributes = DSL.attributes.values.select(&:file_patterns?)
attributes.each do |attrb|
patterns = consumer.send(attrb.name)
if patterns.is_a?(Hash)
patterns = patterns.values.flatten(1)
end
patterns.each do |pattern|
if pattern.start_with?('/')
error "File patterns must be relative and cannot start with a " \
"slash (#{attrb.name})."
end
end
end
end

# @todo remove in 0.18 and switch the default to true.
#
def check_tmp_arc_not_nil
if consumer.spec.attributes_hash["requires_arc"].nil?
warning "A value for `requires_arc` should be specified until the " \
"migration to a `true` default."
end
end

# Check empty subspec attributes
#
def check_if_spec_is_empty
methods = %w( source_files resources preserve_paths dependencies vendored_libraries vendored_frameworks )
empty_patterns = methods.all? { |m| consumer.send(m).empty? }
empty = empty_patterns && consumer.spec.subspecs.empty?
if empty
error "The #{consumer.spec} spec is empty (no source files, " \
"resources, preserve paths, vendored_libraries, " \
"vendored_frameworks dependencies or subspecs)."
end
end

# Check the hooks
#
def check_install_hooks
unless consumer.spec.pre_install_callback.nil?
warning "The pre install hook of the specification DSL has been " \
"deprecated, use the `resource_bundles` or the " \
"`prepare_command` attributes."
end

unless consumer.spec.post_install_callback.nil?
warning "The post install hook of the specification DSL has been " \
"deprecated, use the `resource_bundles` or the " \
" `prepare_command` attributes."
end
end
end
end
end
end
Loading