Redesigned action to use CMake file api. (#3)
Redesign action to use the CMake file API rather than editing the environment before a regular build.
This commit is contained in:
Родитель
cdb7e0fbcd
Коммит
502db28262
|
@ -0,0 +1,81 @@
|
|||
name: 'run-tests'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
schedule:
|
||||
- cron: '0 2 * * SUN'
|
||||
|
||||
env:
|
||||
sample: '${{ github.workspace }}/test/sample'
|
||||
build: '${{ github.workspace }}/test/sample/build'
|
||||
result: '${{ github.workspace }}/test/sample/build/results.sarif'
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: Build and Test
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout action
|
||||
uses: actions/checkout@v2.3.4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2.4.0
|
||||
with:
|
||||
node-version: '12.x'
|
||||
- name: Build
|
||||
run: npm install
|
||||
- name: Test
|
||||
run: npm test
|
||||
|
||||
functional_test:
|
||||
name: Functional - ${{ matrix.test }}
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- test: Regular
|
||||
outcome: success
|
||||
ignoredTargetPaths: test/sample/failure
|
||||
- test: Compilation Error
|
||||
outcome: failure
|
||||
ignoredTargetPaths: ''
|
||||
|
||||
steps:
|
||||
- name: Checkout action
|
||||
uses: actions/checkout@v2.3.4
|
||||
|
||||
- name: Initialize sample CMake Project
|
||||
uses: lukka/run-cmake@v3
|
||||
with:
|
||||
cmakeListsTxtPath: ${{ env.sample }}/CMakeLists.txt
|
||||
buildDirectory: ${{ env.build }}
|
||||
cmakeGenerator: VS16Win64
|
||||
buildWithCMake: false
|
||||
|
||||
- name: Run action
|
||||
id: run_action
|
||||
continue-on-error: true
|
||||
uses: ./
|
||||
with:
|
||||
cmakeBuildDirectory: ${{ env.build }}
|
||||
resultPath: ${{ env.result }}
|
||||
ruleset: AllRules.ruleset
|
||||
ignoreSystemHeaders: true
|
||||
ignoredTargetPaths: ${{ matrix.ignoredTargetPaths }}
|
||||
|
||||
- name: Validate expected action outcome
|
||||
if: steps.run_action.outcome != matrix.outcome
|
||||
run: exit 1
|
||||
|
||||
- name: Upload SARIF artifact
|
||||
if: steps.run_action.outcome == 'success'
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: sarif-file
|
||||
path: ${{ steps.run_action.outputs.sarif }}
|
||||
if-no-files-found: error
|
|
@ -0,0 +1,3 @@
|
|||
.vscode
|
||||
node_modules/
|
||||
test/sample/build/
|
109
README.md
109
README.md
|
@ -1,83 +1,64 @@
|
|||
# msvc-code-analysis-action
|
||||
# Microsoft C++ Code Analysis Action
|
||||
|
||||
This action enables code analysis to run while building a project with the Microsoft Visual C++ Compiler. The analysis
|
||||
will produce SARIF results that can be uploaded to the GitHub Code Scanning Alerts experience.
|
||||
This actions run code analysis for any CMake project built with the Microsoft Visual C++ Compiler. The analysis
|
||||
will produce SARIF results that can be uploaded to the GitHub Code Scanning Alerts experience and/or included as
|
||||
an artifact to view locally in the Sarif Viewer VSCode Extension.
|
||||
|
||||
## Usage
|
||||
|
||||
### Pre-requisites
|
||||
|
||||
Include a workflow `.yml` file using an [example](#example) below as a template. Run the `msvc-code-analysis-action`
|
||||
before re-building your project using the appropriate operation mode detailed below.
|
||||
after configuring CMake for your project. Building the project is only required if the C++ source files involve the use
|
||||
of generated files.
|
||||
|
||||
### Inputs
|
||||
- `mode` (**default:** General) operation mode given different environments and build systems:
|
||||
- **General:** enable Code Analysis for any build system. The MSVC compiler with the desired host and target
|
||||
architecture must be available on the PATH.
|
||||
- **MSBuild:** enable MSBuild Code Analysis experience. This is the preferred method if using MSBuild projects as it
|
||||
can use Code Analysis settings as configured in Visual Studio.
|
||||
- `results` (**default:** ${{ github.workspace }}) root directory containing all SARIF files produced in build.
|
||||
This is commonly the root directory of the project (i.e. MSBuild) or build folder (i.e. CMake).
|
||||
- `ruleset` (**default:** NativeRecommendedRules.ruleset) ruleset file used to determine what checks are run. This can
|
||||
reference a ruleset that ship with Visual Studio or a custom file in the project.
|
||||
- `cleanSarif` (**default:** true) SARIF files will under `results` directory are considered stale and will be deleted.
|
||||
- `args` optional parameters to pass to every instance of the compiler.
|
||||
|
||||
### Examples
|
||||
### Input Parameters
|
||||
|
||||
#### CMake
|
||||
Description of all input parameters: [action.yml](https://github.com/microsoft/msvc-code-analysis-action/blob/main/action.yml)
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```yml
|
||||
# Use VCPKG to make MSVC discoverable on the PATH
|
||||
- name: Add MSVC to the PATH
|
||||
uses: lukka/run-vcpkg@v7
|
||||
with:
|
||||
setupOnly: true
|
||||
env:
|
||||
build: '${{ github.workspace }}/build'
|
||||
|
||||
# Configure MSVC to run code analysis during build
|
||||
- name: Initialize MSVC Code Analysis
|
||||
uses: microsoft/msvc-code-analysis-action
|
||||
with:
|
||||
# Path to directory that will contain produced sarif files
|
||||
results: build
|
||||
# Ruleset file that will determine what checks will be run
|
||||
ruleset: NativeRecommendRules.ruleset
|
||||
jobs:
|
||||
build:
|
||||
steps:
|
||||
# Configure project with CMake
|
||||
- name: Configure CMake
|
||||
uses: lukka/run-cmake@v3
|
||||
with:
|
||||
buildDirectory: ${{ env.build }}
|
||||
# Build is not require unless generated source files are used
|
||||
buildWithCMake: false
|
||||
cmakeGenerator: 'VS16Win64'
|
||||
cmakeListsTxtPath: ${{ github.workspace }}/CMakeLists.txt
|
||||
|
||||
# Rebuild the project using any MSVC compatible build system
|
||||
- name: Build Project
|
||||
run: cmake -G Ninja -B build --clean-first
|
||||
# Run Microsoft Visual C++ code analysis
|
||||
- name: Initialize MSVC Code Analysis
|
||||
uses: microsoft/msvc-code-analysis-action
|
||||
# Provide a unique ID to access the sarif output path
|
||||
id: run-analysis
|
||||
with:
|
||||
cmakeBuildDirectory: ${{ env.build }}
|
||||
# Ruleset file that will determine what checks will be run
|
||||
ruleset: NativeRecommendRules.ruleset
|
||||
|
||||
# Upload all SARIF files generated in the build directory tree
|
||||
- name: Upload SARIF files
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: build
|
||||
```
|
||||
# Upload SARIF file to GitHub Code Scanning Alerts
|
||||
- name: Upload SARIF to Github
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
sarif_file: ${{ steps.run-analysis.outputs.sarif }}
|
||||
|
||||
#### MSBuild
|
||||
|
||||
```yml
|
||||
# Make MSBuild discoverable on the PATH
|
||||
- name: Add MSBuild to PATH
|
||||
uses: microsoft/setup-msbuild@v1.0.2
|
||||
|
||||
# Configure MSVC to run code analysis during build
|
||||
- name: Initialize MSVC Code Analysis
|
||||
uses: microsoft/msvc-code-analysis-action@v1
|
||||
with:
|
||||
# Root of MSBuild Solution containing all project directories
|
||||
result: ${{ github.workspace }}
|
||||
|
||||
# Rebuild the project using MSBuild
|
||||
- name: Build Project
|
||||
run: msbuild Project.sln /p:Configuration=Release /p:Platform=x64 /t:rebuild
|
||||
|
||||
# Upload all SARIF files generated in the build directory tree
|
||||
- name: Upload SARIF files
|
||||
uses: github/codeql-action/upload-sarif@v1
|
||||
with:
|
||||
# Root of MSBuild Solution containing all project directories
|
||||
sarif_file: ${{ github.workspace }}
|
||||
# Upload SARIF file as an Artifact to download and view
|
||||
- name: Upload SARIF as an Artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: sarif-file
|
||||
path: ${{ steps.run-analysis.outputs.sarif }}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
|
44
action.yml
44
action.yml
|
@ -1,22 +1,38 @@
|
|||
name: 'MSVC Code Analysis Action'
|
||||
description: 'Setup MSVC to produce Code Analysis SARIF files for use in github/codeql-action/upload-sarif@v1'
|
||||
name: 'Microsoft C++ Code Analysis Action'
|
||||
description: 'Run Microsoft C++ Code Analysis to produce SARIF files for use in github/codeql-action/upload-sarif@v1'
|
||||
inputs:
|
||||
mode:
|
||||
description: 'Operation mode given different environments and build systems.'
|
||||
default: 'General'
|
||||
results:
|
||||
description: 'root directory containing all SARIF files produced in build.'
|
||||
default: '${{ github.workspace }}'
|
||||
cmakeBuildDirectory:
|
||||
description: 'The CMake build directory that should already be generated.'
|
||||
required: true
|
||||
ruleset:
|
||||
description: 'Ruleset file to use during analysis.'
|
||||
description: 'Ruleset file used to determine what checks are run.'
|
||||
default: 'NativeRecommendedRules.ruleset'
|
||||
cleanSarif:
|
||||
description: 'SARIF files will under `results` directory are considered stale and will be deleted.'
|
||||
default: 'True'
|
||||
args:
|
||||
ignoredTargetPaths:
|
||||
description: 'Any CMake targets defined inside these paths will be excluded from analysis. This is useful for excluding
|
||||
code such as unit tests. List is ";" seperated and can be absolute or relative to "github.workspace"'
|
||||
required: false
|
||||
ignoredIncludePaths:
|
||||
description: 'Any paths in this list will be treated as SYSTEM includes and will be excluded from analysis. This requires
|
||||
"ignoreSystemHeaders == true". List is ";" seperated and can be absolute or relative to "github.workspace"'
|
||||
required: false
|
||||
ignoreSystemHeaders:
|
||||
description: 'Uses /external arguments to ignore warnings from any headers marked as SYSTEM in CMake.'
|
||||
default: true
|
||||
resultsPath:
|
||||
description: 'Optional path to generate the SARIF file to. If not supplied "results.sarif" will be created in the CMake
|
||||
build directory. Path can be absolute or relative to "github.workspace".'
|
||||
required: false
|
||||
loadImplicitCompilerEnv:
|
||||
description: 'Load implicit includes/libs for the given MSVC toolset using Visual Studio Command Prompt. Set to
|
||||
false if already loaded or a custom include path is needed.'
|
||||
default: true
|
||||
additionalArgs:
|
||||
description: 'Optional parameters to pass to every instance of the compiler.'
|
||||
required: false
|
||||
outputs:
|
||||
sarif:
|
||||
description: 'The path to the SARIF file that is generated containing all the results.'
|
||||
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'index.js'
|
||||
main: 'dist/index.js'
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,343 @@
|
|||
@actions/core
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@actions/exec
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@actions/io
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
balanced-match
|
||||
MIT
|
||||
(MIT)
|
||||
|
||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
brace-expansion
|
||||
MIT
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2013 Julian Gruber <julian@juliangruber.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
concat-map
|
||||
MIT
|
||||
This software is released under the MIT license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
fs.realpath
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
----
|
||||
|
||||
This library bundles a version of the `fs.realpath` and `fs.realpathSync`
|
||||
methods from Node.js v0.10 under the terms of the Node.js MIT license.
|
||||
|
||||
Node's license follows, also included at the header of `old.js` which contains
|
||||
the licensed code:
|
||||
|
||||
Copyright Joyent, Inc. and other Node contributors.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
glob
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
## Glob Logo
|
||||
|
||||
Glob's logo created by Tanya Brassie <http://tanyabrassie.com/>, licensed
|
||||
under a Creative Commons Attribution-ShareAlike 4.0 International License
|
||||
https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
||||
|
||||
inflight
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
inherits
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
|
||||
minimatch
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
once
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
path-is-absolute
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
rimraf
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
|
||||
tmp
|
||||
MIT
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 KARASZI István
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
|
||||
wrappy
|
||||
ISC
|
||||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,3 @@
|
|||
@CALL %1 %2 -vcvars_ver=%3
|
||||
@ECHO INCLUDE=%INCLUDE%
|
||||
@ECHO LIB=%LIB%
|
839
index.js
839
index.js
|
@ -1,230 +1,749 @@
|
|||
"use strict";
|
||||
|
||||
const core = require('@actions/core');
|
||||
const exec = require('@actions/exec');
|
||||
const fs = require('fs');
|
||||
const io = require('@actions/io');
|
||||
const path = require('path');
|
||||
const util = require('util');
|
||||
const tmp = require('tmp');
|
||||
const toolrunner = require('@actions/exec/lib/toolrunner');
|
||||
|
||||
const RelativeRulesetPath = '..\\..\\..\\..\\..\\..\\..\\Team Tools\\Static Analysis Tools\\Rule Sets';
|
||||
const DefaultRulesetName = 'NativeRecommendedRules.ruleset';
|
||||
const CMakeApiClientName = "client-msvc-ca-action";
|
||||
// Paths relative to absolute path to cl.exe
|
||||
const RelativeRulesetPath = '..\\..\\..\\..\\..\\..\\..\\..\\Team Tools\\Static Analysis Tools\\Rule Sets';
|
||||
const RelativeToolsetPath = '..\\..\\..\\..';
|
||||
const RelativeCommandPromptPath = '..\\..\\..\\..\\..\\..\\..\\Auxiliary\\Build\\vcvarsall.bat';
|
||||
|
||||
//
|
||||
// Utility functions
|
||||
//
|
||||
|
||||
// Add Quoted command-line argument for MSVC that handles spaces and trailing backslashes.
|
||||
function addArg(clArgs, arg) {
|
||||
// find number of consecutive trailing backslashes
|
||||
var i = 0;
|
||||
while (i < arg.length && arg[arg.length - 1 - i] == '\\') {
|
||||
i++;
|
||||
}
|
||||
|
||||
// escape all trailing backslashes
|
||||
if (i > 0) {
|
||||
arg += new Array(i + 1).join('\\');
|
||||
}
|
||||
|
||||
clArgs.push('"' + arg + '"');
|
||||
/**
|
||||
* Validate if the given directory both exists and is non-empty.
|
||||
* @param {string} targetDir directory to test
|
||||
* @returns {boolean} true if the directory is empty
|
||||
*/
|
||||
function isDirectoryEmpty(targetDir) {
|
||||
return !targetDir || !fs.existsSync(targetDir) || (fs.readdirSync(targetDir).length) == 0;
|
||||
}
|
||||
|
||||
// Find executable relative to the CWD or the system PATH
|
||||
function findExecutableOnPath(executable) {
|
||||
var paths = process.cwd() + ';' + process.env.PATH;
|
||||
for (const pathDir of paths.split(';')) {
|
||||
const executablePath = path.join(pathDir, executable);
|
||||
if (fs.existsSync(executablePath)) {
|
||||
return executablePath;
|
||||
/**
|
||||
* Validate if the targetDir is either equal or a sub-directory of the parentDir
|
||||
* @param {string} parentDir parent directory
|
||||
* @param {string} targetDir directory to test
|
||||
* @returns {boolean} true if sub-directory
|
||||
*/
|
||||
function isSubdirectory(parentDir, targetDir) {
|
||||
return path.normalize(targetDir).startsWith(path.normalize(parentDir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get normalized relative path from a given file/directory.
|
||||
* @param {string} fromPath path to join relative path to
|
||||
* @param {string} relativePath relative path to append
|
||||
* @returns normalized path
|
||||
*/
|
||||
function getRelativeTo(fromPath, relativePath) {
|
||||
return path.normalize(path.join(fromPath, relativePath))
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and resolve path by making non-absolute paths relative to GitHub
|
||||
* repository root.
|
||||
* @param {string} unresolvedPath path to resolve
|
||||
* @returns the resolved absolute path
|
||||
*/
|
||||
function resolvePath(unresolvedPath) {
|
||||
return path.normalize(path.isAbsolute(unresolvedPath) ?
|
||||
unresolvedPath : path.join(process.env.GITHUB_WORKSPACE, unresolvedPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and resolve action input path by making non-absolute paths relative to
|
||||
* GitHub repository root.
|
||||
* @param {string} input name of GitHub action input variable
|
||||
* @param {boolean} required if true the input must be non-empty
|
||||
* @returns the absolute path to the input path if specified
|
||||
*/
|
||||
function resolveInputPath(input, required = false) {
|
||||
let inputPath = core.getInput(input);
|
||||
if (!inputPath) {
|
||||
if (required) {
|
||||
throw new Error(input + " input path can not be empty.");
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolvePath(inputPath, required);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and resolve action input paths making non-absolute paths relative to
|
||||
* GitHub repository root. Paths are seperated by the provided string.
|
||||
* @param {string} input name of GitHub action input variable
|
||||
* @param {boolean} required if true the input must be non-empty
|
||||
* @returns the absolute path to the input path if specified
|
||||
*/
|
||||
function resolveInputPaths(input, required = false, seperator = ';') {
|
||||
const inputPaths = core.getInput(input);
|
||||
if (!inputPaths) {
|
||||
if (required) {
|
||||
throw new Error(input + " input paths can not be empty.");
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return inputPaths.split(seperator)
|
||||
.map((inputPath) => resolvePath(inputPath))
|
||||
.filter((inputPath) => inputPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a query file for the CMake API
|
||||
* @param {string} apiDir CMake API directory '.cmake/api/v1'
|
||||
*/
|
||||
async function createApiQuery(apiDir) {
|
||||
const queryDir = path.join(apiDir, "query", CMakeApiClientName);
|
||||
if (!fs.existsSync(queryDir)) {
|
||||
await io.mkdirP(queryDir);
|
||||
}
|
||||
|
||||
const queryFile = path.join(queryDir, "query.json");
|
||||
const queryData = {
|
||||
"requests": [
|
||||
{ kind: "codemodel", version: 2 },
|
||||
{ kind: "toolchains", version: 1 }
|
||||
]};
|
||||
|
||||
try {
|
||||
fs.writeFileSync(queryFile, JSON.stringify(queryData), 'utf-8');
|
||||
} catch (err) {
|
||||
throw new Error("Failed to write query.json file for CMake API.", err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and parse the given JSON reply file.
|
||||
* @param {string} replyFile absolute path to JSON reply
|
||||
* @returns parsed JSON data of the reply file
|
||||
*/
|
||||
function parseReplyFile(replyFile) {
|
||||
if (!fs.existsSync(replyFile)) {
|
||||
throw new Error("Failed to find CMake API reply file: " + replyFile);
|
||||
}
|
||||
|
||||
let jsonData = fs.readFileSync(replyFile, (err) => {
|
||||
if (err) {
|
||||
throw new Error("Failed to read CMake API reply file: " + replyFile, err);
|
||||
}
|
||||
});
|
||||
|
||||
return JSON.parse(jsonData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON filepath for the given response kind.
|
||||
* @param {string} replyDir CMake API directory for replies '.cmake/api/v1/reply'
|
||||
* @param {object} indexReply parsed JSON data from index-xxx.json reply
|
||||
* @param {string} kind the kind of response to search for
|
||||
* @returns the absolute path to the JSON response file, null if not found
|
||||
*/
|
||||
function getResponseFilepath(replyDir, clientResponses, kind) {
|
||||
const response = clientResponses.find((response) => response["kind"] == kind);
|
||||
return response ? path.join(replyDir, response.jsonFile) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information extracted from CMake API index reply which details all other requested responses.
|
||||
* @param {string} replyDir CMake API directory for replies '.cmake/api/v1/reply'
|
||||
* @param {object} indexReply parsed JSON data from index-xxx.json reply
|
||||
*/
|
||||
function ReplyIndexInfo(replyDir, indexReply) {
|
||||
const clientResponses = indexReply.reply[CMakeApiClientName]["query.json"].responses;
|
||||
this.codemodelResponseFile = getResponseFilepath(replyDir, clientResponses, "codemodel");
|
||||
this.toolchainsResponseFile = getResponseFilepath(replyDir, clientResponses, "toolchains");
|
||||
this.version = indexReply.cmake.version.string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the information needed from the reply index file for the CMake API
|
||||
* @param {string} apiDir CMake API directory '.cmake/api/v1'
|
||||
* @returns ReplyIndexInfo info extracted from index-xxx.json reply
|
||||
*/
|
||||
function getApiReplyIndex(apiDir) {
|
||||
const replyDir = path.join(apiDir, "reply");
|
||||
|
||||
let indexFilepath;
|
||||
if (fs.existsSync(replyDir)) {
|
||||
for (const filename of fs.readdirSync(replyDir)) {
|
||||
if (filename.startsWith("index-")) {
|
||||
// get the most recent index query file (ordered lexicographically)
|
||||
const filepath = path.join(replyDir, filename);
|
||||
if (!indexFilepath || filepath > indexFilepath) {
|
||||
indexFilepath = filepath;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(executable + ' is not accessible on the PATH');
|
||||
if (!indexFilepath) {
|
||||
throw new Error("Failed to find CMake API index reply file.");
|
||||
}
|
||||
|
||||
const indexReply = parseReplyFile(indexFilepath);
|
||||
const replyIndexInfo = new ReplyIndexInfo(replyDir, indexReply);
|
||||
|
||||
core.info(`Loaded '${indexFilepath}' reply generated from CMake API.`);
|
||||
|
||||
return replyIndexInfo;
|
||||
}
|
||||
|
||||
// Ensure results directory for SARIF files exists and delete stale files if needed.
|
||||
function prepareResultsDir() {
|
||||
var outputDir = core.getInput('results');
|
||||
if (outputDir == '') {
|
||||
// set path to repo root if not specified
|
||||
outputDir = process.env.GITHUB_WORKSPACE;
|
||||
} else if (!path.isAbsolute(outputDir)) {
|
||||
// make path relative to the repo root if not absolute
|
||||
outputDir = path.join(process.env.GITHUB_WORKSPACE, outputDir);
|
||||
/**
|
||||
* Load reply data from the CMake API. This will:
|
||||
* - Create a query file in cmake API directory requesting data needed
|
||||
* - Re-run CMake on build directory to generate reply data
|
||||
* - Extract required information from the index-xxx.json reply
|
||||
* - Validate the version of CMake to ensure required reply data exists
|
||||
* @param {string} buildRoot build directory of CMake project
|
||||
* @return ReplyIndexInfo info extracted from index-xxx.json reply
|
||||
*/
|
||||
async function loadCMakeApiReplies(buildRoot) {
|
||||
if (isDirectoryEmpty(buildRoot)) {
|
||||
throw new Error("CMake build root must exist, be non-empty and be configured with CMake");
|
||||
}
|
||||
|
||||
if (fs.existsSync(outputDir)) {
|
||||
var cleanSarif = core.getInput('cleanSarif');
|
||||
switch (cleanSarif.toLowerCase()) {
|
||||
case 'true':
|
||||
{
|
||||
// delete existing Sarif files that are consider stale
|
||||
files = fs.readdirSync(outputDir, { withFileTypes: true });
|
||||
files.forEach(file => {
|
||||
if (file.isFile() && path.extname(file.name).toLowerCase() == '.sarif') {
|
||||
fs.unlinkSync(path.join(outputDir, file.name));
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'false':
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unsupported value for \'cleanSarif\'. Must be either \'True\' or \'False\'');
|
||||
}
|
||||
// validate CMake can be found on the PATH
|
||||
await io.which("cmake", true);
|
||||
|
||||
// create CMake API query file for the generation of replies needed
|
||||
const apiDir = path.join(buildRoot, ".cmake/api/v1");
|
||||
await createApiQuery(apiDir);
|
||||
|
||||
// regenerate CMake build directory to acquire CMake file API reply
|
||||
core.info(`Running CMake to generate reply data.`);
|
||||
try {
|
||||
await exec.exec("cmake", [ buildRoot ])
|
||||
} catch (err) {
|
||||
throw new Error(`CMake failed to reconfigure project with error: ${err}`);
|
||||
}
|
||||
|
||||
return outputDir;
|
||||
// load reply index generated from the CMake Api
|
||||
const replyIndexInfo = getApiReplyIndex(apiDir);
|
||||
if (replyIndexInfo.version < "3.20.5") {
|
||||
throw new Error("Action requires CMake version >= 3.20.5");
|
||||
}
|
||||
|
||||
return replyIndexInfo;
|
||||
}
|
||||
|
||||
// EspXEngine.dll only exists in host/target bin for MSVC Visual Studio release.
|
||||
function findEspXEngine(clPath) {
|
||||
const clDir = path.dirname(clPath);
|
||||
/**
|
||||
* Information on compiler include path.
|
||||
* @param {string} path the absolute path to the include directory
|
||||
* @param {boolean} isSystem true if this should be treated as a CMake SYSTEM path
|
||||
*/
|
||||
function IncludePath(path, isSystem) {
|
||||
this.path = path;
|
||||
this.isSystem = isSystem;
|
||||
}
|
||||
|
||||
// check if we already have the correct host/target pair
|
||||
var dllPath = path.join(clDir, 'EspXEngine.dll');
|
||||
if (fs.existsSync(dllPath)) {
|
||||
return dllPath;
|
||||
}
|
||||
/**
|
||||
* Information about the language and compiler being used to compile a source file.
|
||||
* @param {object} toolchain ReplyIndexInfo info extracted from index-xxx.json reply
|
||||
*/
|
||||
function ToolchainInfo(toolchain) {
|
||||
this.language = toolchain.language;
|
||||
this.path = toolchain.compiler.path;
|
||||
this.version = toolchain.compiler.version;
|
||||
this.includes = (toolchain.compiler.implicit.includeDirectories || []).map(
|
||||
(include) => new IncludePath(include, true));
|
||||
|
||||
var targetName = '';
|
||||
var hostDir = path.dirname(clDir);
|
||||
// extract toolset-version & host/target arch from folder layout in VS
|
||||
this.toolsetVersion = path.basename(getRelativeTo(this.path, RelativeToolsetPath));
|
||||
const targetDir = path.dirname(this.path);
|
||||
const hostDir = path.dirname(targetDir);
|
||||
this.targetArch = path.basename(targetDir);
|
||||
switch (path.basename(hostDir)) {
|
||||
case 'HostX86':
|
||||
targetName = 'x86';
|
||||
case 'Hostx86':
|
||||
this.hostArch = 'x86';
|
||||
break;
|
||||
case 'HostX64':
|
||||
targetName = 'x64';
|
||||
case 'Hostx64':
|
||||
this.hostArch = 'x64';
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown MSVC toolset layout');
|
||||
}
|
||||
|
||||
dllPath = path.join(hostDir, targetName, 'EspXEngine.dll');
|
||||
if (fs.existsSync(dllPath)) {
|
||||
return dllPath;
|
||||
}
|
||||
|
||||
throw new Error('Unable to find EspXEngine.dll');
|
||||
}
|
||||
|
||||
// Find official ruleset directory using the known path of MSVC compiler in Visual Studio.
|
||||
function findRulesetDirectory(clPath) {
|
||||
const rulesetDirectory = path.normalize(path.join(path.dirname(clPath), RelativeRulesetPath));
|
||||
/**
|
||||
* Parse the toolchain-xxx.json file to find information on any MSVC toolchains used. If none are
|
||||
* found issue an error.
|
||||
* @param {ReplyIndexInfo} replyIndexInfo ReplyIndexInfo info extracted from index-xxx.json reply
|
||||
* @returns Toolchain info extracted from toolchain-xxx.json
|
||||
*/
|
||||
function loadToolchainMap(replyIndexInfo) {
|
||||
if (!fs.existsSync(replyIndexInfo.toolchainsResponseFile)) {
|
||||
throw new Error("Failed to load toolchains response from CMake API");
|
||||
}
|
||||
|
||||
const toolchainMap = {};
|
||||
const toolchains = parseReplyFile(replyIndexInfo.toolchainsResponseFile);
|
||||
const cToolchain = toolchains.toolchains.find(
|
||||
(t) => t.language == "C" && t.compiler.id == "MSVC");
|
||||
if (cToolchain) {
|
||||
toolchainMap[cToolchain.language] = new ToolchainInfo(cToolchain);
|
||||
}
|
||||
|
||||
const cxxToolchain = toolchains.toolchains.find(
|
||||
(t) => t.language == "CXX" && t.compiler.id == "MSVC");
|
||||
if (cxxToolchain) {
|
||||
toolchainMap[cxxToolchain.language] = new ToolchainInfo(cxxToolchain);
|
||||
}
|
||||
|
||||
|
||||
if (Object.keys(toolchainMap).length === 0) {
|
||||
throw new Error("Action requires use of MSVC for either/both C or C++.");
|
||||
}
|
||||
|
||||
return toolchainMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Information on each compilation unit extracted from the CMake targets.
|
||||
* @param {object} group compilation data shared between one or more source files
|
||||
* @param {string} source absolute path to source file being compiled
|
||||
*/
|
||||
function CompileCommand(group, source) {
|
||||
// Filepath to source file being compiled
|
||||
this.source = source;
|
||||
// Compiler language used
|
||||
this.language = group.language;
|
||||
// C++ Standard
|
||||
this.standard = group.languageStandard ? group.languageStandard.standard : undefined;
|
||||
// Compile command line fragments appended into a single string
|
||||
this.args = (group.compileCommandFragments || []).map((c) => c.fragment).join(" ");
|
||||
// includes, both regular and system
|
||||
this.includes = (group.includes || []).map((inc) =>
|
||||
new IncludePath(inc.path, inc.isSystem || false));
|
||||
// defines
|
||||
this.defines = (group.defines || []).map((d) => d.define);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the codemodel-xxx.json and each target-xxx.json to find information on required to compile
|
||||
* each source file in the project.
|
||||
* @param {ReplyIndexInfo} replyIndexInfo ReplyIndexInfo info extracted from index-xxx.json reply
|
||||
* @returns CompileCommand information for each compiled source file in the project
|
||||
*/
|
||||
function loadCompileCommands(replyIndexInfo, excludedTargetPaths) {
|
||||
if (!fs.existsSync(replyIndexInfo.codemodelResponseFile)) {
|
||||
throw new Error("Failed to load codemodel response from CMake API");
|
||||
}
|
||||
|
||||
let compileCommands = [];
|
||||
const codemodel = parseReplyFile(replyIndexInfo.codemodelResponseFile);
|
||||
const sourceRoot = codemodel.paths.source;
|
||||
const replyDir = path.dirname(replyIndexInfo.codemodelResponseFile);
|
||||
const codemodelInfo = codemodel.configurations[0];
|
||||
for (const targetInfo of codemodelInfo.targets) {
|
||||
const targetDir = path.join(sourceRoot, codemodelInfo.directories[targetInfo.directoryIndex].source);
|
||||
if (excludedTargetPaths.some((excludePath) => isSubdirectory(excludePath, targetDir))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const target = parseReplyFile(path.join(replyDir, targetInfo.jsonFile));
|
||||
for (const group of target.compileGroups || []) {
|
||||
for (const sourceIndex of group.sourceIndexes) {
|
||||
const source = path.join(sourceRoot, target.sources[sourceIndex].path);
|
||||
compileCommands.push(new CompileCommand(group, source));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return compileCommands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find path to EspXEngine.dll as it only exists in host/target bin for MSVC Visual Studio release.
|
||||
* @param {ToolchainInfo} toolchain information on the toolchain being used
|
||||
* @returns absolute path to EspXEngine.dll
|
||||
*/
|
||||
function findEspXEngine(toolchain) {
|
||||
const hostDir = path.dirname(path.dirname(toolchain.path));
|
||||
const espXEnginePath = path.join(hostDir, toolchain.hostArch, 'EspXEngine.dll');
|
||||
if (fs.existsSync(espXEnginePath)) {
|
||||
return espXEnginePath;
|
||||
}
|
||||
|
||||
throw new Error(`Unable to find: ${espXEnginePath}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find official ruleset directory using the known path of MSVC compiler in Visual Studio.
|
||||
* @param {ToolchainInfo} toolchain information on the toolchain being used
|
||||
* @returns absolute path to directory containing all Visual Studio rulesets
|
||||
*/
|
||||
function findRulesetDirectory(toolchain) {
|
||||
const rulesetDirectory = getRelativeTo(toolchain.path, RelativeRulesetPath);
|
||||
return fs.existsSync(rulesetDirectory) ? rulesetDirectory : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find ruleset first searching relative to GitHub repository and then relative to the official ruleset directory
|
||||
* shipped in Visual Studio.
|
||||
* @param {string} rulesetDirectory path to directory containing all Visual Studio rulesets
|
||||
* @returns path to ruleset found locally or inside Visual Studio
|
||||
*/
|
||||
function findRuleset(rulesetDirectory) {
|
||||
var rulesetPath = core.getInput('ruleset');
|
||||
if (rulesetPath == '') {
|
||||
const repoRulesetPath = resolveInputPath("ruleset");
|
||||
if (!repoRulesetPath) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (path.isAbsolute(rulesetPath)) {
|
||||
return fs.existsSync(rulesetPath) ? rulesetPath : undefined;
|
||||
}
|
||||
|
||||
// search for a path relative to the project directory
|
||||
const repoRulesetPath = path.join(process.env.GITHUB_WORKSPACE, rulesetPath);
|
||||
if (fs.existsSync(repoRulesetPath)) {
|
||||
} else if (fs.existsSync(repoRulesetPath)) {
|
||||
core.info(`Found local ruleset: ${repoRulesetPath}`);
|
||||
return repoRulesetPath;
|
||||
}
|
||||
|
||||
// search official ruleset directory that ships inside of Visual Studio
|
||||
const rulesetPath = core.getInput("ruleset");
|
||||
if (rulesetDirectory != undefined) {
|
||||
const officialRulesetPath = path.join(rulesetDirectory, rulesetPath);
|
||||
if (fs.existsSync(officialRulesetPath)) {
|
||||
core.info(`Found official ruleset: ${officialRulesetPath}`);
|
||||
return officialRulesetPath;
|
||||
}
|
||||
} else {
|
||||
core.warning('Unable to find official rulesets shipped with Visual Studio');
|
||||
core.warning("Unable to find official rulesets shipped with Visual Studio.");
|
||||
}
|
||||
|
||||
throw new Error('Unable to fine ruleset specified: ' + rulesetPath);
|
||||
throw new Error(`Unable to find local or official ruleset specified: ${rulesetPath}`);
|
||||
}
|
||||
|
||||
//
|
||||
// Build 'mode' functions
|
||||
//
|
||||
/**
|
||||
* Options to enable/disable different compiler features.
|
||||
*/
|
||||
function CompilerCommandOptions() {
|
||||
// Use /external command line options to ignore warnings in CMake SYSTEM headers.
|
||||
this.ignoreSystemHeaders = core.getInput("ignoreSystemHeaders");
|
||||
// Toggle whether implicit includes/libs are loaded from Visual Studio Command Prompt
|
||||
this.loadImplicitCompilerEnv = core.getInput("loadImplicitCompilerEnv");
|
||||
// Ignore analysis on any CMake targets defined in these paths
|
||||
this.ignoredTargetPaths = resolveInputPaths("ignoredTargetPaths");
|
||||
// Additional include paths to exclude from analysis
|
||||
this.ignoredIncludePaths = resolveInputPaths("ignoredIncludePaths")
|
||||
.map((include) => new IncludePath(include, true));
|
||||
if (this.ignoredIncludePaths && !this.ignoreSystemHeaders) {
|
||||
throw new Error("Use of 'ignoredIncludePaths' requires 'ignoreSystemHeaders == true'");
|
||||
}
|
||||
// Additional arguments to add the command-line of every analysis instance
|
||||
this.additionalArgs = core.getInput("additionalArgs");
|
||||
// TODO: add support to build precompiled headers before running analysis.
|
||||
this.usePrecompiledHeaders = false; // core.getInput("usePrecompiledHeaders");
|
||||
}
|
||||
|
||||
// Configuration if (mode == General).
|
||||
function configureGeneralProject() {
|
||||
const clArgs = ["/analyze:quiet", "/analyze:log:format:sarif"];
|
||||
/**
|
||||
* Construct all command-line arguments that will be common among all sources files of a given compiler.
|
||||
* @param {*} toolchain information on the toolchain being used
|
||||
* @param {CompilerCommandOptions} options options for different compiler features
|
||||
* @returns list of analyze arguments common to the given toolchain
|
||||
*/
|
||||
function getCommonAnalyzeArguments(toolchain, options) {
|
||||
const args = ["/analyze:only", "/analyze:quiet", "/analyze:log:format:sarif", "/nologo"];
|
||||
|
||||
// fine cl.exe on the corresponding EspXEngine.dll
|
||||
const clPath = findExecutableOnPath('cl.exe');
|
||||
const espXEngine = findEspXEngine(clPath);
|
||||
addArg(clArgs, util.format('/analyze:plugin%s', espXEngine));
|
||||
const espXEngine = findEspXEngine(toolchain);
|
||||
args.push(`/analyze:plugin${espXEngine}`);
|
||||
|
||||
// find ruleset directory that ships inside of Visual Studio
|
||||
const rulesetDirectory = findRulesetDirectory(clPath);
|
||||
|
||||
// find ruleset if specified
|
||||
const rulesetDirectory = findRulesetDirectory(toolchain);
|
||||
const rulesetPath = findRuleset(rulesetDirectory);
|
||||
if (rulesetPath != undefined) {
|
||||
addArg(clArgs, util.format('/analyze:ruleset%s', rulesetPath));
|
||||
args.push(`/analyze:ruleset${rulesetPath}`);
|
||||
|
||||
// add ruleset directories incase user includes any official rulesets
|
||||
if (rulesetDirectory != undefined) {
|
||||
addArg(clArgs, util.format('/analyze:rulesetdirectory%s', rulesetDirectory));
|
||||
args.push(`/analyze:rulesetdirectory${rulesetDirectory}`);
|
||||
}
|
||||
} else {
|
||||
core.warning('Ruleset is not being used, all warnings will be enabled.');
|
||||
}
|
||||
|
||||
if (options.ignoreSystemHeaders) {
|
||||
args.push(`/external:W0`);
|
||||
args.push(`/analyze:external-`);
|
||||
}
|
||||
|
||||
if (options.additionalArgs) {
|
||||
args = args.concat(toolrunner.argStringToArray(options.additionalArgs));
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the the implicit includes that should be used with the given compiler from the
|
||||
* Visual Studio command prompt corresponding with the toolchain used. This is required
|
||||
* as MSVC does not populate the CMake API `toolchain.implicit.includeDirectories` property.
|
||||
* @param {ToolchainInfo} toolchain information on the toolchain being used
|
||||
* @returns array of default includes used by the given MSVC toolset
|
||||
*/
|
||||
async function extractEnvironmentFromCommandPrompt(toolchain) {
|
||||
// use bat file to output environment variable required after running 'vcvarsall.bat'
|
||||
const vcEnvScript = path.join(__dirname, "vc_env.bat");
|
||||
// init arguments for 'vcvarsall.bat' to match the toolset version/arch used
|
||||
const commandPromptPath = getRelativeTo(toolchain.path, RelativeCommandPromptPath);
|
||||
const arch = (toolchain.hostArch == toolchain.targetArch) ?
|
||||
toolchain.hostArch : `${toolchain.hostArch}_${toolchain.targetArch}`;
|
||||
|
||||
core.info("Extracting environment from VS Command Prompt");
|
||||
const execOptions = { silent: true };
|
||||
const execOutput = await exec.getExecOutput(vcEnvScript,
|
||||
[commandPromptPath, arch, toolchain.toolsetVersion], execOptions);
|
||||
if (execOutput.exitCode != 0) {
|
||||
core.debug(execOutput.stdout);
|
||||
throw new Error("Failed to run VS Command Prompt to collect implicit includes/libs");
|
||||
}
|
||||
|
||||
const env = { INCLUDE: "", LIB: "" };
|
||||
for (const line of execOutput.stdout.split(/\r?\n/)) {
|
||||
const index = line.indexOf('=');
|
||||
if (index != -1) {
|
||||
const envVar = line.substring(0, index);
|
||||
if (envVar in env) {
|
||||
env[envVar] = line.substring(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// add additional command-line arguments to MSVC if specified
|
||||
const additionalArgs = core.getInput('args');
|
||||
if (additionalArgs != '') {
|
||||
clArgs.push(additionalArgs);
|
||||
}
|
||||
|
||||
// add analysis arguments to _CL_ env variable
|
||||
core.exportVariable('_CL_', clArgs.join(' '));
|
||||
|
||||
// enable compatibility mode as GitHub does not support some sarif options
|
||||
core.exportVariable('CAEmitSarifLog', '1');
|
||||
return env;
|
||||
}
|
||||
|
||||
// Configuration if (mode == MSBuild).
|
||||
function configureMSBuildProject() {
|
||||
/**
|
||||
* Construct all environment variables that will be common among all sources files of a given compiler.
|
||||
* @param {ToolchainInfo} toolchain information on the toolchain being used
|
||||
* @param {CompilerCommandOptions} options options for different compiler features
|
||||
* @returns map of environment variables common to the given toolchain
|
||||
*/
|
||||
async function getCommonAnalyzeEnvironment(toolchain, options) {
|
||||
const env = {
|
||||
CAEmitSarifLog: "1", // enable compatibility mode as GitHub does not support some sarif options
|
||||
CAExcludePath: process.env.CAExcludePath || "",
|
||||
INCLUDE: process.env.INCLUDE || "",
|
||||
LIB: process.env.LIB || "",
|
||||
};
|
||||
|
||||
// ensure ruleset is empty or not modified from default
|
||||
var rulesetPath = core.getInput('ruleset');
|
||||
if (rulesetPath != '' || rulesetPath != DefaultRulesetName) {
|
||||
throw new Error(
|
||||
'Custom ruleset not support in MSBuild mode. Configure ruleset in project or use /p:CodeAnalysisRuleset=XXX');
|
||||
if (options.loadImplicitCompilerEnv) {
|
||||
const commandPromptEnv = await extractEnvironmentFromCommandPrompt(toolchain);
|
||||
env.CAExcludePath += `;${commandPromptEnv.INCLUDE}`; // exclude all implicit includes
|
||||
env.INCLUDE += `;${commandPromptEnv.INCLUDE}`;
|
||||
env.LIB += `;${commandPromptEnv.LIB}`;
|
||||
}
|
||||
|
||||
// add additional command-line arguments to MSVC if specified
|
||||
const additionalArgs = core.getInput('args');
|
||||
if (additionalArgs != '') {
|
||||
core.exportVariable('_CL_', additionalArgs);
|
||||
}
|
||||
|
||||
// force Code Analysis to run
|
||||
core.exportVariable('RunCodeAnalysis', 'true');
|
||||
|
||||
// extra redundancy in case the user has RunCodeAnalysis manually configured in project
|
||||
core.exportVariable('RunCodeAnalysisOnce', 'true');
|
||||
|
||||
// force generation of Sarif output that us only used in the IDE experience
|
||||
core.exportVariable('VCCodeAnalysisUX', 'true');
|
||||
return env;
|
||||
}
|
||||
|
||||
//
|
||||
// Main
|
||||
//
|
||||
/**
|
||||
* Information required to run analysis on a single source file.
|
||||
* @param {string} source absolute path to the source file being compiled
|
||||
* @param {string} compiler absolute path to compiler used
|
||||
* @param {string[]} args all compilation and analyze arguments to pass to cl.exe
|
||||
* @param {[key: string]: string} env environment to use when running cl.exe
|
||||
* @param {string} sarifLog absolute path to SARIF log file that will be produced
|
||||
*/
|
||||
function AnalyzeCommand(source, compiler, args, env, sarifLog) {
|
||||
this.source = source;
|
||||
this.compiler = compiler;
|
||||
this.args = args;
|
||||
this.env = env;
|
||||
this.sarifLog = sarifLog;
|
||||
}
|
||||
|
||||
try {
|
||||
const mode = core.getInput('mode');
|
||||
switch (mode.toLowerCase()) {
|
||||
case 'general':
|
||||
configureGeneralProject()
|
||||
break;
|
||||
case 'msbuild':
|
||||
configureMSBuildProject()
|
||||
break;
|
||||
default:
|
||||
throw new Error('Unknown operation mode: ' + mode);
|
||||
/**
|
||||
* Load information needed to compile and analyze each source file in the given CMake project.
|
||||
* This makes use of the CMake file API and other sources to collect this data.
|
||||
* @param {string} buildRoot absolute path to the build directory of the CMake project
|
||||
* @param {CompilerCommandOptions} options options for different compiler features
|
||||
* @returns list of information to compile and analyze each source file in the project
|
||||
*/
|
||||
async function createAnalysisCommands(buildRoot, options) {
|
||||
const replyIndexInfo = await loadCMakeApiReplies(buildRoot);
|
||||
const toolchainMap = loadToolchainMap(replyIndexInfo);
|
||||
const compileCommands = loadCompileCommands(replyIndexInfo, options.ignoredTargetPaths);
|
||||
|
||||
let commonArgsMap = {};
|
||||
let commonEnvMap = {};
|
||||
for (const toolchain of Object.values(toolchainMap)) {
|
||||
if (!(toolchain.path in commonArgsMap)) {
|
||||
commonArgsMap[toolchain.path] = getCommonAnalyzeArguments(toolchain, options);
|
||||
commonEnvMap[toolchain.path] = await getCommonAnalyzeEnvironment(toolchain, options);
|
||||
}
|
||||
}
|
||||
|
||||
prepareResultsDir();
|
||||
let analyzeCommands = []
|
||||
for (const command of compileCommands) {
|
||||
const toolchain = toolchainMap[command.language];
|
||||
if (toolchain) {
|
||||
let args = toolrunner.argStringToArray(command.args);
|
||||
const allIncludes = toolchain.includes.concat(
|
||||
command.includes, options.ignoredIncludePaths);
|
||||
for (const include of allIncludes) {
|
||||
if (options.ignoreSystemHeaders && include.isSystem) {
|
||||
// TODO: filter compilers that don't support /external.
|
||||
args.push(`/external:I${include.path}`);
|
||||
} else {
|
||||
args.push(`/I${include.path}`);
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
for (const define of command.defines) {
|
||||
args.push(`/D${define}`);
|
||||
}
|
||||
|
||||
args.push(command.source);
|
||||
|
||||
let sarifLog = null;
|
||||
try {
|
||||
sarifLog = tmp.fileSync({ postfix: '.sarif', discardDescriptor: true }).name;
|
||||
} catch (err) {
|
||||
// Clean up all temp SARIF logs
|
||||
analyzeCommands.forEach(command => fs.unlinkSync(command.sarifLog));
|
||||
throw Error(`Failed to create temporary file to write SARIF: ${err}`, err);
|
||||
}
|
||||
|
||||
args.push(`/analyze:log${sarifLog}`);
|
||||
|
||||
args = args.concat(commonArgsMap[toolchain.path]);
|
||||
analyzeCommands.push(new AnalyzeCommand(
|
||||
command.source, toolchain.path, args, commonEnvMap[toolchain.path], sarifLog));
|
||||
}
|
||||
}
|
||||
|
||||
return analyzeCommands;
|
||||
}
|
||||
|
||||
// TODO: use a more performant data-structure such a hash-set
|
||||
function ResultCache() {
|
||||
this.files = {};
|
||||
this.addIfUnique = function(sarifResult) {
|
||||
const file = sarifResult.locations[0].physicalLocation.artifactLocation.uri;
|
||||
const id = sarifResult.ruleId;
|
||||
const line = sarifResult.locations[0].physicalLocation.region.startLine;
|
||||
const column = sarifResult.locations[0].physicalLocation.region.startColumn;
|
||||
const message = sarifResult.message.text;
|
||||
this.files[file] = this.files[file] || {};
|
||||
this.files[file][id] = this.files[file][id] || [];
|
||||
|
||||
const ruleCache = this.files[file][id];
|
||||
if (ruleCache.some((result) =>
|
||||
result.line == line && result.column == column && result.message == message)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ruleCache.push({
|
||||
line: line,
|
||||
column: column,
|
||||
message: message
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
function filterRun(run, resultCache) {
|
||||
// remove any artifacts that don't contain results to reduce log size
|
||||
run.artifacts = run.artifacts.filter(artifact => artifact.roles &&
|
||||
artifact.roles.some(r => r == "resultFile"));
|
||||
|
||||
// remove any duplicate results from other sarif runs
|
||||
run.results = run.results.filter(result => resultCache.addIfUnique(result));
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
function combineSarif(resultPath, sarifFiles) {
|
||||
const resultCache = new ResultCache();
|
||||
const combinedSarif = {
|
||||
"version": "2.1.0",
|
||||
"$schema": "https://schemastore.azurewebsites.net/schemas/json/sarif-2.1.0-rtm.5.json",
|
||||
"runs": []
|
||||
};
|
||||
|
||||
for (const sarifFile of sarifFiles) {
|
||||
const sarifLog = parseReplyFile(sarifFile);
|
||||
combinedSarif.runs.push(filterRun(sarifLog.runs[0], resultCache));
|
||||
}
|
||||
|
||||
try {
|
||||
fs.writeFileSync(resultPath, JSON.stringify(combinedSarif), 'utf-8');
|
||||
} catch (err) {
|
||||
throw new Error("Failed to write combined SARIF result file.", err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Main
|
||||
*/
|
||||
async function main() {
|
||||
var analyzeCommands = [];
|
||||
try {
|
||||
const buildDir = resolveInputPath("cmakeBuildDirectory", true);
|
||||
if (!fs.existsSync(buildDir)) {
|
||||
throw new Error("CMake build directory does not exist. Ensure CMake is already configured.");
|
||||
}
|
||||
|
||||
let resultPath = resolveInputPath("resultsPath", false);
|
||||
if (!resultPath) {
|
||||
resultPath = path.join(buildDir, "results.sarif");
|
||||
} else if (!fs.existsSync(path.dirname(resultPath))) {
|
||||
throw new Error("Directory of the 'resultPath' file must already exist.");
|
||||
}
|
||||
|
||||
const options = new CompilerCommandOptions();
|
||||
analyzeCommands = await createAnalysisCommands(buildDir, options);
|
||||
if (analyzeCommands.length == 0) {
|
||||
throw new Error('No C/C++ files were found in the project that could be analyzed.');
|
||||
}
|
||||
|
||||
// TODO: parallelism
|
||||
const failedSourceFiles = [];
|
||||
for (const command of analyzeCommands) {
|
||||
const execOptions = {
|
||||
cwd: buildDir,
|
||||
env: command.env,
|
||||
};
|
||||
|
||||
// TODO: timeouts
|
||||
core.info(`Running analysis on: ${command.source}`);
|
||||
try {
|
||||
await exec.exec(`"${command.compiler}"`, command.args, execOptions);
|
||||
} catch (err) {
|
||||
core.debug(`Compilation failed with error: ${err}`);
|
||||
core.debug("Environment:");
|
||||
core.debug(execOptions.env);
|
||||
failedSourceFiles.push(command.source);
|
||||
}
|
||||
}
|
||||
|
||||
if (failedSourceFiles.length > 0) {
|
||||
const fileList = failedSourceFiles
|
||||
.map(file => path.basename(file))
|
||||
.join(",");
|
||||
throw new Error(`Analysis failed due to compiler errors in files: ${fileList}`);
|
||||
}
|
||||
|
||||
const sarifResults = analyzeCommands.map(command => command.sarifLog);
|
||||
combineSarif(resultPath, sarifResults);
|
||||
core.setOutput("sarif", resultPath);
|
||||
|
||||
} catch (error) {
|
||||
if (core.isDebug()) {
|
||||
core.setFailed(error.stack)
|
||||
} else {
|
||||
core.setFailed(error)
|
||||
}
|
||||
} finally {
|
||||
analyzeCommands.map(command => command.sarifLog)
|
||||
.filter(log => fs.existsSync(log))
|
||||
.forEach(log => fs.unlinkSync(log));
|
||||
}
|
||||
}
|
||||
|
||||
if (require.main === module) {
|
||||
(async () => {
|
||||
await main();
|
||||
})();
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"name": "msvc-code-analysis-action",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"node_modules/@actions/core": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.4.0.tgz",
|
||||
"integrity": "sha512-CGx2ilGq5i7zSLgiiGUtBCxhRRxibJYU6Fim0Q1Wg2aQL2LTnF27zbqZOrxfvFQ55eSBW0L8uVStgtKMpa0Qlg=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright 2019 GitHub
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1,209 +0,0 @@
|
|||
# `@actions/core`
|
||||
|
||||
> Core functions for setting results, logging, registering secrets and exporting variables across actions
|
||||
|
||||
## Usage
|
||||
|
||||
### Import the package
|
||||
|
||||
```js
|
||||
// javascript
|
||||
const core = require('@actions/core');
|
||||
|
||||
// typescript
|
||||
import * as core from '@actions/core';
|
||||
```
|
||||
|
||||
#### Inputs/Outputs
|
||||
|
||||
Action inputs can be read with `getInput` which returns a `string` or `getBooleanInput` which parses a boolean based on the [yaml 1.2 specification](https://yaml.org/spec/1.2/spec.html#id2804923). If `required` set to be false, the input should have a default value in `action.yml`.
|
||||
|
||||
Outputs can be set with `setOutput` which makes them available to be mapped into inputs of other actions to ensure they are decoupled.
|
||||
|
||||
```js
|
||||
const myInput = core.getInput('inputName', { required: true });
|
||||
const myBooleanInput = core.getBooleanInput('booleanInputName', { required: true });
|
||||
const myMultilineInput = core.getMultilineInput('multilineInputName', { required: true });
|
||||
core.setOutput('outputKey', 'outputVal');
|
||||
```
|
||||
|
||||
#### Exporting variables
|
||||
|
||||
Since each step runs in a separate process, you can use `exportVariable` to add it to this step and future steps environment blocks.
|
||||
|
||||
```js
|
||||
core.exportVariable('envVar', 'Val');
|
||||
```
|
||||
|
||||
#### Setting a secret
|
||||
|
||||
Setting a secret registers the secret with the runner to ensure it is masked in logs.
|
||||
|
||||
```js
|
||||
core.setSecret('myPassword');
|
||||
```
|
||||
|
||||
#### PATH Manipulation
|
||||
|
||||
To make a tool's path available in the path for the remainder of the job (without altering the machine or containers state), use `addPath`. The runner will prepend the path given to the jobs PATH.
|
||||
|
||||
```js
|
||||
core.addPath('/path/to/mytool');
|
||||
```
|
||||
|
||||
#### Exit codes
|
||||
|
||||
You should use this library to set the failing exit code for your action. If status is not set and the script runs to completion, that will lead to a success.
|
||||
|
||||
```js
|
||||
const core = require('@actions/core');
|
||||
|
||||
try {
|
||||
// Do stuff
|
||||
}
|
||||
catch (err) {
|
||||
// setFailed logs the message and sets a failing exit code
|
||||
core.setFailed(`Action failed with error ${err}`);
|
||||
}
|
||||
```
|
||||
|
||||
Note that `setNeutral` is not yet implemented in actions V2 but equivalent functionality is being planned.
|
||||
|
||||
#### Logging
|
||||
|
||||
Finally, this library provides some utilities for logging. Note that debug logging is hidden from the logs by default. This behavior can be toggled by enabling the [Step Debug Logs](../../docs/action-debugging.md#step-debug-logs).
|
||||
|
||||
```js
|
||||
const core = require('@actions/core');
|
||||
|
||||
const myInput = core.getInput('input');
|
||||
try {
|
||||
core.debug('Inside try block');
|
||||
|
||||
if (!myInput) {
|
||||
core.warning('myInput was not set');
|
||||
}
|
||||
|
||||
if (core.isDebug()) {
|
||||
// curl -v https://github.com
|
||||
} else {
|
||||
// curl https://github.com
|
||||
}
|
||||
|
||||
// Do stuff
|
||||
core.info('Output to the actions build log')
|
||||
}
|
||||
catch (err) {
|
||||
core.error(`Error ${err}, action may still succeed though`);
|
||||
}
|
||||
```
|
||||
|
||||
This library can also wrap chunks of output in foldable groups.
|
||||
|
||||
```js
|
||||
const core = require('@actions/core')
|
||||
|
||||
// Manually wrap output
|
||||
core.startGroup('Do some function')
|
||||
doSomeFunction()
|
||||
core.endGroup()
|
||||
|
||||
// Wrap an asynchronous function call
|
||||
const result = await core.group('Do something async', async () => {
|
||||
const response = await doSomeHTTPRequest()
|
||||
return response
|
||||
})
|
||||
```
|
||||
|
||||
#### Styling output
|
||||
|
||||
Colored output is supported in the Action logs via standard [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code). 3/4 bit, 8 bit and 24 bit colors are all supported.
|
||||
|
||||
Foreground colors:
|
||||
|
||||
```js
|
||||
// 3/4 bit
|
||||
core.info('\u001b[35mThis foreground will be magenta')
|
||||
|
||||
// 8 bit
|
||||
core.info('\u001b[38;5;6mThis foreground will be cyan')
|
||||
|
||||
// 24 bit
|
||||
core.info('\u001b[38;2;255;0;0mThis foreground will be bright red')
|
||||
```
|
||||
|
||||
Background colors:
|
||||
|
||||
```js
|
||||
// 3/4 bit
|
||||
core.info('\u001b[43mThis background will be yellow');
|
||||
|
||||
// 8 bit
|
||||
core.info('\u001b[48;5;6mThis background will be cyan')
|
||||
|
||||
// 24 bit
|
||||
core.info('\u001b[48;2;255;0;0mThis background will be bright red')
|
||||
```
|
||||
|
||||
Special styles:
|
||||
|
||||
```js
|
||||
core.info('\u001b[1mBold text')
|
||||
core.info('\u001b[3mItalic text')
|
||||
core.info('\u001b[4mUnderlined text')
|
||||
```
|
||||
|
||||
ANSI escape codes can be combined with one another:
|
||||
|
||||
```js
|
||||
core.info('\u001b[31;46mRed foreground with a cyan background and \u001b[1mbold text at the end');
|
||||
```
|
||||
|
||||
> Note: Escape codes reset at the start of each line
|
||||
|
||||
```js
|
||||
core.info('\u001b[35mThis foreground will be magenta')
|
||||
core.info('This foreground will reset to the default')
|
||||
```
|
||||
|
||||
Manually typing escape codes can be a little difficult, but you can use third party modules such as [ansi-styles](https://github.com/chalk/ansi-styles).
|
||||
|
||||
```js
|
||||
const style = require('ansi-styles');
|
||||
core.info(style.color.ansi16m.hex('#abcdef') + 'Hello world!')
|
||||
```
|
||||
|
||||
#### Action state
|
||||
|
||||
You can use this library to save state and get state for sharing information between a given wrapper action:
|
||||
|
||||
**action.yml**:
|
||||
|
||||
```yaml
|
||||
name: 'Wrapper action sample'
|
||||
inputs:
|
||||
name:
|
||||
default: 'GitHub'
|
||||
runs:
|
||||
using: 'node12'
|
||||
main: 'main.js'
|
||||
post: 'cleanup.js'
|
||||
```
|
||||
|
||||
In action's `main.js`:
|
||||
|
||||
```js
|
||||
const core = require('@actions/core');
|
||||
|
||||
core.saveState("pidToKill", 12345);
|
||||
```
|
||||
|
||||
In action's `cleanup.js`:
|
||||
|
||||
```js
|
||||
const core = require('@actions/core');
|
||||
|
||||
var pid = core.getState("pidToKill");
|
||||
|
||||
process.kill(pid);
|
||||
```
|
|
@ -1,16 +0,0 @@
|
|||
interface CommandProperties {
|
||||
[key: string]: any;
|
||||
}
|
||||
/**
|
||||
* Commands
|
||||
*
|
||||
* Command Format:
|
||||
* ::name key=value,key=value::message
|
||||
*
|
||||
* Examples:
|
||||
* ::warning::This is the message
|
||||
* ::set-env name=MY_VAR::some value
|
||||
*/
|
||||
export declare function issueCommand(command: string, properties: CommandProperties, message: any): void;
|
||||
export declare function issue(name: string, message?: string): void;
|
||||
export {};
|
|
@ -1,92 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.issue = exports.issueCommand = void 0;
|
||||
const os = __importStar(require("os"));
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Commands
|
||||
*
|
||||
* Command Format:
|
||||
* ::name key=value,key=value::message
|
||||
*
|
||||
* Examples:
|
||||
* ::warning::This is the message
|
||||
* ::set-env name=MY_VAR::some value
|
||||
*/
|
||||
function issueCommand(command, properties, message) {
|
||||
const cmd = new Command(command, properties, message);
|
||||
process.stdout.write(cmd.toString() + os.EOL);
|
||||
}
|
||||
exports.issueCommand = issueCommand;
|
||||
function issue(name, message = '') {
|
||||
issueCommand(name, {}, message);
|
||||
}
|
||||
exports.issue = issue;
|
||||
const CMD_STRING = '::';
|
||||
class Command {
|
||||
constructor(command, properties, message) {
|
||||
if (!command) {
|
||||
command = 'missing.command';
|
||||
}
|
||||
this.command = command;
|
||||
this.properties = properties;
|
||||
this.message = message;
|
||||
}
|
||||
toString() {
|
||||
let cmdStr = CMD_STRING + this.command;
|
||||
if (this.properties && Object.keys(this.properties).length > 0) {
|
||||
cmdStr += ' ';
|
||||
let first = true;
|
||||
for (const key in this.properties) {
|
||||
if (this.properties.hasOwnProperty(key)) {
|
||||
const val = this.properties[key];
|
||||
if (val) {
|
||||
if (first) {
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
cmdStr += ',';
|
||||
}
|
||||
cmdStr += `${key}=${escapeProperty(val)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
|
||||
return cmdStr;
|
||||
}
|
||||
}
|
||||
function escapeData(s) {
|
||||
return utils_1.toCommandValue(s)
|
||||
.replace(/%/g, '%25')
|
||||
.replace(/\r/g, '%0D')
|
||||
.replace(/\n/g, '%0A');
|
||||
}
|
||||
function escapeProperty(s) {
|
||||
return utils_1.toCommandValue(s)
|
||||
.replace(/%/g, '%25')
|
||||
.replace(/\r/g, '%0D')
|
||||
.replace(/\n/g, '%0A')
|
||||
.replace(/:/g, '%3A')
|
||||
.replace(/,/g, '%2C');
|
||||
}
|
||||
//# sourceMappingURL=command.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"command.js","sourceRoot":"","sources":["../src/command.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA,uCAAwB;AACxB,mCAAsC;AAWtC;;;;;;;;;GASG;AACH,SAAgB,YAAY,CAC1B,OAAe,EACf,UAA6B,EAC7B,OAAY;IAEZ,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,CAAA;IACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAC/C,CAAC;AAPD,oCAOC;AAED,SAAgB,KAAK,CAAC,IAAY,EAAE,OAAO,GAAG,EAAE;IAC9C,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACjC,CAAC;AAFD,sBAEC;AAED,MAAM,UAAU,GAAG,IAAI,CAAA;AAEvB,MAAM,OAAO;IAKX,YAAY,OAAe,EAAE,UAA6B,EAAE,OAAe;QACzE,IAAI,CAAC,OAAO,EAAE;YACZ,OAAO,GAAG,iBAAiB,CAAA;SAC5B;QAED,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED,QAAQ;QACN,IAAI,MAAM,GAAG,UAAU,GAAG,IAAI,CAAC,OAAO,CAAA;QAEtC,IAAI,IAAI,CAAC,UAAU,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9D,MAAM,IAAI,GAAG,CAAA;YACb,IAAI,KAAK,GAAG,IAAI,CAAA;YAChB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,UAAU,EAAE;gBACjC,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;oBACvC,MAAM,GAAG,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAA;oBAChC,IAAI,GAAG,EAAE;wBACP,IAAI,KAAK,EAAE;4BACT,KAAK,GAAG,KAAK,CAAA;yBACd;6BAAM;4BACL,MAAM,IAAI,GAAG,CAAA;yBACd;wBAED,MAAM,IAAI,GAAG,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,EAAE,CAAA;qBAC1C;iBACF;aACF;SACF;QAED,MAAM,IAAI,GAAG,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAA;QACpD,OAAO,MAAM,CAAA;IACf,CAAC;CACF;AAED,SAAS,UAAU,CAAC,CAAM;IACxB,OAAO,sBAAc,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAA;AAC1B,CAAC;AAED,SAAS,cAAc,CAAC,CAAM;IAC5B,OAAO,sBAAc,CAAC,CAAC,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;SACpB,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AACzB,CAAC"}
|
|
@ -1,146 +0,0 @@
|
|||
/**
|
||||
* Interface for getInput options
|
||||
*/
|
||||
export interface InputOptions {
|
||||
/** Optional. Whether the input is required. If required and not present, will throw. Defaults to false */
|
||||
required?: boolean;
|
||||
/** Optional. Whether leading/trailing whitespace will be trimmed for the input. Defaults to true */
|
||||
trimWhitespace?: boolean;
|
||||
}
|
||||
/**
|
||||
* The code to exit an action
|
||||
*/
|
||||
export declare enum ExitCode {
|
||||
/**
|
||||
* A code indicating that the action was successful
|
||||
*/
|
||||
Success = 0,
|
||||
/**
|
||||
* A code indicating that the action was a failure
|
||||
*/
|
||||
Failure = 1
|
||||
}
|
||||
/**
|
||||
* Sets env variable for this action and future actions in the job
|
||||
* @param name the name of the variable to set
|
||||
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
|
||||
*/
|
||||
export declare function exportVariable(name: string, val: any): void;
|
||||
/**
|
||||
* Registers a secret which will get masked from logs
|
||||
* @param secret value of the secret
|
||||
*/
|
||||
export declare function setSecret(secret: string): void;
|
||||
/**
|
||||
* Prepends inputPath to the PATH (for this action and future actions)
|
||||
* @param inputPath
|
||||
*/
|
||||
export declare function addPath(inputPath: string): void;
|
||||
/**
|
||||
* Gets the value of an input.
|
||||
* Unless trimWhitespace is set to false in InputOptions, the value is also trimmed.
|
||||
* Returns an empty string if the value is not defined.
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns string
|
||||
*/
|
||||
export declare function getInput(name: string, options?: InputOptions): string;
|
||||
/**
|
||||
* Gets the values of an multiline input. Each value is also trimmed.
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns string[]
|
||||
*
|
||||
*/
|
||||
export declare function getMultilineInput(name: string, options?: InputOptions): string[];
|
||||
/**
|
||||
* Gets the input value of the boolean type in the YAML 1.2 "core schema" specification.
|
||||
* Support boolean input list: `true | True | TRUE | false | False | FALSE` .
|
||||
* The return value is also in boolean type.
|
||||
* ref: https://yaml.org/spec/1.2/spec.html#id2804923
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns boolean
|
||||
*/
|
||||
export declare function getBooleanInput(name: string, options?: InputOptions): boolean;
|
||||
/**
|
||||
* Sets the value of an output.
|
||||
*
|
||||
* @param name name of the output to set
|
||||
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
|
||||
*/
|
||||
export declare function setOutput(name: string, value: any): void;
|
||||
/**
|
||||
* Enables or disables the echoing of commands into stdout for the rest of the step.
|
||||
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
|
||||
*
|
||||
*/
|
||||
export declare function setCommandEcho(enabled: boolean): void;
|
||||
/**
|
||||
* Sets the action status to failed.
|
||||
* When the action exits it will be with an exit code of 1
|
||||
* @param message add error issue message
|
||||
*/
|
||||
export declare function setFailed(message: string | Error): void;
|
||||
/**
|
||||
* Gets whether Actions Step Debug is on or not
|
||||
*/
|
||||
export declare function isDebug(): boolean;
|
||||
/**
|
||||
* Writes debug message to user log
|
||||
* @param message debug message
|
||||
*/
|
||||
export declare function debug(message: string): void;
|
||||
/**
|
||||
* Adds an error issue
|
||||
* @param message error issue message. Errors will be converted to string via toString()
|
||||
*/
|
||||
export declare function error(message: string | Error): void;
|
||||
/**
|
||||
* Adds an warning issue
|
||||
* @param message warning issue message. Errors will be converted to string via toString()
|
||||
*/
|
||||
export declare function warning(message: string | Error): void;
|
||||
/**
|
||||
* Writes info to log with console.log.
|
||||
* @param message info message
|
||||
*/
|
||||
export declare function info(message: string): void;
|
||||
/**
|
||||
* Begin an output group.
|
||||
*
|
||||
* Output until the next `groupEnd` will be foldable in this group
|
||||
*
|
||||
* @param name The name of the output group
|
||||
*/
|
||||
export declare function startGroup(name: string): void;
|
||||
/**
|
||||
* End an output group.
|
||||
*/
|
||||
export declare function endGroup(): void;
|
||||
/**
|
||||
* Wrap an asynchronous function call in a group.
|
||||
*
|
||||
* Returns the same type as the function itself.
|
||||
*
|
||||
* @param name The name of the group
|
||||
* @param fn The function to wrap in the group
|
||||
*/
|
||||
export declare function group<T>(name: string, fn: () => Promise<T>): Promise<T>;
|
||||
/**
|
||||
* Saves state for current action, the state can only be retrieved by this action's post job execution.
|
||||
*
|
||||
* @param name name of the state to store
|
||||
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
|
||||
*/
|
||||
export declare function saveState(name: string, value: any): void;
|
||||
/**
|
||||
* Gets the value of an state set by this action's main execution.
|
||||
*
|
||||
* @param name name of the state to get
|
||||
* @returns string
|
||||
*/
|
||||
export declare function getState(name: string): string;
|
|
@ -1,294 +0,0 @@
|
|||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0;
|
||||
const command_1 = require("./command");
|
||||
const file_command_1 = require("./file-command");
|
||||
const utils_1 = require("./utils");
|
||||
const os = __importStar(require("os"));
|
||||
const path = __importStar(require("path"));
|
||||
/**
|
||||
* The code to exit an action
|
||||
*/
|
||||
var ExitCode;
|
||||
(function (ExitCode) {
|
||||
/**
|
||||
* A code indicating that the action was successful
|
||||
*/
|
||||
ExitCode[ExitCode["Success"] = 0] = "Success";
|
||||
/**
|
||||
* A code indicating that the action was a failure
|
||||
*/
|
||||
ExitCode[ExitCode["Failure"] = 1] = "Failure";
|
||||
})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
|
||||
//-----------------------------------------------------------------------
|
||||
// Variables
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Sets env variable for this action and future actions in the job
|
||||
* @param name the name of the variable to set
|
||||
* @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function exportVariable(name, val) {
|
||||
const convertedVal = utils_1.toCommandValue(val);
|
||||
process.env[name] = convertedVal;
|
||||
const filePath = process.env['GITHUB_ENV'] || '';
|
||||
if (filePath) {
|
||||
const delimiter = '_GitHubActionsFileCommandDelimeter_';
|
||||
const commandValue = `${name}<<${delimiter}${os.EOL}${convertedVal}${os.EOL}${delimiter}`;
|
||||
file_command_1.issueCommand('ENV', commandValue);
|
||||
}
|
||||
else {
|
||||
command_1.issueCommand('set-env', { name }, convertedVal);
|
||||
}
|
||||
}
|
||||
exports.exportVariable = exportVariable;
|
||||
/**
|
||||
* Registers a secret which will get masked from logs
|
||||
* @param secret value of the secret
|
||||
*/
|
||||
function setSecret(secret) {
|
||||
command_1.issueCommand('add-mask', {}, secret);
|
||||
}
|
||||
exports.setSecret = setSecret;
|
||||
/**
|
||||
* Prepends inputPath to the PATH (for this action and future actions)
|
||||
* @param inputPath
|
||||
*/
|
||||
function addPath(inputPath) {
|
||||
const filePath = process.env['GITHUB_PATH'] || '';
|
||||
if (filePath) {
|
||||
file_command_1.issueCommand('PATH', inputPath);
|
||||
}
|
||||
else {
|
||||
command_1.issueCommand('add-path', {}, inputPath);
|
||||
}
|
||||
process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
|
||||
}
|
||||
exports.addPath = addPath;
|
||||
/**
|
||||
* Gets the value of an input.
|
||||
* Unless trimWhitespace is set to false in InputOptions, the value is also trimmed.
|
||||
* Returns an empty string if the value is not defined.
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns string
|
||||
*/
|
||||
function getInput(name, options) {
|
||||
const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
|
||||
if (options && options.required && !val) {
|
||||
throw new Error(`Input required and not supplied: ${name}`);
|
||||
}
|
||||
if (options && options.trimWhitespace === false) {
|
||||
return val;
|
||||
}
|
||||
return val.trim();
|
||||
}
|
||||
exports.getInput = getInput;
|
||||
/**
|
||||
* Gets the values of an multiline input. Each value is also trimmed.
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns string[]
|
||||
*
|
||||
*/
|
||||
function getMultilineInput(name, options) {
|
||||
const inputs = getInput(name, options)
|
||||
.split('\n')
|
||||
.filter(x => x !== '');
|
||||
return inputs;
|
||||
}
|
||||
exports.getMultilineInput = getMultilineInput;
|
||||
/**
|
||||
* Gets the input value of the boolean type in the YAML 1.2 "core schema" specification.
|
||||
* Support boolean input list: `true | True | TRUE | false | False | FALSE` .
|
||||
* The return value is also in boolean type.
|
||||
* ref: https://yaml.org/spec/1.2/spec.html#id2804923
|
||||
*
|
||||
* @param name name of the input to get
|
||||
* @param options optional. See InputOptions.
|
||||
* @returns boolean
|
||||
*/
|
||||
function getBooleanInput(name, options) {
|
||||
const trueValue = ['true', 'True', 'TRUE'];
|
||||
const falseValue = ['false', 'False', 'FALSE'];
|
||||
const val = getInput(name, options);
|
||||
if (trueValue.includes(val))
|
||||
return true;
|
||||
if (falseValue.includes(val))
|
||||
return false;
|
||||
throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` +
|
||||
`Support boolean input list: \`true | True | TRUE | false | False | FALSE\``);
|
||||
}
|
||||
exports.getBooleanInput = getBooleanInput;
|
||||
/**
|
||||
* Sets the value of an output.
|
||||
*
|
||||
* @param name name of the output to set
|
||||
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function setOutput(name, value) {
|
||||
process.stdout.write(os.EOL);
|
||||
command_1.issueCommand('set-output', { name }, value);
|
||||
}
|
||||
exports.setOutput = setOutput;
|
||||
/**
|
||||
* Enables or disables the echoing of commands into stdout for the rest of the step.
|
||||
* Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set.
|
||||
*
|
||||
*/
|
||||
function setCommandEcho(enabled) {
|
||||
command_1.issue('echo', enabled ? 'on' : 'off');
|
||||
}
|
||||
exports.setCommandEcho = setCommandEcho;
|
||||
//-----------------------------------------------------------------------
|
||||
// Results
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Sets the action status to failed.
|
||||
* When the action exits it will be with an exit code of 1
|
||||
* @param message add error issue message
|
||||
*/
|
||||
function setFailed(message) {
|
||||
process.exitCode = ExitCode.Failure;
|
||||
error(message);
|
||||
}
|
||||
exports.setFailed = setFailed;
|
||||
//-----------------------------------------------------------------------
|
||||
// Logging Commands
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Gets whether Actions Step Debug is on or not
|
||||
*/
|
||||
function isDebug() {
|
||||
return process.env['RUNNER_DEBUG'] === '1';
|
||||
}
|
||||
exports.isDebug = isDebug;
|
||||
/**
|
||||
* Writes debug message to user log
|
||||
* @param message debug message
|
||||
*/
|
||||
function debug(message) {
|
||||
command_1.issueCommand('debug', {}, message);
|
||||
}
|
||||
exports.debug = debug;
|
||||
/**
|
||||
* Adds an error issue
|
||||
* @param message error issue message. Errors will be converted to string via toString()
|
||||
*/
|
||||
function error(message) {
|
||||
command_1.issue('error', message instanceof Error ? message.toString() : message);
|
||||
}
|
||||
exports.error = error;
|
||||
/**
|
||||
* Adds an warning issue
|
||||
* @param message warning issue message. Errors will be converted to string via toString()
|
||||
*/
|
||||
function warning(message) {
|
||||
command_1.issue('warning', message instanceof Error ? message.toString() : message);
|
||||
}
|
||||
exports.warning = warning;
|
||||
/**
|
||||
* Writes info to log with console.log.
|
||||
* @param message info message
|
||||
*/
|
||||
function info(message) {
|
||||
process.stdout.write(message + os.EOL);
|
||||
}
|
||||
exports.info = info;
|
||||
/**
|
||||
* Begin an output group.
|
||||
*
|
||||
* Output until the next `groupEnd` will be foldable in this group
|
||||
*
|
||||
* @param name The name of the output group
|
||||
*/
|
||||
function startGroup(name) {
|
||||
command_1.issue('group', name);
|
||||
}
|
||||
exports.startGroup = startGroup;
|
||||
/**
|
||||
* End an output group.
|
||||
*/
|
||||
function endGroup() {
|
||||
command_1.issue('endgroup');
|
||||
}
|
||||
exports.endGroup = endGroup;
|
||||
/**
|
||||
* Wrap an asynchronous function call in a group.
|
||||
*
|
||||
* Returns the same type as the function itself.
|
||||
*
|
||||
* @param name The name of the group
|
||||
* @param fn The function to wrap in the group
|
||||
*/
|
||||
function group(name, fn) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
startGroup(name);
|
||||
let result;
|
||||
try {
|
||||
result = yield fn();
|
||||
}
|
||||
finally {
|
||||
endGroup();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
exports.group = group;
|
||||
//-----------------------------------------------------------------------
|
||||
// Wrapper action state
|
||||
//-----------------------------------------------------------------------
|
||||
/**
|
||||
* Saves state for current action, the state can only be retrieved by this action's post job execution.
|
||||
*
|
||||
* @param name name of the state to store
|
||||
* @param value value to store. Non-string values will be converted to a string via JSON.stringify
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
function saveState(name, value) {
|
||||
command_1.issueCommand('save-state', { name }, value);
|
||||
}
|
||||
exports.saveState = saveState;
|
||||
/**
|
||||
* Gets the value of an state set by this action's main execution.
|
||||
*
|
||||
* @param name name of the state to get
|
||||
* @returns string
|
||||
*/
|
||||
function getState(name) {
|
||||
return process.env[`STATE_${name}`] || '';
|
||||
}
|
||||
exports.getState = getState;
|
||||
//# sourceMappingURL=core.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"core.js","sourceRoot":"","sources":["../src/core.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uCAA6C;AAC7C,iDAA+D;AAC/D,mCAAsC;AAEtC,uCAAwB;AACxB,2CAA4B;AAa5B;;GAEG;AACH,IAAY,QAUX;AAVD,WAAY,QAAQ;IAClB;;OAEG;IACH,6CAAW,CAAA;IAEX;;OAEG;IACH,6CAAW,CAAA;AACb,CAAC,EAVW,QAAQ,GAAR,gBAAQ,KAAR,gBAAQ,QAUnB;AAED,yEAAyE;AACzE,YAAY;AACZ,yEAAyE;AAEzE;;;;GAIG;AACH,8DAA8D;AAC9D,SAAgB,cAAc,CAAC,IAAY,EAAE,GAAQ;IACnD,MAAM,YAAY,GAAG,sBAAc,CAAC,GAAG,CAAC,CAAA;IACxC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,YAAY,CAAA;IAEhC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;IAChD,IAAI,QAAQ,EAAE;QACZ,MAAM,SAAS,GAAG,qCAAqC,CAAA;QACvD,MAAM,YAAY,GAAG,GAAG,IAAI,KAAK,SAAS,GAAG,EAAE,CAAC,GAAG,GAAG,YAAY,GAAG,EAAE,CAAC,GAAG,GAAG,SAAS,EAAE,CAAA;QACzF,2BAAgB,CAAC,KAAK,EAAE,YAAY,CAAC,CAAA;KACtC;SAAM;QACL,sBAAY,CAAC,SAAS,EAAE,EAAC,IAAI,EAAC,EAAE,YAAY,CAAC,CAAA;KAC9C;AACH,CAAC;AAZD,wCAYC;AAED;;;GAGG;AACH,SAAgB,SAAS,CAAC,MAAc;IACtC,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,CAAA;AACtC,CAAC;AAFD,8BAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,SAAiB;IACvC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,EAAE,CAAA;IACjD,IAAI,QAAQ,EAAE;QACZ,2BAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;KACpC;SAAM;QACL,sBAAY,CAAC,UAAU,EAAE,EAAE,EAAE,SAAS,CAAC,CAAA;KACxC;IACD,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAA;AAC7E,CAAC;AARD,0BAQC;AAED;;;;;;;;GAQG;AACH,SAAgB,QAAQ,CAAC,IAAY,EAAE,OAAsB;IAC3D,MAAM,GAAG,GACP,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;IACrE,IAAI,OAAO,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,EAAE,CAAC,CAAA;KAC5D;IAED,IAAI,OAAO,IAAI,OAAO,CAAC,cAAc,KAAK,KAAK,EAAE;QAC/C,OAAO,GAAG,CAAA;KACX;IAED,OAAO,GAAG,CAAC,IAAI,EAAE,CAAA;AACnB,CAAC;AAZD,4BAYC;AAED;;;;;;;GAOG;AACH,SAAgB,iBAAiB,CAC/B,IAAY,EACZ,OAAsB;IAEtB,MAAM,MAAM,GAAa,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;SAC7C,KAAK,CAAC,IAAI,CAAC;SACX,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,CAAA;IAExB,OAAO,MAAM,CAAA;AACf,CAAC;AATD,8CASC;AAED;;;;;;;;;GASG;AACH,SAAgB,eAAe,CAAC,IAAY,EAAE,OAAsB;IAClE,MAAM,SAAS,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;IAC1C,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IAC9C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnC,IAAI,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IACxC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAA;IAC1C,MAAM,IAAI,SAAS,CACjB,6DAA6D,IAAI,IAAI;QACnE,4EAA4E,CAC/E,CAAA;AACH,CAAC;AAVD,0CAUC;AAED;;;;;GAKG;AACH,8DAA8D;AAC9D,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAU;IAChD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAA;IAC5B,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAHD,8BAGC;AAED;;;;GAIG;AACH,SAAgB,cAAc,CAAC,OAAgB;IAC7C,eAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;AACvC,CAAC;AAFD,wCAEC;AAED,yEAAyE;AACzE,UAAU;AACV,yEAAyE;AAEzE;;;;GAIG;AACH,SAAgB,SAAS,CAAC,OAAuB;IAC/C,OAAO,CAAC,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAA;IAEnC,KAAK,CAAC,OAAO,CAAC,CAAA;AAChB,CAAC;AAJD,8BAIC;AAED,yEAAyE;AACzE,mBAAmB;AACnB,yEAAyE;AAEzE;;GAEG;AACH,SAAgB,OAAO;IACrB,OAAO,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,GAAG,CAAA;AAC5C,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,sBAAY,CAAC,OAAO,EAAE,EAAE,EAAE,OAAO,CAAC,CAAA;AACpC,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,KAAK,CAAC,OAAuB;IAC3C,eAAK,CAAC,OAAO,EAAE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AACzE,CAAC;AAFD,sBAEC;AAED;;;GAGG;AACH,SAAgB,OAAO,CAAC,OAAuB;IAC7C,eAAK,CAAC,SAAS,EAAE,OAAO,YAAY,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAA;AAC3E,CAAC;AAFD,0BAEC;AAED;;;GAGG;AACH,SAAgB,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACxC,CAAC;AAFD,oBAEC;AAED;;;;;;GAMG;AACH,SAAgB,UAAU,CAAC,IAAY;IACrC,eAAK,CAAC,OAAO,EAAE,IAAI,CAAC,CAAA;AACtB,CAAC;AAFD,gCAEC;AAED;;GAEG;AACH,SAAgB,QAAQ;IACtB,eAAK,CAAC,UAAU,CAAC,CAAA;AACnB,CAAC;AAFD,4BAEC;AAED;;;;;;;GAOG;AACH,SAAsB,KAAK,CAAI,IAAY,EAAE,EAAoB;;QAC/D,UAAU,CAAC,IAAI,CAAC,CAAA;QAEhB,IAAI,MAAS,CAAA;QAEb,IAAI;YACF,MAAM,GAAG,MAAM,EAAE,EAAE,CAAA;SACpB;gBAAS;YACR,QAAQ,EAAE,CAAA;SACX;QAED,OAAO,MAAM,CAAA;IACf,CAAC;CAAA;AAZD,sBAYC;AAED,yEAAyE;AACzE,uBAAuB;AACvB,yEAAyE;AAEzE;;;;;GAKG;AACH,8DAA8D;AAC9D,SAAgB,SAAS,CAAC,IAAY,EAAE,KAAU;IAChD,sBAAY,CAAC,YAAY,EAAE,EAAC,IAAI,EAAC,EAAE,KAAK,CAAC,CAAA;AAC3C,CAAC;AAFD,8BAEC;AAED;;;;;GAKG;AACH,SAAgB,QAAQ,CAAC,IAAY;IACnC,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;AAC3C,CAAC;AAFD,4BAEC"}
|
|
@ -1 +0,0 @@
|
|||
export declare function issueCommand(command: string, message: any): void;
|
|
@ -1,42 +0,0 @@
|
|||
"use strict";
|
||||
// For internal use, subject to change.
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.issueCommand = void 0;
|
||||
// We use any as a valid input type
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
const fs = __importStar(require("fs"));
|
||||
const os = __importStar(require("os"));
|
||||
const utils_1 = require("./utils");
|
||||
function issueCommand(command, message) {
|
||||
const filePath = process.env[`GITHUB_${command}`];
|
||||
if (!filePath) {
|
||||
throw new Error(`Unable to find environment variable for file command ${command}`);
|
||||
}
|
||||
if (!fs.existsSync(filePath)) {
|
||||
throw new Error(`Missing file at path: ${filePath}`);
|
||||
}
|
||||
fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
exports.issueCommand = issueCommand;
|
||||
//# sourceMappingURL=file-command.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"file-command.js","sourceRoot":"","sources":["../src/file-command.ts"],"names":[],"mappings":";AAAA,uCAAuC;;;;;;;;;;;;;;;;;;;;;;AAEvC,mCAAmC;AACnC,uDAAuD;AAEvD,uCAAwB;AACxB,uCAAwB;AACxB,mCAAsC;AAEtC,SAAgB,YAAY,CAAC,OAAe,EAAE,OAAY;IACxD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,OAAO,EAAE,CAAC,CAAA;IACjD,IAAI,CAAC,QAAQ,EAAE;QACb,MAAM,IAAI,KAAK,CACb,wDAAwD,OAAO,EAAE,CAClE,CAAA;KACF;IACD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE;QAC5B,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAA;KACrD;IAED,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,GAAG,sBAAc,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE;QACjE,QAAQ,EAAE,MAAM;KACjB,CAAC,CAAA;AACJ,CAAC;AAdD,oCAcC"}
|
|
@ -1,5 +0,0 @@
|
|||
/**
|
||||
* Sanitizes an input into a string so it can be passed into issueCommand safely
|
||||
* @param input input to sanitize into a string
|
||||
*/
|
||||
export declare function toCommandValue(input: any): string;
|
|
@ -1,20 +0,0 @@
|
|||
"use strict";
|
||||
// We use any as a valid input type
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.toCommandValue = void 0;
|
||||
/**
|
||||
* Sanitizes an input into a string so it can be passed into issueCommand safely
|
||||
* @param input input to sanitize into a string
|
||||
*/
|
||||
function toCommandValue(input) {
|
||||
if (input === null || input === undefined) {
|
||||
return '';
|
||||
}
|
||||
else if (typeof input === 'string' || input instanceof String) {
|
||||
return input;
|
||||
}
|
||||
return JSON.stringify(input);
|
||||
}
|
||||
exports.toCommandValue = toCommandValue;
|
||||
//# sourceMappingURL=utils.js.map
|
|
@ -1 +0,0 @@
|
|||
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":";AAAA,mCAAmC;AACnC,uDAAuD;;;AAEvD;;;GAGG;AACH,SAAgB,cAAc,CAAC,KAAU;IACvC,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,EAAE;QACzC,OAAO,EAAE,CAAA;KACV;SAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,YAAY,MAAM,EAAE;QAC/D,OAAO,KAAe,CAAA;KACvB;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC;AAPD,wCAOC"}
|
|
@ -1,41 +0,0 @@
|
|||
{
|
||||
"name": "@actions/core",
|
||||
"version": "1.4.0",
|
||||
"description": "Actions core lib",
|
||||
"keywords": [
|
||||
"github",
|
||||
"actions",
|
||||
"core"
|
||||
],
|
||||
"homepage": "https://github.com/actions/toolkit/tree/main/packages/core",
|
||||
"license": "MIT",
|
||||
"main": "lib/core.js",
|
||||
"types": "lib/core.d.ts",
|
||||
"directories": {
|
||||
"lib": "lib",
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"!.DS_Store"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/actions/toolkit.git",
|
||||
"directory": "packages/core"
|
||||
},
|
||||
"scripts": {
|
||||
"audit-moderate": "npm install && npm audit --json --audit-level=moderate > audit.json",
|
||||
"test": "echo \"Error: run tests from root\" && exit 1",
|
||||
"tsc": "tsc"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/actions/toolkit/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.0.2"
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
13
package.json
13
package.json
|
@ -1,5 +1,16 @@
|
|||
{
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.4.0"
|
||||
"@actions/core": "^1.4.0",
|
||||
"@actions/exec": "^1.1.0",
|
||||
"@actions/io": "^1.1.1",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"mocha": "^9.0.3",
|
||||
"rewire": "^5.0.0",
|
||||
"testdouble": "^3.16.1",
|
||||
"tmp": "^0.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,379 @@
|
|||
"use strict";
|
||||
|
||||
const assert = require("assert");
|
||||
const chai = require("chai");
|
||||
const chaiAsPromised = require("chai-as-promised");
|
||||
const path = require("path");
|
||||
const rewire = require("rewire");
|
||||
const td = require("testdouble");
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const expect = chai.expect;
|
||||
chai.should();
|
||||
|
||||
const cmakeExePath = path.normalize("C:\\path\\to\\cmake.exe");
|
||||
const cmakeBuildDir = path.normalize("path\\to\\cmake\\build");
|
||||
const cmakeSrcDir = path.normalize("path\\to\\project\\src");
|
||||
|
||||
const cmakeApiDir = path.join(cmakeBuildDir, path.normalize(".cmake\\api\\v1"));
|
||||
const cmakeQueryDir = path.join(cmakeApiDir, "query");
|
||||
const cmakeReplyDir = path.join(cmakeApiDir, "reply");
|
||||
|
||||
const clPath = "C:/VS/root/Tools/MSVC/14.29.30133/bin/Hostx86/x86/cl.exe";
|
||||
const clInclude = "C:/VS/root/Tools/MSVC/14.29.30133/include";
|
||||
|
||||
const PATHEnv = [
|
||||
path.dirname(clPath),
|
||||
path.dirname(cmakeExePath)
|
||||
].join(";");
|
||||
|
||||
const cmakeIndexReply = "index-1.json";
|
||||
const cmakeCacheReply = "cache-1.json";
|
||||
const cmakeCodemodelReply = "codemodel-1.json";
|
||||
const cmakeToolchainsReply = "toolchains-1.json";
|
||||
const cmakeTarget1Reply = "target-1.json";
|
||||
const cmakeTarget2Reply = "target-2.json";
|
||||
|
||||
let defaultFileContents = {};
|
||||
defaultFileContents[cmakeIndexReply] = {
|
||||
"cmake": {
|
||||
"generator": {
|
||||
"multiConfig": true,
|
||||
"name": "Visual Studio 17 2022"
|
||||
},
|
||||
"paths": {
|
||||
"cmake": cmakeExePath
|
||||
},
|
||||
"version": {
|
||||
"string": "3.21.6"
|
||||
},
|
||||
},
|
||||
"reply" : {
|
||||
"client-fake": {
|
||||
"invalid": "data"
|
||||
},
|
||||
"client-msvc-ca-action" : {
|
||||
"query.json" : {
|
||||
"responses": [
|
||||
{ "kind" : "cache", "jsonFile" : cmakeCacheReply },
|
||||
{ "kind" : "codemodel", "jsonFile" : cmakeCodemodelReply },
|
||||
{ "kind" : "toolchains", "jsonFile" : cmakeToolchainsReply }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defaultFileContents[cmakeCodemodelReply] = {
|
||||
"kind": "codemodel",
|
||||
"paths": {
|
||||
"build": cmakeBuildDir,
|
||||
"source": cmakeSrcDir
|
||||
},
|
||||
"configurations" : [
|
||||
{
|
||||
"name": "Regular",
|
||||
"directories": [
|
||||
{ "source": "." },
|
||||
{ "source": "test" }
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"directoryIndex": 0,
|
||||
"jsonFile": cmakeTarget1Reply
|
||||
},
|
||||
{
|
||||
"directoryIndex": 0,
|
||||
"jsonFile": cmakeTarget2Reply
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "OnlyTarget2",
|
||||
"directories": [
|
||||
{ "source": "." }
|
||||
],
|
||||
"targets": [
|
||||
{
|
||||
"directoryIndex": 0,
|
||||
"jsonFile": cmakeTarget2Reply
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const CLangIndex = 0;
|
||||
const CXXLangIndex = 1;
|
||||
defaultFileContents[cmakeCacheReply] = {
|
||||
"kind": "cache",
|
||||
"entries": [
|
||||
{ // CLangIndex
|
||||
"name": "CMAKE_C_COMPILER",
|
||||
"value": clPath
|
||||
},
|
||||
{ // CXXLangIndex
|
||||
"name": "CMAKE_CXX_COMPILER",
|
||||
"value": clPath
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
defaultFileContents[cmakeToolchainsReply] = {
|
||||
"kind": "toolchains",
|
||||
"toolchains": [
|
||||
{ // CLangIndex
|
||||
"language": "C",
|
||||
"compiler" : {
|
||||
"path": clPath,
|
||||
"id": "MSVC",
|
||||
"version": "14.29.30133",
|
||||
"implicit": {
|
||||
"includeDirectories": [
|
||||
clInclude
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
{ // CXXLangIndex
|
||||
"language": "CXX",
|
||||
"compiler" : {
|
||||
"path": clPath,
|
||||
"id": "MSVC",
|
||||
"version": "14.29.30133",
|
||||
"implicit": {
|
||||
"includeDirectories": [
|
||||
clInclude
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
]
|
||||
};
|
||||
|
||||
const sharedArgs = "/a /b /c";
|
||||
const uniqueArgs = "/e /f";
|
||||
const totalCompileCommands = 4;
|
||||
defaultFileContents[cmakeTarget1Reply] = {
|
||||
"compileGroups": [
|
||||
{
|
||||
"compileCommandFragments": [
|
||||
{ "fragment": sharedArgs },
|
||||
{ "fragment": uniqueArgs }
|
||||
],
|
||||
"includes": [
|
||||
{ "path": "regular/include"},
|
||||
{ "path": "external/include", "isSystem": true }
|
||||
],
|
||||
"language": "CXX",
|
||||
"sourceIndexes": [
|
||||
0,
|
||||
2
|
||||
]
|
||||
},
|
||||
{
|
||||
"compileCommandFragments": [
|
||||
{ "fragment": sharedArgs }
|
||||
],
|
||||
"includes": [
|
||||
{ "path": "regular/include", "isSystem": false },
|
||||
{ "path": "external/include", "isSystem": true }
|
||||
],
|
||||
"language": "C",
|
||||
"sourceIndexes": [
|
||||
1
|
||||
]
|
||||
},
|
||||
],
|
||||
"sources": [
|
||||
{ "path" : "src/file1.cpp"},
|
||||
{ "path" : "src/file2.c"},
|
||||
{ "path" : "src/file3.cxx"},
|
||||
]
|
||||
};
|
||||
|
||||
defaultFileContents[cmakeTarget2Reply] = {
|
||||
"compileGroups": [
|
||||
{
|
||||
"compileCommandFragments": [
|
||||
{ "fragment": sharedArgs }
|
||||
],
|
||||
"includes": [
|
||||
{ "path": "regular/include" },
|
||||
{ "path": "external/include", "isSystem": true }
|
||||
],
|
||||
"defines": [
|
||||
{ "define": "a=b"},
|
||||
{ "define": "c=d"},
|
||||
],
|
||||
"language": "CXX",
|
||||
"sourceIndexes": [
|
||||
0
|
||||
]
|
||||
},
|
||||
],
|
||||
"sources": [
|
||||
{ "path" : "src/file4.cpp"},
|
||||
]
|
||||
};
|
||||
|
||||
describe("CMakeApi", () => {
|
||||
let action;
|
||||
let exec;
|
||||
let fs;
|
||||
let io;
|
||||
|
||||
let getApiReplyIndex;
|
||||
let loadCMakeApiReplies;
|
||||
let loadToolchainMap;
|
||||
let loadCompileCommands;
|
||||
|
||||
function setReplyContents(filename) {
|
||||
const filepath = path.join(cmakeReplyDir, filename);
|
||||
td.when(fs.existsSync(filepath)).thenReturn(true);
|
||||
td.when(fs.readFileSync(filepath, td.matchers.anything())).thenReturn(
|
||||
JSON.stringify(defaultFileContents[filename]));
|
||||
}
|
||||
|
||||
function editReplyContents(filename, editCallback) {
|
||||
const filepath = path.join(cmakeReplyDir, filename);
|
||||
let contents = JSON.parse(JSON.stringify(defaultFileContents[filename]));
|
||||
editCallback(contents);
|
||||
td.when(fs.readFileSync(filepath, td.matchers.anything())).thenReturn(JSON.stringify(contents));
|
||||
}
|
||||
|
||||
function validateCompileCommands(compileCommands) {
|
||||
for (const command of compileCommands) {
|
||||
command.args.should.contain(sharedArgs);
|
||||
command.includes.length.should.equal(2);
|
||||
switch (path.basename(command.source)) {
|
||||
case "file1.cpp":
|
||||
case "file3.cxx":
|
||||
command.args.should.contain(uniqueArgs);
|
||||
break;
|
||||
case "file2.c":
|
||||
break;
|
||||
case "file4.cpp":
|
||||
command.defines.should.contain("a=b");
|
||||
command.defines.should.contain("c=d");
|
||||
break;
|
||||
default:
|
||||
assert.fail("Unknown source file: " + compileCommand.source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
// modules
|
||||
exec = td.replace('@actions/exec');
|
||||
fs = td.replace("fs");
|
||||
io = td.replace('@actions/io');
|
||||
action = rewire("../index.js");
|
||||
|
||||
getApiReplyIndex = action.__get__("getApiReplyIndex");
|
||||
loadCMakeApiReplies = action.__get__("loadCMakeApiReplies");
|
||||
loadToolchainMap = action.__get__("loadToolchainMap");
|
||||
loadCompileCommands = action.__get__("loadCompileCommands");
|
||||
|
||||
// default cmake folders
|
||||
td.when(fs.existsSync(cmakeBuildDir)).thenReturn(true);
|
||||
td.when(fs.existsSync(cmakeSrcDir)).thenReturn(true);
|
||||
td.when(fs.existsSync(cmakeApiDir)).thenReturn(true);
|
||||
td.when(fs.existsSync(cmakeQueryDir)).thenReturn(true);
|
||||
td.when(fs.existsSync(cmakeReplyDir)).thenReturn(true);
|
||||
// cmakeBuildDir must be non-empty
|
||||
td.when(fs.readdirSync(cmakeBuildDir)).thenReturn([".cmake"]);
|
||||
|
||||
// cmake discoverable and successfully executable
|
||||
td.when(io.which("cmake", true)).thenResolve(cmakeExePath);
|
||||
td.when(exec.exec("cmake", [cmakeBuildDir])).thenResolve(0);
|
||||
|
||||
// default MSVC toolset
|
||||
td.when(fs.existsSync(clPath)).thenReturn(true);
|
||||
td.when(fs.existsSync(clInclude)).thenReturn(true);
|
||||
|
||||
// default reply files
|
||||
td.when(fs.readdirSync(cmakeReplyDir)).thenReturn(Object.keys(defaultFileContents));
|
||||
for (const filename of Object.keys(defaultFileContents)) {
|
||||
setReplyContents(filename);
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
td.reset();
|
||||
});
|
||||
|
||||
// Common tests.
|
||||
it("loadCompileCommands", async () => {
|
||||
const replyIndexInfo = getApiReplyIndex(cmakeApiDir);
|
||||
const compileCommands = loadCompileCommands(replyIndexInfo, []);
|
||||
validateCompileCommands(compileCommands);
|
||||
compileCommands.length.should.equal(totalCompileCommands);
|
||||
});
|
||||
|
||||
it("filterAllCommands", async () => {
|
||||
const replyIndexInfo = getApiReplyIndex(cmakeApiDir);
|
||||
const compileCommands = loadCompileCommands(replyIndexInfo, [cmakeSrcDir]);
|
||||
validateCompileCommands(compileCommands);
|
||||
compileCommands.length.should.equal(0);
|
||||
});
|
||||
|
||||
it("loadToolchainMap", async () => {
|
||||
const replyIndexInfo = getApiReplyIndex(cmakeApiDir);
|
||||
const toolchainMap = loadToolchainMap(replyIndexInfo);
|
||||
toolchainMap.should.have.keys(["C", "CXX"]);
|
||||
});
|
||||
|
||||
// only testing user errors, assume format of query/reply files is valid
|
||||
describe("errors", () => {
|
||||
it("empty buildRoot", async () => {
|
||||
await expect(loadCMakeApiReplies("")).to.be.rejectedWith(
|
||||
"CMake build root must exist, be non-empty and be configured with CMake");
|
||||
});
|
||||
|
||||
it("buildRoot does not exist", async () => {
|
||||
td.when(fs.existsSync(cmakeBuildDir)).thenReturn(false);
|
||||
await expect(loadCMakeApiReplies(cmakeBuildDir)).to.be.rejectedWith(
|
||||
"CMake build root must exist, be non-empty and be configured with CMake");
|
||||
});
|
||||
|
||||
it("cmake not on path", async () => {
|
||||
td.when(io.which("cmake", true)).thenReject(new Error("cmake missing"));
|
||||
await expect(loadCMakeApiReplies(cmakeBuildDir)).to.be.rejectedWith("cmake missing");
|
||||
});
|
||||
|
||||
it("cmake.exe failed to run", async () => {
|
||||
td.when(exec.exec("cmake", td.matchers.anything())).thenReject(new Error());
|
||||
await expect(loadCMakeApiReplies(cmakeBuildDir)).to.be.rejectedWith(
|
||||
"CMake failed to reconfigure project with error:");
|
||||
});
|
||||
|
||||
it("cmake not run (missing .cmake/api dir)", async () => {
|
||||
td.when(fs.existsSync(cmakeReplyDir)).thenReturn(false);
|
||||
await expect(loadCMakeApiReplies(cmakeBuildDir)).to.be.rejectedWith(
|
||||
"Failed to find CMake API index reply file.");
|
||||
});
|
||||
|
||||
it("cmake version < 3.20.5", async () => {
|
||||
editReplyContents(cmakeIndexReply, (reply) => {
|
||||
reply.cmake.version.string = "3.20.4";
|
||||
});
|
||||
await expect(loadCMakeApiReplies(cmakeBuildDir)).to.be.rejectedWith(
|
||||
"Action requires CMake version >= 3.20.5");
|
||||
});
|
||||
|
||||
it("msvc for neither C/C++", async () => {
|
||||
editReplyContents(cmakeToolchainsReply, (reply) => {
|
||||
reply.toolchains[CLangIndex].compiler.path = "clang.exe";
|
||||
reply.toolchains[CLangIndex].compiler.id = "Clang";
|
||||
reply.toolchains[CXXLangIndex].compiler.path = "clang.exe";
|
||||
reply.toolchains[CXXLangIndex].compiler.id = "Clang";
|
||||
});
|
||||
|
||||
const replyIndexInfo = getApiReplyIndex(cmakeApiDir);
|
||||
expect(() => loadToolchainMap(replyIndexInfo)).to.throw(
|
||||
"Action requires use of MSVC for either/both C or C++.");
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,27 @@
|
|||
cmake_minimum_required (VERSION 3.20.5)
|
||||
project (Sample)
|
||||
|
||||
add_library (${PROJECT_NAME}
|
||||
src/fileA.cpp
|
||||
src/fileb.cpp
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT_NAME}
|
||||
PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
||||
|
||||
target_include_directories (${PROJECT_NAME}
|
||||
SYSTEM PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/system
|
||||
)
|
||||
|
||||
target_include_directories (${PROJECT_NAME}
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include/regular
|
||||
)
|
||||
|
||||
|
||||
add_subdirectory(failure)
|
|
@ -0,0 +1,12 @@
|
|||
set(TARGET_NAME Failure)
|
||||
|
||||
add_library (${TARGET_NAME}
|
||||
fail.cpp
|
||||
)
|
||||
|
||||
set_target_properties(${TARGET_NAME}
|
||||
PROPERTIES
|
||||
CXX_STANDARD 20
|
||||
CXX_STANDARD_REQUIRED ON
|
||||
CXX_EXTENSIONS OFF
|
||||
)
|
|
@ -0,0 +1,4 @@
|
|||
// This file is design to purposfully fail compilation because it should be ignored by the
|
||||
// 'ignoredTargetPaths' action parameter
|
||||
|
||||
invalid C++
|
|
@ -0,0 +1,8 @@
|
|||
#include <string>
|
||||
|
||||
// Duplicate warnings will be avoided in headers included > 1 times
|
||||
const char *regularHeaderFunction()
|
||||
{
|
||||
std::string s;
|
||||
return s.c_str(); // C26816
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#include <string>
|
||||
|
||||
// Marked as System header in CMake
|
||||
// No warning should be issued if ignoreSystemHeaders is used
|
||||
const char *systemHeaderFunction()
|
||||
{
|
||||
std::string s;
|
||||
return s.c_str(); // C26816
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#include <system.h>
|
||||
#include <regular.h>
|
||||
#include <optional>
|
||||
|
||||
std::optional<int> getTempOptional() noexcept { return {}; }
|
||||
|
||||
void C26815() noexcept
|
||||
{
|
||||
if (const auto val = *getTempOptional()) // C26815
|
||||
{
|
||||
(void)val;
|
||||
}
|
||||
}
|
||||
|
||||
int main() noexcept {
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Include the same header as fileA to ensure duplicate warnings are not produced
|
||||
#include <regular.h>
|
||||
|
||||
constexpr void C6001()
|
||||
{
|
||||
int x[4];
|
||||
x[4] = 1;
|
||||
}
|
||||
|
||||
int main() noexcept {
|
||||
return 0;
|
||||
}
|
Загрузка…
Ссылка в новой задаче