wixgem 0.32.0 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/command.rb +24 -0
- data/lib/wixgem.rb +78 -70
- data/spec/COM_spec.rb +28 -14
- data/spec/WindowsInstaller.rb +139 -0
- data/spec/command_spec.rb +18 -0
- data/spec/installation_spec.rb +2 -2
- data/spec/mergemodule_spec.rb +4 -5
- data/spec/multiple_product_installation_spec.rb +0 -3
- data/spec/test_install.rb +14 -12
- metadata +19 -5
- data/lib/templates/Install.wxs +0 -26
- data/lib/templates/mergemodule.wxs +0 -15
- data/spec/execute.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ad9f8a4070ee4bddfa138baf13adfa2b6bc74d2
|
4
|
+
data.tar.gz: fd44d0d360a288cc10d5f07e88e77c2017c0591c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb6c4dddc389dee7cdba6d8596a4eb8939c657ce4f11675115cd3f3101543602255032e6c1cb4933b834f2088a58ca0984db62bb41f4a2b199f4dab95c1601a0
|
7
|
+
data.tar.gz: 2c5c97aeb97acb74ed7b37f8121a34c83485bb5fbd662ab35736d6b2fb80283dd41c27cef717ff9e307ac78b3cd680d7451ad66d7dafd5ee455618f7903b94a3
|
data/lib/command.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'hash'
|
3
|
+
|
4
|
+
class Command < Hash
|
5
|
+
def initialize(cmd)
|
6
|
+
self[:command]=cmd
|
7
|
+
self[:output] = ''
|
8
|
+
self[:error] = ''
|
9
|
+
self[:exit_code] = ''
|
10
|
+
self[:ignore_exit_code] = false
|
11
|
+
end
|
12
|
+
|
13
|
+
def execute
|
14
|
+
begin
|
15
|
+
self[:output],self[:error], self[:exit_code] = Open3.capture3(self[:command])
|
16
|
+
self[:exit_code]=self[:exit_code].to_i
|
17
|
+
rescue Exception => e
|
18
|
+
self[:error] = "Exception: " + e.to_s
|
19
|
+
self[:exit_code]=1
|
20
|
+
end
|
21
|
+
|
22
|
+
raise self[:error] unless((self[:exit_code] == 0) || self[:ignore_exit_code])
|
23
|
+
end
|
24
|
+
end
|
data/lib/wixgem.rb
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'SecureRandom'
|
3
|
+
require 'logging'
|
4
|
+
require 'tempfile'
|
3
5
|
require 'tmpdir.rb'
|
4
6
|
require 'rexml/document'
|
7
|
+
require "#{File.dirname(__FILE__)}/command.rb"
|
5
8
|
|
6
9
|
class Wix
|
7
10
|
def self.initialize
|
8
11
|
@install_path = ''
|
9
12
|
@debug = false
|
13
|
+
@logger = nil
|
14
|
+
@log_file = nil
|
10
15
|
end
|
11
16
|
def self.install_path=(path)
|
12
17
|
@install_path = path
|
@@ -33,17 +38,23 @@ class Wix
|
|
33
38
|
end
|
34
39
|
|
35
40
|
private
|
41
|
+
def self.start_logger
|
42
|
+
@logger = ::Logging.logger['Wixgem_logger']
|
43
|
+
@log_file = Tempfile.new('wixgem')
|
44
|
+
@logger.add_appenders(Logging.appenders.file(@log_file.path))
|
45
|
+
@logger.level = :debug
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.end_logger
|
49
|
+
@logger = nil
|
50
|
+
@log_file = nil
|
51
|
+
end
|
52
|
+
|
36
53
|
def self.manage_upgrade(xml_doc, input)
|
37
54
|
product = REXML::XPath.match(xml_doc, '//Wix/Product')
|
38
55
|
return xml_doc if(product.length == 0)
|
39
56
|
|
40
|
-
|
41
|
-
manufacturer = input[:manufacturer] if(input.kind_of?(Hash) && input.has_key?(:manufacturer))
|
42
|
-
|
43
|
-
if(input.kind_of?(Hash) &&
|
44
|
-
input.has_key?(:remove_existing_products) &&
|
45
|
-
input[:remove_existing_products])
|
46
|
-
|
57
|
+
if(input.has_key?(:remove_existing_products) && input[:remove_existing_products])
|
47
58
|
raise 'Hash must have a version key if the hash has a :remove_existing_products key' unless(input.has_key?(:version))
|
48
59
|
raise 'Hash must have an upgrade_code key if the hash has a :remove_existing_products key' unless(input.has_key?(:upgrade_code))
|
49
60
|
|
@@ -58,27 +69,9 @@ class Wix
|
|
58
69
|
return xml_doc
|
59
70
|
end
|
60
71
|
|
61
|
-
def self.manage_com_files(xml_doc)
|
62
|
-
component_groups = REXML::XPath.match(xml_doc, '//Wix/Fragment/ComponentGroup')
|
63
|
-
return xml_doc if(component_groups.nil?)
|
64
|
-
|
65
|
-
com_dlls = {}
|
66
|
-
component_groups.each do |component_group|
|
67
|
-
component_group.each_element do |component|
|
68
|
-
classes = component.get_elements('Class')
|
69
|
-
if((classes.length == 1) && (classes[0].attributes["Context"] == 'InprocServer32'))
|
70
|
-
files = component.get_elements('File')
|
71
|
-
files[0].add_attribute('Assembly','.net')
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
return xml_doc
|
77
|
-
end
|
78
|
-
|
79
72
|
def self.manage_custom_actions(xml_doc, input)
|
80
73
|
manufacturer = 'Not Set'
|
81
|
-
manufacturer = input[:manufacturer] if(input.
|
74
|
+
manufacturer = input[:manufacturer] if(input.has_key?(:manufacturer))
|
82
75
|
|
83
76
|
install_path = '[ProgramFilesFolder][ProductName]'
|
84
77
|
install_path = "[ProgramFilesFolder][Manufacturer]\\[ProductName]" unless(manufacturer == 'Not Set')
|
@@ -122,16 +115,14 @@ class Wix
|
|
122
115
|
end
|
123
116
|
|
124
117
|
def self.copy_install_files(directory, input)
|
125
|
-
|
126
|
-
files = input[:files] if(input.kind_of?(Hash))
|
127
|
-
|
118
|
+
files = input[:files]
|
128
119
|
raise 'No files were given to wixgem' if(files.length == 0)
|
129
120
|
|
130
121
|
missing_files = []
|
131
122
|
files.each do |file|
|
132
123
|
if(File.file?(file))
|
133
124
|
install_path = file
|
134
|
-
if(input.
|
125
|
+
if(input.has_key?(:modify_file_paths))
|
135
126
|
input[:modify_file_paths].each { |regex, replacement_string| install_path = install_path.gsub(regex, replacement_string) }
|
136
127
|
end
|
137
128
|
|
@@ -146,20 +137,21 @@ class Wix
|
|
146
137
|
if(@debug)
|
147
138
|
max_path = files.max { |a, b| a.length <=> b.length }
|
148
139
|
columen_size = max_path.length + 10
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
f.printf("%-#{columen_size}s %s\n" % [file, install_path])
|
140
|
+
|
141
|
+
@logger.debug "------------------------------------ Installation Paths -----------------------------------"
|
142
|
+
@logger.debug "%-#{columen_size}s %s\n" % ['File path', 'Installation Path']
|
143
|
+
files.each do |file|
|
144
|
+
if(File.file?(file))
|
145
|
+
install_path = file
|
146
|
+
if(input.has_key?(:modify_file_paths))
|
147
|
+
input[:modify_file_paths].each { |regex, replacement_string| install_path = install_path.gsub(regex, replacement_string) }
|
158
148
|
end
|
159
|
-
|
160
|
-
|
149
|
+
@logger.debug "%-#{columen_size}s %s\n" % [file, install_path]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
@logger.debug "-------------------------------------------------------------------------------------------"
|
161
153
|
end
|
162
|
-
|
154
|
+
|
163
155
|
if(missing_files.length > 0)
|
164
156
|
missing_files_str = ''
|
165
157
|
missing_files.each { |f|
|
@@ -177,28 +169,32 @@ class Wix
|
|
177
169
|
template_option = "-template product"
|
178
170
|
template_option = "-template module" unless(ext == ".msi")
|
179
171
|
|
180
|
-
|
181
|
-
|
172
|
+
cmd = "\"#{install_path}/bin/heat.exe\" dir . #{template_option} -cg InstallionFiles -gg -nologo -srd -o \"#{wxs_file}\""
|
173
|
+
cmd = cmd.gsub(/-srd/, '-svb6 -srd') if(input.has_key?(:has_vb6_files))
|
182
174
|
|
183
|
-
|
175
|
+
heat_cmd = Command.new(cmd)
|
176
|
+
@logger.debug "command: #{heat_cmd[:command]}" if(@debug)
|
184
177
|
|
185
|
-
|
186
|
-
|
187
|
-
|
178
|
+
heat_cmd.execute
|
179
|
+
if(@debug)
|
180
|
+
@logger.debug "--------------------------- Heat output -----------------------------------"
|
181
|
+
@logger.debug heat_cmd[:output]
|
182
|
+
end
|
183
|
+
|
188
184
|
product_name = File.basename(wxs_file, '.wxs')
|
189
|
-
product_name = input[:product_name] if(input.
|
185
|
+
product_name = input[:product_name] if(input.has_key?(:product_name))
|
190
186
|
|
191
187
|
manufacturer = 'Not Set'
|
192
|
-
manufacturer = input[:manufacturer] if(input.
|
188
|
+
manufacturer = input[:manufacturer] if(input.has_key?(:manufacturer))
|
193
189
|
|
194
190
|
product_version = ''
|
195
|
-
product_version = input[:version] if(input.
|
191
|
+
product_version = input[:version] if(input.has_key?(:version))
|
196
192
|
|
197
193
|
product_code = ''
|
198
|
-
product_code = input[:product_code] if(input.
|
194
|
+
product_code = input[:product_code] if(input.has_key?(:product_code))
|
199
195
|
|
200
196
|
upgrade_code = ''
|
201
|
-
upgrade_code = input[:upgrade_code] if(input.
|
197
|
+
upgrade_code = input[:upgrade_code] if(input.has_key?(:upgrade_code))
|
202
198
|
|
203
199
|
wxs_text = File.read(wxs_file)
|
204
200
|
|
@@ -213,9 +209,11 @@ class Wix
|
|
213
209
|
wxs_text = wxs_text.gsub(/UpgradeCode=\"[^\"]+\"/) { |s| s = "UpgradeCode=\"#{upgrade_code}\"" } unless(upgrade_code.empty?)
|
214
210
|
|
215
211
|
xml_doc = REXML::Document.new(wxs_text)
|
212
|
+
packages = REXML::XPath.match(xml_doc, '//Wix/Product/Package')
|
213
|
+
packages.each { |package| package.add_attribute('InstallScope', 'perMachine') } if(input.has_key?(:all_users))
|
214
|
+
|
216
215
|
xml_doc = manage_custom_actions(xml_doc, input)
|
217
216
|
xml_doc = manage_upgrade(xml_doc,input)
|
218
|
-
xml_doc = manage_com_files(xml_doc)
|
219
217
|
xml_doc = manage_msm_files(xml_doc)
|
220
218
|
|
221
219
|
File.open(wxs_file, 'w') { |f| f.puts(xml_doc.to_s) }
|
@@ -229,21 +227,32 @@ class Wix
|
|
229
227
|
def self.create_output(wxs_file, output)
|
230
228
|
wixobj_file = "#{File.basename(wxs_file,'.wxs')}.wixobj"
|
231
229
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
230
|
+
candle_cmd = Command.new("\"#{install_path}\\bin\\candle.exe\" -out \"#{wixobj_file}\" \"#{wxs_file}\"")
|
231
|
+
@logger.debug "command: #{candle_cmd[:command]}" if(@debug)
|
232
|
+
|
233
|
+
candle_cmd.execute
|
234
|
+
if(@debug)
|
235
|
+
@logger.debug "--------------------------- Candle output -----------------------------------"
|
236
|
+
@logger.debug candle_cmd[:output]
|
237
|
+
end
|
238
|
+
|
239
|
+
light_cmd = Command.new("\"#{install_path}\\bin\\light.exe\" -nologo -out \"#{output}\" \"#{wixobj_file}\"")
|
240
|
+
@logger.debug "command: #{light_cmd[:command]}" if(@debug)
|
236
241
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
242
|
+
light_cmd.execute
|
243
|
+
if(@debug)
|
244
|
+
@logger.debug "--------------------------- Light output -----------------------------------"
|
245
|
+
@logger.debug light_cmd[:output]
|
246
|
+
end
|
241
247
|
end
|
242
248
|
|
243
249
|
def self.apply_wix_template(output, input, template)
|
244
250
|
raise 'WIX path is not set!' if(install_path.nil?)
|
251
|
+
input = { files: input } unless(input.kind_of?(Hash))
|
252
|
+
@debug = input[:debug] if(!@debug && input.has_key?(:debug))
|
245
253
|
|
246
|
-
|
254
|
+
start_logger if(@debug)
|
255
|
+
|
247
256
|
FileUtils.mkpath(File.dirname(output)) unless(Dir.exists?(File.dirname(output)))
|
248
257
|
|
249
258
|
ext = File.extname(output)
|
@@ -260,18 +269,17 @@ class Wix
|
|
260
269
|
begin
|
261
270
|
create_wxs_file(wxs_file, input, ext)
|
262
271
|
create_output(wxs_file, output_absolute_path)
|
272
|
+
rescue Exception => e
|
273
|
+
raise "Wixgem exception: #{e}"
|
263
274
|
ensure
|
264
|
-
if(@debug)
|
265
|
-
|
266
|
-
wix_cmds_file = "#{File.basename(wxs_file,'.wxs')}.wix_cmds.txt"
|
267
|
-
FileUtils.cp(wix_cmds_file, "#{output_absolute_path}.wix_cmds.txt") if(File.exists?(wix_cmds_file))
|
268
|
-
end
|
275
|
+
FileUtils.cp(wxs_file, "#{output_absolute_path}.wxs") if(File.exists?(wxs_file) && @debug)
|
276
|
+
FileUtils.cp(@log_file.path, "#{output_absolute_path}.log") if(@debug)
|
269
277
|
end
|
270
278
|
end
|
271
|
-
|
272
|
-
FileUtils.mv('installation_files.txt', "#{output_absolute_path}_paths.txt") if(File.exists?('installation_files.txt'))
|
273
279
|
end
|
274
280
|
pdb_file = output_absolute_path.gsub(ext,'.wixpdb')
|
275
281
|
FileUtils.rm(pdb_file) if(File.exists?(pdb_file))
|
282
|
+
|
283
|
+
end_logger if(@debug)
|
276
284
|
end
|
277
285
|
end
|
data/spec/COM_spec.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'rspec'
|
2
2
|
require './lib/wixgem.rb'
|
3
3
|
require './spec/wixpath.rb'
|
4
|
-
require './WindowsInstaller.rb'
|
4
|
+
require './spec/WindowsInstaller.rb'
|
5
|
+
require './spec/test_files_exist.rb'
|
5
6
|
require 'win32ole'
|
7
|
+
require './admin.rb'
|
6
8
|
|
9
|
+
if(admin?)
|
7
10
|
describe 'Wixgem' do
|
8
11
|
describe 'Installation of a COM object' do
|
9
12
|
it 'should not be able to instance a COM object' do
|
@@ -15,28 +18,39 @@ describe 'Wixgem' do
|
|
15
18
|
WindowsInstaller.uninstall(installation_file)
|
16
19
|
end
|
17
20
|
|
21
|
+
installation_hash = { debug: true, modify_file_paths: {/^.+Release\// => ''}, all_users: 'perMachine', files: ['COMObject/bin/Release/COMObject.dll']}
|
18
22
|
it "should create an installation file using: #{installation_file}" do
|
19
|
-
Wix.make_installation(installation_file,
|
23
|
+
Wix.make_installation(installation_file, installation_hash)
|
20
24
|
expect(File.exists?(installation_file)).to be(true)
|
21
25
|
end
|
22
26
|
|
23
|
-
|
27
|
+
it 'should install' do
|
28
|
+
WindowsInstaller.install(installation_file)
|
29
|
+
expect(WindowsInstaller.installed?(installation_file)).to be(true)
|
30
|
+
end
|
24
31
|
|
32
|
+
it 'should have installed the COMObject.dll' do
|
33
|
+
test_files_exist(installation_file, installation_hash)
|
34
|
+
end
|
35
|
+
|
25
36
|
it 'should be able to instance a COM object with a GUID' do
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
37
|
+
object = WIN32OLE.new('{863AEADA-EE73-4f4a-ABC0-3FB384CB41AA}')
|
38
|
+
expect(object.nil?).to eq(false)
|
39
|
+
puts "Text: #{object.GetText}"
|
40
|
+
expect(object.GetText).to eq('Hello World')
|
30
41
|
end
|
31
42
|
|
32
|
-
it 'should be able to instance a COM object with a Program Id' do
|
33
|
-
|
34
|
-
|
35
|
-
#
|
36
|
-
|
37
|
-
#expect(object.GetText).to eq('Hello World')
|
43
|
+
it 'should be able to instance a COM object with a Program Id' do
|
44
|
+
object = WIN32OLE.new('COMObject.ComClassExample')
|
45
|
+
expect(object.nil?).to eq(false)
|
46
|
+
puts "Text: #{object.GetText}"
|
47
|
+
expect(object.GetText).to eq('Hello World')
|
38
48
|
end
|
39
49
|
|
40
|
-
|
50
|
+
it 'should uninstall' do
|
51
|
+
WindowsInstaller.uninstall(installation_file) if(WindowsInstaller.installed?(installation_file))
|
52
|
+
expect(WindowsInstaller.installed?(installation_file)).to be(false)
|
53
|
+
end
|
41
54
|
end
|
42
55
|
end
|
56
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'win32ole'
|
2
|
+
require 'dev_tasks'
|
3
|
+
|
4
|
+
class WindowsInstaller
|
5
|
+
def self.installed?(product_name)
|
6
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer')
|
7
|
+
installer.Products.each { |prod_code|
|
8
|
+
name = installer.ProductInfo(prod_code, "ProductName")
|
9
|
+
return true if (product_name == name)
|
10
|
+
}
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.install(msi)
|
15
|
+
raise "#{msi} is already installed" if(WindowsInstaller.installed?(msi))
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.uninstall(msi)
|
19
|
+
execute("msiexec.exe /quiet /x #{msi}") if(File.exists?(msi))
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.product_code_installed?(product_code)
|
23
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer')
|
24
|
+
installer.Products.each { |prod_code| return true if (product_code == prod_code) }
|
25
|
+
return false
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.version?(product_name)
|
29
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer')
|
30
|
+
info = product_info(installer, product_code?(product_name, installer))
|
31
|
+
return info['VersionString']
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.product_code?(product_name, installer = nil)
|
35
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer') if(installer.nil?)
|
36
|
+
installer.Products.each { |prod_code|
|
37
|
+
name = installer.ProductInfo(prod_code, "ProductName")
|
38
|
+
return prod_code if (product_name == name)
|
39
|
+
}
|
40
|
+
raise "Failed to find product code for product: #{product_name}"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def self.product_info(installer, code)
|
45
|
+
raise 'Windows installer cannot be nil' if(installer.nil?)
|
46
|
+
hash = Hash.new
|
47
|
+
# known product keywords found on internet. Would be nice to generate.
|
48
|
+
%w[Language PackageCode Transforms AssignmentType PackageName InstalledProductName VersionString RegCompany
|
49
|
+
RegOwner ProductID ProductIcon InstallLocation InstallSource InstallDate Publisher LocalPackage HelpLink
|
50
|
+
HelpTelephone URLInfoAbout URLUpdateInfo InstanceType].sort.each do |prop|
|
51
|
+
value = installer.ProductInfo(code, prop)
|
52
|
+
hash[prop] = value unless(value.nil? || value == '')
|
53
|
+
end
|
54
|
+
return hash
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.msi_info(installer, msi_file)
|
58
|
+
raise 'Windows installer cannot be nil' if(installer.nil?)
|
59
|
+
hash = Hash.new
|
60
|
+
# known product keywords found on internet. Would be nice to generate.
|
61
|
+
%w[Language PackageCode Transforms AssignmentType PackageName InstalledProductName VersionString RegCompany
|
62
|
+
RegOwner ProductID ProductIcon InstallLocation InstallSource InstallDate Publisher LocalPackage HelpLink
|
63
|
+
HelpTelephone URLInfoAbout URLUpdateInfo InstanceType].sort.each do |prop|
|
64
|
+
value = installer.ProductInfo(code, prop)
|
65
|
+
hash[prop] = value unless(value.nil? || value == '')
|
66
|
+
end
|
67
|
+
return hash
|
68
|
+
end
|
69
|
+
|
70
|
+
public
|
71
|
+
def self.dump_info(product_name)
|
72
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer')
|
73
|
+
properties = product_info(installer, product_code?(product_name, installer))
|
74
|
+
properties.each { |id, value| puts "#{id}: #{value}" }
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.msi_records(msi)
|
78
|
+
records = {}
|
79
|
+
|
80
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer')
|
81
|
+
sql_query = "SELECT * FROM `Property`"
|
82
|
+
|
83
|
+
db = installer.OpenDatabase(msi, 0)
|
84
|
+
|
85
|
+
view = db.OpenView(sql_query)
|
86
|
+
view.Execute(nil)
|
87
|
+
|
88
|
+
record = view.Fetch()
|
89
|
+
return '' if(record == nil)
|
90
|
+
|
91
|
+
while(!record.nil?)
|
92
|
+
records[record.StringData(1)] = record.StringData(2)
|
93
|
+
record = view.Fetch()
|
94
|
+
end
|
95
|
+
db = nil
|
96
|
+
installer = nil
|
97
|
+
|
98
|
+
return records
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.dump_msi_records(msi)
|
102
|
+
records = msi_records(msi)
|
103
|
+
|
104
|
+
puts "#{msi} Properties:"
|
105
|
+
records.each do |key,value|
|
106
|
+
puts "#{key}: #{value}"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.dump_product(product_name)
|
111
|
+
installer = WIN32OLE.new('WindowsInstaller.Installer')
|
112
|
+
# only one session per process!
|
113
|
+
session = installer.OpenProduct(product_code?(product_name, installer))
|
114
|
+
db = session.Database
|
115
|
+
|
116
|
+
sql_query = "SELECT * FROM `Property`"
|
117
|
+
view = db.OpenView(sql_query)
|
118
|
+
view.Execute(nil)
|
119
|
+
|
120
|
+
record = view.Fetch()
|
121
|
+
return '' if(record == nil)
|
122
|
+
|
123
|
+
puts "Session Properties:"
|
124
|
+
while(!record.nil?)
|
125
|
+
puts "#{record.StringData(1)}: #{record.StringData(2)}"
|
126
|
+
record = view.Fetch()
|
127
|
+
end
|
128
|
+
db = nil
|
129
|
+
installer = nil
|
130
|
+
puts ''
|
131
|
+
end
|
132
|
+
|
133
|
+
def execute(cmd)
|
134
|
+
command = Command.new(cmd)
|
135
|
+
command.execute
|
136
|
+
|
137
|
+
raise "Failed: #{cmd} Status: #{command[:exit_code]}\nStdout: #{command[:output]}\nStderr: #{command[:error]}" unless(command[:exit_code] == 0)
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require './lib/command.rb'
|
3
|
+
|
4
|
+
describe 'Command' do
|
5
|
+
it 'should be able to execute: dir' do
|
6
|
+
cmd = Command.new('dir')
|
7
|
+
cmd.execute
|
8
|
+
expect(cmd[:output].empty?).to eq(false)
|
9
|
+
expect(cmd[:output].include?('Directory')).to eq(true)
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should fail executing: isnotacommand' do
|
13
|
+
cmd = Command.new('isnotacommand')
|
14
|
+
expect { cmd.execute }.to raise_error
|
15
|
+
expect(cmd[:error].include?('No such file or directory')).to eq(true)
|
16
|
+
expect(cmd[:exit_status]).to_not eq(0)
|
17
|
+
end
|
18
|
+
end
|
data/spec/installation_spec.rb
CHANGED
@@ -51,8 +51,8 @@ describe 'Wixgem' do
|
|
51
51
|
|
52
52
|
describe 'including vb6 files' do
|
53
53
|
it "the wix's heat command should contain the -svb6 flag" do
|
54
|
-
Wix.make_installation('test/wixgem_install_vb6_files.msi', {manufacturer: 'musco', has_vb6_files: true, files: ['rakefile.rb'], debug: true})
|
55
|
-
wix_cmd_text = File.read('test/wixgem_install_vb6_files.msi.
|
54
|
+
Wix.make_installation('test/wixgem_install_vb6_files.msi', {debug: true, manufacturer: 'musco', has_vb6_files: true, files: ['rakefile.rb'], debug: true})
|
55
|
+
wix_cmd_text = File.read('test/wixgem_install_vb6_files.msi.log')
|
56
56
|
expect(wix_cmd_text.include?('-svb6')).to eq(true)
|
57
57
|
end
|
58
58
|
end
|
data/spec/mergemodule_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'rspec'
|
|
2
2
|
require './lib/wixgem.rb'
|
3
3
|
require './spec/wixpath.rb'
|
4
4
|
require './spec/test_install.rb'
|
5
|
-
require './spec/
|
5
|
+
require './spec/WindowsInstaller.rb'
|
6
6
|
require './admin.rb'
|
7
7
|
|
8
8
|
Wix.debug=true
|
@@ -37,8 +37,7 @@ describe 'Wixgem' do
|
|
37
37
|
it "should produce the debug files" do
|
38
38
|
if(key == :test5)
|
39
39
|
expect(File.exists?("#{value[0]}.wxs")).to be(true)
|
40
|
-
expect(File.exists?("#{value[0]}.
|
41
|
-
expect(File.exists?("#{value[0]}_paths.txt")).to be(true)
|
40
|
+
expect(File.exists?("#{value[0]}.log")).to be(true)
|
42
41
|
end
|
43
42
|
end
|
44
43
|
}
|
@@ -63,13 +62,13 @@ describe 'Wixgem' do
|
|
63
62
|
|
64
63
|
it "should install contents of merge module" do
|
65
64
|
begin
|
66
|
-
|
65
|
+
WindowsInstaller.install(msi_file)
|
67
66
|
|
68
67
|
install_dir = "C:/Program Files (x86)/#{File.basename(msi_file, '.msi')}"
|
69
68
|
expect(File.exists?("#{install_dir}/rakefile.rb")).to be(true)
|
70
69
|
expect(File.exists?("#{install_dir}/Gemfile")).to be(true)
|
71
70
|
ensure
|
72
|
-
|
71
|
+
WindowsInstaller.uninstall(msi_file)
|
73
72
|
end
|
74
73
|
end
|
75
74
|
end
|
data/spec/test_install.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
require './WindowsInstaller.rb'
|
1
|
+
require './spec/WindowsInstaller.rb'
|
2
|
+
|
2
3
|
require './admin.rb'
|
3
|
-
require './
|
4
|
+
require './lib/command.rb'
|
4
5
|
|
5
6
|
def get_product_name(msi_file, arg2)
|
6
7
|
product_name = File.basename(msi_file, File.extname(msi_file))
|
7
|
-
product_name = arg2[:product_name] if(arg2.
|
8
|
+
product_name = arg2[:product_name] if(arg2.has_key?(:product_name))
|
8
9
|
|
9
10
|
return product_name
|
10
11
|
end
|
@@ -15,30 +16,32 @@ def test_msi(msi_file, arg2)
|
|
15
16
|
msi_info = WindowsInstaller.msi_records(msi_file)
|
16
17
|
#puts msi_info.to_s
|
17
18
|
|
18
|
-
if(arg2.
|
19
|
+
if(arg2.has_key?(:product_name))
|
19
20
|
raise "ProductName is #{msi_info['ProductName']} expected #{product_name}" unless(product_name == msi_info['ProductName'])
|
20
21
|
end
|
21
22
|
|
22
|
-
if(arg2.
|
23
|
+
if(arg2.has_key?(:product_code))
|
23
24
|
expected = arg2[:product_code].upcase
|
24
25
|
raise "ProductCode is #{msi_info['ProductCode']} expected #{expected}" unless(expected == msi_info['ProductCode'])
|
25
26
|
end
|
26
27
|
|
27
|
-
if(arg2.
|
28
|
+
if(arg2.has_key?(:upgrade_code))
|
28
29
|
expected = arg2[:upgrade_code].upcase
|
29
30
|
raise "UpgradeCode is #{msi_info['UpgradeCode']} expected #{expected}" unless(expected == msi_info['UpgradeCode'])
|
30
31
|
end
|
31
32
|
|
32
33
|
expected_product_version = '1.0.0.0'
|
33
|
-
expected_product_version = arg2[:version] if(arg2.
|
34
|
+
expected_product_version = arg2[:version] if(arg2.has_key?(:version))
|
34
35
|
raise "Invalid product version #{msi_info['ProductVersion']}" if(msi_info['ProductVersion'] != expected_product_version)
|
35
36
|
|
36
37
|
expected_manufacturer = 'Not Set'
|
37
|
-
expected_manufacturer = arg2[:manufacturer] if(arg2.
|
38
|
+
expected_manufacturer = arg2[:manufacturer] if(arg2.has_key?(:manufacturer))
|
38
39
|
raise "Invalid Manufacturer #{msi_info['Manufacturer']}" if(msi_info['Manufacturer'] != expected_manufacturer)
|
39
40
|
end
|
40
41
|
|
41
42
|
def test_install(name, msi_file, arg2, callback=nil)
|
43
|
+
arg2 = { files: arg2} unless(arg2.kind_of?(Hash))
|
44
|
+
|
42
45
|
msi_file = msi_file.gsub(/\//) { |s| s = '\\' }
|
43
46
|
|
44
47
|
test_msi(msi_file, arg2)
|
@@ -49,19 +52,18 @@ def test_install(name, msi_file, arg2, callback=nil)
|
|
49
52
|
|
50
53
|
if(admin?)
|
51
54
|
while(WindowsInstaller.installed?(product_name))
|
52
|
-
|
55
|
+
WindowsInstaller.uninstall(product_name)
|
53
56
|
end
|
54
57
|
raise "#{name}: Uninstall #{product_name} before running tests" if(WindowsInstaller.installed?(product_name))
|
55
58
|
|
56
59
|
begin
|
57
|
-
|
58
|
-
#WindowsInstaller.dump_info(product_name)
|
60
|
+
WindowsInstaller.install(msi_file)
|
59
61
|
|
60
62
|
raise "#{name}: Product name #{product_name} is not installed" unless(WindowsInstaller.installed?(product_name))
|
61
63
|
|
62
64
|
eval callback unless(callback == nil)
|
63
65
|
ensure
|
64
|
-
|
66
|
+
WindowsInstaller.uninstall(msi_file) if(WindowsInstaller.installed?(product_name))
|
65
67
|
raise "Failed to uninstall product #{product_name}" if(WindowsInstaller.installed?(product_name))
|
66
68
|
end
|
67
69
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wixgem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.33.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Marshall
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,6 +72,20 @@ dependencies:
|
|
72
72
|
- - '='
|
73
73
|
- !ruby/object:Gem::Version
|
74
74
|
version: 1.0.28
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: logging
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
83
|
+
prerelease: false
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - "~>"
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
75
89
|
description: Simple Ruby interface to facilitate creating and compiling windows installation
|
76
90
|
files with the Wix Toolset.
|
77
91
|
email: KCCKSMarshall@gmail.com
|
@@ -86,11 +100,11 @@ files:
|
|
86
100
|
- example/install_files/directory/file2.txt
|
87
101
|
- example/install_files/file1.txt
|
88
102
|
- example/rakefile.rb
|
89
|
-
- lib/
|
90
|
-
- lib/templates/mergemodule.wxs
|
103
|
+
- lib/command.rb
|
91
104
|
- lib/wixgem.rb
|
92
105
|
- spec/COM_spec.rb
|
93
|
-
- spec/
|
106
|
+
- spec/WindowsInstaller.rb
|
107
|
+
- spec/command_spec.rb
|
94
108
|
- spec/installation_spec.rb
|
95
109
|
- spec/mergemodule_spec.rb
|
96
110
|
- spec/multiple_product_installation_spec.rb
|
data/lib/templates/Install.wxs
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
3
|
-
<Product Id="PRODUCT_CODE" Name="PRODUCT_NAME" Language="1033" Version="VERSION" Manufacturer="MANUFACTURER" UpgradeCode="UPGRADE_CODE">
|
4
|
-
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
|
5
|
-
|
6
|
-
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
|
7
|
-
<Media Id="1" Cabinet="WixgemTest.cab" EmbedCab="yes" />
|
8
|
-
|
9
|
-
<!-- Step 1: Define the directory structure -->
|
10
|
-
<Directory Id="TARGETDIR" Name="SourceDir">
|
11
|
-
<Directory Id="ProgramFilesFolder">
|
12
|
-
INSTALL_DIR
|
13
|
-
</Directory>
|
14
|
-
</Directory>
|
15
|
-
|
16
|
-
<!-- Step 2: Add files to installer package -->
|
17
|
-
<DirectoryRef Id="INSTALLFOLDER">
|
18
|
-
FILES
|
19
|
-
</DirectoryRef>
|
20
|
-
|
21
|
-
<!-- Step 3: Tell Wix to install the files-->
|
22
|
-
<Feature Id="ProductFeature" Title="PRODUCT_NAME Installation" Level="1">
|
23
|
-
COMPONENT_REFS
|
24
|
-
</Feature>
|
25
|
-
</Product>
|
26
|
-
</Wix>
|
@@ -1,15 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
2
|
-
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
|
3
|
-
<Module Id="MODULE_NAME" Language="1033" Version="VERSION">
|
4
|
-
<Package Id="PRODUCT_CODE" InstallerVersion="200" Manufacturer="MANUFACTURER" />
|
5
|
-
COMPONENT_REFS
|
6
|
-
<Directory Id="TARGETDIR" Name="SourceDir">
|
7
|
-
<Directory Id="MergeRedirectFolder" />
|
8
|
-
</Directory>
|
9
|
-
</Module>
|
10
|
-
<Fragment>
|
11
|
-
<DirectoryRef Id="MergeRedirectFolder">
|
12
|
-
FILES
|
13
|
-
</DirectoryRef>
|
14
|
-
</Fragment>
|
15
|
-
</Wix>
|
data/spec/execute.rb
DELETED