344 lines
8.2 KiB
Ruby
Raw Normal View History

require "cxxstdlib"
require "ostruct"
require "options"
require "utils/json"
require "development_tools"
# Inherit from OpenStruct to gain a generic initialization method that takes a
# hash and creates an attribute for each key and value. `Tab.new` probably
# should not be called directly, instead use one of the class methods like
2013-01-23 00:26:20 -06:00
# `Tab.create`.
class Tab < OpenStruct
FILENAME = "INSTALL_RECEIPT.json".freeze
CACHE = {}
def self.clear_cache
CACHE.clear
end
2013-01-23 00:26:20 -06:00
# Instantiates a Tab for a new installation of a formula.
def self.create(formula, compiler, stdlib)
build = formula.build
attributes = {
"used_options" => build.used_options.as_flags,
"unused_options" => build.unused_options.as_flags,
"tabfile" => formula.prefix.join(FILENAME),
2015-03-07 23:53:33 -05:00
"built_as_bottle" => build.bottle?,
"poured_from_bottle" => false,
"changed_files" => [],
"time" => Time.now.to_i,
"source_modified_time" => formula.source_modified_time.to_i,
"HEAD" => HOMEBREW_REPOSITORY.git_head,
"compiler" => compiler,
"stdlib" => stdlib,
"runtime_dependencies" => formula.runtime_dependencies.map do |dep|
f = dep.to_formula
{ "full_name" => f.full_name, "version" => f.version.to_s }
end,
"source" => {
"path" => formula.specified_path.to_s,
2015-12-06 22:33:41 +08:00
"tap" => formula.tap ? formula.tap.name : nil,
2016-07-04 12:39:08 +03:00
"spec" => formula.active_spec_sym.to_s,
"versions" => {
"stable" => formula.stable ? formula.stable.version.to_s : nil,
"devel" => formula.devel ? formula.devel.version.to_s : nil,
"head" => formula.head ? formula.head.version.to_s : nil,
2016-08-11 10:00:39 +02:00
"version_scheme" => formula.version_scheme,
},
},
}
new(attributes)
end
2016-09-07 23:17:19 +01:00
# Returns the Tab for an install receipt at `path`.
# Results are cached.
def self.from_file(path)
CACHE.fetch(path) { |p| CACHE[p] = from_file_content(File.read(p), p) }
2015-05-23 18:08:07 +08:00
end
2016-09-07 23:17:19 +01:00
# Like Tab.from_file, but bypass the cache.
def self.from_file_content(content, path)
2015-05-23 18:08:07 +08:00
attributes = Utils::JSON.load(content)
attributes["tabfile"] = path
2016-01-14 18:55:47 +08:00
attributes["source_modified_time"] ||= 0
attributes["changed_files"] ||= []
attributes["source"] ||= {}
tapped_from = attributes["tapped_from"]
unless tapped_from.nil? || tapped_from == "path or URL"
attributes["source"]["tap"] = attributes.delete("tapped_from")
end
if attributes["source"]["tap"] == "mxcl/master" ||
attributes["source"]["tap"] == "Homebrew/homebrew"
attributes["source"]["tap"] = "homebrew/core"
end
2015-07-28 15:33:07 +08:00
if attributes["source"]["spec"].nil?
version = PkgVersion.parse path.to_s.split("/")[-2]
if version.head?
attributes["source"]["spec"] = "head"
else
attributes["source"]["spec"] = "stable"
end
end
2016-07-04 12:39:08 +03:00
if attributes["source"]["versions"].nil?
attributes["source"]["versions"] = {
"stable" => nil,
"devel" => nil,
"head" => nil,
2016-08-11 10:00:39 +02:00
"version_scheme" => 0,
2016-07-04 12:39:08 +03:00
}
end
2014-06-29 23:15:13 -05:00
new(attributes)
end
def self.for_keg(keg)
2013-01-23 00:26:20 -06:00
path = keg.join(FILENAME)
if path.exist?
2014-06-29 22:26:14 -05:00
from_file(path)
else
2015-02-21 12:15:39 -05:00
empty
end
end
2016-09-07 23:17:19 +01:00
# Returns a tab for the named formula's installation,
# or a fake one if the formula is not installed.
def self.for_name(name)
for_formula(Formulary.factory(name))
end
def self.remap_deprecated_options(deprecated_options, options)
deprecated_options.each do |deprecated_option|
option = options.find { |o| o.name == deprecated_option.old }
next unless option
options -= [option]
options << Option.new(deprecated_option.current, option.description)
end
options
end
# Returns a Tab for an already installed formula,
# or a fake one if the formula is not installed.
def self.for_formula(f)
paths = []
if f.opt_prefix.symlink? && f.opt_prefix.directory?
paths << f.opt_prefix.resolved_path
end
if f.linked_keg.symlink? && f.linked_keg.directory?
paths << f.linked_keg.resolved_path
end
2015-11-29 15:40:45 +08:00
if (dirs = f.installed_prefixes).length == 1
paths << dirs.first
end
paths << f.installed_prefix
path = paths.map { |pn| pn.join(FILENAME) }.find(&:file?)
if path
tab = from_file(path)
used_options = remap_deprecated_options(f.deprecated_options, tab.used_options)
tab.used_options = used_options.as_flags
else
# Formula is not installed. Return a fake tab.
2015-02-21 12:15:39 -05:00
tab = empty
tab.unused_options = f.options.as_flags
2015-12-06 22:33:41 +08:00
tab.source = {
"path" => f.specified_path.to_s,
2015-12-06 22:33:41 +08:00
"tap" => f.tap ? f.tap.name : f.tap,
"spec" => f.active_spec_sym.to_s,
"versions" => {
"stable" => f.stable ? f.stable.version.to_s : nil,
"devel" => f.devel ? f.devel.version.to_s : nil,
"head" => f.head ? f.head.version.to_s : nil,
2016-08-11 10:00:39 +02:00
"version_scheme" => f.version_scheme,
},
2015-12-06 22:33:41 +08:00
}
end
2015-02-21 12:15:39 -05:00
tab
end
2015-02-21 12:15:39 -05:00
def self.empty
attributes = {
"used_options" => [],
"unused_options" => [],
"built_as_bottle" => false,
"poured_from_bottle" => false,
"changed_files" => [],
"time" => nil,
2016-01-14 18:55:47 +08:00
"source_modified_time" => 0,
"HEAD" => nil,
"stdlib" => nil,
2016-07-29 15:53:19 -06:00
"compiler" => DevelopmentTools.default_compiler,
"runtime_dependencies" => [],
"source" => {
"path" => nil,
"tap" => nil,
2016-07-04 12:39:08 +03:00
"spec" => "stable",
"versions" => {
"stable" => nil,
"devel" => nil,
"head" => nil,
2016-08-11 10:00:39 +02:00
"version_scheme" => 0,
},
},
}
new(attributes)
end
def with?(val)
option_names = val.respond_to?(:option_names) ? val.option_names : [val]
option_names.any? do |name|
include?("with-#{name}") || unused_options.include?("without-#{name}")
end
end
def without?(val)
!with?(val)
2014-07-30 21:04:17 -05:00
end
def include?(opt)
used_options.include? opt
end
def universal?
include?("universal")
end
def cxx11?
include?("c++11")
end
def build_32_bit?
include?("32-bit")
end
def head?
spec == :head
end
def devel?
spec == :devel
end
def stable?
spec == :stable
end
def used_options
Options.create(super)
end
def unused_options
Options.create(super)
end
def compiler
super || DevelopmentTools.default_compiler
end
def cxxstdlib
# Older tabs won't have these values, so provide sensible defaults
lib = stdlib.to_sym if stdlib
CxxStdlib.create(lib, compiler.to_sym)
end
2014-10-15 00:52:57 -05:00
def build_bottle?
built_as_bottle && !poured_from_bottle
end
def bottle?
built_as_bottle
end
def tap
2015-12-06 22:33:41 +08:00
tap_name = source["tap"]
Tap.fetch(tap_name) if tap_name
end
2015-05-27 09:57:41 +01:00
def tap=(tap)
2015-12-06 22:33:41 +08:00
tap_name = tap.respond_to?(:name) ? tap.name : tap
source["tap"] = tap_name
2015-05-27 09:57:41 +01:00
end
2015-07-28 15:33:07 +08:00
def spec
source["spec"].to_sym
end
2016-07-04 12:39:08 +03:00
def versions
source["versions"]
end
def stable_version
Version.create(versions["stable"]) if versions["stable"]
end
def devel_version
Version.create(versions["devel"]) if versions["devel"]
end
def head_version
Version.create(versions["head"]) if versions["head"]
end
2016-08-11 10:00:39 +02:00
def version_scheme
versions["version_scheme"] || 0
end
2016-01-14 18:55:47 +08:00
def source_modified_time
Time.at(super)
end
def to_json
attributes = {
"used_options" => used_options.as_flags,
"unused_options" => unused_options.as_flags,
"built_as_bottle" => built_as_bottle,
"poured_from_bottle" => poured_from_bottle,
"changed_files" => changed_files.map(&:to_s),
"time" => time,
2016-01-14 18:55:47 +08:00
"source_modified_time" => source_modified_time.to_i,
"HEAD" => self.HEAD,
"stdlib" => (stdlib.to_s if stdlib),
"compiler" => (compiler.to_s if compiler),
"runtime_dependencies" => runtime_dependencies,
"source" => source,
}
Utils::JSON.dump(attributes)
end
def write
CACHE[tabfile] = self
2014-03-22 11:13:33 -05:00
tabfile.atomic_write(to_json)
end
def to_s
s = []
if poured_from_bottle
s << "Poured from bottle"
else
s << "Built from source"
end
2016-09-23 11:01:40 +02:00
s << Time.at(time).strftime("on %Y-%m-%d at %H:%M:%S") if time
unless used_options.empty?
s << "with:"
s << used_options.to_a.join(" ")
end
s.join(" ")
end
end