Class: DepCheck::DepChecker

Inherits:
CLAide::Command
  • Object
show all
Defined in:
lib/dep_check/DepChecker.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(argv) ⇒ DepChecker

Returns a new instance of DepChecker.



37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/dep_check/DepChecker.rb', line 37

def initialize(argv)
  puts "initialize in checker"
  @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::SourcesManager.sources([@repo_name]).first
  else
      @repo = Pod::SourcesManager.sources(['master']).first
  end
  super
end

Class Method Details

.argumentsObject



30
31
32
33
34
35
# File 'lib/dep_check/DepChecker.rb', line 30

def self.arguments
  [
    CLAide::Argument.new('PODSPEC_NAME', true),
    CLAide::Argument.new('REPO_NAME', false)
  ].concat(super)
end

.optionsObject



22
23
24
25
26
27
28
# File 'lib/dep_check/DepChecker.rb', line 22

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

Instance Method Details

#dep_graph_build(repo, spec, builded, nodes) ⇒ Object

构建依赖关系图

Parameters:

  • repo (Source)

    依赖关系图建立过程中使用的podspec仓库

  • spec (Specification)

    待构建的spec

  • builded (Array<Node>)

    已经构建完成的Node数组

  • nodes (Array<Node>)

    已经存在的Node数组



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/dep_check/DepChecker.rb', line 124

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) ⇒ Object

循环依赖检测算法实现



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/dep_check/DepChecker.rb', line 148

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) ⇒ Object

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



204
205
206
207
# File 'lib/dep_check/DepChecker.rb', line 204

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) ⇒ Object

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



197
198
199
200
# File 'lib/dep_check/DepChecker.rb', line 197

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) ⇒ Object

利用 GraphViz 构建可视化图数据



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/dep_check/DepChecker.rb', line 179

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) ⇒ Object

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



108
109
110
111
112
113
114
115
116
117
# File 'lib/dep_check/DepChecker.rb', line 108

def podspec_with_name(repo, name)
  if name
    path = Pathname.new(name)
    if path.exist?
      podspec = Pod::Specification.from_file(path)
    else
      podspec = repo.specification(name,repo.versions(name).first)
    end
  end
end

#runObject



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/dep_check/DepChecker.rb', line 74

def run
  puts "run in checker"
  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
  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
end

#unique_node(name, nodes) ⇒ Object

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



166
167
168
169
170
171
172
173
174
175
# File 'lib/dep_check/DepChecker.rb', line 166

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!Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/dep_check/DepChecker.rb', line 52

def validate!
  super 
  if @podspec_name
    require 'pathname'
    path = Pathname.new(@podspec_name)
    if path.exist?
      @podspec = Pod::Specification.from_file(path)
    else
      @podspec = Pod::SourcesManager.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