forked from ruby/setup-ruby
-
Notifications
You must be signed in to change notification settings - Fork 1
/
windows.js
285 lines (237 loc) · 10.1 KB
/
windows.js
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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
// 7z arguments
// -aoa overwrite existing, -bd disable progress indicator
// OpenSSL version detection is needed in installGCCTools and installJRubyTools
const fs = require('fs')
const path = require('path')
const cp = require('child_process')
const core = require('@actions/core')
const exec = require('@actions/exec')
const io = require('@actions/io')
const tc = require('@actions/tool-cache')
const common = require('./common')
const rubyInstallerVersions = require('./windows-versions.json')
const drive = common.drive
const msys2GCCReleaseURI = 'https://github.com/ruby/setup-msys2-gcc/releases/download'
const msys2BasePath = process.env['GHCUP_MSYS2']
const vcPkgBasePath = process.env['VCPKG_INSTALLATION_ROOT']
// needed for Ruby 2.0-2.3, cert file used by Git for Windows
let certFile = 'C:\\Program Files\\Git\\mingw64\\etc\\ssl\\cert.pem'
if (!fs.existsSync(certFile)) {
certFile = 'C:\\Program Files\\Git\\mingw64\\ssl\\cert.pem'
if (!fs.existsSync(certFile)) {
throw new Error("Cannot find Git's cert file")
}
}
// location & path for old RubyInstaller DevKit (MSYS1), used with Ruby 2.0-2.3
const msys1 = `${drive}:\\DevKit64`
const msysPathEntries = [`${msys1}\\mingw\\x86_64-w64-mingw32\\bin`, `${msys1}\\mingw\\bin`, `${msys1}\\bin`]
export function getAvailableVersions(platform, engine) {
if (engine === 'ruby') {
return Object.keys(rubyInstallerVersions)
} else {
return undefined
}
}
export async function install(platform, engine, version) {
const url = rubyInstallerVersions[version]
// The windows-2016 and windows-2019 images have MSYS2 build tools (C:/msys64/usr)
// and MinGW build tools installed. The windows-2022 image has neither.
const hasMSYS2PreInstalled = ['windows-2019', 'windows-2016'].includes(platform)
if (!url.endsWith('.7z')) {
throw new Error(`URL should end in .7z: ${url}`)
}
const base = url.slice(url.lastIndexOf('/') + 1, url.length - '.7z'.length)
let rubyPrefix, inToolCache
if (common.shouldUseToolCache(engine, version)) {
inToolCache = common.toolCacheFind(engine, version)
if (inToolCache) {
rubyPrefix = inToolCache
} else {
rubyPrefix = common.getToolCacheRubyPrefix(platform, engine, version)
}
} else {
rubyPrefix = `${drive}:\\${base}`
}
let toolchainPaths = (version === 'mswin') ? await setupMSWin() : await setupMingw(version)
if (!inToolCache) {
await downloadAndExtract(engine, version, url, base, rubyPrefix);
}
const msys2Type = common.setupPath([`${rubyPrefix}\\bin`, ...toolchainPaths])
// install msys2 tools for all Ruby versions, only install mingw or ucrt for Rubies >= 2.4
if (!hasMSYS2PreInstalled) {
await installMSYS2Tools()
}
// windows 2016 and 2019 need ucrt64 installed, 2022 and future images need
// ucrt64 or mingw64 installed, depending on Ruby version
if (((msys2Type === 'ucrt64') || !hasMSYS2PreInstalled) && common.floatVersion(version) >= 2.4) {
await installGCCTools(msys2Type, version)
}
if (version === 'mswin') {
await installVCPkg()
}
const ridk = `${rubyPrefix}\\bin\\ridk.cmd`
if (fs.existsSync(ridk)) {
await common.measure('Adding ridk env variables', async () => addRidkEnv(ridk))
}
return rubyPrefix
}
// Actions windows-2022 image does not contain any mingw or ucrt build tools. Install tools for it,
// and also install ucrt tools on earlier versions, which have msys2 and mingw tools preinstalled.
async function installGCCTools(type, version) {
// 2022-Dec ruby/msys2-gcc-pkgs now uses a suffix to delineate different archive versions.
// At present, the only use is to indicate the included OpenSSL version.
// With no suffix, archives include OpenSSL 1.1.1, with a '-3.0' suffix, they include
// OpenSSL 3.0.x. Note that the mswin archive uses OpenSSL 3.
// As of Jan-2023, OpenSSL 3 is used in Ruby 3.2 & all head versions. MSYS2 updated
// to OpenSSL 3 on 14-Jan-2023.
let suffix = ''
if (common.isHeadVersion(version) || common.floatVersion(version) >= 3.2) {
suffix = '-3.0'
}
const downloadPath = await common.measure(`Downloading ${type}${suffix} build tools`, async () => {
let url = `${msys2GCCReleaseURI}/msys2-gcc-pkgs/${type}${suffix}.7z`
console.log(url)
return await tc.downloadTool(url)
})
await common.measure(`Extracting ${type}${suffix} build tools`, async () =>
exec.exec('7z', ['x', downloadPath, '-aoa', '-bd', `-o${msys2BasePath}`], { silent: true }))
}
// Actions windows-2022 image does not contain any MSYS2 build tools. Install tools for it.
// A subset of the MSYS2 base-devel group
async function installMSYS2Tools() {
const downloadPath = await common.measure(`Downloading msys2 build tools`, async () => {
let url = `${msys2GCCReleaseURI}/msys2-gcc-pkgs/msys2.7z`
console.log(url)
return await tc.downloadTool(url)
})
// need to remove all directories, since they may indicate old packages are installed,
// otherwise, error of "error: duplicated database entry"
fs.rmSync(`${msys2BasePath}\\var\\lib\\pacman\\local`, { recursive: true, force: true })
await common.measure(`Extracting msys2 build tools`, async () =>
exec.exec('7z', ['x', downloadPath, '-aoa', '-bd', `-o${msys2BasePath}`], { silent: true }))
}
// Windows JRuby can install gems that require compile tools, only needed for
// windows-2022 and later images
export async function installJRubyTools() {
await installMSYS2Tools()
// Will need to add OpenSSL version detection when JRuby uses OpenSSL 3.0 ?
await installGCCTools('mingw64', '2.7')
}
// Install vcpkg files needed to build mswin Ruby
async function installVCPkg() {
const downloadPath = await common.measure(`Downloading mswin vcpkg packages`, async () => {
let url = `${msys2GCCReleaseURI}/msys2-gcc-pkgs/mswin.7z`
console.log(url)
return await tc.downloadTool(url)
})
await common.measure(`Extracting mswin vcpkg packages`, async () =>
exec.exec('7z', ['x', downloadPath, '-aoa', '-bd', `-o${vcPkgBasePath}`], { silent: true }))
}
async function downloadAndExtract(engine, version, url, base, rubyPrefix) {
const parentDir = path.dirname(rubyPrefix)
const downloadPath = await common.measure('Downloading Ruby', async () => {
console.log(url)
return await tc.downloadTool(url)
})
await common.measure('Extracting Ruby', async () =>
// -xr extract but exclude share\doc files
exec.exec('7z', ['x', downloadPath, '-bd', `-xr!${base}\\share\\doc`, `-o${parentDir}`], { silent: true }))
if (base !== path.basename(rubyPrefix)) {
await io.mv(path.join(parentDir, base), rubyPrefix)
}
if (common.shouldUseToolCache(engine, version)) {
common.createToolCacheCompleteFile(rubyPrefix)
}
}
async function setupMingw(version) {
core.exportVariable('MAKE', 'make.exe')
// rename these to avoid confusion when Ruby is using OpenSSL 1.0.2.
// most current extconf files look for 1.1.x dll files first, which is the version of the renamed files
if (common.floatVersion(version) <= 2.4) {
renameSystem32Dlls()
}
if (common.floatVersion(version) <= 2.3) {
core.exportVariable('SSL_CERT_FILE', certFile)
await common.measure('Installing MSYS1', async () => installMSYS1(version))
return msysPathEntries
} else {
return []
}
}
// Ruby 2.0-2.3
async function installMSYS1(version) {
const url = 'https://github.com/oneclick/rubyinstaller/releases/download/devkit-4.7.2/DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe'
const downloadPath = await tc.downloadTool(url)
await exec.exec('7z', ['x', downloadPath, `-o${msys1}`], { silent: true })
// below are set in the old devkit.rb file ?
core.exportVariable('RI_DEVKIT', msys1)
core.exportVariable('CC' , 'gcc')
core.exportVariable('CXX', 'g++')
core.exportVariable('CPP', 'cpp')
core.info(`Installed RubyInstaller DevKit for Ruby ${version}`)
}
async function setupMSWin() {
core.exportVariable('MAKE', 'nmake.exe')
return await common.measure('Setting up MSVC environment', async () => addVCVARSEnv())
}
/* Sets MSVC environment for use in Actions
* allows steps to run without running vcvars*.bat, also for PowerShell
* adds a convenience VCVARS environment variable
* this assumes a single Visual Studio version being available in the Windows images */
export function addVCVARSEnv() {
let cmd = 'vswhere -latest -property installationPath'
let vcVars = `${cp.execSync(cmd).toString().trim()}\\VC\\Auxiliary\\Build\\vcvars64.bat`
if (!fs.existsSync(vcVars)) {
throw new Error(`Missing vcVars file: ${vcVars}`)
}
core.exportVariable('VCVARS', vcVars)
cmd = `cmd.exe /c ""${vcVars}" && set"`
let newEnv = new Map()
let newSet = cp.execSync(cmd).toString().trim().split(/\r?\n/)
newSet = newSet.filter(line => /\S=\S/.test(line))
newSet.forEach(s => {
let [k,v] = common.partition(s, '=')
newEnv.set(k,v)
})
let newPathEntries = undefined
for (let [k, v] of newEnv) {
if (process.env[k] !== v) {
if (/^Path$/i.test(k)) {
const newPathStr = v.replace(`${path.delimiter}${process.env['Path']}`, '')
newPathEntries = newPathStr.split(path.delimiter)
} else {
core.exportVariable(k, v)
}
}
}
return newPathEntries
}
// ssl files cause issues with non RI2 Rubies (<2.4) and ruby/ruby's CI from build folder due to dll resolution
function renameSystem32Dlls() {
const sys32 = 'C:\\Windows\\System32\\'
const badFiles = [`${sys32}libcrypto-1_1-x64.dll`, `${sys32}libssl-1_1-x64.dll`]
const existing = badFiles.filter((dll) => fs.existsSync(dll))
if (existing.length > 0) {
console.log(`Renaming ${existing.join(' and ')} to avoid dll resolution conflicts on Ruby <= 2.4`)
existing.forEach(dll => fs.renameSync(dll, `${dll}_`))
}
}
// Sets MSYS2 ENV variables set from running `ridk enable`
function addRidkEnv(ridk) {
let newEnv = new Map()
let cmd = `cmd.exe /c "${ridk} enable && set"`
let newSet = cp.execSync(cmd).toString().trim().split(/\r?\n/)
newSet = newSet.filter(line => /^\S+=\S+/.test(line))
newSet.forEach(s => {
let [k, v] = common.partition(s, '=')
newEnv.set(k, v)
})
for (let [k, v] of newEnv) {
if (process.env[k] !== v) {
if (!/^Path$/i.test(k)) {
console.log(`${k}=${v}`)
core.exportVariable(k, v)
}
}
}
}