diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f76e62e..b4b04f6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -8,8 +8,10 @@ on: - 'releases/*' jobs: - ubuntu_2004_latestllvm: - name: "Ubuntu Linux 20.04 (latest LLVM)" + # Fetch tests do not produce results, but test the functionality of the + # "grab LLVM" and "grab and build CodeChecker" features. + fetch_ubuntu_2004_latestllvm: + name: "Fetch test: Ubuntu Linux 20.04 (latest LLVM)" runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 @@ -17,8 +19,9 @@ jobs: with: version: "master" llvm-version: "latest" - ubuntu_1804_12llvm: - name: "Ubuntu Linux 18.04 (LLVM 12.y)" + logfile: "test/empty/compile_commands.json" + fetch_ubuntu_1804_12llvm: + name: "Fetch test: Ubuntu Linux 18.04 (LLVM 12.y)" runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 @@ -26,3 +29,25 @@ jobs: with: version: "master" llvm-version: 12 + logfile: "test/empty/compile_commands.json" + + # Test simple build and analysis configuration. + simple_analysis_test: + strategy: + fail-fast: false + matrix: + logfile: ['', 'test/simple/compile_commands.json'] + build-command: ['', 'cd test/simple; g++ -c main.cpp -o main.o'] + + name: "Simple analysis: (${{ matrix.logfile && 'logfile' || 'no-logfile' }} ${{ matrix.build-command && 'build-command' || 'no-build-command' }})" + runs-on: ubuntu-20.04 + + # Allow continuing the build, this step is "expected failure". + continue-on-error: ${{ (matrix.logfile != '') == (matrix.build-command != '') }} + + steps: + - uses: actions/checkout@v2 + - uses: ./ + with: + logfile: ${{ matrix.logfile }} + build-command: ${{ matrix.build-command }} diff --git a/README.md b/README.md index 4cc735b..798a32c 100644 --- a/README.md +++ b/README.md @@ -12,9 +12,79 @@ This single action composite script encompasses the following steps: 1. Obtain a package of the LLVM Clang suite's analysers, and CodeChecker. +â„šī¸ **Note:** Static analysis can be a time-consuming process. +It's recommended that the static analysis step is not sequential with the rest of a CI execution, but either runs as its own job in a workflow, or a completely distinct workflow altogether. + +Please ensure that your project is completely configured for a build before executing this action. + +â„šī¸ **Note:** Static analysers can rely on additional information that is optimised out in a true release build. +Hence, it's recommended to configure your project in a **`Debug`** configuration. + +Add the job into your CI as follows. +The two versions are mutually exclusive — you either can give a compilation database, or you instruct CodeChecker to create one. + +### Project can generate a [JSON Compilation Database](http://clang.llvm.org/docs/JSONCompilationDatabase.html) and build cleanly (no generated code) + +Some projects are trivial enough in their build configuration that no additional steps need to be taken after executing `configure.sh`, `cmake`, or similar tools. +If you are able to generate a *compilation database* from your build system **without** running the build itself, you can save some time, and go to the analysis immediately. + +You can specify the generated compilation database in the `logfile` variable + +~~~{.yml} +runs: + steps: + # Check YOUR project out! + - name: "Check out repository" + uses: actions/checkout@v2 + + # Prepare a build + - name: "Prepare build" + run: | + mkdir -pv Build + cd Build + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON + + # Run the analysis + - uses: whisperity/codechecker-analysis-action + with: + logfile: ${{ github.workspace }}/Build/compile_commands.json +~~~ + +### Projects that need to self-creating a *JSON Compilation Database* or require generated code + +Other kinds of projects might rely heavily on *generated code*. +When looking at the source code of these projects **without** a build having been executed beforehand, they do not compile — as such, analysis cannot be executed either. + +In this case, you will need to instruct CodeChecker to log a build (and spend time doing the build) just before analysis. + +You can specify the build to execute in the `build-command` variable. + +~~~{.yml} +runs: + steps: + # Check YOUR project out! + - name: "Check out repository" + uses: actions/checkout@v2 + + # Prepare a build + - name: "Prepare build" + run: | + mkdir -pv Build + cd Build + cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=OFF + + # Run the analysis + - uses: whisperity/codechecker-analysis-action + with: + build-command: "cd ${{ github.workspace }}/Build; cmake --build ." +~~~ + ## Action configuration +| Variable | Default | Description | +|----------|-------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `config` | `$(project-root)/.codechecker.json` | The configuration file containing flags to be appended to the analysis commands. It is recommended that most of the analysis configuration is versioned with the project. 🔖 Read more about the [`codechecker.json`](http://codechecker.readthedocs.io/en/latest/analyzer/user_guide/#configuration-file) configuration file in the official documentation. | ### Versions to install @@ -23,3 +93,14 @@ This single action composite script encompasses the following steps: | `repository` | [`Ericsson/CodeChecker`](http://github.com/Ericsson/CodeChecker) | The CodeChecker repository to check out and build | | `version` | `master` | The branch, tag, or commit SHA in the `repository` to use. | | `llvm-version` | `latest` | The major version of LLVM to install and use. LLVM is installed from [PPA](http://apt.llvm.org/). If `latest`, automatically gather the latest version. If `ignore`, don't install anything. (Not recommended) | + +### Build log configuration + +🔖 Read more about [`CodeChecker log`](http://codechecker.readthedocs.io/en/latest/analyzer/user_guide/#log) in the official documentation. + +| Variable | Default | Description | +|-----------------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `logfile` | | The location of the JSON Compilation Database which describes how the project is built. This flag is used if the build system can pre-generate the file for us. | +| `build-command` | | The build command to execute. CodeChecker is capable of executing and logging the build for itself. This flag is used if the build-system can not generate the information by itself, or the project relies on other generated code. | + + diff --git a/action.yml b/action.yml index b631379..6c76938 100644 --- a/action.yml +++ b/action.yml @@ -19,6 +19,17 @@ inputs: required: true default: 'latest' + config: + description: 'The CodeChecker configuration JSON that contains for each CodeChecker action (analyze, parse, ...) the list of flags that should be appended to the invocation of the command.' + required: false + + logfile: + description: 'The location of the JSON Compilation Database for the project. This file describes how the project is compiled, and thus how it should be analysed. Mutually exclusive with "build-command".' + required: false + build-command: + description: 'The build command to execute and log for the creation of a JSON Compilation Database. Mutually exclusive with "logfile".' + required: false + runs: using: "composite" steps: @@ -78,3 +89,13 @@ runs: echo "::set-output name=GITSEVEN::$(./build/CodeChecker/bin/CodeChecker analyzer-version | grep 'Git commit' | cut -d'|' -f 2 | cut -c 2-8)" popd + - name: "Prepare JSON Compilation Database" + env: + CODECHECKER_PATH: ${{ steps.codechecker.outputs.PATH }} + + IN_LOGFILE: ${{ inputs.logfile }} + IN_COMMAND: ${{ inputs.build-command }} + + OUT_FILE: ${{ github.workspace }}/codechecker_compilation_database.json + shell: bash + run: ${{ github.action_path }}/src/get-or-create-build-json.sh diff --git a/src/get-or-create-build-json.sh b/src/get-or-create-build-json.sh new file mode 100755 index 0000000..f6413c3 --- /dev/null +++ b/src/get-or-create-build-json.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -ex + +if [[ ! -z "$IN_LOGFILE" && ! -z "$IN_COMMAND" ]]; then + echo "::error title=Configuration error::'logfile' and 'build-command' both specified!" + exit 1 +fi + +if [[ ! -z "$IN_LOGFILE" ]]; then + # Pretty trivial. + cp -v "$IN_LOGFILE" "$OUT_FILE" + exit $? +fi + +if [[ ! -z "$IN_COMMAND" ]]; then + "$CODECHECKER_PATH"/CodeChecker log \ + --build "$IN_COMMAND" \ + --output "$OUT_FILE" + exit $? +fi + +echo "::error title=Configuration error::neither 'logfile' nor 'build-command' specified!" +echo "[]" > "$OUT_FILE" +exit 1 diff --git a/test/empty/compile_commands.json b/test/empty/compile_commands.json new file mode 100644 index 0000000..32960f8 --- /dev/null +++ b/test/empty/compile_commands.json @@ -0,0 +1,2 @@ +[ +] \ No newline at end of file diff --git a/test/simple/compile_commands.json b/test/simple/compile_commands.json new file mode 100644 index 0000000..c96fcb4 --- /dev/null +++ b/test/simple/compile_commands.json @@ -0,0 +1,7 @@ +[ + { + "directory": ".", + "command": "g++ -c main.cpp -o main.o", + "file": "main.cpp" + } +] diff --git a/test/simple/main.cpp b/test/simple/main.cpp new file mode 100644 index 0000000..a9336fc --- /dev/null +++ b/test/simple/main.cpp @@ -0,0 +1,3 @@ +int main() { + return sizeof(42); +}