Skip to content

Instantly share code, notes, and snippets.

@UniqMartin
Last active November 27, 2016 06:46
Show Gist options
  • Save UniqMartin/35564916af5c5c4a07cb5588735d4255 to your computer and use it in GitHub Desktop.
Save UniqMartin/35564916af5c5c4a07cb5588735d4255 to your computer and use it in GitHub Desktop.
Graph of Homebrew's "require"
# brew graph-require [--command=<command>] [--full|--start=<file>]
require "set"
require "pp"
# Handle arguments.
dump_command = ARGV.value("command")
dump_full = ARGV.include?("--full")
dump_start = ARGV.value("start") || "../brew"
# Find files to scan and link to.
base = HOMEBREW_LIBRARY_PATH.to_s
files = ["../brew"]
files += Dir["#{base}/**/*.rb"].map { |f| f[(base.length + 1)..-4] }
files.reject! { |f| f.start_with?("test/", "vendor/") }
# Scan selected files for
missing = Set.new
links = {}
files.each do |source|
links[source] = Set.new
lines = IO.readlines("#{base}/#{source}.rb").map(&:chomp)
lines.grep(/^(\s*)require\s+(.*)$/) do |line|
indent, arg = line.match(/^(\s*)require\s+(.*)$/)[1..2]
if arg =~ /^"([^"]+)"(.*)$/
target, tail = $1, $2
$stderr.puts "#{source}: Bogus require: >#{target}<" if target.include?(".")
next if target.start_with?("test/", "vendor/")
if !files.include?(target)
missing << target
next
end
if indent.empty? && tail.empty?
links[source] << [target, :required]
else
links[source] << [target, :conditional]
end
else
$stderr.puts "#{source}: Skipped 'require' line: >#{line}<"
end
end
end
$stderr.puts "Missing/external: #{missing.sort.join(" ")}"
# Add non-discoverable links from `../brew` to (developer) commands.
if dump_command
file = files.find { |f| f.end_with?("cmd/#{dump_command}") }
links["../brew"] << [file, :conditional]
else
files.select { |f| f.start_with?("cmd/", "dev-cmd/") }.each do |file|
links["../brew"] << [file, :conditional]
end
end
# Filter output.
if dump_full
graph = links
else
graph = {}
queue = [dump_start]
until queue.empty?
source = queue.pop
graph[source] = links[source]
links[source].each { |f, _| queue << f unless graph.key?(f) }
end
$stderr.puts "Graph: #{graph.size}/#{links.size} nodes retained"
end
# Output graph.
node_index = 0
node_map = Hash.new { |h, k| h[k] = format("node_%04d", node_index += 1) }
puts "digraph G {"
puts " rankdir=LR;"
graph.each do |node, edges|
puts " #{node_map[node]} [label=\"#{node}\"];"
edges.each do |to_node, kind|
edge_style = kind == :conditional ? " [style=dashed]" : ""
puts " #{node_map[node]} -> #{node_map[to_node]}#{edge_style};"
end
end
puts "}"
#$stderr.puts graph.pretty_inspect
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment