Compare commits

..

6 Commits

Author SHA1 Message Date
ilammy e9437d41fc [test] Okay, let's use the fauly spec literally 2020-02-11 23:51:18 +02:00
ilammy 5cf1eeecd8 Test with multiple Python versions
In particular, runners and Node.js seems to be failing with Python 2.
We'd better test those to see if they ever break on us.
2020-02-11 23:47:59 +02:00
ilammy 060bdc7cb5 [debug] add self-test 2020-02-11 23:43:32 +02:00
ilammy b6e52219de Integration test for release version
Make sure that the released version is also fine. Run this test every
time a push is made into the release branch as well as on schedule.
2020-02-11 23:42:24 +02:00
ilammy a46bb485fb Use cmd.exe as shell
It works well and we don't need PowerShell which has weird notion of
default executable search path.
2020-02-11 23:37:34 +02:00
ilammy ce962cf525 [debug] add self-test 2020-02-11 23:33:09 +02:00
9 changed files with 645 additions and 2184 deletions
+11
View File
@@ -0,0 +1,11 @@
name: Python package
on: [push]
jobs:
build:
runs-on: "windows-latest"
strategy:
matrix:
python-version:
- "2.7"
steps:
- uses: ilammy/msvc-dev-cmd@v1
+11 -58
View File
@@ -6,6 +6,7 @@ on:
branches: branches:
- master - master
- release/* - release/*
- wip/debugging
schedule: schedule:
- cron: '0 6 * * *' - cron: '0 6 * * *'
@@ -13,70 +14,22 @@ jobs:
test: test:
name: default name: default
runs-on: windows-latest runs-on: windows-latest
strategy:
fail-fast: false
matrix:
python-version:
- '2.7'
- '3.5'
- '3.6'
- '3.7'
- '3.8'
steps: steps:
- name: Check out source code - name: Check out source code
uses: actions/checkout@v4 uses: actions/checkout@v1
- name: Download Internet
run: npm install
- name: Run eslint
run: npm run lint
- name: Enable Developer Command Prompt (amd64)
uses: ./
with:
arch: amd64
- name: Compile and run some C code (amd64)
shell: cmd
run: |
cl.exe hello.c
hello.exe
- name: Enable Developer Command Prompt (amd64_x86)
uses: ./
with:
arch: amd64_x86
- name: Compile and run some C code (x86)
shell: cmd
run: |
cl.exe hello.c
hello.exe
- name: Enable Developer Command Prompt (amd64_arm)
uses: ./
with:
arch: amd64_arm
- name: Compile some C code (arm)
shell: cmd
run: |
cl.exe hello.c
dumpbin /headers hello.exe
- name: Enable Developer Command Prompt (amd64_arm64)
uses: ./
with:
arch: amd64_arm64
- name: Compile some C code (arm64)
shell: cmd
run: |
cl.exe hello.c
dumpbin /headers hello.exe
audit:
name: npm audit
runs-on: ubuntu-latest
steps:
- name: Check out source code
uses: actions/checkout@v4
- run: npm install
- run: npm audit --audit-level=moderate --production
- run: npm audit --audit-level=critical
alias-arch:
name: arch aliases
runs-on: windows-latest
steps:
- name: Check out source code
uses: actions/checkout@v4
- name: Download Internet - name: Download Internet
run: npm install run: npm install
- name: Enable Developer Command Prompt - name: Enable Developer Command Prompt
uses: ./ uses: ./
with:
arch: Win32
- name: Compile and run some C code - name: Compile and run some C code
shell: cmd shell: cmd
run: | run: |
+12 -10
View File
@@ -4,6 +4,7 @@ on:
push: push:
branches: branches:
- release/* - release/*
- wip/debugging
schedule: schedule:
- cron: '0 6 * * *' - cron: '0 6 * * *'
@@ -11,21 +12,22 @@ jobs:
test: test:
name: release name: release
runs-on: windows-latest runs-on: windows-latest
strategy:
fail-fast: false
matrix:
python-version:
- '2.7'
- '3.5'
- '3.6'
- '3.7'
- '3.8'
steps: steps:
- name: Setup Developer Command Prompt - name: Setup Developer Command Prompt
uses: ilammy/msvc-dev-cmd@release/v1 uses: ilammy/msvc-dev-cmd@v1
- name: Check out source code - name: Check out source code
uses: actions/checkout@v4 uses: actions/checkout@v1
- name: Compile and run some C code - name: Compile and run some C code
shell: cmd shell: cmd
run: | run: |
cl.exe hello.c cl.exe hello.c
hello.exe hello.exe
audit:
name: npm audit
runs-on: ubuntu-latest
steps:
- name: Check out source code
uses: actions/checkout@v4
- run: npm audit --audit-level=moderate --production
- run: npm audit --audit-level=critical
+12 -102
View File
@@ -8,57 +8,12 @@ This sets up the environment for compiling C/C++ code from command line.
Supports Windows. Does nothing on Linux and macOS. Supports Windows. Does nothing on Linux and macOS.
## Example usage
Basic usage for default compilation settings is like this:
```yaml
jobs:
test:
steps:
- uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1
- name: Build something requiring CL.EXE
run: |
cmake -G "NMake Makefiles" .
nmake
# ...
```
If you want something non-default,
like using a specific version of Visual Studio,
or cross-compling for a different target,
you will need to configure those settings via inputs:
```yaml
jobs:
test:
# Run a job for each of the specified target architectures:
strategy:
matrix:
arch:
- amd64
- amd64_x86
- amd64_arm64
steps:
- uses: actions/checkout@v4
- uses: ilammy/msvc-dev-cmd@v1
with:
arch: ${{ matrix.arch }}
- name: Build something requiring CL.EXE
run: |
cmake -G "NMake Makefiles" .
nmake
# ...
```
## Inputs ## Inputs
- `arch` target architecture - `arch` target architecture
- native compilation: - native compilation: `x86`, `x64` (default), `amd64` (synonym for x64)
- `x64` (default) or its synonyms: `amd64`, `win64`, `x86_64`, `x86-64` - cross-compilation: `x86_amd64`, `x86_arm`, `x86_arm64`,
- `x86` or its synonyms: `win32` `amd64_x86`, `amd64_arm`, `amd64_arm64`
- cross-compilation: `x86_amd64`, `x86_arm`, `x86_arm64`, `amd64_x86`, `amd64_arm`, `amd64_arm64`
- `sdk` Windows SDK to use - `sdk` Windows SDK to use
- do not specify to use the default SDK - do not specify to use the default SDK
- or specify full Windows 10 SDK number (e.g, `10.0.10240.0`) - or specify full Windows 10 SDK number (e.g, `10.0.10240.0`)
@@ -70,66 +25,21 @@ jobs:
- `14.XX.YYYYY` for a specific full version number (e.g, `14.11.25503`) - `14.XX.YYYYY` for a specific full version number (e.g, `14.11.25503`)
- `uwp` set `true` to build for Universal Windows Platform (i.e., for Windows Store) - `uwp` set `true` to build for Universal Windows Platform (i.e., for Windows Store)
- `spectre` set `true` to use Visual Studio libraries with [Spectre](https://meltdownattack.com) mitigations - `spectre` set `true` to use Visual Studio libraries with [Spectre](https://meltdownattack.com) mitigations
- `vsversion` the Visual Studio version to use. This can be the version number (e.g. 16.0 for 2019) or the year (e.g. "2019"); omit this input to select the latest version of Visual Studio. On [GitHub-hosted runners](https://docs.github.com/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners), this input is not required because there is only one Visual Studio in the environment.
## Caveats ## Example usage
### Name conflicts with `shell: bash`
Using `shell: bash` in Actions may shadow some of the paths added by MSVC.
In particular, `link.exe` (Microsoft C linker) is prone to be shadowed by `/usr/bin/link` (GNU filesystem link tool).
Unfortunately, this happens because GitHub Actions unconditionally *prepend* GNU paths when `shell: bash` is used,
on top of any paths set by `msvc-dev-cmd`, every time at the start of each new step.
Hence, there aren't many non-destructive options here.
If you experience compilation errors where `link` complains about unreasonable command-line arguments,
“extra operand *something-something* that's probably it.
Recommended workaround is to remove `/usr/bin/link` if that interferes with your builds.
If this is not acceptable, please file an issue, then we'll figure out something better.
### Reconfiguration
You can invoke `ilammy/msvc-dev-cmd` multiple times during your jobs with different inputs
to reconfigure the environment for building with different settings
(e.g., to target multiple architectures).
```yaml ```yaml
jobs: jobs:
release: test:
steps: - uses: actions/checkout@v1
# ... - uses: ilammy/msvc-dev-cmd@v1
- name: Configure build for amd64 - name: Build something requiring CL.EXE
uses: ilammy/msvc-dev-cmd@v1 run: |
with: cmake -G "NMake Makefiles" .
arch: amd64 nmake
# ...
- run: build # (for amd64)
- name: Configure build for x86
uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64_x86
- run: build # (for x86)
- name: Configure build for ARM64
uses: ilammy/msvc-dev-cmd@v1
with:
arch: amd64_arm64
- run: build # (for ARM64)
# ...
``` ```
This mostly works but it's not really recommended
since Developer Command Prompt was not meant for recursive reconfiguration.
That said, if it does not work for you, please file an issue.
Consider using [`strategy.matrix`](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix)
to execute different build configuration in parallel, independent environments.
## License ## License
MIT, see [LICENSE](LICENSE). MIT, see [LICENSE](LICENSE).
+1 -3
View File
@@ -12,10 +12,8 @@ inputs:
description: VC++ compiler toolset version description: VC++ compiler toolset version
uwp: uwp:
description: Build for Universal Windows Platform description: Build for Universal Windows Platform
vsversion:
description: The Visual Studio version to use. This can be the version number (e.g. 16.0 for 2019) or the year (e.g. "2019").
runs: runs:
using: node20 using: node12
main: index.js main: index.js
branding: branding:
icon: terminal icon: terminal
+91 -11
View File
@@ -1,20 +1,100 @@
const { setupMSVCDevCmd } = require('./lib')
const core = require('@actions/core') const core = require('@actions/core')
const execFile = require('util').promisify(require('child_process').execFile)
const fs = require('fs').promises
const os = require('os')
const path = require('path')
const process = require('process')
function main() { const MSVS_2017 = '%ProgramFiles(x86)%\\Microsoft Visual Studio\\2017\\Enterprise\\VC\\Auxiliary\\Build'
var arch = core.getInput('arch') const MSVS_2019 = '%ProgramFiles(x86)%\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build'
const InterestingVariables = [
'INCLUDE',
'LIB',
'LIBPATH',
'Path',
'Platform',
/^VCTools/,
/^VSCMD_/,
/^WindowsSDK/i,
]
async function main() {
if (process.platform != 'win32') {
core.info('This is not a Windows virtual environment, bye!')
return
}
const arch = core.getInput('arch')
const sdk = core.getInput('sdk') const sdk = core.getInput('sdk')
const toolset = core.getInput('toolset') const toolset = core.getInput('toolset')
const uwp = core.getInput('uwp') const uwp = core.getInput('uwp')
const spectre = core.getInput('spectre') const spectre = core.getInput('spectre')
const vsversion = core.getInput('vsversion')
setupMSVCDevCmd(arch, sdk, toolset, uwp, spectre, vsversion) // Due to the way Microsoft Visual C++ is configured, we have to resort to the following hack:
// write a helper batch file which calls the configuration batch file and then outputs the
// configured environment variables, which we then pass to GitHub Actions.
const helper = path.join(os.homedir(), 'msvc-dev-cmd.bat')
var args = [arch]
if (uwp == 'true') {
args.push('uwp')
}
if (sdk) {
args.push(sdk)
}
if (toolset) {
args.push(`-vcvars_ver=${toolset}`)
}
if (spectre == 'true') {
args.push('-vcvars_spectre_libs=spectre')
}
core.debug(`Arguments: ${args.join(' ')}`)
core.debug(`Writing helper file: ${helper}`)
await fs.writeFile(helper, `
@IF EXIST "${MSVS_2017}\\vcvarsall.bat" GOTO :2017
@IF EXIST "${MSVS_2019}\\vcvarsall.bat" GOTO :2019
@ECHO "Microsoft Visual Studio not found"
@EXIT 1
:2017
@CALL "${MSVS_2017}\\vcvarsall.bat" ${args.join(' ')}
@GOTO ENV
:2019
@CALL "${MSVS_2019}\\vcvarsall.bat" ${args.join(' ')}
@GOTO ENV
:ENV
@IF ERRORLEVEL 1 EXIT
@SET
`)
var environment
try {
core.debug('Executing helper')
const { stdout } = await execFile('cmd.exe', ['/q', '/c', helper])
environment = stdout.split('\r\n')
}
catch (error) {
core.debug(`Helper failed: ${error.message}`)
throw error
}
finally {
core.debug('Removing helper')
await fs.unlink(helper)
}
for (let string of environment) {
const [name, value] = string.split('=')
for (let pattern of InterestingVariables) {
if (name.match(pattern)) {
core.exportVariable(name, value)
break
}
}
}
core.info(`Configured Developer Command Prompt`)
} }
try { main().catch(() => core.setFailed('Could not setup Developer Command Prompt'))
main()
}
catch (e) {
core.setFailed('Could not setup Developer Command Prompt: ' + e.message)
}
-225
View File
@@ -1,225 +0,0 @@
const core = require('@actions/core')
const child_process = require('child_process')
const fs = require('fs')
const path = require('path')
const process = require('process')
const PROGRAM_FILES_X86 = process.env['ProgramFiles(x86)']
const PROGRAM_FILES = [process.env['ProgramFiles(x86)'], process.env['ProgramFiles']]
const EDITIONS = ['Enterprise', 'Professional', 'Community', 'BuildTools']
const YEARS = ['2022', '2019', '2017']
const VsYearVersion = {
'2022': '17.0',
'2019': '16.0',
'2017': '15.0',
'2015': '14.0',
'2013': '12.0',
}
function vsversion_to_versionnumber(vsversion) {
if (Object.values(VsYearVersion).includes(vsversion)) {
return vsversion
} else {
if (vsversion in VsYearVersion) {
return VsYearVersion[vsversion]
}
}
return vsversion
}
exports.vsversion_to_versionnumber = vsversion_to_versionnumber
function vsversion_to_year(vsversion) {
if (Object.keys(VsYearVersion).includes(vsversion)) {
return vsversion
} else {
for (const [year, ver] of Object.entries(VsYearVersion)) {
if (ver === vsversion) {
return year
}
}
}
return vsversion
}
exports.vsversion_to_year = vsversion_to_year
const VSWHERE_PATH = `${PROGRAM_FILES_X86}\\Microsoft Visual Studio\\Installer`
function findWithVswhere(pattern, version_pattern) {
try {
let installationPath = child_process.execSync(`vswhere -products * ${version_pattern} -prerelease -property installationPath`).toString().trim()
return installationPath + '\\' + pattern
} catch (e) {
core.warning(`vswhere failed: ${e}`)
}
return null
}
exports.findWithVswhere = findWithVswhere
function findVcvarsall(vsversion) {
const vsversion_number = vsversion_to_versionnumber(vsversion)
let version_pattern
if (vsversion_number) {
const upper_bound = vsversion_number.split('.')[0] + '.9'
version_pattern = `-version "${vsversion_number},${upper_bound}"`
} else {
version_pattern = "-latest"
}
// If vswhere is available, ask it about the location of the latest Visual Studio.
let path = findWithVswhere('VC\\Auxiliary\\Build\\vcvarsall.bat', version_pattern)
if (path && fs.existsSync(path)) {
core.info(`Found with vswhere: ${path}`)
return path
}
core.info("Not found with vswhere")
// If that does not work, try the standard installation locations,
// starting with the latest and moving to the oldest.
const years = vsversion ? [vsversion_to_year(vsversion)] : YEARS
for (const prog_files of PROGRAM_FILES) {
for (const ver of years) {
for (const ed of EDITIONS) {
path = `${prog_files}\\Microsoft Visual Studio\\${ver}\\${ed}\\VC\\Auxiliary\\Build\\vcvarsall.bat`
core.info(`Trying standard location: ${path}`)
if (fs.existsSync(path)) {
core.info(`Found standard location: ${path}`)
return path
}
}
}
}
core.info("Not found in standard locations")
// Special case for Visual Studio 2015 (and maybe earlier), try it out too.
path = `${PROGRAM_FILES_X86}\\Microsoft Visual C++ Build Tools\\vcbuildtools.bat`
if (fs.existsSync(path)) {
core.info(`Found VS 2015: ${path}`)
return path
}
core.info(`Not found in VS 2015 location: ${path}`)
throw new Error('Microsoft Visual Studio not found')
}
exports.findVcvarsall = findVcvarsall
function isPathVariable(name) {
const pathLikeVariables = ['PATH', 'INCLUDE', 'LIB', 'LIBPATH']
return pathLikeVariables.indexOf(name.toUpperCase()) != -1
}
function filterPathValue(path) {
let paths = path.split(';')
// Remove duplicates by keeping the first occurance and preserving order.
// This keeps path shadowing working as intended.
function unique(value, index, self) {
return self.indexOf(value) === index
}
return paths.filter(unique).join(';')
}
/** See https://github.com/ilammy/msvc-dev-cmd#inputs */
function setupMSVCDevCmd(arch, sdk, toolset, uwp, spectre, vsversion) {
if (process.platform != 'win32') {
core.info('This is not a Windows virtual environment, bye!')
return
}
// Add standard location of "vswhere" to PATH, in case it's not there.
process.env.PATH += path.delimiter + VSWHERE_PATH
// There are all sorts of way the architectures are called. In addition to
// values supported by Microsoft Visual C++, recognize some common aliases.
let arch_aliases = {
"win32": "x86",
"win64": "x64",
"x86_64": "x64",
"x86-64": "x64",
}
// Ignore case when matching as that's what humans expect.
if (arch.toLowerCase() in arch_aliases) {
arch = arch_aliases[arch.toLowerCase()]
}
// Due to the way Microsoft Visual C++ is configured, we have to resort to the following hack:
// Call the configuration batch file and then output *all* the environment variables.
var args = [arch]
if (uwp == 'true') {
args.push('uwp')
}
if (sdk) {
args.push(sdk)
}
if (toolset) {
args.push(`-vcvars_ver=${toolset}`)
}
if (spectre == 'true') {
args.push('-vcvars_spectre_libs=spectre')
}
const vcvars = `"${findVcvarsall(vsversion)}" ${args.join(' ')}`
core.debug(`vcvars command-line: ${vcvars}`)
const cmd_output_string = child_process.execSync(`set && cls && ${vcvars} && cls && set`, {shell: "cmd"}).toString()
const cmd_output_parts = cmd_output_string.split('\f')
const old_environment = cmd_output_parts[0].split('\r\n')
const vcvars_output = cmd_output_parts[1].split('\r\n')
const new_environment = cmd_output_parts[2].split('\r\n')
// If vsvars.bat is given an incorrect command line, it will print out
// an error and *still* exit successfully. Parse out errors from output
// which don't look like environment variables, and fail if appropriate.
const error_messages = vcvars_output.filter((line) => {
if (line.match(/^\[ERROR.*\]/)) {
// Don't print this particular line which will be confusing in output.
if (!line.match(/Error in script usage. The correct usage is:$/)) {
return true
}
}
return false
})
if (error_messages.length > 0) {
throw new Error('invalid parameters' + '\r\n' + error_messages.join('\r\n'))
}
// Convert old environment lines into a dictionary for easier lookup.
let old_env_vars = {}
for (let string of old_environment) {
const [name, value] = string.split('=')
old_env_vars[name] = value
}
// Now look at the new environment and export everything that changed.
// These are the variables set by vsvars.bat. Also export everything
// that was not there during the first sweep: those are new variables.
core.startGroup('Environment variables')
for (let string of new_environment) {
// vsvars.bat likes to print some fluff at the beginning.
// Skip lines that don't look like environment variables.
if (!string.includes('=')) {
continue;
}
let [name, new_value] = string.split('=')
let old_value = old_env_vars[name]
// For new variables "old_value === undefined".
if (new_value !== old_value) {
core.info(`Setting ${name}`)
// Special case for a bunch of PATH-like variables: vcvarsall.bat
// just prepends its stuff without checking if its already there.
// This makes repeated invocations of this action fail after some
// point, when the environment variable overflows. Avoid that.
if (isPathVariable(name)) {
new_value = filterPathValue(new_value)
}
core.exportVariable(name, new_value)
}
}
core.endGroup()
core.info(`Configured Developer Command Prompt`)
}
exports.setupMSVCDevCmd = setupMSVCDevCmd
+502 -1771
View File
File diff suppressed because it is too large Load Diff
+5 -4
View File
@@ -1,10 +1,11 @@
{ {
"name": "msvc-dev-cmd", "name": "msvc-dev-cmd",
"version": "1.13.0-dev", "version": "1.0.0",
"description": "GitHub Action to setup Developer Command Prompt for Microsoft Visual C++", "description": "GitHub Action to setup Developer Command Prompt for Microsoft Visual C++",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
"lint": "eslint index.js" "lint": "eslint index.js",
"test": "eslint index.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@@ -27,9 +28,9 @@
}, },
"homepage": "https://github.com/ilammy/msvc-dev-cmd#readme", "homepage": "https://github.com/ilammy/msvc-dev-cmd#readme",
"dependencies": { "dependencies": {
"@actions/core": "^1.10.0" "@actions/core": "^1.1.1"
}, },
"devDependencies": { "devDependencies": {
"eslint": "^7" "eslint": "^6.5.0"
} }
} }