cocoapods-binary-ht 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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