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.
- checksums.yaml +7 -0
- data/lib/cocoapods-binary-cache.rb +2 -0
- data/lib/cocoapods-binary-ht/cache/all.rb +9 -0
- data/lib/cocoapods-binary-ht/cache/validation_result.rb +73 -0
- data/lib/cocoapods-binary-ht/cache/validator.rb +20 -0
- data/lib/cocoapods-binary-ht/cache/validator_accumulated.rb +4 -0
- data/lib/cocoapods-binary-ht/cache/validator_base.rb +112 -0
- data/lib/cocoapods-binary-ht/cache/validator_dependencies_graph.rb +25 -0
- data/lib/cocoapods-binary-ht/cache/validator_dev_pods.rb +30 -0
- data/lib/cocoapods-binary-ht/cache/validator_exclusion.rb +14 -0
- data/lib/cocoapods-binary-ht/cache/validator_non_dev_pods.rb +13 -0
- data/lib/cocoapods-binary-ht/cache/validator_with_podfile.rb +9 -0
- data/lib/cocoapods-binary-ht/dependencies_graph/dependencies_graph.rb +108 -0
- data/lib/cocoapods-binary-ht/dependencies_graph/graph_visualizer.rb +65 -0
- data/lib/cocoapods-binary-ht/diagnosis/base.rb +13 -0
- data/lib/cocoapods-binary-ht/diagnosis/diagnosis.rb +24 -0
- data/lib/cocoapods-binary-ht/diagnosis/integration.rb +23 -0
- data/lib/cocoapods-binary-ht/env.rb +32 -0
- data/lib/cocoapods-binary-ht/helper/benchmark_show.rb +11 -0
- data/lib/cocoapods-binary-ht/helper/checksum.rb +18 -0
- data/lib/cocoapods-binary-ht/helper/json.rb +37 -0
- data/lib/cocoapods-binary-ht/helper/lockfile.rb +90 -0
- data/lib/cocoapods-binary-ht/helper/path_utils.rb +8 -0
- data/lib/cocoapods-binary-ht/helper/podspec.rb +20 -0
- data/lib/cocoapods-binary-ht/hooks/post_install.rb +23 -0
- data/lib/cocoapods-binary-ht/hooks/pre_install.rb +121 -0
- data/lib/cocoapods-binary-ht/main.rb +21 -0
- data/lib/cocoapods-binary-ht/pod-binary/LICENSE.txt +22 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/build.rb +37 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/detected_prebuilt_pods/installer.rb +25 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/detected_prebuilt_pods/target_definition.rb +29 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/names.rb +27 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/podfile_options.rb +2 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/prebuild_sandbox.rb +71 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/sandbox.rb +9 -0
- data/lib/cocoapods-binary-ht/pod-binary/helper/target_checker.rb +42 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/alter_specs.rb +150 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/patch/embed_framework_script.rb +36 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/patch/resolve_dependencies.rb +20 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/patch/sandbox_analyzer_state.rb +29 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/patch/source_installation.rb +55 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/source_installer.rb +114 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration/validation.rb +20 -0
- data/lib/cocoapods-binary-ht/pod-binary/integration.rb +11 -0
- data/lib/cocoapods-binary-ht/pod-binary/prebuild.rb +166 -0
- data/lib/cocoapods-binary-ht/pod-binary/prebuild_dsl.rb +10 -0
- data/lib/cocoapods-binary-ht/pod-binary/prebuild_hook.rb +10 -0
- data/lib/cocoapods-binary-ht/pod-rome/LICENSE.txt +22 -0
- data/lib/cocoapods-binary-ht/pod-rome/xcodebuild_command.rb +266 -0
- data/lib/cocoapods-binary-ht/pod-rome/xcodebuild_raw.rb +68 -0
- data/lib/cocoapods-binary-ht/prebuild_output/metadata.rb +63 -0
- data/lib/cocoapods-binary-ht/prebuild_output/output.rb +44 -0
- data/lib/cocoapods-binary-ht/state_store.rb +21 -0
- data/lib/cocoapods-binary-ht/ui.rb +9 -0
- data/lib/cocoapods_plugin.rb +5 -0
- data/lib/command/binary.rb +37 -0
- data/lib/command/config.rb +215 -0
- data/lib/command/executor/base.rb +37 -0
- data/lib/command/executor/fetcher.rb +67 -0
- data/lib/command/executor/prebuilder.rb +61 -0
- data/lib/command/executor/pusher.rb +35 -0
- data/lib/command/executor/visualizer.rb +23 -0
- data/lib/command/fetch.rb +22 -0
- data/lib/command/helper/zip.rb +20 -0
- data/lib/command/prebuild.rb +47 -0
- data/lib/command/push.rb +22 -0
- data/lib/command/visualize.rb +34 -0
- 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,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,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,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
|