cocoapods-binary-cache 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/cocoapods-binary-cache.rb +4 -0
- data/lib/cocoapods-binary-cache/cache/all.rb +9 -0
- data/lib/cocoapods-binary-cache/cache/validation_result.rb +69 -0
- data/lib/cocoapods-binary-cache/cache/validator.rb +21 -0
- data/lib/cocoapods-binary-cache/cache/validator_accumulated.rb +4 -0
- data/lib/cocoapods-binary-cache/cache/validator_base.rb +92 -0
- data/lib/cocoapods-binary-cache/cache/validator_dependencies_graph.rb +20 -0
- data/lib/cocoapods-binary-cache/cache/validator_dev_pods.rb +22 -0
- data/lib/cocoapods-binary-cache/cache/validator_exclusion.rb +14 -0
- data/lib/cocoapods-binary-cache/cache/validator_non_dev_pods.rb +13 -0
- data/lib/cocoapods-binary-cache/cache/validator_with_podfile.rb +9 -0
- data/lib/cocoapods-binary-cache/dependencies_graph/dependencies_graph.rb +95 -0
- data/lib/cocoapods-binary-cache/dependencies_graph/graph_visualizer.rb +74 -0
- data/lib/cocoapods-binary-cache/gem_version.rb +6 -0
- data/lib/cocoapods-binary-cache/helper/benchmark_show.rb +11 -0
- data/lib/cocoapods-binary-cache/helper/checksum.rb +12 -0
- data/lib/cocoapods-binary-cache/helper/json.rb +37 -0
- data/lib/cocoapods-binary-cache/helper/lockfile.rb +67 -0
- data/lib/cocoapods-binary-cache/helper/path_utils.rb +8 -0
- data/lib/cocoapods-binary-cache/helper/podspec.rb +17 -0
- data/lib/cocoapods-binary-cache/hooks/post_install.rb +16 -0
- data/lib/cocoapods-binary-cache/hooks/pre_install.rb +141 -0
- data/lib/cocoapods-binary-cache/main.rb +21 -0
- data/lib/cocoapods-binary-cache/pod-binary/LICENSE.txt +22 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/detected_prebuilt_pods/installer.rb +25 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/detected_prebuilt_pods/target_definition.rb +36 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/feature_switches.rb +90 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/names.rb +36 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/passer.rb +25 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/podfile_options.rb +2 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/prebuild_sandbox.rb +71 -0
- data/lib/cocoapods-binary-cache/pod-binary/helper/target_checker.rb +45 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration.rb +12 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/alter_specs.rb +93 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/patch/embed_framework_script.rb +36 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/patch/resolve_dependencies.rb +23 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/patch/source_installation.rb +28 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/remove_target_files.rb +29 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/source_installer.rb +111 -0
- data/lib/cocoapods-binary-cache/pod-binary/integration/validation.rb +20 -0
- data/lib/cocoapods-binary-cache/pod-binary/prebuild.rb +224 -0
- data/lib/cocoapods-binary-cache/pod-binary/prebuild_dsl.rb +69 -0
- data/lib/cocoapods-binary-cache/pod-binary/prebuild_hook.rb +11 -0
- data/lib/cocoapods-binary-cache/pod-binary/tool/tool.rb +12 -0
- data/lib/cocoapods-binary-cache/pod-rome/LICENSE.txt +22 -0
- data/lib/cocoapods-binary-cache/pod-rome/build_framework.rb +247 -0
- data/lib/cocoapods-binary-cache/prebuild_cache.rb +49 -0
- data/lib/cocoapods-binary-cache/prebuild_output/metadata.rb +47 -0
- data/lib/cocoapods-binary-cache/prebuild_output/output.rb +71 -0
- data/lib/cocoapods-binary-cache/scheme_editor.rb +35 -0
- data/lib/cocoapods-binary-cache/state_store.rb +11 -0
- data/lib/cocoapods-binary-cache/ui.rb +9 -0
- data/lib/cocoapods_plugin.rb +5 -0
- data/lib/command/binary.rb +18 -0
- data/lib/command/config.rb +31 -0
- data/lib/command/executor/base.rb +20 -0
- data/lib/command/executor/fetcher.rb +44 -0
- data/lib/command/executor/prebuilder.rb +58 -0
- data/lib/command/executor/pusher.rb +19 -0
- data/lib/command/executor/visualizer.rb +20 -0
- data/lib/command/fetch.rb +23 -0
- data/lib/command/helper/zip.rb +20 -0
- data/lib/command/prebuild.rb +29 -0
- data/lib/command/visualize.rb +30 -0
- metadata +193 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 284d9870e8f4938c92a779543fffa2810804096cb6cf8504a68cc20c0307acfe
|
4
|
+
data.tar.gz: 7f0ef9d6f2a0afe33cd6fe1c05c77c0ec51d1598f746fbe090faea61ac6cc513
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c3287b32dda1506122f0b1d89be9cf413970c8f3389bc538680db8addfe83ca273df223ded9b31dd3c9ee59c5791331ebee87c93f6f85c3437920d29500952ec
|
7
|
+
data.tar.gz: 7ff8a381f96027f7f45321ef2979f43e5383b8f7dfb787adafd2c7afa0348454632a7ab73b43cb2ff05a89aa15c1aac6ff54f245192d86549d63521b612f56af
|
@@ -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,69 @@
|
|
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 missed
|
11
|
+
@missed_with_reasons.keys.to_set
|
12
|
+
end
|
13
|
+
|
14
|
+
def missed?(name)
|
15
|
+
@missed_with_reasons.key?(name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def hit?(name)
|
19
|
+
@hit.include?(name)
|
20
|
+
end
|
21
|
+
|
22
|
+
def include?(name)
|
23
|
+
missed?(name) || hit?(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def merge(other)
|
27
|
+
PodPrebuild::CacheValidationResult.new(
|
28
|
+
@missed_with_reasons.merge(other.missed_with_reasons),
|
29
|
+
@hit + other.hit
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_to(path)
|
34
|
+
FileUtils.mkdir_p(File.dirname(path))
|
35
|
+
json_file = PodPrebuild::JSONFile.new(path)
|
36
|
+
json_file["cache_missed"] = missed.to_a
|
37
|
+
json_file["cache_hit"] = hit.to_a
|
38
|
+
json_file.save!
|
39
|
+
end
|
40
|
+
|
41
|
+
def keep(names)
|
42
|
+
base_names = names.map { |name| name.split("/")[0] }.to_set
|
43
|
+
select { |name| base_names.include?(name.split("/")[0]) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def discard(names)
|
47
|
+
base_names = names.map { |name| name.split("/")[0] }.to_set
|
48
|
+
reject { |name| base_names.include?(name.split("/")[0]) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def select(&predicate)
|
52
|
+
PodPrebuild::CacheValidationResult.new(
|
53
|
+
@missed_with_reasons.select { |name, _| predicate.call(name) },
|
54
|
+
@hit.select(&predicate)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reject(&predicate)
|
59
|
+
select { |name| !predicate.call(name) }
|
60
|
+
end
|
61
|
+
|
62
|
+
def print_summary
|
63
|
+
Pod::UI.puts "Cache validation: hit (#{@hit.count}) #{@hit.to_a}"
|
64
|
+
@missed_with_reasons.each do |name, reason|
|
65
|
+
Pod::UI.puts "Cache validation: missed #{name}. Reason: #{reason}".yellow
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PodPrebuild
|
2
|
+
class CacheValidator < BaseCacheValidator
|
3
|
+
def initialize(options)
|
4
|
+
super(options)
|
5
|
+
@validators = [
|
6
|
+
PodPrebuild::PodfileChangesCacheValidator.new(options),
|
7
|
+
PodPrebuild::NonDevPodsCacheValidator.new(options)
|
8
|
+
]
|
9
|
+
@validators << PodPrebuild::DevPodsCacheValidator.new(options) if Pod::Podfile::DSL.dev_pods_enabled
|
10
|
+
@validators << PodPrebuild::DependenciesGraphCacheValidator.new(options)
|
11
|
+
@validators << PodPrebuild::ExclusionCacheValidator.new(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate(*)
|
15
|
+
@validators.reduce(PodPrebuild::CacheValidationResult.new) do |acc, validator|
|
16
|
+
validation = validator.validate(acc)
|
17
|
+
validator.is_a?(AccumulatedCacheValidator) ? validation : acc.merge(validation)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,92 @@
|
|
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
|
+
version = pods[name]
|
41
|
+
prebuilt_version = prebuilt_pods[name]
|
42
|
+
result = false
|
43
|
+
if prebuilt_version.nil?
|
44
|
+
missed[name] = "Not available (#{version})"
|
45
|
+
elsif prebuilt_version != version
|
46
|
+
missed[name] = "Outdated: (prebuilt: #{prebuilt_version}) vs (#{version})"
|
47
|
+
else
|
48
|
+
settings_diff = incompatible_build_settings(name)
|
49
|
+
if settings_diff.empty?
|
50
|
+
hit << name
|
51
|
+
result = true
|
52
|
+
else
|
53
|
+
missed[name] = "Incompatible build settings: #{settings_diff}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
subspec_pods.each do |parent, children|
|
60
|
+
missed_children = children.reject { |child| check_pod.call(child) }
|
61
|
+
if missed_children.empty?
|
62
|
+
hit << parent
|
63
|
+
else
|
64
|
+
missed[parent] = "Subspec pods were missed: #{missed_children}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
non_subspec_pods = pods.reject { |pod| subspec_pods.include?(pod) }
|
69
|
+
non_subspec_pods.each { |pod, _| check_pod.call(pod) }
|
70
|
+
PodPrebuild::CacheValidationResult.new(missed, hit)
|
71
|
+
end
|
72
|
+
|
73
|
+
def read_prebuilt_build_settings(name)
|
74
|
+
return {} if generated_framework_path.nil?
|
75
|
+
|
76
|
+
metadata = PodPrebuild::Metadata.in_dir(generated_framework_path + name)
|
77
|
+
metadata.build_settings
|
78
|
+
end
|
79
|
+
|
80
|
+
def incompatible_build_settings(name)
|
81
|
+
settings_diff = {}
|
82
|
+
prebuilt_build_settings = read_prebuilt_build_settings(name)
|
83
|
+
validate_prebuilt_settings&.(name)&.each do |key, value|
|
84
|
+
prebuilt_value = prebuilt_build_settings[key]
|
85
|
+
unless prebuilt_value.nil? || value == prebuilt_value
|
86
|
+
settings_diff[key] = { :current => value, :prebuilt => prebuilt_value }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
settings_diff
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module PodPrebuild
|
2
|
+
class DependenciesGraphCacheValidator < AccumulatedCacheValidator
|
3
|
+
def validate(accumulated)
|
4
|
+
return accumulated if library_evolution_supported? || @pod_lockfile.nil?
|
5
|
+
|
6
|
+
dependencies_graph = DependenciesGraph.new(@pod_lockfile.lockfile)
|
7
|
+
clients = dependencies_graph.get_clients(accumulated.missed.to_a)
|
8
|
+
unless Pod::Podfile::DSL.dev_pods_enabled
|
9
|
+
clients = clients.reject { |client| @pod_lockfile.dev_pods.keys.include?(client) }
|
10
|
+
end
|
11
|
+
|
12
|
+
missed = clients.map { |client| [client, "Dependencies were missed"] }.to_h
|
13
|
+
accumulated.merge(PodPrebuild::CacheValidationResult.new(missed, Set.new))
|
14
|
+
end
|
15
|
+
|
16
|
+
def library_evolution_supported?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module PodPrebuild
|
2
|
+
class DevPodsCacheValidator < BaseCacheValidator
|
3
|
+
def initialize(options)
|
4
|
+
super(options)
|
5
|
+
@sandbox_root = options[:sandbox_root]
|
6
|
+
end
|
7
|
+
|
8
|
+
def validate(*)
|
9
|
+
return PodPrebuild::CacheValidationResult.new if @pod_lockfile.nil?
|
10
|
+
|
11
|
+
# TODO (thuyen): Logic needs to be revised
|
12
|
+
# TODO (thuyen): Migrate the code PodCacheValidator.verify_devpod_checksum to this place
|
13
|
+
missed_with_checksum, hit_with_checksum = PodCacheValidator.verify_devpod_checksum(
|
14
|
+
@sandbox_root,
|
15
|
+
@generated_framework_path,
|
16
|
+
@pod_lockfile.lockfile
|
17
|
+
)
|
18
|
+
missed = missed_with_checksum.transform_values { |checksum| "Checksum changed: #{checksum}" }
|
19
|
+
PodPrebuild::CacheValidationResult.new(missed, hit_with_checksum.keys.to_set)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
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_pods,
|
9
|
+
prebuilt_pods: @prebuilt_lockfile.nil? ? {} : @prebuilt_lockfile.non_dev_pods
|
10
|
+
)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# Copyright 2019 Grabtaxi Holdings PTE LTE (GRAB), 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 but we need to traverse a substree from any node
|
9
|
+
# https://github.com/monora/rgl/blob/master/lib/rgl/adjacency.rb
|
10
|
+
|
11
|
+
class DependenciesGraph
|
12
|
+
def initialize(lockfile)
|
13
|
+
@lockfile = lockfile
|
14
|
+
@invert_edge = true # A normal edge is an edge (one direction) from library A to library B which is a dependency of A.
|
15
|
+
end
|
16
|
+
|
17
|
+
# Input : a list of library names.
|
18
|
+
# Output: a set of library names which are clients (directly and indirectly) of those input libraries.
|
19
|
+
def get_clients(libnames)
|
20
|
+
result = Set.new()
|
21
|
+
libnames.each do |lib|
|
22
|
+
if graph.has_vertex?(lib)
|
23
|
+
result.merge(traverse_sub_tree(graph, lib))
|
24
|
+
else
|
25
|
+
puts "Warning: cannot find lib: #{lib}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
result
|
29
|
+
end
|
30
|
+
|
31
|
+
def write_graphic_file(output_graphic_fmt, filename='graph', highlight_nodes=Set[])
|
32
|
+
if !output_graphic_fmt
|
33
|
+
puts 'Error: Need graphic format.'
|
34
|
+
return
|
35
|
+
end
|
36
|
+
graph.write_to_graphic_file(output_graphic_fmt, dotfile=filename, options={}, highlight_nodes)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def dependencies
|
42
|
+
@dependencies ||= begin
|
43
|
+
if @lockfile
|
44
|
+
@lockfile.to_hash['PODS']
|
45
|
+
else
|
46
|
+
nil
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Convert array of dictionaries -> a dictionary with format {A: [A's dependencies]}
|
52
|
+
def pod_to_dependencies
|
53
|
+
dependencies.map { |d| d.is_a?(Hash) ? d : { d => [] } }.reduce({}) { |combined, individual| combined.merge!(individual) }
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_vertex(graph, pod)
|
57
|
+
node_name = sanitized_pod_name(pod)
|
58
|
+
graph.add_vertex(node_name)
|
59
|
+
node_name
|
60
|
+
end
|
61
|
+
|
62
|
+
def sanitized_pod_name(name)
|
63
|
+
Pod::Dependency.from_string(name).name
|
64
|
+
end
|
65
|
+
|
66
|
+
def graph
|
67
|
+
@graph ||= begin
|
68
|
+
graph = RGL::DirectedAdjacencyGraph.new()
|
69
|
+
|
70
|
+
pod_to_dependencies.each do |pod, dependencies|
|
71
|
+
pod_node = add_vertex(graph, pod)
|
72
|
+
next if pod_node.nil?
|
73
|
+
dependencies.each do |dependency|
|
74
|
+
dep_node = add_vertex(graph, dependency)
|
75
|
+
next if dep_node.nil?
|
76
|
+
if @invert_edge
|
77
|
+
graph.add_edge(dep_node, pod_node)
|
78
|
+
else
|
79
|
+
graph.add_edge(pod_node, dep_node)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
graph
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def traverse_sub_tree(graph, vertex)
|
88
|
+
visited_nodes = Set.new()
|
89
|
+
graph.each_adjacent(vertex) do |v|
|
90
|
+
visited_nodes.add(v)
|
91
|
+
visited_nodes.merge(traverse_sub_tree(graph, v))
|
92
|
+
end
|
93
|
+
visited_nodes
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright 2019 Grabtaxi Holdings PTE LTE (GRAB), 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(params={}, highlight_nodes)
|
10
|
+
params['name'] ||= self.class.name.gsub(/:/, '_')
|
11
|
+
fontsize = params['fontsize'] ? params['fontsize'] : '12'
|
12
|
+
graph = (directed? ? DOT::Digraph : DOT::Graph).new(params)
|
13
|
+
edge_class = directed? ? DOT::DirectedEdge : DOT::Edge
|
14
|
+
vertex_options = params['vertex'] || {}
|
15
|
+
edge_options = params['edge'] || {}
|
16
|
+
|
17
|
+
each_vertex do |v|
|
18
|
+
default_vertex_options = {
|
19
|
+
'name' => vertex_id(v),
|
20
|
+
'fontsize' => fontsize,
|
21
|
+
'label' => vertex_label(v),
|
22
|
+
'style' => 'filled',
|
23
|
+
}
|
24
|
+
if highlight_nodes.include?(v)
|
25
|
+
default_vertex_options = default_vertex_options.merge({
|
26
|
+
'color' => 'red',
|
27
|
+
'fillcolor' => 'red'
|
28
|
+
})
|
29
|
+
else
|
30
|
+
default_vertex_options = default_vertex_options.merge({
|
31
|
+
'color' => 'blue',
|
32
|
+
'fillcolor' => 'blue'
|
33
|
+
})
|
34
|
+
end
|
35
|
+
|
36
|
+
each_vertex_options = default_vertex_options.merge(vertex_options)
|
37
|
+
vertex_options.each{|option, val| each_vertex_options[option] = val.call(v) if val.is_a?(Proc)}
|
38
|
+
graph << DOT::Node.new(each_vertex_options)
|
39
|
+
end
|
40
|
+
|
41
|
+
each_edge do |u, v|
|
42
|
+
default_edge_options = {
|
43
|
+
'from' => vertex_id(u),
|
44
|
+
'to' => vertex_id(v),
|
45
|
+
'fontsize' => fontsize
|
46
|
+
}
|
47
|
+
each_edge_options = default_edge_options.merge(edge_options)
|
48
|
+
edge_options.each{|option, val| each_edge_options[option] = val.call(u, v) if val.is_a?(Proc)}
|
49
|
+
graph << edge_class.new(each_edge_options)
|
50
|
+
end
|
51
|
+
|
52
|
+
graph
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_to_graphic_file(fmt='png', dotfile="graph", options={}, highlight_nodes)
|
56
|
+
src = dotfile + ".dot"
|
57
|
+
dot = dotfile + "." + fmt
|
58
|
+
|
59
|
+
File.open(src, 'w') do |f|
|
60
|
+
f << self.to_dot_graph(params=options, highlight_nodes=highlight_nodes).to_s << "\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
unless system("dot -T#{fmt} #{src} -o #{dot}")
|
64
|
+
message = <<-HEREDOC # Use <<- to indent End of String terminator
|
65
|
+
Error executing dot. Did you install GraphViz?
|
66
|
+
Try installing it via Homebrew: `brew install graphviz`.
|
67
|
+
Visit https://graphviz.org/download/ for more installation instructions.
|
68
|
+
HEREDOC
|
69
|
+
raise message
|
70
|
+
end
|
71
|
+
dot
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|