cocoapods-binary-ht 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/lib/cocoapods-binary-cache.rb +2 -0
  3. data/lib/cocoapods-binary-ht/cache/all.rb +9 -0
  4. data/lib/cocoapods-binary-ht/cache/validation_result.rb +73 -0
  5. data/lib/cocoapods-binary-ht/cache/validator.rb +20 -0
  6. data/lib/cocoapods-binary-ht/cache/validator_accumulated.rb +4 -0
  7. data/lib/cocoapods-binary-ht/cache/validator_base.rb +112 -0
  8. data/lib/cocoapods-binary-ht/cache/validator_dependencies_graph.rb +25 -0
  9. data/lib/cocoapods-binary-ht/cache/validator_dev_pods.rb +30 -0
  10. data/lib/cocoapods-binary-ht/cache/validator_exclusion.rb +14 -0
  11. data/lib/cocoapods-binary-ht/cache/validator_non_dev_pods.rb +13 -0
  12. data/lib/cocoapods-binary-ht/cache/validator_with_podfile.rb +9 -0
  13. data/lib/cocoapods-binary-ht/dependencies_graph/dependencies_graph.rb +108 -0
  14. data/lib/cocoapods-binary-ht/dependencies_graph/graph_visualizer.rb +65 -0
  15. data/lib/cocoapods-binary-ht/diagnosis/base.rb +13 -0
  16. data/lib/cocoapods-binary-ht/diagnosis/diagnosis.rb +24 -0
  17. data/lib/cocoapods-binary-ht/diagnosis/integration.rb +23 -0
  18. data/lib/cocoapods-binary-ht/env.rb +32 -0
  19. data/lib/cocoapods-binary-ht/helper/benchmark_show.rb +11 -0
  20. data/lib/cocoapods-binary-ht/helper/checksum.rb +18 -0
  21. data/lib/cocoapods-binary-ht/helper/json.rb +37 -0
  22. data/lib/cocoapods-binary-ht/helper/lockfile.rb +90 -0
  23. data/lib/cocoapods-binary-ht/helper/path_utils.rb +8 -0
  24. data/lib/cocoapods-binary-ht/helper/podspec.rb +20 -0
  25. data/lib/cocoapods-binary-ht/hooks/post_install.rb +23 -0
  26. data/lib/cocoapods-binary-ht/hooks/pre_install.rb +121 -0
  27. data/lib/cocoapods-binary-ht/main.rb +21 -0
  28. data/lib/cocoapods-binary-ht/pod-binary/LICENSE.txt +22 -0
  29. data/lib/cocoapods-binary-ht/pod-binary/helper/build.rb +37 -0
  30. data/lib/cocoapods-binary-ht/pod-binary/helper/detected_prebuilt_pods/installer.rb +25 -0
  31. data/lib/cocoapods-binary-ht/pod-binary/helper/detected_prebuilt_pods/target_definition.rb +29 -0
  32. data/lib/cocoapods-binary-ht/pod-binary/helper/names.rb +27 -0
  33. data/lib/cocoapods-binary-ht/pod-binary/helper/podfile_options.rb +2 -0
  34. data/lib/cocoapods-binary-ht/pod-binary/helper/prebuild_sandbox.rb +71 -0
  35. data/lib/cocoapods-binary-ht/pod-binary/helper/sandbox.rb +9 -0
  36. data/lib/cocoapods-binary-ht/pod-binary/helper/target_checker.rb +42 -0
  37. data/lib/cocoapods-binary-ht/pod-binary/integration/alter_specs.rb +150 -0
  38. data/lib/cocoapods-binary-ht/pod-binary/integration/patch/embed_framework_script.rb +36 -0
  39. data/lib/cocoapods-binary-ht/pod-binary/integration/patch/resolve_dependencies.rb +20 -0
  40. data/lib/cocoapods-binary-ht/pod-binary/integration/patch/sandbox_analyzer_state.rb +29 -0
  41. data/lib/cocoapods-binary-ht/pod-binary/integration/patch/source_installation.rb +55 -0
  42. data/lib/cocoapods-binary-ht/pod-binary/integration/source_installer.rb +114 -0
  43. data/lib/cocoapods-binary-ht/pod-binary/integration/validation.rb +20 -0
  44. data/lib/cocoapods-binary-ht/pod-binary/integration.rb +11 -0
  45. data/lib/cocoapods-binary-ht/pod-binary/prebuild.rb +166 -0
  46. data/lib/cocoapods-binary-ht/pod-binary/prebuild_dsl.rb +10 -0
  47. data/lib/cocoapods-binary-ht/pod-binary/prebuild_hook.rb +10 -0
  48. data/lib/cocoapods-binary-ht/pod-rome/LICENSE.txt +22 -0
  49. data/lib/cocoapods-binary-ht/pod-rome/xcodebuild_command.rb +266 -0
  50. data/lib/cocoapods-binary-ht/pod-rome/xcodebuild_raw.rb +68 -0
  51. data/lib/cocoapods-binary-ht/prebuild_output/metadata.rb +63 -0
  52. data/lib/cocoapods-binary-ht/prebuild_output/output.rb +44 -0
  53. data/lib/cocoapods-binary-ht/state_store.rb +21 -0
  54. data/lib/cocoapods-binary-ht/ui.rb +9 -0
  55. data/lib/cocoapods_plugin.rb +5 -0
  56. data/lib/command/binary.rb +37 -0
  57. data/lib/command/config.rb +215 -0
  58. data/lib/command/executor/base.rb +37 -0
  59. data/lib/command/executor/fetcher.rb +67 -0
  60. data/lib/command/executor/prebuilder.rb +61 -0
  61. data/lib/command/executor/pusher.rb +35 -0
  62. data/lib/command/executor/visualizer.rb +23 -0
  63. data/lib/command/fetch.rb +22 -0
  64. data/lib/command/helper/zip.rb +20 -0
  65. data/lib/command/prebuild.rb +47 -0
  66. data/lib/command/push.rb +22 -0
  67. data/lib/command/visualize.rb +34 -0
  68. metadata +209 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 8fb1e861e5728064394631f9037d1b7d164ee4385f5dc5bd768bae537aa9c5eb
4
+ data.tar.gz: 820ba6fd23aa548cbebc2de961ef4805fde49d3ea49838c2936cf51c78378829
5
+ SHA512:
6
+ metadata.gz: 10aa6a928916f62360f0a705a461b487623205df3f6369a22eb21101a886febc774ada9626caf0bdd788ac5f6d972fb0deb2d5417f6f89c125a3b3883d837faf
7
+ data.tar.gz: aee491cdcc6e53f549aa7cac39074c55614eb3a843c81347255578551cca8d6b77a4d1c8c07d8d635b2648a33d0c33b79e999d7646cbf4603de1eea8eaf22284
@@ -0,0 +1,2 @@
1
+ # Copyright 2019 panda Holdings PTE LTE (panda), All rights reserved.
2
+ # Use of this source code is governed by an MIT-style license that can be found in the LICENSE file
@@ -0,0 +1,9 @@
1
+ require_relative "validation_result"
2
+ require_relative "validator_base"
3
+ require_relative "validator_accumulated"
4
+ require_relative "validator_with_podfile"
5
+ require_relative "validator_non_dev_pods"
6
+ require_relative "validator_dev_pods"
7
+ require_relative "validator_dependencies_graph"
8
+ require_relative "validator_exclusion"
9
+ require_relative "validator"
@@ -0,0 +1,73 @@
1
+ module PodPrebuild
2
+ class CacheValidationResult
3
+ attr_reader :hit, :missed_with_reasons
4
+
5
+ def initialize(missed_with_reasons = {}, hit = Set.new)
6
+ @missed_with_reasons = missed_with_reasons
7
+ @hit = hit.to_set - missed_with_reasons.keys
8
+ end
9
+
10
+ def all
11
+ (hit + missed).to_set
12
+ end
13
+
14
+ def missed
15
+ @missed_with_reasons.keys.to_set
16
+ end
17
+
18
+ def missed?(name)
19
+ @missed_with_reasons.key?(name)
20
+ end
21
+
22
+ def hit?(name)
23
+ @hit.include?(name)
24
+ end
25
+
26
+ def include?(name)
27
+ missed?(name) || hit?(name)
28
+ end
29
+
30
+ def merge(other)
31
+ PodPrebuild::CacheValidationResult.new(
32
+ @missed_with_reasons.merge(other.missed_with_reasons),
33
+ @hit + other.hit
34
+ )
35
+ end
36
+
37
+ def update_to(path)
38
+ FileUtils.mkdir_p(File.dirname(path))
39
+ json_file = PodPrebuild::JSONFile.new(path)
40
+ json_file["cache_missed"] = missed.to_a
41
+ json_file["cache_hit"] = hit.to_a
42
+ json_file.save!
43
+ end
44
+
45
+ def keep(names)
46
+ base_names = names.map { |name| name.split("/")[0] }.to_set
47
+ select { |name| base_names.include?(name.split("/")[0]) }
48
+ end
49
+
50
+ def discard(names)
51
+ base_names = names.map { |name| name.split("/")[0] }.to_set
52
+ reject { |name| base_names.include?(name.split("/")[0]) }
53
+ end
54
+
55
+ def select(&predicate)
56
+ PodPrebuild::CacheValidationResult.new(
57
+ @missed_with_reasons.select { |name, _| predicate.call(name) },
58
+ @hit.select(&predicate)
59
+ )
60
+ end
61
+
62
+ def reject(&predicate)
63
+ select { |name| !predicate.call(name) }
64
+ end
65
+
66
+ def print_summary
67
+ Pod::UI.puts "Cache validation: hit (#{@hit.count}) #{@hit.to_a}"
68
+ @missed_with_reasons.each do |name, reason|
69
+ Pod::UI.puts "Cache validation: missed #{name}. Reason: #{reason}".yellow
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,20 @@
1
+ module PodPrebuild
2
+ class CacheValidator
3
+ def initialize(options)
4
+ @validators = [
5
+ PodPrebuild::PodfileChangesCacheValidator.new(options),
6
+ PodPrebuild::NonDevPodsCacheValidator.new(options)
7
+ ]
8
+ @validators << PodPrebuild::DevPodsCacheValidator.new(options) if PodPrebuild.config.dev_pods_enabled?
9
+ @validators << PodPrebuild::DependenciesGraphCacheValidator.new(options)
10
+ @validators << PodPrebuild::ExclusionCacheValidator.new(options)
11
+ end
12
+
13
+ def validate(*)
14
+ @validators.reduce(PodPrebuild::CacheValidationResult.new) do |acc, validator|
15
+ validation = validator.validate(acc)
16
+ validator.is_a?(AccumulatedCacheValidator) ? validation : acc.merge(validation)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ module PodPrebuild
2
+ class AccumulatedCacheValidator < BaseCacheValidator
3
+ end
4
+ end
@@ -0,0 +1,112 @@
1
+ module PodPrebuild
2
+ class BaseCacheValidator
3
+ attr_reader :podfile, :pod_lockfile, :prebuilt_lockfile
4
+ attr_reader :validate_prebuilt_settings, :generated_framework_path
5
+
6
+ def initialize(options)
7
+ @podfile = options[:podfile]
8
+ @pod_lockfile = options[:pod_lockfile] && PodPrebuild::Lockfile.new(options[:pod_lockfile])
9
+ @prebuilt_lockfile = options[:prebuilt_lockfile] && PodPrebuild::Lockfile.new(options[:prebuilt_lockfile])
10
+ @validate_prebuilt_settings = options[:validate_prebuilt_settings]
11
+ @generated_framework_path = options[:generated_framework_path]
12
+ end
13
+
14
+ def validate(*)
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def changes_of_prebuilt_lockfile_vs_podfile
19
+ @changes_of_prebuilt_lockfile_vs_podfile ||= Pod::Installer::Analyzer::SpecsState.new(
20
+ @prebuilt_lockfile.lockfile.detect_changes_with_podfile(@podfile)
21
+ )
22
+ end
23
+
24
+ def validate_with_podfile
25
+ changes = changes_of_prebuilt_lockfile_vs_podfile
26
+ missed = changes.added.map { |pod| [pod, "Added from Podfile"] }.to_h
27
+ missed.merge!(changes.changed.map { |pod| [pod, "Updated from Podfile"] }.to_h)
28
+ PodPrebuild::CacheValidationResult.new(missed, changes.unchanged)
29
+ end
30
+
31
+ def validate_pods(options)
32
+ pods = options[:pods]
33
+ subspec_pods = options[:subspec_pods]
34
+ prebuilt_pods = options[:prebuilt_pods]
35
+
36
+ missed = {}
37
+ hit = Set.new
38
+
39
+ check_pod = lambda do |name|
40
+ root_name = name.split("/")[0]
41
+ version = pods[name]
42
+ prebuilt_version = prebuilt_pods[name]
43
+ result = false
44
+ if prebuilt_version.nil?
45
+ missed[name] = "Not available (#{version})"
46
+ elsif prebuilt_version != version
47
+ missed[name] = "Outdated: (prebuilt: #{prebuilt_version}) vs (#{version})"
48
+ elsif load_metadata(root_name).blank?
49
+ missed[name] = "Metadata not available (probably #{root_name}.zip is not in GeneratedFrameworks)"
50
+ else
51
+ diff = incompatible_pod(root_name)
52
+ if diff.empty?
53
+ hit << name
54
+ result = true
55
+ else
56
+ missed[name] = "Incompatible: #{diff}"
57
+ end
58
+ end
59
+ result
60
+ end
61
+
62
+ subspec_pods.each do |parent, children|
63
+ missed_children = children.reject { |child| check_pod.call(child) }
64
+ if missed_children.empty?
65
+ hit << parent
66
+ else
67
+ missed[parent] = "Subspec pods were missed: #{missed_children}"
68
+ end
69
+ end
70
+
71
+ non_subspec_pods = pods.reject { |pod| subspec_pods.include?(pod) }
72
+ non_subspec_pods.each { |pod, _| check_pod.call(pod) }
73
+ PodPrebuild::CacheValidationResult.new(missed, hit)
74
+ end
75
+
76
+ def incompatible_pod(name)
77
+ # Pod incompatibility is a universal concept. Generally, it requires build settings compatibility.
78
+ # For more checks, do override this function to define what it means by `incompatible`.
79
+ incompatible_build_settings(name)
80
+ end
81
+
82
+ def incompatible_build_settings(name)
83
+ settings_diff = {}
84
+ prebuilt_build_settings = read_prebuilt_build_settings(name)
85
+ validate_prebuilt_settings&.(name)&.each do |key, value|
86
+ prebuilt_value = prebuilt_build_settings[key]
87
+ unless prebuilt_value.nil? || value == prebuilt_value
88
+ settings_diff[key] = { :current => value, :prebuilt => prebuilt_value }
89
+ end
90
+ end
91
+ settings_diff
92
+ end
93
+
94
+ def load_metadata(name)
95
+ @metadata_cache ||= {}
96
+ cache = @metadata_cache[name]
97
+ return cache unless cache.nil?
98
+
99
+ metadata = PodPrebuild::Metadata.in_dir(generated_framework_path + name)
100
+ @metadata_cache[name] = metadata
101
+ metadata
102
+ end
103
+
104
+ def read_prebuilt_build_settings(name)
105
+ load_metadata(name).build_settings
106
+ end
107
+
108
+ def read_source_hash(name)
109
+ load_metadata(name).source_hash
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,25 @@
1
+ module PodPrebuild
2
+ class DependenciesGraphCacheValidator < AccumulatedCacheValidator
3
+ def initialize(options)
4
+ super(options)
5
+ @ignored_pods = options[:ignored_pods] || Set.new
6
+ end
7
+
8
+ def validate(accumulated)
9
+ return accumulated if library_evolution_supported? || @pod_lockfile.nil?
10
+
11
+ dependencies_graph = DependenciesGraph.new(lockfile: @pod_lockfile.lockfile, invert_edge: true)
12
+ clients = dependencies_graph.get_clients(accumulated.discard(@ignored_pods).missed.to_a)
13
+ unless PodPrebuild.config.dev_pods_enabled?
14
+ clients = clients.reject { |client| @pod_lockfile.dev_pods.keys.include?(client) }
15
+ end
16
+
17
+ missed = clients.map { |client| [client, "Dependencies were missed"] }.to_h
18
+ accumulated.merge(PodPrebuild::CacheValidationResult.new(missed, Set.new))
19
+ end
20
+
21
+ def library_evolution_supported?
22
+ false
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,30 @@
1
+ module PodPrebuild
2
+ class DevPodsCacheValidator < BaseCacheValidator
3
+ def validate(*)
4
+ return PodPrebuild::CacheValidationResult.new if @pod_lockfile.nil?
5
+
6
+ validate_pods(
7
+ pods: @pod_lockfile.dev_pods,
8
+ subspec_pods: [],
9
+ prebuilt_pods: @prebuilt_lockfile.nil? ? {} : @prebuilt_lockfile.dev_pods
10
+ )
11
+ end
12
+
13
+ def incompatible_pod(name)
14
+ diff = super(name)
15
+ return diff unless diff.empty?
16
+
17
+ incompatible_source(name)
18
+ end
19
+
20
+ def incompatible_source(name)
21
+ diff = {}
22
+ prebuilt_hash = read_source_hash(name)
23
+ expected_hash = pod_lockfile.dev_pod_hash(name)
24
+ unless prebuilt_hash == expected_hash
25
+ diff[name] = { :prebuilt_hash => prebuilt_hash, :expected_hash => expected_hash}
26
+ end
27
+ diff
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,14 @@
1
+ module PodPrebuild
2
+ class ExclusionCacheValidator < AccumulatedCacheValidator
3
+ def initialize(options)
4
+ super(options)
5
+ @ignored_pods = options[:ignored_pods] || Set.new
6
+ @prebuilt_pod_names = options[:prebuilt_pod_names]
7
+ end
8
+
9
+ def validate(accumulated)
10
+ validation = @prebuilt_pod_names.nil? ? accumulated : accumulated.keep(@prebuilt_pod_names)
11
+ validation.discard(@ignored_pods)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ module PodPrebuild
2
+ class NonDevPodsCacheValidator < BaseCacheValidator
3
+ def validate(*)
4
+ return PodPrebuild::CacheValidationResult.new if @pod_lockfile.nil?
5
+
6
+ validate_pods(
7
+ pods: @pod_lockfile.non_dev_pods,
8
+ subspec_pods: @pod_lockfile.subspec_vendor_pods,
9
+ prebuilt_pods: @prebuilt_lockfile.nil? ? {} : @prebuilt_lockfile.non_dev_pods
10
+ )
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ module PodPrebuild
2
+ class PodfileChangesCacheValidator < BaseCacheValidator
3
+ def validate(*)
4
+ return PodPrebuild::CacheValidationResult.new if @prebuilt_lockfile.nil? || @podfile.nil?
5
+
6
+ validate_with_podfile
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,108 @@
1
+ # Copyright 2019 panda Holdings PTE LTE (panda), All rights reserved.
2
+ # Use of this source code is governed by an MIT-style license that can be found in the LICENSE file
3
+
4
+ require "rgl/adjacency"
5
+ require "rgl/dot"
6
+ require_relative "graph_visualizer"
7
+
8
+ # Using RGL graph because GraphViz doesn't store adjacent of a node/vertex
9
+ # but we need to traverse a substree from any node
10
+ # https://github.com/monora/rgl/blob/master/lib/rgl/adjacency.rb
11
+
12
+ class DependenciesGraph
13
+ def initialize(options)
14
+ @lockfile = options[:lockfile]
15
+ @devpod_only = options[:devpod_only]
16
+ @max_deps = options[:max_deps].to_i if options[:max_deps]
17
+ # A normal edge is an edge (one direction) from library A to library B which is a dependency of A.
18
+ @invert_edge = options[:invert_edge] || false
19
+ end
20
+
21
+ # Input : a list of library names.
22
+ # Output: a set of library names which are clients (directly and indirectly) of those input libraries.
23
+ def get_clients(libnames)
24
+ result = Set.new
25
+ libnames.each do |lib|
26
+ if graph.has_vertex?(lib)
27
+ result.merge(traverse_sub_tree(graph, lib))
28
+ else
29
+ Pod::UI.puts "Warning: cannot find lib: #{lib}"
30
+ end
31
+ end
32
+ result
33
+ end
34
+
35
+ def write_graphic_file(options)
36
+ graph.write_to_graphic_file(options)
37
+ end
38
+
39
+ private
40
+
41
+ def dependencies
42
+ @dependencies ||= (@lockfile && @lockfile.to_hash["PODS"])
43
+ end
44
+
45
+ def dev_pod_sources
46
+ @dev_pod_sources ||= @lockfile.to_hash["EXTERNAL SOURCES"].select { |_, attributes| attributes.key?(:path) } || {}
47
+ end
48
+
49
+ # Convert array of dictionaries -> a dictionary with format {A: [A's dependencies]}
50
+ def pod_to_dependencies
51
+ dependencies
52
+ .map { |d| d.is_a?(Hash) ? d : { d => [] } }
53
+ .reduce({}) { |combined, individual| combined.merge!(individual) }
54
+ end
55
+
56
+ def add_vertex(graph, pod)
57
+ node_name = sanitized_pod_name(pod)
58
+ return if @devpod_only && dev_pod_sources[node_name].nil?
59
+
60
+ graph.add_vertex(node_name)
61
+ node_name
62
+ end
63
+
64
+ def sanitized_pod_name(name)
65
+ Pod::Dependency.from_string(name).name
66
+ end
67
+
68
+ def reach_max_deps(deps)
69
+ return unless @max_deps
70
+ return deps.count > @max_deps unless @devpod_only
71
+
72
+ deps = deps.reject { |name| dev_pod_sources[name].nil? }
73
+ deps.count > @max_deps
74
+ end
75
+
76
+ def graph
77
+ @graph ||= begin
78
+ graph = RGL::DirectedAdjacencyGraph.new
79
+ pod_to_dependencies.each do |pod, dependencies|
80
+ next if reach_max_deps(dependencies)
81
+
82
+ pod_node = add_vertex(graph, pod)
83
+ next if pod_node.nil?
84
+
85
+ dependencies.each do |dependency|
86
+ dep_node = add_vertex(graph, dependency)
87
+ next if dep_node.nil?
88
+
89
+ if @invert_edge
90
+ graph.add_edge(dep_node, pod_node)
91
+ else
92
+ graph.add_edge(pod_node, dep_node)
93
+ end
94
+ end
95
+ end
96
+ graph
97
+ end
98
+ end
99
+
100
+ def traverse_sub_tree(graph, vertex)
101
+ visited_nodes = Set.new
102
+ graph.each_adjacent(vertex) do |v|
103
+ visited_nodes.add(v)
104
+ visited_nodes.merge(traverse_sub_tree(graph, v))
105
+ end
106
+ visited_nodes
107
+ end
108
+ end
@@ -0,0 +1,65 @@
1
+ # Copyright 2019 panda Holdings PTE LTE (panda), All rights reserved.
2
+ # Use of this source code is governed by an MIT-style license that can be found in the LICENSE file
3
+ # https://github.com/monora/rgl/blob/0b526e16f9fb344abf387f4c5523d7917ce8f4b1/lib/rgl/dot.rb
4
+
5
+ require "rgl/rdot"
6
+
7
+ module RGL
8
+ module Graph
9
+ def to_dot_graph(options)
10
+ highlight_nodes = options[:highlight_nodes] || Set.new
11
+ options["name"] ||= self.class.name.gsub(/:/, "_")
12
+ fontsize = options["fontsize"] || "12"
13
+ graph = (directed? ? DOT::Digraph : DOT::Graph).new(options)
14
+ edge_class = directed? ? DOT::DirectedEdge : DOT::Edge
15
+ vertex_options = options["vertex"] || {}
16
+ edge_options = options["edge"] || {}
17
+
18
+ each_vertex do |v|
19
+ default_vertex_options = {
20
+ "name" => vertex_id(v),
21
+ "fontsize" => fontsize,
22
+ "label" => vertex_label(v),
23
+ "style" => "filled"
24
+ }
25
+ default_vertex_options.merge!("color" => "red", "fillcolor" => "red") if highlight_nodes.include?(v)
26
+ each_vertex_options = default_vertex_options.merge(vertex_options)
27
+ vertex_options.each { |option, val| each_vertex_options[option] = val.call(v) if val.is_a?(Proc) }
28
+ graph << DOT::Node.new(each_vertex_options)
29
+ end
30
+
31
+ each_edge do |u, v|
32
+ default_edge_options = {
33
+ "from" => vertex_id(u),
34
+ "to" => vertex_id(v),
35
+ "fontsize" => fontsize
36
+ }
37
+ each_edge_options = default_edge_options.merge(edge_options)
38
+ edge_options.each { |option, val| each_edge_options[option] = val.call(u, v) if val.is_a?(Proc) }
39
+ graph << edge_class.new(each_edge_options)
40
+ end
41
+
42
+ graph
43
+ end
44
+
45
+ def write_to_graphic_file(options)
46
+ output_path = Pathname.new(options[:output_path])
47
+ fmt = output_path.extname.delete_prefix(".")
48
+ dotfile = output_path.sub_ext(".dot")
49
+
50
+ File.open(dotfile, "w") do |f|
51
+ f << to_dot_graph(options).to_s
52
+ end
53
+
54
+ unless system("dot -T#{fmt} #{dotfile} -o #{output_path}")
55
+ message = <<~HEREDOC
56
+ Error executing dot. Did you install GraphViz?
57
+ Try installing it via Homebrew: `brew install graphviz`.
58
+ Visit https://graphviz.org/download/ for more installation instructions.
59
+ HEREDOC
60
+ raise message
61
+ end
62
+ output_path
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module PodPrebuild
2
+ class BaseDiagnosis
3
+ def initialize(options)
4
+ @cache_validation = options[:cache_validation]
5
+ @standard_sandbox = options[:standard_sandbox]
6
+ @specs = (options[:specs] || []).map { |s| [s.name, s] }.to_h
7
+ end
8
+
9
+ def spec(name)
10
+ @specs[name]
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "base"
2
+ require_relative "integration"
3
+
4
+ module PodPrebuild
5
+ class Diagnosis
6
+ def initialize(options)
7
+ @diagnosers = [
8
+ IntegrationDiagnosis
9
+ ].map { |klazz| klazz.new(options) }
10
+ end
11
+
12
+ def run
13
+ diagnosis = @diagnosers.map(&:run)
14
+ errors = diagnosis.select { |d| d[0] == :error }.map { |d| d[1] }
15
+ warnings = diagnosis.select { |d| d[0] == :error }.map { |d| d[1] }
16
+
17
+ warnings.each { |d| Pod::UI.puts "⚠️ #{d[1]}" }
18
+ errors.each { |d| Pod::UI.puts "🚩 #{d[1]}" }
19
+ return if errors.empty? || !PodPrebuild.config.strict_diagnosis?
20
+
21
+ raise "There are #{errors.count} error(s) spotted after the diagnosis"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,23 @@
1
+ require_relative "base"
2
+
3
+ module PodPrebuild
4
+ class IntegrationDiagnosis < BaseDiagnosis
5
+ def run
6
+ should_be_integrated = if PodPrebuild.config.prebuild_job? \
7
+ then @cache_validation.hit + @cache_validation.missed \
8
+ else @cache_validation.hit \
9
+ end
10
+ should_be_integrated = should_be_integrated.map { |name| name.split("/")[0] }.to_set
11
+ unintegrated = should_be_integrated.reject do |name|
12
+ module_name = spec(name)&.module_name || name
13
+ framework_path = \
14
+ @standard_sandbox.pod_dir(name) + \
15
+ PodPrebuild.config.prebuilt_path(path: "#{module_name}.framework")
16
+ framework_path.exist?
17
+ end
18
+ return [] if unintegrated.empty?
19
+
20
+ [[:error, "Unintegrated frameworks: #{unintegrated}"]]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,32 @@
1
+ module PodPrebuild
2
+ class Env
3
+ @stage_idx = 0
4
+
5
+ class << self
6
+ def reset!
7
+ @stage_idx = 0
8
+ @stages = nil
9
+ end
10
+
11
+ def next_stage!
12
+ @stage_idx += 1 if @stage_idx < stages.count - 1
13
+ end
14
+
15
+ def stages
16
+ @stages ||= PodPrebuild.config.prebuild_job? ? [:prebuild, :integration] : [:integration]
17
+ end
18
+
19
+ def current_stage
20
+ stages[@stage_idx]
21
+ end
22
+
23
+ def prebuild_stage?
24
+ current_stage == :prebuild
25
+ end
26
+
27
+ def integration_stage?
28
+ current_stage == :integration
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,11 @@
1
+ # Copyright 2019 panda Holdings PTE LTE (panda), All rights reserved.
2
+ # Use of this source code is governed by an MIT-style license that can be found in the LICENSE file
3
+
4
+ require "benchmark"
5
+
6
+ class BenchmarkShow
7
+ def self.benchmark
8
+ time = Benchmark.measure { yield }
9
+ Pod::UI.puts "🕛 Time elapsed: #{time}"
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ # Copyright 2019 panda Holdings PTE LTE (panda), All rights reserved.
2
+ # Use of this source code is governed by an MIT-style license that can be found in the LICENSE file
3
+
4
+ require "digest/md5"
5
+
6
+ class FolderChecksum
7
+ def self.git_checksum(dir)
8
+ checksum_of_files(`git ls-files #{File.realdirpath(dir).shellescape}`.split("\n"))
9
+ rescue => e
10
+ Pod::UI.warn "Cannot get checksum of tracked files under #{dir}: #{e}"
11
+ checksum_of_files(Dir["#{dir}/**/*"].reject { |f| File.directory?(f) })
12
+ end
13
+
14
+ def self.checksum_of_files(files)
15
+ checksums = files.sort.map { |f| Digest::MD5.hexdigest(File.read(f)) }
16
+ Digest::MD5.hexdigest(checksums.join)
17
+ end
18
+ end
@@ -0,0 +1,37 @@
1
+ require "json"
2
+
3
+ module PodPrebuild
4
+ class JSONFile
5
+ attr_reader :path
6
+ attr_reader :data
7
+
8
+ def initialize(path)
9
+ @path = path
10
+ @data = load_json
11
+ end
12
+
13
+ def empty?
14
+ @data.empty?
15
+ end
16
+
17
+ def [](key)
18
+ @data[key]
19
+ end
20
+
21
+ def []=(key, value)
22
+ @data[key] = value
23
+ end
24
+
25
+ def save!
26
+ File.open(@path, "w") { |f| f.write(JSON.pretty_generate(@data)) }
27
+ end
28
+
29
+ private
30
+
31
+ def load_json
32
+ File.open(@path) { |f| JSON.parse(f.read) }
33
+ rescue
34
+ {}
35
+ end
36
+ end
37
+ end