forked from IronLanguages/main
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathharvest.rb
182 lines (151 loc) · 4.55 KB
/
harvest.rb
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
180
181
182
#
# A replacement for heat - heat is bugy as it generates duplicate file-ids.
#
abort(".NET Framework is required.") unless defined?(System::Guid)
require 'digest'
require 'stringio'
class Harvester
def harvest(dir_or_files, wxi_path, generator_name, root)
puts "#{wxi_path} <- #{dir_or_files.kind_of?(String) ? dir_or_files : '[...]'}"
load_component_guids wxi_path
@root = root
@name = file_name_without_extension wxi_path
@indent = 0
@out = StringIO.new
out %{<?xml version="1.0" encoding="utf-8"?>}
out %{<!-- Generated by #{generator_name} -->}
out %{<Include Id="LibsInclude_#@name">}
indent
if dir_or_files.kind_of? String
generate_from_file_system(dir_or_files)
else
generate_from_tree("", build_dir_tree(dir_or_files))
end
unindent
out %{</Include>}
new_content = @out.string
puts
# update the file only if it changed
unless File.exists?(wxi_path) and File.open(wxi_path, "r") { |f| f.read } == new_content
tf_edit wxi_path
File.open(wxi_path, "w") { |f| f.write new_content }
else
puts "File is up to date."
end
end
private # =================================================
# TODO: merge with generate_from_tree
def generate_from_file_system dir
print '.'
full_path = File.join(ENV["DLR_ROOT"], dir)
dirs = []
files = []
Dir.foreach(full_path) do |entry|
# skip .gitignore files - we need them to keep otherwise empty directories in git repo
next if entry == '.' or entry == '..' or entry == '.gitignore'
if File.directory? File.join(full_path, entry)
dirs << entry
else
files << entry
end
end
dirs.sort.each do |entry|
entry_relative_path = File.join(dir, entry)
write_directory entry_relative_path, entry do
generate_from_file_system entry_relative_path
end
end
unless files.empty?
write_component dir do
files.sort.each do |entry|
entry_relative_path = File.join(dir, entry)
out %{<File Id="F#{make_id(entry_relative_path)}" Source="#{@root}#{entry_relative_path.gsub('/', '\\')}" />}
end
end
end
end
def generate_from_tree dir, node
print '.'
files = node["."] || []
dirs = node.keys
dirs.sort.each do |entry|
next if entry == "."
entry_relative_path = File.join(dir, entry)
write_directory entry_relative_path, entry do
generate_from_tree entry_relative_path, node[entry]
end
end
unless files.empty?
write_component dir do
files.sort.each do |entry|
entry_relative_path = File.join(dir, entry)
out %{<File Id="F#{make_id(entry_relative_path)}" Source="#{@root}#{entry_relative_path.gsub('/', '\\')}" />}
end
end
end
end
def build_dir_tree files
root = {}
files.each do |file|
components = file.gsub('/', '\\').split('\\')
file_name = components.delete_at(-1)
# skip .gitignore files - we need them to keep otherwise empty directories in git repo
unless file_name == '.gitignore'
dir = components.reduce(root) { |node, component| node[component] ||= {} }
(dir["."] ||= []) << file_name
end
end
root
end
def load_component_guids wxi_path
@component_guids = {}
if File.exists? wxi_path
File.foreach(wxi_path) do |line|
if /Component Id="C([^"]+)".*Guid="([^"]+)"/ =~ line
@component_guids[$1] = $2
end
end
end
end
def make_component_guid id
@component_guids[id] or System::Guid.new_guid
end
def write_component dir
id = make_id(dir)
out %{<Component Id="C#{id}" DiskId="1" Guid="#{make_component_guid(id)}">}
indent
yield
unindent
out %{</Component>}
end
def write_directory relative_path, name
out %{<Directory Id="D#{make_id(relative_path)}" Name="#{name}">}
indent
yield
unindent
out %{</Directory>}
end
def make_id(path)
# the hash needs to be short enough yet unique within a module to make msi happy:
Digest::MD5.hexdigest(path).to_i(16).to_s(36)
end
def out str
@out.print(' ' * @indent)
@out.puts str
end
def indent
@indent += 2
end
def unindent
@indent -= 2
end
def file_name_without_extension path
File.basename(path)[0..-File.extname(path).size-1]
end
def tf_edit path
if File.exists?(path) and not File.writable?(path)
puts tf = "tf edit #{path}"
puts `#{tf}`
end
end
end