1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
|
class Keg
PREFIX_PLACEHOLDER = "@@HOMEBREW_PREFIX@@".freeze
CELLAR_PLACEHOLDER = "@@HOMEBREW_CELLAR@@".freeze
REPOSITORY_PLACEHOLDER = "@@HOMEBREW_REPOSITORY@@".freeze
Relocation = Struct.new(:old_prefix, :old_cellar, :old_repository,
:new_prefix, :new_cellar, :new_repository) do
# Use keyword args instead of positional args for initialization
def initialize(**kwargs)
super(*members.map { |k| kwargs[k] })
end
end
def fix_dynamic_linkage
symlink_files.each do |file|
link = file.readlink
# Don't fix relative symlinks
next unless link.absolute?
if link.to_s.start_with?(HOMEBREW_CELLAR.to_s) || link.to_s.start_with?(HOMEBREW_PREFIX.to_s)
FileUtils.ln_sf(link.relative_path_from(file.parent), file)
end
end
end
alias generic_fix_dynamic_linkage fix_dynamic_linkage
def relocate_dynamic_linkage(_relocation)
[]
end
def replace_locations_with_placeholders
relocation = Relocation.new(
old_prefix: HOMEBREW_PREFIX.to_s,
old_cellar: HOMEBREW_CELLAR.to_s,
old_repository: HOMEBREW_REPOSITORY.to_s,
new_prefix: PREFIX_PLACEHOLDER,
new_cellar: CELLAR_PLACEHOLDER,
new_repository: REPOSITORY_PLACEHOLDER,
)
relocate_dynamic_linkage(relocation)
replace_text_in_files(relocation)
end
def replace_placeholders_with_locations(files, skip_linkage: false)
relocation = Relocation.new(
old_prefix: PREFIX_PLACEHOLDER,
old_cellar: CELLAR_PLACEHOLDER,
old_repository: REPOSITORY_PLACEHOLDER,
new_prefix: HOMEBREW_PREFIX.to_s,
new_cellar: HOMEBREW_CELLAR.to_s,
new_repository: HOMEBREW_REPOSITORY.to_s,
)
relocate_dynamic_linkage(relocation) unless skip_linkage
replace_text_in_files(relocation, files: files)
end
def replace_text_in_files(relocation, files: nil)
files ||= text_files | libtool_files
changed_files = []
files.map(&path.method(:join)).group_by { |f| f.stat.ino }.each_value do |first, *rest|
s = first.open("rb", &:read)
replacements = {
relocation.old_prefix => relocation.new_prefix,
relocation.old_cellar => relocation.new_cellar,
relocation.old_repository => relocation.new_repository,
}
# Order matters here since `HOMEBREW_CELLAR` and `HOMEBREW_REPOSITORY` are
# children of `HOMEBREW_PREFIX` by default.
regexp = Regexp.union(
relocation.old_cellar,
relocation.old_repository,
relocation.old_prefix,
)
changed = s.gsub!(regexp, replacements)
next unless changed
changed_files += [first, *rest].map { |file| file.relative_path_from(path) }
begin
first.atomic_write(s)
rescue SystemCallError
first.ensure_writable do
first.open("wb") { |f| f.write(s) }
end
else
rest.each { |file| FileUtils.ln(first, file, force: true) }
end
end
changed_files
end
def detect_cxx_stdlibs(_options = {})
[]
end
def each_unique_file_matching(string)
Utils.popen_read("/usr/bin/fgrep", "-lr", string, to_s) do |io|
hardlinks = Set.new
until io.eof?
file = Pathname.new(io.readline.chomp)
next if file.symlink?
yield file if hardlinks.add? file.stat.ino
end
end
end
def lib
path.join("lib")
end
def text_files
text_files = []
return text_files unless File.exist?("/usr/bin/file")
# file has known issues with reading files on other locales. Has
# been fixed upstream for some time, but a sufficiently new enough
# file with that fix is only available in macOS Sierra.
# http://bugs.gw.com/view.php?id=292
with_custom_locale("C") do
files = Set.new path.find.reject { |pn|
next true if pn.symlink?
next true if pn.directory?
next true if Metafiles::EXTENSIONS.include?(pn.extname)
if pn.text_executable?
text_files << pn
next true
end
false
}
output, _status = Open3.capture2("/usr/bin/xargs -0 /usr/bin/file --no-dereference --print0",
stdin_data: files.to_a.join("\0"))
# `file` output sometimes contains data from the file, which may include
# invalid UTF-8 entities, so tell Ruby this is just a bytestring
output.force_encoding(Encoding::ASCII_8BIT)
output.each_line do |line|
path, info = line.split("\0", 2)
# `file` sometimes prints more than one line of output per file;
# subsequent lines do not contain a null-byte separator, so `info`
# will be `nil` for those lines
next unless info
next unless info.include?("text")
path = Pathname.new(path)
next unless files.include?(path)
text_files << path
end
end
text_files
end
def libtool_files
libtool_files = []
path.find do |pn|
next if pn.symlink? || pn.directory? || pn.extname != ".la"
libtool_files << pn
end
libtool_files
end
def symlink_files
symlink_files = []
path.find do |pn|
symlink_files << pn if pn.symlink?
end
symlink_files
end
def self.file_linked_libraries(_file, _string)
[]
end
end
require "extend/os/keg_relocate"
|