|
| 1 | +const { setupMSVCDevCmd } = require('./lib') |
1 | 2 | const core = require('@actions/core') |
2 | | -const child_process = require('child_process') |
3 | | -const fs = require('fs') |
4 | | -const path = require('path') |
5 | | -const process = require('process') |
6 | | - |
7 | | -const PROGRAM_FILES_X86 = process.env['ProgramFiles(x86)'] |
8 | | -const PROGRAM_FILES = [process.env['ProgramFiles(x86)'], process.env['ProgramFiles']] |
9 | | - |
10 | | - |
11 | | -const EDITIONS = ['Enterprise', 'Professional', 'Community', 'BuildTools'] |
12 | | -const YEARS = ['2022', '2019', '2017'] |
13 | | - |
14 | | -const VsYearVersion = { |
15 | | - '2022': '17.0', |
16 | | - '2019': '16.0', |
17 | | - '2017': '15.0', |
18 | | - '2015': '14.0', |
19 | | - '2013': '12.0', |
20 | | -} |
21 | | - |
22 | | -function vsversion_to_versionnumber(vsversion) { |
23 | | - if (Object.values(VsYearVersion).includes(vsversion)) { |
24 | | - return vsversion |
25 | | - } else { |
26 | | - if (vsversion in VsYearVersion) { |
27 | | - return VsYearVersion[vsversion] |
28 | | - } |
29 | | - } |
30 | | - return vsversion |
31 | | -} |
32 | | -exports.vsversion_to_versionnumber = vsversion_to_versionnumber |
33 | | - |
34 | | -function vsversion_to_year(vsversion) { |
35 | | - if (Object.keys(VsYearVersion).includes(vsversion)) { |
36 | | - return vsversion |
37 | | - } else { |
38 | | - for (const [year, ver] of Object.entries(VsYearVersion)) { |
39 | | - if (ver === vsversion) { |
40 | | - return year |
41 | | - } |
42 | | - } |
43 | | - } |
44 | | - return vsversion |
45 | | -} |
46 | | -exports.vsversion_to_year = vsversion_to_year |
47 | | - |
48 | | -const VSWHERE_PATH = `${PROGRAM_FILES_X86}\\Microsoft Visual Studio\\Installer` |
49 | | - |
50 | | -function findWithVswhere(pattern, version_pattern) { |
51 | | - try { |
52 | | - let installationPath = child_process.execSync(`vswhere -products * ${version_pattern} -prerelease -property installationPath`).toString().trim() |
53 | | - return installationPath + '\\' + pattern |
54 | | - } catch (e) { |
55 | | - core.warning(`vswhere failed: ${e}`) |
56 | | - } |
57 | | - return null |
58 | | -} |
59 | | -exports.findWithVswhere = findWithVswhere |
60 | | - |
61 | | -function findVcvarsall(vsversion) { |
62 | | - const vsversion_number = vsversion_to_versionnumber(vsversion) |
63 | | - let version_pattern |
64 | | - if (vsversion_number) { |
65 | | - const upper_bound = vsversion_number.split('.')[0] + '.9' |
66 | | - version_pattern = `-version "${vsversion_number},${upper_bound}"` |
67 | | - } else { |
68 | | - version_pattern = "-latest" |
69 | | - } |
70 | | - |
71 | | - // If vswhere is available, ask it about the location of the latest Visual Studio. |
72 | | - let path = findWithVswhere('VC\\Auxiliary\\Build\\vcvarsall.bat', version_pattern) |
73 | | - if (path && fs.existsSync(path)) { |
74 | | - core.info(`Found with vswhere: ${path}`) |
75 | | - return path |
76 | | - } |
77 | | - core.info("Not found with vswhere") |
78 | | - |
79 | | - // If that does not work, try the standard installation locations, |
80 | | - // starting with the latest and moving to the oldest. |
81 | | - const years = vsversion ? [vsversion_to_year(vsversion)] : YEARS |
82 | | - for (const prog_files of PROGRAM_FILES) { |
83 | | - for (const ver of years) { |
84 | | - for (const ed of EDITIONS) { |
85 | | - path = `${prog_files}\\Microsoft Visual Studio\\${ver}\\${ed}\\VC\\Auxiliary\\Build\\vcvarsall.bat` |
86 | | - core.info(`Trying standard location: ${path}`) |
87 | | - if (fs.existsSync(path)) { |
88 | | - core.info(`Found standard location: ${path}`) |
89 | | - return path |
90 | | - } |
91 | | - } |
92 | | - } |
93 | | - } |
94 | | - core.info("Not found in standard locations") |
95 | | - |
96 | | - // Special case for Visual Studio 2015 (and maybe earlier), try it out too. |
97 | | - path = `${PROGRAM_FILES_X86}\\Microsoft Visual C++ Build Tools\\vcbuildtools.bat` |
98 | | - if (fs.existsSync(path)) { |
99 | | - core.info(`Found VS 2015: ${path}`) |
100 | | - return path |
101 | | - } |
102 | | - core.info(`Not found in VS 2015 location: ${path}`) |
103 | | - |
104 | | - throw new Error('Microsoft Visual Studio not found') |
105 | | -} |
106 | | -exports.findVcvarsall = findVcvarsall |
107 | | - |
108 | | -function isPathVariable(name) { |
109 | | - const pathLikeVariables = ['PATH', 'INCLUDE', 'LIB', 'LIBPATH'] |
110 | | - return pathLikeVariables.indexOf(name.toUpperCase()) != -1 |
111 | | -} |
112 | | - |
113 | | -function filterPathValue(path) { |
114 | | - let paths = path.split(';') |
115 | | - // Remove duplicates by keeping the first occurance and preserving order. |
116 | | - // This keeps path shadowing working as intended. |
117 | | - function unique(value, index, self) { |
118 | | - return self.indexOf(value) === index |
119 | | - } |
120 | | - return paths.filter(unique).join(';') |
121 | | -} |
122 | | - |
123 | | -/** See https://github.com/ilammy/msvc-dev-cmd#inputs */ |
124 | | -function setupMSVCDevCmd(arch, sdk, toolset, uwp, spectre, vsversion) { |
125 | | - if (process.platform != 'win32') { |
126 | | - core.info('This is not a Windows virtual environment, bye!') |
127 | | - return |
128 | | - } |
129 | | - |
130 | | - // Add standard location of "vswhere" to PATH, in case it's not there. |
131 | | - process.env.PATH += path.delimiter + VSWHERE_PATH |
132 | | - |
133 | | - // There are all sorts of way the architectures are called. In addition to |
134 | | - // values supported by Microsoft Visual C++, recognize some common aliases. |
135 | | - let arch_aliases = { |
136 | | - "win32": "x86", |
137 | | - "win64": "x64", |
138 | | - "x86_64": "x64", |
139 | | - "x86-64": "x64", |
140 | | - } |
141 | | - // Ignore case when matching as that's what humans expect. |
142 | | - if (arch.toLowerCase() in arch_aliases) { |
143 | | - arch = arch_aliases[arch.toLowerCase()] |
144 | | - } |
145 | | - |
146 | | - // Due to the way Microsoft Visual C++ is configured, we have to resort to the following hack: |
147 | | - // Call the configuration batch file and then output *all* the environment variables. |
148 | | - |
149 | | - var args = [arch] |
150 | | - if (uwp == 'true') { |
151 | | - args.push('uwp') |
152 | | - } |
153 | | - if (sdk) { |
154 | | - args.push(sdk) |
155 | | - } |
156 | | - if (toolset) { |
157 | | - args.push(`-vcvars_ver=${toolset}`) |
158 | | - } |
159 | | - if (spectre == 'true') { |
160 | | - args.push('-vcvars_spectre_libs=spectre') |
161 | | - } |
162 | | - |
163 | | - const vcvars = `"${findVcvarsall(vsversion)}" ${args.join(' ')}` |
164 | | - core.debug(`vcvars command-line: ${vcvars}`) |
165 | | - |
166 | | - const cmd_output_string = child_process.execSync(`set && cls && ${vcvars} && cls && set`, {shell: "cmd"}).toString() |
167 | | - const cmd_output_parts = cmd_output_string.split('\f') |
168 | | - |
169 | | - const old_environment = cmd_output_parts[0].split('\r\n') |
170 | | - const vcvars_output = cmd_output_parts[1].split('\r\n') |
171 | | - const new_environment = cmd_output_parts[2].split('\r\n') |
172 | | - |
173 | | - // If vsvars.bat is given an incorrect command line, it will print out |
174 | | - // an error and *still* exit successfully. Parse out errors from output |
175 | | - // which don't look like environment variables, and fail if appropriate. |
176 | | - const error_messages = vcvars_output.filter((line) => { |
177 | | - if (line.match(/^\[ERROR.*\]/)) { |
178 | | - // Don't print this particular line which will be confusing in output. |
179 | | - if (!line.match(/Error in script usage. The correct usage is:$/)) { |
180 | | - return true |
181 | | - } |
182 | | - } |
183 | | - return false |
184 | | - }) |
185 | | - if (error_messages.length > 0) { |
186 | | - throw new Error('invalid parameters' + '\r\n' + error_messages.join('\r\n')) |
187 | | - } |
188 | | - |
189 | | - // Convert old environment lines into a dictionary for easier lookup. |
190 | | - let old_env_vars = {} |
191 | | - for (let string of old_environment) { |
192 | | - const [name, value] = string.split('=') |
193 | | - old_env_vars[name] = value |
194 | | - } |
195 | | - |
196 | | - // Now look at the new environment and export everything that changed. |
197 | | - // These are the variables set by vsvars.bat. Also export everything |
198 | | - // that was not there during the first sweep: those are new variables. |
199 | | - core.startGroup('Environment variables') |
200 | | - for (let string of new_environment) { |
201 | | - // vsvars.bat likes to print some fluff at the beginning. |
202 | | - // Skip lines that don't look like environment variables. |
203 | | - if (!string.includes('=')) { |
204 | | - continue; |
205 | | - } |
206 | | - let [name, new_value] = string.split('=') |
207 | | - let old_value = old_env_vars[name] |
208 | | - // For new variables "old_value === undefined". |
209 | | - if (new_value !== old_value) { |
210 | | - core.info(`Setting ${name}`) |
211 | | - // Special case for a bunch of PATH-like variables: vcvarsall.bat |
212 | | - // just prepends its stuff without checking if its already there. |
213 | | - // This makes repeated invocations of this action fail after some |
214 | | - // point, when the environment variable overflows. Avoid that. |
215 | | - if (isPathVariable(name)) { |
216 | | - new_value = filterPathValue(new_value) |
217 | | - } |
218 | | - core.exportVariable(name, new_value) |
219 | | - } |
220 | | - } |
221 | | - core.endGroup() |
222 | | - |
223 | | - core.info(`Configured Developer Command Prompt`) |
224 | | -} |
225 | | -exports.setupMSVCDevCmd = setupMSVCDevCmd |
226 | 3 |
|
227 | 4 | function main() { |
228 | 5 | var arch = core.getInput('arch') |
|
0 commit comments