wixgem 0.66.0 → 0.84.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +25 -10
- data/example/example.msi +0 -0
- data/example/example.msm +0 -0
- data/lib/admin.rb +1 -1
- data/lib/associate_extension.rb +46 -0
- data/lib/custom_action.rb +74 -0
- data/lib/temp_directory.rb +18 -0
- data/lib/wixgem.rb +150 -62
- metadata +21 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f903e98ba851435dde4f9a8d002455b1f85341d
|
4
|
+
data.tar.gz: 0579fd5fb3f257d9bf7d172cebd9f2463dc55f99
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 84d696956ef7cb66ee5e711f7ab24b45aa212ccbbdab504a7ef196bc7714b4c4981844b6c350fb7b18e43af74ff38af5c9f30bfaa4f6106839e6059fe417cfde
|
7
|
+
data.tar.gz: 313a096755a6b651314c8e91fbef7dcec01ec7ca616decd5184b5c7bb432733cb4292cb670c60abc1f496a9fcf487c088b49b69804f1502b50ac77f046bc011a
|
data/README.md
CHANGED
@@ -13,8 +13,6 @@ The [WiX Toolset](http://wixtoolset.org) must be installed.
|
|
13
13
|
```ruby
|
14
14
|
require 'wixgem'
|
15
15
|
|
16
|
-
WIX_TOOLSET_ROOT='path to root of Wix toolset'
|
17
|
-
Wixgem::Wix.install_path = WIX_TOOLSET_ROOT
|
18
16
|
Wixgen::Wix.make_installation('Product.msi', ['rakefile.rb']])
|
19
17
|
|
20
18
|
Wixgen::Wix.make_installation('Product.msi', {product_name: 'productname',
|
@@ -31,8 +29,7 @@ Wixgen::Wix.make_installation('Product.msi', {modify_file_paths: {/\Atest_files\
|
|
31
29
|
require 'wixgem'
|
32
30
|
|
33
31
|
WIX_TOOLSET_ROOT='path to root of Wix toolset'
|
34
|
-
|
35
|
-
Wixgen::Wix.make_mergemodule('Product.msi', ['rakefile.rb']])
|
32
|
+
Wixgen::Wix.make_mergemodule('Product.msm', ['rakefile.rb']])
|
36
33
|
|
37
34
|
```
|
38
35
|
An example rakefile.rb is included in the example directory of the gem.
|
@@ -42,7 +39,7 @@ An example rakefile.rb is included in the example directory of the gem.
|
|
42
39
|
Wixgem will generate an installation or merge module from an array of files. The Wixgem also supports a
|
43
40
|
small set of optional arguments allowing the developer to customize the generated installation file.
|
44
41
|
|
45
|
-
#### Optional input hash
|
42
|
+
#### Optional input hash keys
|
46
43
|
* **product_name**: String specifing the product name of the installation.
|
47
44
|
* **manufacturer**: String specifing the manufacturer of the installation.
|
48
45
|
* **version**: String specifing the version of the installation. i.e. '1.1.0.0'
|
@@ -53,14 +50,32 @@ small set of optional arguments allowing the developer to customize the generate
|
|
53
50
|
* **modify_file_paths**: A hash of regex objects to replacement string pairs. The regular expressions are applied to
|
54
51
|
the file paths allowing the developer to modify the relative location of the files in the installation.
|
55
52
|
* **has_vb6_files**: Required if installation contains any ocx's or dll's compiled with Visual Basic 6.
|
56
|
-
* **remove_existing_products**: A boolean value. If the value is true the installation will remove
|
57
|
-
|
53
|
+
* **remove_existing_products**: A boolean value. If the value is true the installation will remove the previous
|
54
|
+
installation of the product before installing the product.
|
58
55
|
* **all_users**: String value perUser or perMachine. The default is perUser.
|
59
|
-
* **suppress_registry_harvesting** Suppress registry harvesting. Can fix the Runtime Error E6034.
|
56
|
+
* **suppress_registry_harvesting** Suppress's heat's registry harvesting. Can fix the Runtime Error E6034.
|
60
57
|
* **suppress_COM_elements** Suppress COM elements.
|
61
58
|
* **installer_version** Represents the minimum version of the Windows installer required to install
|
62
|
-
this package. The default version is 4.
|
63
|
-
3.0, 3.5, 4.0, 4.5.
|
59
|
+
this package. The default version is 4.5. Other valid versions are 2.0,
|
60
|
+
3.0, 3.5, 4.0, 4.5.
|
61
|
+
* **requires_netframework** Tests to see if the require netframework version is installed on the machine. The
|
62
|
+
associated value specifies the netframework version. The net framework strings can
|
63
|
+
be found at http://wixtoolset.org/documentation/manual/v3/customactions/wixnetfxextension.html
|
64
|
+
* **binary_table** Key is associated with an array of hashes specifying a file and a binary key to place into the
|
65
|
+
binary table.
|
66
|
+
* **custom_actions** Key is associated with an array of hashes specifying the arguments for custom actions.
|
67
|
+
* ** shortcuts ** Key is associated with a hash specifying arguments to create shortcuts. Currently,
|
68
|
+
the only supported shortcut is a desktop shortcut.
|
69
|
+
* ** extensions ** Key is associated with a hash to register executables with extensions.
|
70
|
+
* ** com_self_register ** Is a key associated with an array of files com dll's/exe's to mark for self
|
71
|
+
registration
|
72
|
+
* ** ignore_files ** Is a key associated with an array of files to ignore when generating the installation. This
|
73
|
+
key is useful for having files copied to the installation folder to generating the installation,
|
74
|
+
but not adding the files to the installation. An example of usage is I had a COM dll causing
|
75
|
+
heat some problems, so I needed to add the dependency dll's to the install directory so heat could
|
76
|
+
harvest the com registry entries, but heat was choking on adding the support dll to the
|
77
|
+
installation. Thus, I was able to create a merge module for the COM dll and another merge module for
|
78
|
+
the supporting dlls.
|
64
79
|
* **debug**: Boolean value. If debug is true the Product's wxs file and a log file are copied
|
65
80
|
to the same directory as the output msi file. If you are familiar with WiX this can helpful
|
66
81
|
if there is a problem.
|
data/example/example.msi
ADDED
Binary file
|
data/example/example.msm
ADDED
Binary file
|
data/lib/admin.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
class AssociateExtension
|
2
|
+
def initialize(xml_doc)
|
3
|
+
@xml_doc = xml_doc
|
4
|
+
end
|
5
|
+
|
6
|
+
def associate(file, extension)
|
7
|
+
file = file.gsub(/\//,'\\')
|
8
|
+
file_elements = REXML::XPath.match(@xml_doc, "//File[@Source='.\\#{file}']")
|
9
|
+
raise "Unable to find file '#{file}' to associate with extension '#{extension}'" if(file_elements.nil? || file_elements.size != 1)
|
10
|
+
|
11
|
+
file_parent = file_elements[0].parent
|
12
|
+
|
13
|
+
app=File.basename(file)
|
14
|
+
# App Paths to support Start,Run -> "myapp"
|
15
|
+
#<RegistryValue Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\MyApp.exe" Value="[!MyApp.exe]" Type="string" />
|
16
|
+
file_parent.add_element 'RegistryValue', { 'Root' => 'HKLM', 'Key' => "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\#{app}",
|
17
|
+
'Value' => "[INSTALLDIR]#{file}", 'Type' => 'string' }
|
18
|
+
#<RegistryValue Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\MyApp.exe" Name="Path" Value="[APPLICATIONFOLDER]" Type="string" />
|
19
|
+
file_parent.add_element 'RegistryValue', { 'Root' => 'HKLM', 'Key' => "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\#{app}",
|
20
|
+
'Name' => 'Path', 'Value' => '[INSTALLDIR]', 'Type' => 'string' }
|
21
|
+
|
22
|
+
# Extend to the "open with" list + Win7 jump menu pinning
|
23
|
+
#<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\MyApp.exe\SupportedTypes" Name=".xyz" Value="" Type="string" />
|
24
|
+
file_parent.add_element 'RegistryValue', { 'Root' => 'HKLM', 'Key' => "SOFTWARE\\Classes\\Applications\\#{app}\\SupportedTypes",
|
25
|
+
'Name' => extension, 'Value' => '', 'Type' => 'string' }
|
26
|
+
#<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\Applications\MyApp.exe\shell\open" Name="FriendlyAppName" Value="!(loc.ApplicationName)" Type="string" />
|
27
|
+
file_parent.add_element 'RegistryValue', { 'Root' => 'HKLM', 'Key' => "SOFTWARE\\Classes\\Applications\\#{app}\\shell\\open\\command",
|
28
|
+
'Value' => "[INSTALLDIR]#{file} \"%1\"", 'Type' => 'string' }
|
29
|
+
|
30
|
+
# MyApp.Document ProgID
|
31
|
+
#<RegistryValue Root="HKLM" Key="SOFTWARE\Classes\MyApp.Document" Name="FriendlyTypeName" Value="!(loc.DescXYZ)" Type="string" />
|
32
|
+
file_parent.add_element 'RegistryValue', { 'Root' => 'HKLM', 'Key' => "SOFTWARE\\Classes\\#{File.basename(app, '.exe')}.Document",
|
33
|
+
'Name' => 'Aim Project File', 'Value' => "[INSTALLDIR]#{app} \"%1\"", 'Type' => 'string' }
|
34
|
+
|
35
|
+
#<ProgId Id="MyApp.Document" Description="!(loc.DescXYZ)" Icon="filetype.ico" Advertise="yes">
|
36
|
+
# <Extension Id="xyz">
|
37
|
+
# <Verb Id="open" Command="!(loc.ExplorerMenuOpenXYZ)" Argument=""%1"" />
|
38
|
+
# <MIME Advertise="yes" ContentType="application/xyz" Default="yes" />
|
39
|
+
# </Extension>
|
40
|
+
#</ProgId>
|
41
|
+
prog_id = file_parent.add_element 'ProgId', { 'Id' => "#{File.basename(app, '.exe')}.Document", 'Description' => "Aim project file",
|
42
|
+
'Advertise' => 'yes'}
|
43
|
+
ext = prog_id.add_element 'Extension', { 'Id' => extension.gsub(/\./, '') }
|
44
|
+
ext.add_element 'Verb', { 'Id' => 'open', 'Command' => "[INSTALLDIR]#{app}", 'Argument' => "\"%1\"" }
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'SecureRandom'
|
3
|
+
|
4
|
+
module Wixgem
|
5
|
+
|
6
|
+
class CustomAction
|
7
|
+
def initialize(xml_doc, input)
|
8
|
+
@xml_doc = xml_doc
|
9
|
+
@input = input
|
10
|
+
end
|
11
|
+
def add(custom_action)
|
12
|
+
unless(custom_action.key?(:file) || custom_action.key?(:binary_key))
|
13
|
+
raise 'Currently, only supported custom actions work with installed executable or binary key'
|
14
|
+
end
|
15
|
+
|
16
|
+
file_key=nil
|
17
|
+
if(custom_action.key?(:file))
|
18
|
+
install_path = ".\\#{custom_action[:file].gsub(/\//,'\\')}"
|
19
|
+
file_elements = REXML::XPath.match(@xml_doc, "//File[@Source='#{install_path}']")
|
20
|
+
raise "Unable to locate installation file '#{custom_action[:file]} for custom action'" if(file_elements.nil? || (file_elements.size == 0))
|
21
|
+
|
22
|
+
file_key = file_elements[0].attributes['Id']
|
23
|
+
end
|
24
|
+
|
25
|
+
id = "ca_#{SecureRandom.uuid.gsub(/-/,'')}"
|
26
|
+
id = custom_action[:id] if(custom_action.key?(:id))
|
27
|
+
|
28
|
+
cmd_line = ''
|
29
|
+
cmd_line = custom_action[:exe_command] if(custom_action.key?(:exe_command))
|
30
|
+
|
31
|
+
impersonate = 'yes'
|
32
|
+
impersonate = custom_action[:impersonate] if(custom_action.key?(:impersonate))
|
33
|
+
|
34
|
+
condition='NOT Installed AND NOT REMOVE'
|
35
|
+
condition='1' if(custom_action.key?(:binary_key))
|
36
|
+
condition = custom_action[:condition] if(custom_action.key?(:condition))
|
37
|
+
|
38
|
+
execute='deferred'
|
39
|
+
execute = custom_action[:execute] if(custom_action.key?(:execute))
|
40
|
+
|
41
|
+
ret='check'
|
42
|
+
ret = custom_action[:return] if(custom_action.key?(:return))
|
43
|
+
|
44
|
+
wix_element = REXML::XPath.match(@xml_doc, "/Wix")[0]
|
45
|
+
fragment = wix_element.add_element 'Fragment'
|
46
|
+
|
47
|
+
action = fragment.add_element 'CustomAction', { 'Id' => id, 'ExeCommand' => cmd_line, 'Impersonate' => impersonate, 'Return' => ret, 'HideTarget' => 'no', 'Execute' => execute }
|
48
|
+
if(custom_action.key?(:binary_key))
|
49
|
+
action.attributes['BinaryKey'] = custom_action[:binary_key]
|
50
|
+
else
|
51
|
+
action.attributes['FileKey'] = file_key
|
52
|
+
end
|
53
|
+
|
54
|
+
install_execute_sequence = fragment.add_element 'InstallExecuteSequence'
|
55
|
+
|
56
|
+
custom_action[:before] = 'InstallFinalize' if(!custom_action.key?(:after) && !custom_action.key?(:before))
|
57
|
+
if(custom_action.key?(:after))
|
58
|
+
action = install_execute_sequence.add_element 'Custom', { 'Action' => id, 'After' => custom_action[:after] }
|
59
|
+
action.text = condition
|
60
|
+
else
|
61
|
+
action = install_execute_sequence.add_element 'Custom', { 'Action' => id, 'Before' => custom_action[:before] }
|
62
|
+
action.text = condition
|
63
|
+
end
|
64
|
+
|
65
|
+
control=nil
|
66
|
+
elements = REXML::XPath.match(@xml_doc, "/Wix/Product")
|
67
|
+
elements = REXML::XPath.match(@xml_doc, "/Wix/Module") if(elements.nil? || elements.size == 0)
|
68
|
+
|
69
|
+
elements[0].add_element 'CustomActionRef', { 'Id' => id }
|
70
|
+
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
#module Wixgem
|
4
|
+
|
5
|
+
def temp_directory
|
6
|
+
tmp_file = "#{ENV['TEMP'].gsub(/\\/,'/')}/#{SecureRandom.hex}"
|
7
|
+
FileUtils.mkpath(tmp_file)
|
8
|
+
begin
|
9
|
+
yield tmp_file
|
10
|
+
rescue Exception => e
|
11
|
+
raise e
|
12
|
+
ensure
|
13
|
+
sleep(0.1)
|
14
|
+
FileUtils.rm_rf(tmp_file)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
#end
|
data/lib/wixgem.rb
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
require 'fileutils'
|
2
|
-
require 'tmpdir.rb'
|
3
2
|
require 'rexml/document'
|
4
3
|
require 'CMD'
|
5
4
|
require 'SecureRandom'
|
6
|
-
|
7
|
-
require_relative
|
5
|
+
|
6
|
+
require_relative 'file.rb'
|
7
|
+
require_relative 'shortcut.rb'
|
8
|
+
require_relative 'custom_action.rb'
|
9
|
+
require_relative 'temp_directory.rb'
|
10
|
+
require_relative 'associate_extension.rb'
|
11
|
+
|
12
|
+
# Editor for wix Files WixEdit: http://http://wixedit.sourceforge.net/
|
13
|
+
# Full list of Wix editors : http://robmensching.com/blog/posts/2007/11/20/wix-editors/
|
14
|
+
# I guess SharpDevelop is good
|
15
|
+
|
16
|
+
# Good web page for including the dialog https://www.packtpub.com/books/content/windows-installer-xml-wix-adding-user-interface
|
8
17
|
|
9
18
|
module Wixgem
|
10
19
|
|
11
20
|
class Wix
|
21
|
+
|
12
22
|
@@install_path = ''
|
13
23
|
@@install_path = ENV['WIX'] unless(ENV['WIX'].nil?)
|
14
24
|
|
@@ -50,53 +60,77 @@ class Wix
|
|
50
60
|
@logger = nil
|
51
61
|
end
|
52
62
|
|
63
|
+
def self.manage_installdir(xml_doc, input)
|
64
|
+
product_target_elements = REXML::XPath.match(xml_doc, "/Wix/Product/Directory[@Id='TARGETDIR']")
|
65
|
+
if(!product_target_elements.nil? && product_target_elements.size == 1)
|
66
|
+
targetdir = product_target_elements[0]
|
67
|
+
installdir = targetdir.add_element 'Directory', { 'Id' => 'ProgramFilesFolder' }
|
68
|
+
installdir = installdir.add_element 'Directory', { 'Id' => "Dir_#{input[:manufacturer]}", 'Name' => input[:manufacturer] } if(input.has_key?(:manufacturer))
|
69
|
+
installdir = installdir.add_element 'Directory', { 'Id' => 'INSTALLDIR', 'Name' => input[:product_name] }
|
70
|
+
else
|
71
|
+
module_target_elements = REXML::XPath.match(xml_doc, "/Wix/Module/Directory[@Id='TARGETDIR']")
|
72
|
+
targetdir = module_target_elements[0]
|
73
|
+
targetdir.add_element 'Directory', { 'Id' => 'MergeRedirectFolder', 'FileSource' => '.' }
|
74
|
+
end
|
75
|
+
return xml_doc
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.manage_netframework(xml_doc, input)
|
79
|
+
wix = REXML::XPath.match(xml_doc, "/Wix")[0]
|
80
|
+
add_netfx_namespace = false
|
81
|
+
if(input.key?(:requires_netframework))
|
82
|
+
add_netfx_namespace=true
|
83
|
+
fragment = wix.add_element 'Fragment'
|
84
|
+
fragment.add_element 'PropertyRef', { 'Id' => input[:requires_netframework] }
|
85
|
+
condition = fragment.add_element 'Condition', { 'Message' => "This application requires .NET Framework #{input[:requires_netframework]}. Please install the .NET Framework then run this installer again." }
|
86
|
+
condition.text = "<![CDATA[Installed OR #{input[:requires_netframework]}]]>"
|
87
|
+
end
|
88
|
+
|
89
|
+
wix.attributes['xmlns:netfx'] = 'https://schemas.microsoft.com/wix/NetFxExtension' if(add_netfx_namespace)
|
90
|
+
return xml_doc
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.manage_ui(xml_doc, input)
|
94
|
+
product_elements = REXML::XPath.match(xml_doc, "/Wix/Product")
|
95
|
+
|
96
|
+
return if(product_elements.nil? || product_elements.size != 1)
|
97
|
+
return unless(input.key?(:ui))
|
98
|
+
|
99
|
+
product = product_elements[0]
|
100
|
+
ui = product.add_element 'UIRef', { 'Id' => input[:ui] }
|
101
|
+
|
102
|
+
return xml_doc
|
103
|
+
end
|
104
|
+
|
53
105
|
def self.manage_upgrade(xml_doc, input)
|
54
|
-
|
55
|
-
return xml_doc if(
|
106
|
+
products = REXML::XPath.match(xml_doc, '//Wix/Product')
|
107
|
+
return xml_doc if(products.length == 0)
|
56
108
|
|
57
|
-
if(input
|
109
|
+
if(input[:remove_existing_products])
|
58
110
|
raise 'Hash must have a version key if the hash has a :remove_existing_products key' unless(input.has_key?(:version))
|
59
111
|
raise 'Hash must have an upgrade_code key if the hash has a :remove_existing_products key' unless(input.has_key?(:upgrade_code))
|
60
112
|
|
61
|
-
upgrade =
|
62
|
-
upgrade.add_element 'UpgradeVersion', { 'Minimum' => input[:version], 'OnlyDetect'=>'yes', 'Property'=>'NEWERVERSIONDETECTED' }
|
63
|
-
upgrade.add_element 'UpgradeVersion', { 'Minimum' => '0.0.0', 'IncludeMinimum'=>'yes','Maximum'=>input[:version],'IncludeMaximum'=>'no','Property'=>'OLDERVERSIONBEINGUPGRADED' }
|
64
|
-
|
65
|
-
install_and_execute = REXML::XPath.match(xml_doc, '//Wix/Product/InstallExecuteSequence')
|
66
|
-
install_and_execute[0].add_element 'RemoveExistingProducts', { 'After'=>'InstallValidate' }
|
113
|
+
upgrade = products[0].add_element 'MajorUpgrade', { 'AllowDowngrades' => 'yes' }
|
67
114
|
end
|
68
115
|
|
69
116
|
return xml_doc
|
70
117
|
end
|
71
118
|
|
72
119
|
def self.manage_custom_actions(xml_doc, input)
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
manufacturer = input[:manufacturer] if(input.has_key?(:manufacturer))
|
78
|
-
|
79
|
-
install_path = '[ProgramFilesFolder][ProductName]'
|
80
|
-
install_path = "[ProgramFilesFolder][Manufacturer]\\[ProductName]" unless(manufacturer == 'Default Manufacturer')
|
81
|
-
|
82
|
-
product = REXML::XPath.match(xml_doc, '//Wix/Product')
|
83
|
-
return xml_doc if(product.length == 0)
|
84
|
-
|
85
|
-
product[0].add_element 'SetProperty', { 'Id' => 'ARPINSTALLLOCATION', 'Value' => "#{install_path}", 'After' => 'CostFinalize', 'Sequence' => 'both' }
|
86
|
-
product[0].add_element 'CustomAction', { 'Id' => 'SetTARGETDIR', 'Property' => 'TARGETDIR', 'Value' => "#{install_path}", 'Execute' => 'firstSequence', 'Return' => 'check'}
|
120
|
+
custom_actions = CustomAction.new(xml_doc, input)
|
121
|
+
return xml_doc if(input[:custom_actions].nil?)
|
122
|
+
|
123
|
+
input[:custom_actions].each { |ca| custom_actions.add(ca) } if(input.key?(:custom_actions))
|
87
124
|
|
88
|
-
|
89
|
-
|
125
|
+
return xml_doc
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.manage_associate_extensions(xml_doc, input)
|
129
|
+
return xml_doc unless(input.key?(:extensions))
|
90
130
|
|
91
|
-
|
92
|
-
|
131
|
+
ext = AssociateExtension.new(xml_doc)
|
132
|
+
input[:extensions].each { |exe, file_ext| ext.associate(exe, file_ext) }
|
93
133
|
|
94
|
-
admin_execute_sequence = product[0].add_element 'AdminExecuteSequence'
|
95
|
-
custom_action = admin_execute_sequence.add_element 'Custom', { 'Action' => 'SetTARGETDIR', 'Before'=>'CostInitialize' }
|
96
|
-
|
97
|
-
admin_ui_sequence = product[0].add_element 'AdminUISequence'
|
98
|
-
custom_action = admin_ui_sequence.add_element 'Custom', { 'Action' => 'SetTARGETDIR', 'Before'=>'CostInitialize' }
|
99
|
-
|
100
134
|
return xml_doc
|
101
135
|
end
|
102
136
|
|
@@ -112,7 +146,7 @@ class Wix
|
|
112
146
|
}
|
113
147
|
return xml_doc if(merge_modules.length == 0)
|
114
148
|
|
115
|
-
directory_root = REXML::XPath.match(xml_doc,
|
149
|
+
directory_root = REXML::XPath.match(xml_doc, "//Directory[@Id='INSTALLDIR']")
|
116
150
|
if(directory_root.length == 0)
|
117
151
|
module_root = REXML::XPath.match(xml_doc, '//Wix/Module')
|
118
152
|
raise 'Merge modules can not be added to a merge module' unless(module_root.nil?)
|
@@ -161,6 +195,30 @@ class Wix
|
|
161
195
|
return xml_doc
|
162
196
|
end
|
163
197
|
|
198
|
+
def self.manage_self_register(xml_doc, input)
|
199
|
+
return xml_doc unless(input.has_key?(:com_self_register))
|
200
|
+
input[:com_self_register].each do |file|
|
201
|
+
file_elements = REXML::XPath.match(xml_doc, "//File[@Source='.\\#{file.gsub(/\//,'\\')}']")
|
202
|
+
raise "Unable to find file '#{file}' for self registering" unless (file_elements.length == 1)
|
203
|
+
file_elements[0].attributes['SelfRegCost'] = '0'
|
204
|
+
end
|
205
|
+
|
206
|
+
return xml_doc
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.manage_binary_table(xml_doc, input)
|
210
|
+
return xml_doc unless(input.has_key?(:binary_table))
|
211
|
+
wix_element = REXML::XPath.match(xml_doc, "/Wix")[0]
|
212
|
+
|
213
|
+
fragment = wix_element.add_element 'Fragment'
|
214
|
+
input[:binary_table].each { |entry|
|
215
|
+
#puts "File: #{entry[:file]}"
|
216
|
+
binary = fragment.add_element 'Binary', { 'Id' => entry[:id], 'SourceFile' => entry[:file] }
|
217
|
+
}
|
218
|
+
|
219
|
+
return xml_doc
|
220
|
+
end
|
221
|
+
|
164
222
|
def self.modify_file_path(input, file)
|
165
223
|
return file unless(input.kind_of?(Hash) && input.has_key?(:modify_file_paths))
|
166
224
|
|
@@ -208,9 +266,17 @@ class Wix
|
|
208
266
|
max_path = files.max { |a, b| a.length <=> b.length }
|
209
267
|
columen_size = max_path.length + 10
|
210
268
|
end
|
211
|
-
|
269
|
+
|
270
|
+
ingore_files = self.ignore_files(input)
|
271
|
+
if(input.has_key?(:ignore_files))
|
272
|
+
@logger << "------------------------------------ ignoring files -----------------------------------" unless(@logger.nil?)
|
273
|
+
input[:ignore_files].each { |file| @logger << file }
|
274
|
+
end
|
275
|
+
|
212
276
|
@logger << "------------------------------------ Installation Paths -----------------------------------" unless(@logger.nil?)
|
213
277
|
@logger << "%-#{columen_size}s %s\n" % ['File path', 'Installation Path'] unless(@logger.nil?)
|
278
|
+
files.reject! { |f| ingore_files.include?(f) }
|
279
|
+
|
214
280
|
files.each do |file|
|
215
281
|
if(File.file?(file))
|
216
282
|
install_path = file
|
@@ -219,13 +285,7 @@ class Wix
|
|
219
285
|
end
|
220
286
|
@logger << "%-#{columen_size}s %s\n" % [file, install_path] unless(@logger.nil?)
|
221
287
|
end
|
222
|
-
end
|
223
|
-
|
224
|
-
if(input.has_key?(:ignore_files))
|
225
|
-
@logger << "------------------------------------ ignoring files -----------------------------------" unless(@logger.nil?)
|
226
|
-
input[:ignore_files].each { |file| @logger << file }
|
227
|
-
end
|
228
|
-
|
288
|
+
end
|
229
289
|
@logger << "-------------------------------------------------------------------------------------------" unless(@logger.nil?)
|
230
290
|
end
|
231
291
|
|
@@ -244,6 +304,11 @@ class Wix
|
|
244
304
|
end
|
245
305
|
end
|
246
306
|
|
307
|
+
def self.modify_binary_files(input)
|
308
|
+
return unless(input.key?(:binary_table))
|
309
|
+
input[:binary_table].each { |entry| entry[:file] = File.absolute_path(entry[:file]) }
|
310
|
+
end
|
311
|
+
|
247
312
|
def self.log_wix_output(cmd)
|
248
313
|
return unless(@debug && !@logger.nil?)
|
249
314
|
|
@@ -265,6 +330,11 @@ class Wix
|
|
265
330
|
cmd = cmd.gsub(/-srd/, '-svb6 -srd') if(input.has_key?(:has_vb6_files) && input[:has_vb6_files])
|
266
331
|
cmd = cmd.gsub(/-srd/, '-sreg -srd') if(input.has_key?(:suppress_registry_harvesting) && input[:suppress_registry_harvesting])
|
267
332
|
cmd = cmd.gsub(/-srd/, '-scom -srd') if(input.has_key?(:suppress_COM_elements) && input[:suppress_COM_elements])
|
333
|
+
if(input[:msi?])
|
334
|
+
cmd = cmd.gsub(/-srd/, '-dr INSTALLDIR -srd')
|
335
|
+
else
|
336
|
+
cmd = cmd.gsub(/-srd/, '-dr MergeRedirectFolder -srd')
|
337
|
+
end
|
268
338
|
return cmd
|
269
339
|
end
|
270
340
|
|
@@ -310,9 +380,10 @@ class Wix
|
|
310
380
|
file_elements = REXML::XPath.match(xml_doc, '//Wix/Fragment/DirectoryRef/Component/File')
|
311
381
|
file_elements[0].attributes['Source'] = "SourceDir\\#{file.gsub(/\//,'\\')}" if(file_elements.length == 1)
|
312
382
|
file_elements[0].attributes['Id'] = "fil#{SecureRandom.uuid.gsub(/-/,'')}" if(file_elements.length == 1) # Assigning new Id, because the id is somehow generated from the filename. So it is possible for heat to generate duplicate id's
|
383
|
+
|
313
384
|
File.open(filename, 'w') { |f| f.puts(xml_doc.to_s) }
|
314
385
|
end
|
315
|
-
directory_fragments['.'] = '
|
386
|
+
directory_fragments['.'] = 'INSTALLDIR'
|
316
387
|
|
317
388
|
xml_doc = REXML::Document.new(File.read(wxs_file))
|
318
389
|
|
@@ -362,6 +433,7 @@ class Wix
|
|
362
433
|
end
|
363
434
|
|
364
435
|
product_name = File.basename(wxs_file, '.wxs')
|
436
|
+
input[:product_name] = product_name unless(input.has_key?(:product_name))
|
365
437
|
product_name = input[:product_name] if(input.has_key?(:product_name))
|
366
438
|
|
367
439
|
manufacturer = 'Not Set'
|
@@ -375,6 +447,9 @@ class Wix
|
|
375
447
|
|
376
448
|
upgrade_code = ''
|
377
449
|
upgrade_code = input[:upgrade_code] if(input.has_key?(:upgrade_code))
|
450
|
+
|
451
|
+
install_path = '[ProgramFilesFolder][ProductName]'
|
452
|
+
install_path = "[ProgramFilesFolder][Manufacturer]\\[ProductName]" unless(manufacturer == 'Not Set')
|
378
453
|
|
379
454
|
wxs_text = File.read(wxs_file)
|
380
455
|
|
@@ -388,36 +463,50 @@ class Wix
|
|
388
463
|
wxs_text = wxs_text.gsub(/Product Id=\"[^\"]+\"/) { |s| s = "Product Id=\"#{product_code}\"" } unless(product_code.empty?)
|
389
464
|
wxs_text = wxs_text.gsub(/UpgradeCode=\"[^\"]+\"/) { |s| s = "UpgradeCode=\"#{upgrade_code}\"" } unless(upgrade_code.empty?)
|
390
465
|
|
466
|
+
File.open(wxs_file, 'w') { |f| f.write(wxs_text) }
|
467
|
+
|
391
468
|
xml_doc = REXML::Document.new(wxs_text)
|
469
|
+
products = REXML::XPath.match(xml_doc, "/Wix/Product")
|
470
|
+
products.each do |product|
|
471
|
+
product.add_element 'SetProperty', { 'Id' => 'ARPINSTALLLOCATION', 'Value' => "#{install_path}", 'After' => 'CostFinalize', 'Sequence' => 'both' }
|
472
|
+
end
|
473
|
+
|
392
474
|
packages = REXML::XPath.match(xml_doc, '//Wix/Product/Package')
|
393
475
|
packages.each do |package|
|
394
476
|
package.add_attribute('InstallScope', 'perMachine') if(input.has_key?(:all_users))
|
477
|
+
package.add_attribute('InstallScope', input[:install_scope]) if(input.has_key?(:install_scope))
|
395
478
|
package.attributes['InstallerVersion'] = 450
|
396
479
|
package.attributes['InstallerVersion'] = (input[:installer_version]*100).to_i if(input.has_key?(:installer_version))
|
480
|
+
package.attributes['InstallPrivileges']= input[:install_priviledges] if(input.has_key?(:install_priviledges))
|
397
481
|
end
|
398
482
|
|
483
|
+
xml_doc = manage_installdir(xml_doc, input)
|
484
|
+
xml_doc = manage_netframework(xml_doc, input)
|
485
|
+
#xml_doc = manage_ui(xml_doc, input)
|
399
486
|
xml_doc = manage_custom_actions(xml_doc, input)
|
400
487
|
xml_doc = manage_upgrade(xml_doc,input)
|
401
488
|
xml_doc = manage_msm_files(xml_doc)
|
402
489
|
xml_doc = manage_read_only_files(xml_doc,input)
|
403
490
|
xml_doc = manage_shortcuts(xml_doc, input)
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
491
|
+
xml_doc = manage_self_register(xml_doc, input)
|
492
|
+
xml_doc = manage_binary_table(xml_doc, input)
|
493
|
+
xml_doc = manage_associate_extensions(xml_doc, input)
|
494
|
+
|
495
|
+
formatter = REXML::Formatters::Pretty.new(2)
|
496
|
+
formatter.compact = true
|
497
|
+
File.open(wxs_file, 'w') { |f| formatter.write(xml_doc, f) }
|
411
498
|
end
|
412
499
|
|
413
|
-
def self.create_output(wxs_file, output)
|
500
|
+
def self.create_output(wxs_file, input, output)
|
414
501
|
wixobj_file = "#{File.basename(wxs_file,'.wxs')}.wixobj"
|
415
502
|
|
416
503
|
candle_cmd = CMD.new("\"#{install_path}/bin/candle.exe\" -out \"#{wixobj_file}\" \"#{wxs_file}\"", { quiet: true })
|
417
504
|
candle_cmd.execute
|
418
505
|
log_wix_output(candle_cmd)
|
419
506
|
|
420
|
-
|
507
|
+
cmd_args = "-nologo -out \"#{output}\" \"#{wixobj_file}\""
|
508
|
+
cmd_args = "-ext WixUIExtension -cultures:en-us #{cmd_args}" if(input.key?(:ui))
|
509
|
+
light_cmd = CMD.new("\"#{install_path}/bin/light.exe\" #{cmd_args}", { quiet: true })
|
421
510
|
light_cmd.execute
|
422
511
|
log_wix_output(light_cmd)
|
423
512
|
end
|
@@ -434,7 +523,6 @@ class Wix
|
|
434
523
|
verify_input_keys(input)
|
435
524
|
|
436
525
|
@debug = input[:debug] if(!@debug && input.has_key?(:debug))
|
437
|
-
|
438
526
|
start_logger if(@debug)
|
439
527
|
|
440
528
|
FileUtils.mkpath(File.dirname(output)) unless(Dir.exists?(File.dirname(output)))
|
@@ -445,21 +533,21 @@ class Wix
|
|
445
533
|
|
446
534
|
output_absolute_path = File.absolute_path(output)
|
447
535
|
input[:original_pwd] = Dir.pwd
|
536
|
+
input[:msi?] = output.include?('.msi')
|
537
|
+
|
538
|
+
modify_binary_files(input)
|
448
539
|
|
449
|
-
|
450
|
-
#FileUtils.rm_rf(dir) if(Dir.exists?(dir))
|
451
|
-
#FileUtils.mkdir(dir)
|
452
|
-
Dir.mktmpdir do |dir|
|
540
|
+
temp_directory do |dir|
|
453
541
|
wxs_file = "#{basename}.wxs"
|
454
542
|
begin
|
455
543
|
copy_install_files(dir, input)
|
456
544
|
|
457
545
|
Dir.chdir(dir) do |current_dir|
|
458
546
|
create_wxs_file(wxs_file, input, ext)
|
459
|
-
create_output(wxs_file, output_absolute_path)
|
547
|
+
create_output(wxs_file, input, output_absolute_path)
|
460
548
|
end
|
461
549
|
rescue Exception => e
|
462
|
-
raise
|
550
|
+
raise e
|
463
551
|
ensure
|
464
552
|
FileUtils.cp("#{dir}/#{wxs_file}", "#{output_absolute_path}.wxs") if(File.exists?("#{dir}/#{wxs_file}") && @debug)
|
465
553
|
File.open("#{output_absolute_path}.log", 'w') { |f| f.puts(@logger) } if(@debug &!@logger.nil?)
|
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.84.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-12-
|
11
|
+
date: 2015-12-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -94,6 +94,20 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: ocra
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
97
111
|
description: Simple Ruby interface to facilitate creating and compiling windows installation
|
98
112
|
files with the Wix Toolset.
|
99
113
|
email: KCCKSMarshall@gmail.com
|
@@ -103,12 +117,17 @@ extra_rdoc_files: []
|
|
103
117
|
files:
|
104
118
|
- LICENSE
|
105
119
|
- README.md
|
120
|
+
- example/example.msi
|
121
|
+
- example/example.msm
|
106
122
|
- example/install_files/directory/file2.txt
|
107
123
|
- example/install_files/file1.txt
|
108
124
|
- example/rakefile.rb
|
109
125
|
- lib/admin.rb
|
126
|
+
- lib/associate_extension.rb
|
127
|
+
- lib/custom_action.rb
|
110
128
|
- lib/file.rb
|
111
129
|
- lib/shortcut.rb
|
130
|
+
- lib/temp_directory.rb
|
112
131
|
- lib/wixgem.rb
|
113
132
|
homepage: http://rubygems.org/gems/wixgem
|
114
133
|
licenses:
|