class DepCheck::DepChecker

Public Class Methods

arguments() click to toggle source
Calls superclass method
# File lib/dep_check/DepChecker.rb, line 29
def self.arguments
  [
    CLAide::Argument.new('PODSPEC_NAME', true),
    CLAide::Argument.new('REPO_NAME', false)
  ].concat(super)
end
new(argv) click to toggle source
Calls superclass method
# File lib/dep_check/DepChecker.rb, line 36
def initialize(argv)
  @podspec_name = argv.shift_argument
  @repo_name =  argv.shift_argument
  @repo_update = argv.flag?('repo-update', false)
  @produce_graphviz_output = argv.flag?('graphviz', false)
  @produce_image_output = argv.flag?('image', false)
  if @repo_name
      @repo = Pod::Config.instance.sources_manager.sources([@repo_name]).first
  else
      @repo = Pod::Config.instance.sources_manager.sources(['master']).first
  end
  super
end
options() click to toggle source
Calls superclass method
# File lib/dep_check/DepChecker.rb, line 21
def self.options
  [
    ['--repo-update', 'Fetch external podspecs and run `pod repo update` before calculating the dependency graph'],
    ['--graphviz', 'Outputs the dependency graph in Graphviz format to <podspec name>.gv or Podfile.gv'],
    ['--image', 'Outputs the dependency graph as an image to <podsepc name>.png or Podfile.png'],
  ].concat(super)
end

Public Instance Methods

dep_graph_build(repo,spec,builded,nodes) click to toggle source

构建依赖关系图 @param [Source] repo 依赖关系图建立过程中使用的podspec仓库 @param [Specification] spec 待构建的spec @param [Array<Node>] builded 已经构建完成的Node数组 @param [Array<Node>] nodes 已经存在的Node数组

# File lib/dep_check/DepChecker.rb, line 135
def dep_graph_build(repo,spec,builded,nodes)

    node = unique_node(spec.attributes_hash['name'],nodes)
    # puts "resolving  " + node.name
    builded.push(node)
  
    tunples = spec.all_dependencies.map { |dep| 
        {:node => unique_node(dep.name,nodes),:dep => dep}
    }

    tunples.each { |t|
        node.addEdge(t[:node])
    }

    tunples.each { |t|
        dep_spec =  podspec_with_name(repo,t[:dep].name)
        if builded.count(t[:node]) == 0
            dep_graph_build(repo,dep_spec,builded,nodes)
        end
    }
    return builded
end
dep_resolve(node, resolved, unresolved) click to toggle source

循环依赖检测算法实现

# File lib/dep_check/DepChecker.rb, line 159
def dep_resolve(node, resolved, unresolved)
  unresolved.push(node)
  node.edges.each { |edge|
     if resolved.count(edge) == 0 #if edge not in resolved
        if unresolved.count(edge) != 0 #if edge in unresolved
            path = []
            unresolved.each { |n| path << n.name }
            Pod::UI.puts "ERROR: Circular dependency detected! #{node.name} <---> #{edge.name}".red
            raise  CircularDependencyError.new(node.name,edge.name,path) 
        end
        dep_resolve(edge, resolved, unresolved)
     end
  }
  resolved.push(node)
  unresolved.delete(node)
end
graph_dot_output(graph, output_file_basename) click to toggle source

以.gv格式输出一个依赖关系图

# File lib/dep_check/DepChecker.rb, line 215
def graph_dot_output(graph, output_file_basename)
  graph.output( :dot => "#{output_file_basename}.gv")
  Pod::UI.puts "Graph file: #{output_file_basename}.gv... \n"
end
graph_image_out(graph, output_file_basename) click to toggle source

以.png格式输出一个依赖关系图

# File lib/dep_check/DepChecker.rb, line 208
def graph_image_out(graph, output_file_basename)
    graph.output( :png => "#{output_file_basename}.png")
    Pod::UI.puts "Graph file: #{output_file_basename}.png... \n"
end
graphviz_data(builded, output_file_basename) click to toggle source

利用 GraphViz 构建可视化图数据

# File lib/dep_check/DepChecker.rb, line 190
def graphviz_data(builded, output_file_basename)
  @graphviz ||= begin
    require 'graphviz'
    graph = GraphViz::new(output_file_basename, :type => :digraph)
    builded.each { |node|
        graph_node_from = graph.add_node(node.name)
        if !node.edges.empty?
            node.edges.each { |subNode|
              graph_node_to = graph.add_node(subNode.name)
              graph.add_edge(graph_node_from,graph_node_to)
            } 
        end
    }
    graph
  end
end
podspec_with_name(repo, name) click to toggle source

通过模块名称或者模块podspec文件路径 得到一个spec对象

# File lib/dep_check/DepChecker.rb, line 119
def podspec_with_name(repo, name)
  if name
    path = Pathname.new("#{name}.podspec")
    if path.exist?
      podspec = Pod::Specification.from_file(path)
    else
      podspec = repo.specification(name,repo.versions(name).first)
    end
  end
end
run() click to toggle source
# File lib/dep_check/DepChecker.rb, line 72
def run
  builded = []
  Pod::UI.puts "1. Buiding dependency graph for #{@podspec_name}... \n ".green
  Pod::UI.puts "All dependencies : \n".blue
  Pod::UI.puts dep_graph_build(@repo,@podspec,builded,[]) 
  Pod::UI.puts "Done! \n".blue


  Pod::UI.puts "2. Circular dependency check #{@podspec_name}... \n".green
  unresolved = []

  begin
    dep_resolve(builded.first,[],unresolved)
  rescue Exception => e
    Pod::UI.puts e.to_s.red
    circular_found = true
    circular_path = String.new
    e.path.each { |name|
      circular_path << name + '--->'
    }
    circular_path << e.to
    circular_from = e.from
    circular_to = e.to
  end
  if !circular_found
    Pod::UI.puts "No Circular dependency found! \n".green
  end
  Pod::UI.puts "Done! \n".blue

  if @produce_image_output || @produce_graphviz_output
      Pod::UI.puts "3. Visualizing graph for #{@podspec_name}... \n".green
  end
  graph_image_out(graphviz_data(builded,@podspec_name),@podspec_name)  if @produce_image_output
  graph_dot_output(graphviz_data(builded,@podspec_name),@podspec_name) if @produce_graphviz_output
  Pod::UI.puts "Done. \n".blue
  {"circular" => !!circular_found , 
    "root_spec_name" => @podspec_name,
    "circular_from" => circular_from ? circular_from : ''  ,
     "circular_to" => circular_to ?  circular_to : '' ,
      "circular_path" => circular_path ? circular_path : '', 
      "memo" => 'For more infomation , try $ dep_check --help' }
end
unique_node(name,nodes) click to toggle source

通过节点名称获得一个唯一的节点

# File lib/dep_check/DepChecker.rb, line 177
def unique_node(name,nodes)
    exist_nodes = nodes.select { |n| n.name == name }
    if exist_nodes.empty?
        node = Node.new(name)
        nodes.push(node)
    else
        node = exist_nodes.first
    end
    node
end
validate!() click to toggle source
Calls superclass method
# File lib/dep_check/DepChecker.rb, line 50
def validate!
  super 
  if @podspec_name
    require 'pathname'
    path = Pathname.new("#{@podspec_name}.podspec")
    if path.exist?
      @podspec = Pod::Specification.from_file(path)
    else
      @podspec = Pod::Config.instance.sources_manager.search(Pod::Dependency.new(@podspec_name)).specification.subspec_by_name(@podspec_name)
    end
      Pod::UI.puts "Analyzing #{@podspec} (in repo #{@repo})..."
  else
    raise 'Nothing to check. '
  end
  if (@produce_image_output || @produce_graphviz_output) && Pod::Executable.which('dot').nil?
    raise Informative, 'GraphViz must be installed and `dot` must be in ' \
      '$PATH to produce image or graphviz output.'
  end
  
end