Permalink
Fetching contributors…
Cannot retrieve contributors at this time
182 lines (151 sloc) 4.55 KB
#
# 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