Skip to content

Commit ff96dd3

Browse files
committed
Use new side-by-side mechanism only on ruby-3.4+
It it too heavy to be introduced in stable releases. The creation of the junction file for OpenSSL is backported to Ruby-3.2.x and 3.3.x but it links to the old destination /bin/etc/ssl there. This should ease the transition to ruby-3.4 and makes the documentation unifrom.
1 parent 58bbe75 commit ff96dd3

File tree

7 files changed

+271
-159
lines changed

7 files changed

+271
-159
lines changed

recipes/installer-inno/rubyinstaller.iss.erb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,16 @@ Root: <%= regroot %>; Subkey: Software\Classes\<%= rubyname %>File\shell\open\co
133133
; So use Innosetup to add described permissions and icacls to disable unwanted inheritance.
134134
[Dirs]
135135
Name: {app}; Permissions: creatorowner-full users-readexec admins-full
136-
Name: "{app}/lib/ruby/<%= package.rubylibver %>/etc"
136+
Name: "{app}/<%= package.rubyver2 =~ /^3\.[23]$/ ? "bin/etc" : "lib/ruby/#{ package.rubylibver }/etc" %>"
137+
137138
[Run]
138139
; Set permissions on install directories
139140
Filename: "icacls.exe"; Parameters: """{app}"" /inheritancelevel:r "; WorkingDir: "{app}"; StatusMsg: "Changing install Directory Permissions"; Flags: runhidden
140141
<% if with_msys %>
141142
Filename: "icacls.exe"; Parameters: """{app}\<%= package.msysdir %>\tmp"" /inheritancelevel:r /grant *S-1-5-32-545:(CI)(WD,AD,WEA,WA) /grant *S-1-3-0:(OI)(CI)(IO)(F) /grant *S-1-5-32-544:(OI)(CI)(F) /grant *S-1-5-32-545:(NP)(RX) "; WorkingDir: "{app}"; StatusMsg: "Changing MSYS2 /tmp Directory Permissions"; Flags: runhidden; Components: msys2
142143
<% end %>
143144
; Add link to SSL CA certs so that OpenSSL finds them based on the libssl.dll location
144-
Filename: "{cmd}"; Parameters: "/c mklink /j ssl ..\..\..\..\ssl"; WorkingDir: "{app}/lib/ruby/<%= package.rubylibver %>/etc"; StatusMsg: "Add link to SSL CA certs"; Flags: runhidden
145+
Filename: "{cmd}"; Parameters: "/c mklink /j <%= package.rubyver2 =~ /^3\.[23]$/ ? "bin" : "lib\\ruby\\#{package.rubylibver}" %>\etc\ssl ssl"; WorkingDir: "{app}"; StatusMsg: "Add link to SSL CA certs"; Flags: runhidden
145146

146147
[Icons]
147148
Name: {autoprograms}\{#InstallerName}\{cm:InteractiveRubyTitle}; Filename: {app}\bin\irb.<%= package.rubyver2 < '3.1' ? "cmd" : "bat" %>; IconFilename: {app}\bin\ruby.exe
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# Move bundled RubyInstaller DLLs to a subdirectory.
2+
# This avoids interferences with other apps when ruby.exe is in the PATH.
3+
4+
if package.rubyver2 < "3.4"
5+
libruby_regex = /(msvcrt|ucrt)-ruby\d+\.dll$/i
6+
bin_dir = File.join(sandboxdir, "bin")
7+
dlls_dir = File.join(sandboxdir, "bin/ruby_builtin_dlls")
8+
directory bin_dir
9+
directory dlls_dir
10+
11+
# Select the DLLs from "bin/" which shall be moved into "bin/ruby_builtin_dlls/"
12+
dlls = self.sandboxfiles.select do |destpath|
13+
destpath.start_with?(bin_dir+"/") && destpath =~ /\.dll$/i && destpath !~ libruby_regex
14+
end
15+
16+
dlls.each do |destpath|
17+
# Add tasks to write the DLLs into the sub directory
18+
new_destpath = File.join(File.dirname(destpath), "ruby_builtin_dlls", File.basename(destpath))
19+
file new_destpath => [destpath.sub(sandboxdir, unpackdirmgw), dlls_dir] do |t|
20+
cp(t.prerequisites.first, t.name)
21+
end
22+
23+
# Move the DLLs in the dependent files list to the subdirectory
24+
self.sandboxfiles.delete(destpath)
25+
self.sandboxfiles << new_destpath
26+
end
27+
28+
# Add a custom manifest to both ruby.exe and rubyw.exe, so that they find the DLLs to be moved
29+
self.sandboxfiles.select do |destpath|
30+
destpath =~ /\/rubyw?\.exe$/i
31+
end.each do |destpath|
32+
file destpath => [destpath.sub(sandboxdir, unpackdirmgw), bin_dir] do |t|
33+
puts "patching manifest of #{t.name}"
34+
libruby = File.basename(self.sandboxfiles.find{|a| a=~libruby_regex })
35+
36+
image = File.binread(t.prerequisites.first)
37+
# The XML elements we want to add to the default MINGW manifest:
38+
new = <<-EOT
39+
<application xmlns="urn:schemas-microsoft-com:asm.v3">
40+
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
41+
<ws2:longPathAware>true</ws2:longPathAware>
42+
</windowsSettings>
43+
</application>
44+
<dependency>
45+
<dependentAssembly>
46+
<assemblyIdentity version="1.0.0.0" type="win32" name="ruby_builtin_dlls" />
47+
</dependentAssembly>
48+
</dependency>
49+
<file name="#{ libruby }"/>
50+
EOT
51+
52+
# There are two regular options to add a custom manifest:
53+
# 1. Change a given exe file per Microsofts "mt.exe" after the build
54+
# 2. Specify a the manifest while linking with the MINGW toolchain
55+
#
56+
# Since we don't want to depend on particular Microsoft tools and want to avoid additional patching of the ruby build, we do a nifty trick here.
57+
# We patch the exe file manually.
58+
# Removing unnecessary spaces and comments from the embedded XML manifest gives us enough space to add the above XML elements.
59+
# Then the default MINGW manifest gets replaced by our custom XML content.
60+
# The rest of the available bytes is simply padded with spaces, so that we don't change positions within the EXE image.
61+
image.gsub!(/<\?xml.*?<assembly.*?<\/assembly>\n/m) do |m|
62+
newm = m.gsub(/^\s*<\/assembly>\s*$/, new + "</assembly>")
63+
.gsub(/<!--.*?-->/m, "")
64+
.gsub(/^ +/, "")
65+
.gsub(/\n+/m, "\n")
66+
67+
raise "replacement manifest to big #{m.bytesize} < #{newm.bytesize}" if m.bytesize < newm.bytesize
68+
newm + " " * (m.bytesize - newm.bytesize)
69+
end
70+
File.binwrite(t.name, image)
71+
end
72+
end
73+
74+
# Add a detached manifest file within the sub directory that lists all DLLs in question
75+
manifest2 = File.join(sandboxdir, "bin/ruby_builtin_dlls/ruby_builtin_dlls.manifest")
76+
file manifest2 => [dlls_dir] do |t|
77+
puts "generate #{t.name}"
78+
File.binwrite t.name, <<-EOT
79+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
80+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
81+
<assemblyIdentity type="win32" name="ruby_builtin_dlls" version="1.0.0.0"></assemblyIdentity>
82+
83+
#{ dlls.map{|dll| %Q{<file name="#{File.basename(dll)}"/>} }.join }
84+
</assembly>
85+
EOT
86+
end
87+
self.sandboxfiles << manifest2
88+
end
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
# Move bundled RubyInstaller DLLs to a subdirectory.
2+
# This avoids interferences with other apps when ruby.exe is in the PATH.
3+
4+
if package.rubyver2 >= "3.4"
5+
libruby_regex = /(msvcrt|ucrt)-ruby\d+\.dll$/i
6+
bin_dir = File.join(sandboxdir, "bin")
7+
dlls_dir = File.join(sandboxdir, "bin/ruby_builtin_dlls")
8+
directory bin_dir
9+
directory dlls_dir
10+
11+
# Select the DLLs from "bin/" which shall be moved into "bin/ruby_builtin_dlls/"
12+
dlls = self.sandboxfiles.select do |destpath|
13+
destpath.start_with?(bin_dir+"/") && destpath =~ /\.dll$/i && destpath !~ libruby_regex
14+
end
15+
16+
ext_dll_defs = {
17+
"lib/ruby/#{package.rubylibver}/#{package.ruby_arch}/fiddle.so" => /^libffi-\d.dll$/,
18+
"lib/ruby/#{package.rubylibver}/#{package.ruby_arch}/openssl.so" => /^libssl-[\d_]+(-x64)?.dll$|^libcrypto-[\d_]+(-x64)?.dll$/,
19+
"lib/ruby/#{package.rubylibver}/#{package.ruby_arch}/psych.so" => /^libyaml-[-\d]+.dll$/,
20+
"lib/ruby/#{package.rubylibver}/#{package.ruby_arch}/zlib.so" => /^zlib\d.dll$/,
21+
}
22+
23+
core_dll_defs = [
24+
/^libgmp-\d+.dll$/,
25+
/^libwinpthread-\d+.dll$/,
26+
/^libgcc_s_.*.dll$/,
27+
]
28+
29+
# create rake tasks to trigger additional processing of so files
30+
ext_dll_defs.keys.each do |so_file|
31+
self.sandboxfiles << File.join(sandboxdir, so_file)
32+
end
33+
34+
core_dlls, dlls = dlls.partition do |destpath|
35+
core_dll_defs.any? { |re| re =~ File.basename(destpath) }
36+
end
37+
ext_dlls, dlls = dlls.partition do |destpath|
38+
ext_dll_defs.values.any? { |re| re =~ File.basename(destpath) }
39+
end
40+
raise "DLL belonging neither to core nor to exts: #{dlls}" unless dlls.empty?
41+
42+
43+
###########################################################################
44+
# Add manifest to extension.so files pointing to linked MINGW library DLLs
45+
# next to it
46+
###########################################################################
47+
48+
# Add tasks to move the DLLs into the extension directory
49+
ext_dlls.each do |destpath|
50+
so_fname, _ = ext_dll_defs.find { |_, re| re =~ File.basename(destpath) }
51+
52+
new_destpath = File.join(sandboxdir, File.dirname(so_fname), File.basename(destpath))
53+
file new_destpath => [destpath.sub(sandboxdir, unpackdirmgw), File.dirname(new_destpath)] do |t|
54+
cp(t.prerequisites.first, t.name)
55+
end
56+
57+
# Move the DLLs in the dependent files list to the subdirectory
58+
self.sandboxfiles.delete(destpath)
59+
self.sandboxfiles << new_destpath
60+
end
61+
62+
# Add a custom manifest to each extension.so, so that they find the DLLs to be moved
63+
ext_dlls.each do |destpath|
64+
so_fname, _ = ext_dll_defs.find { |_, re| re =~ File.basename(destpath) }
65+
sandbox_so_fname = File.join(sandboxdir, so_fname)
66+
67+
file sandbox_so_fname => [sandbox_so_fname.sub(sandboxdir, unpackdirmgw), File.dirname(sandbox_so_fname)] do |t|
68+
puts "patching manifest of #{t.name}"
69+
70+
# The XML elements we want to add to the default MINGW manifest:
71+
new = <<~EOT
72+
<dependency>
73+
<dependentAssembly>
74+
<assemblyIdentity version="1.0.0.0" type="win32" name="#{File.basename(so_fname)}-assembly" />
75+
</dependentAssembly>
76+
</dependency>
77+
EOT
78+
79+
ManifestUpdater.update_file(t.prerequisites.first, new, t.name)
80+
end
81+
end
82+
83+
# Add a detached manifest file within the ext.so directory that lists all linked DLLs
84+
ext_dll_defs.each do |so_fname, re|
85+
mani_path = File.join(sandboxdir, so_fname + "-assembly.manifest")
86+
e_dlls = ext_dlls.select { |dll| re =~ File.basename(dll) }
87+
88+
file mani_path => [File.dirname(mani_path)] do |t|
89+
puts "generate #{t.name}"
90+
91+
File.binwrite t.name, <<~EOT
92+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
93+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
94+
<assemblyIdentity type="win32" name="#{File.basename(so_fname)}-assembly" version="1.0.0.0"></assemblyIdentity>
95+
96+
#{ e_dlls.map{|dll| %Q{<file name="#{File.basename(dll)}"/>} }.join }
97+
</assembly>
98+
EOT
99+
end
100+
self.sandboxfiles << mani_path
101+
end
102+
103+
104+
#################################################################################
105+
# Add manifest to ruby.exe, rubyw.exe files pointing to DLLs in ruby_builtin_dlls
106+
#################################################################################
107+
108+
core_dlls.each do |destpath|
109+
110+
# Add tasks to write the DLLs into the sub directory
111+
new_destpath = File.join(File.dirname(destpath), "ruby_builtin_dlls", File.basename(destpath))
112+
file new_destpath => [destpath.sub(sandboxdir, unpackdirmgw), dlls_dir] do |t|
113+
cp(t.prerequisites.first, t.name)
114+
end
115+
116+
# Move the DLLs in the dependent files list to the subdirectory
117+
self.sandboxfiles.delete(destpath)
118+
self.sandboxfiles << new_destpath
119+
end
120+
121+
# Add a custom manifest to ruby.exe, rubyw.exe and libruby, so that they find the DLLs to be moved
122+
self.sandboxfiles.select do |destpath|
123+
destpath =~ libruby_regex
124+
end.each do |destpath|
125+
126+
file destpath => [destpath.sub(sandboxdir, unpackdirmgw), bin_dir] do |t|
127+
puts "patching manifest of #{t.name}"
128+
129+
# The XML elements we want to add to the default MINGW manifest:
130+
new = <<~EOT
131+
<dependency>
132+
<dependentAssembly>
133+
<assemblyIdentity version="1.0.0.0" type="win32" name="ruby_builtin_dlls" />
134+
</dependentAssembly>
135+
</dependency>
136+
EOT
137+
138+
ManifestUpdater.update_file(t.prerequisites.first, new, t.name)
139+
end
140+
end
141+
142+
# Add a detached manifest file within the sub directory that lists all DLLs in question
143+
manifest2 = File.join(sandboxdir, "bin/ruby_builtin_dlls/ruby_builtin_dlls.manifest")
144+
file manifest2 => [dlls_dir] do |t|
145+
puts "generate #{t.name}"
146+
File.binwrite t.name, <<~EOT
147+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
148+
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
149+
<assemblyIdentity type="win32" name="ruby_builtin_dlls" version="1.0.0.0"></assemblyIdentity>
150+
151+
#{ core_dlls.map{|dll| %Q{<file name="#{File.basename(dll)}"/>} }.join }
152+
</assembly>
153+
EOT
154+
end
155+
self.sandboxfiles << manifest2
156+
end

0 commit comments

Comments
 (0)