cocoapods-binary-cache 0.1.1
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 +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
|