Linty Documentation


Scan Your Code

Table of Contents:


Provision New Project

As an administrator, log in to the Linty web interface and go to Administration > Projects > Management > Create Project to provision your project:

Create Project

Once your project has been provisioned, update its permissions. To do so, go to Project Setting > Permissions and add the following role(s) to the linty-scanner user:

Set Permissions

Then, you can select which quality profiles (set of rules) to use.

Scan Your Code

To scan your code, run the Linty Scanner that is provided as a Docker image.

Compatibility Matrix with Linty Server

Linty Scanner Linty Server
3.1.0 (latest) 3.1.0
3.0.0 3.0.0
2.1.0 2.1.0
2.0.0 2.0.0
1.2.0 1.2.0
1.1.0 1.1.0

Requirements

Linty scans can be triggered from a machine:

No external connection is required to run the Linty Scanner.

Linux

Either install Docker Engine (with containerd and Docker Compose) 23 or greater or Docker Desktop for Linux (latest version). If you choose to install Docker Desktop, make sure to make it start automatically.

Windows

Linty Scanner requires a version of Windows on which WSL 2 (Windows Subsystem for Linux) is enabled. You can watch the following videos to enable WSL on Windows 10 or Windows 11. Note that the last step (Ubuntu installation is not required).

Then, install Docker Desktop for Windows (latest version). Make Docker Desktop starts automatically. Once Docker Desktop is started, make sure to switch to Linux containers

Mac

Install Docker Desktop for Mac (latest version). Make Docker Desktop starts automatically.

Run Linty Scan

Add a sonar-project.properties file to the root of your project:

# Key of the project you provisioned in the previous step
sonar.projectKey=my-project-key
# Project name that you want to display on the Linty web interface
sonar.projectName=My Project Name
# Comma-separated list of directories containing your source code (VHDL, Verilog/SystemVerilog and Tcl files).
# Paths are relative to the project base directory.
sonar.sources=./src

On Premise (Linux)

Create a directory that will act as a cache for Linty plugins. That way, plugins will not be downloaded from your Linty server each time you run an analysis. Analyses will run faster then.

Without BugFinder (Linter only)

Depending on the activated rules, you might have to set the sonar.hdl.topModule property. See Top-level Module/Entity.

Then, run the following Docker command:

docker run \
    --rm \
    --user="$(id -u):$(id -g)" \
    --net=host \
    -e SONAR_HOST_URL="<linty_platform_url>" \
    -e SONAR_TOKEN="<linty_scanner_user_token>" \
    -v "<path_to_the_project_to_analyze>:/usr/src" \
    -v "<path_to_your_linty_scanner_cache>:/opt/sonar-scanner/.sonar/cache" \
    lintyservices/linty-scanner:<version> -Dsonar.bugfinder.enabled=false <additional_scanner_parameters>

 

# Example: Let's say that you run this Docker command from the root directory of your project.
docker run \
    --rm \
    --user="$(id -u):$(id -g)" \
    --net=host \
    -e SONAR_HOST_URL="http(s)://xxx" \
    -e SONAR_TOKEN="squ_xxx" \
    -v "${PWD}:/usr/src" \
    -v "/home/abc/.sonar/cache:/opt/sonar-scanner/.sonar/cache" \
    lintyservices/linty-scanner:latest -Dsonar.bugfinder.enabled=false
With BugFinder and Linter

Add read.ys file to the root of your project to read all the files of your IP in the right order:

# Read all the files of your IP in the right order
# For instance, let's say that your IP consists of two VHDL and one Verilog files: ./fly.vhd and ./dream.vhd and ./run.v
verific -vhdl ./fly.vhd
verific -vhdl ./dream.vhd
verific -sv ./run.v

 

# It's likely that your IP uses third-party libraries.
# In this case, you will need to read interfaces with those third-party libraries.
#
# If you get the following kind of error:
# VERIFIC-ERROR [VHDL-1240] ./xxx.vhd:xxx: 'vcomponents' is not compiled in library 'xpm',
# You need to add the following at the beginning of your read.ys file:
verific -work xpm -vhdl ./third-party-libraries/.../xpm_VCOMP.vhd

# If you get the following kind of error:
# VERIFIC-ERROR [VHDL-1240] ./xxx.vhd:xxx: 'blabla' is not compiled in library 'work',
# You need to add the following at the beginning of your read.ys file:
verific -vhdl -lib ./third-party-libraries/.../blabla.vhd

 

Add hierarchy.ys file to the root of your project to set the top module/entity and pass generic values (optional):

# Example #1: Let's say that the top module/entity of your IP is 'dream'
hierarchy -top dream

 

# Example #2: Let's say that the top module/entity of your IP is 'dream'
# and that you want to pass generics/parameters 'ABC' and 'DEF'
hierarchy -top dream -chparam ABC 10 -chparam DEF true

 

Set the sonar.hdl.topModule property. See Top-level Module/Entity.

Run the following Docker command:

docker run \
    --rm \
    --user="$(id -u):$(id -g)" \
    --net=host \
    -e SONAR_HOST_URL="<linty_platform_url>" \
    -e SONAR_TOKEN="<linty_scanner_user_token>" \
    -e TABBY_CAD_LICENSE="<tabby_cad_license_key>" \
    -v "<path_to_the_tabby_cad_license_key>:/opt/licenses/yosyshq-license.txt" \
    -v "<path_to_the_project_to_analyze>:/usr/src" \
    -v "<path_to_your_linty_scanner_cache>:/opt/sonar-scanner/.sonar/cache" \
    lintyservices/linty-scanner:<version> <additional_scanner_parameters>

 

# Example #1
# Let's say that:
#   - Your Tabby CAD (Linty Services edition) license is stored as a file at /opt/linty/my-tabby.lic
#   - You run this Docker command from the root directory of your project
docker run \
    --rm \
    --user="$(id -u):$(id -g)" \
    --net=host \
    -e SONAR_HOST_URL="http(s)://xxx" \
    -e SONAR_TOKEN="squ_xxx" \
    -v "${PWD}:/usr/src" \
    -v "/opt/linty/my-tabby.lic:/opt/licenses/yosyshq-license.txt" \
    -v "/home/abc/.sonar/cache:/opt/sonar-scanner/.sonar/cache" \
    lintyservices/linty-scanner:latest

 

# Example #2
# Let's say that:
#   - Your Tabby CAD (Linty Services edition) license is stored as an environment variable: $TABBY_CAD_LICENSE
#   - You run this Docker command from the root directory of your project
docker run \
    --rm \
    --user="$(id -u):$(id -g)" \
    --net=host \
    -e SONAR_HOST_URL="http(s)://xxx" \
    -e SONAR_TOKEN="squ_xxx" \
    -e TABBY_CAD_LICENSE="$TABBY_CAD_LICENSE" \
    -v "${PWD}:/usr/src" \
    -v "/home/abc/.sonar/cache:/opt/sonar-scanner/.sonar/cache" \
    lintyservices/linty-scanner:latest

Browse project sample.

Advanced usage
# Run in debug mode
docker run ... lintyservices/linty-scanner:latest -Dsonar.verbose=true

 

# Add timestamp
docker run ... lintyservices/linty-scanner:latest | ts "[%Y-%m-%d %H:%M:%S]"

On Premise (Windows)

The above On Premise (Linux) documentation applies for Windows with the following notes:

  1. Run commands in PowerShell
  2. Remove the following lines from the docker run command:
    --user="$(id -u):$(id -g)"
    --net=host
    
  3. Add the following line to the docker run command with the MAC address provided with the Linty license. Make sure to use : separators.
    --mac-address=XX:XX:XX:XX:XX:XX
    
  4. Line break in commands is ` instead of \

Example:

# Let's say that:
#   - Your Tabby CAD (Linty Services edition) license is stored as a file at C:\Users\me\linty\my-tabby.lic
#   - You run this Docker command from the root directory of your project
#   - Your Linty cache directory is C:\Users\me\linty\cache
docker run `
    --rm `
    --mac-address="XX:XX:XX:XX:XX:XX" `
    -e SONAR_HOST_URL="http(s)://xxx" `
    -e SONAR_TOKEN="squ_xxx" `
    -v "${PWD}:/usr/src" `
    -v "C:\Users\me\linty\my-tabby.lic:/opt/licenses/yosyshq-license.txt" `
    -v "C:\Users\me\linty\cache:/opt/sonar-scanner/.sonar/cache" `
    lintyservices/linty-scanner:latest

On Premise (Mac)

The above On Premise (Linux) documentation applies for Mac with the following notes:

  1. Remove the following line from the docker run command:
    --net=host
    
  2. Add the following line to the docker run command with the MAC address provided with the Linty license:
    --mac-address=XX:XX:XX:XX:XX:XX
    

Example:

# Let's say that:
#   - Your Tabby CAD (Linty Services edition) license is stored as a file at /home/me/linty/my-tabby.lic
#   - You run this Docker command from the root directory of your project
#   - Your Linty cache directory is /home/me/linty/cache
docker run \
    --rm \
    --user="$(id -u):$(id -g)" \
    --mac-address="XX:XX:XX:XX:XX:XX" \
    -e SONAR_HOST_URL="http(s)://xxx" \
    -e SONAR_TOKEN="squ_xxx" \
    -v "${PWD}:/usr/src" \
    -v "/home/me/linty/my-tabby.lic:/opt/licenses/yosyshq-license.txt" \
    -v "/home/me/linty/cache:/opt/sonar-scanner/.sonar/cache" \
    lintyservices/linty-scanner:latest

From GitHub

Create the following Actions secrets on your repository:

Analyze ‘main’ branch only

Add the following: .github/worfklows/linty.yml file to your project:

name: Linty
on:
  push:
    branches:
      - main

jobs:
  linty:
    name: Linty
    timeout-minutes: 10
    runs-on: ubuntu-latest
    steps:
      - name: Git Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Cache
        uses: actions/cache@v3.3.0
        with:
          path: ~/.sonar/cache
          key: sonar
          restore-keys: sonar

      - name: Run Linty
        run: |
          docker run \
            --rm \
            -e SONAR_HOST_URL="http(s)://xxx" \
            -e SONAR_TOKEN="${{ secrets.LINTY_SCANNER_TOKEN }}" \
            -e GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" \
            -e GITHUB_REPOSITORY_OWNER="$GITHUB_REPOSITORY_OWNER" \
            -e GITHUB_REPOSITORY="$GITHUB_REPOSITORY" \
            -e TABBY_CAD_LICENSE="$TABBY_CAD_LICENSE" \
            -v "${PWD}:/usr/src" \
            lintyservices/linty-scanner:latest <additional_scanner_parameters>
        env:
          TABBY_CAD_LICENSE: ${{secrets.TABBY_CAD_LICENSE}}

      - name: Debug
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: debug
          path: |
            ./bugfinder_workdir/
Analyze all branches and pull requests

Prerequisites:

Add the following: .github/worfklows/linty.yml file to your project:

name: Linty
on:
  push:
  pull_request:

jobs:
  linty:
    name: Linty
    timeout-minutes: 10
    runs-on: ubuntu-latest
    env:
      SONAR_HOST_URL: http(s)://xxx
    steps:
      - name: Git Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Cache
        uses: actions/cache@v3.3.0
        with:
          path: ~/.sonar/cache
          key: sonar
          restore-keys: sonar

      - name: Set Linty Analysis parameters
        uses: haya14busa/action-cond@v1.1.1
        id: linty_analysis_parameters
        with:
          cond: ${{ github.event.pull_request.number != '' }}
          if_true: -Dsonar.pullrequest.key=${{ github.event.pull_request.number }} -Dsonar.pullrequest.base=${{ github.base_ref }} -Dsonar.pullrequest.branch=${{ github.head_ref }}
          if_false: -Dsonar.branch.name=${GITHUB_REF#refs/*/}

      - name: Run Linty
        run: |
          docker run \
            --rm \
            -e SONAR_HOST_URL=${{ env.SONAR_HOST_URL }} \
            -e SONAR_TOKEN="${{ secrets.SONARQUBE_SCANNER_FOR_PRIVATE_REPOSITORIES_TOKEN }}" \
            -e GITHUB_TOKEN="${{ secrets.GITHUB_TOKEN }}" \
            -e GITHUB_REPOSITORY_OWNER="$GITHUB_REPOSITORY_OWNER" \
            -e GITHUB_REPOSITORY="$GITHUB_REPOSITORY" \
            -e TABBY_CAD_LICENSE="$TABBY_CAD_LICENSE" \
            -v "${PWD}:/usr/src" \
            lintyservices/linty-scanner:latest \
            -Dsonar.qualitygate.wait=true ${{ steps.linty_analysis_parameters.outputs.value }}
        env:
          TABBY_CAD_LICENSE: ${{ secrets.TABBY_CAD_LICENSE }}

      - name: Debug
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: debug
          path: |
            ./bugfinder_workdir/

See fully configured project sample:

From GitLab

Create the following variables on your project:

To do so, go to Settings > CI/CD > Variables.

Analyze ‘main’ branch only

Add the following .gitlab-ci.yml file to your project:

linty:
  image:
    name: lintyservices/linty-scanner:latest
  variables:
    GIT_DEPTH: "0"
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true
  artifacts:
    when: always
    paths:
      - ./bugfinder_workdir/
  only:
    - main
Analyze all branches and merge requests

Prerequisites:

Add the following .gitlab-ci.yml file to your project:

workflow:
  rules:
    # Analyze all merge requests and all branches that do not have a related opened merge request
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
    - if: $CI_COMMIT_BRANCH && $CI_OPEN_MERGE_REQUESTS
      when: never
    - if:
        $CI_COMMIT_BRANCH

linty:
  image:
    name: lintyservices/linty-scanner:latest
  variables:
    GIT_DEPTH: "0"
    SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar"
  cache:
    key: "${CI_JOB_NAME}"
    paths:
      - .sonar/cache
  script:
    - sonar-scanner -Dsonar.qualitygate.wait=true
  artifacts:
    when: always
    paths:
      - ./bugfinder_workdir/

 

See fully configured project sample:

Troubleshooting

Linty is unable to analyze file - Caused by: org.sonar.sslr.grammar.GrammarException: The regular expression ‘xxx’ has led to a stack overflow error

If you face the following issue:

ERROR: Error during SonarScanner execution
org.sonar.squidbridge.api.AnalysisException: SonarQube is unable to analyze file: xxx
INFO: Final Memory: 223M/750M
INFO: ------------------------------------------------------------------------
at com.lintyservices.sonar.plugins.verilog.VerilogAnalyzerSensor.analyzeFile(VerilogAnalyzerSensor.java:160)
at com.lintyservices.sonar.plugins.verilog.VerilogAnalyzerSensor.execute(VerilogAnalyzerSensor.java:111)
...

Caused by: org.sonar.sslr.grammar.GrammarException: The regular expression 'xxx' has led to a stack overflow error.
This error is certainly due to an inefficient use of alternations.
See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5050507
at org.sonar.sslr.internal.vm.PatternExpression.execute(PatternExpression.java:48)
at org.sonar.sslr.internal.vm.Machine.execute(Machine.java:162)

Caused by: java.lang.StackOverflowError
at java.base/java.util.regex.Pattern$Branch.match(Unknown Source)
at java.base/java.util.regex.Pattern$GroupHead.match(Unknown Source)

 

You need to increase the JVM stack size by setting the Xss variable to a higher value:

# Add the following environment variable to the Docker container
# Feel free to increase up to 100m. If you need to go higher, please report the issue to support@linty-service.com
-e SONAR_SCANNER_OPTS=-Xss10m

java.lang.OutOfMemoryError: Java heap space

If you face the following issue:

ERROR: Error during SonarScanner execution
java.lang.OutOfMemoryError: Java heap space

 

You need to increase the JVM heap space size by setting the Xmx variable to a higher value:

# Add the following environment variable to the Docker container
# Feel free to increase up to 4096m. If you need to go higher, please report the issue to support@linty-service.com
-e SONAR_SCANNER_OPTS=-Xmx2048m

Insufficient privileges

If you face the following issue, make sure that you generated a token with proper User Token type.

org.sonarqube.ws.client.HttpException:
Error 403 on https://xxx&metricKeys=ncloc_language_distribution : {"errors":[{"msg":"Insufficient privileges"}]}

sonar.hdl.topModule property is not set

If you face the following issue, see Top-level Module/Entity.

ERROR: Error during SonarScanner execution
...
Caused by: java.lang.IllegalStateException: [BUGFINDER] sonar.hdl.topModule property is not set

java.nio.file.AccessDeniedException: /opt/sonar-scanner/.sonar/cache/_tmp

If you face the following issue, it means that you haven’t created the cache directory before running the scan (-v "/<your_cache_directory>>:/opt/sonar-scanner/.sonar/cache").

ERROR: Error during SonarScanner execution
java.lang.IllegalStateException: Unable to create temp dir/opt/sonar-scanner/.sonar/cache/_tmp
...
Caused by: java.nio.file.AccessDeniedException: /opt/sonar-scanner/.sonar/cache/_tmp
...

To fix this issue, remove the cache directory that Docker tried to create (but with wrong access rights), manually recreate the cache directory and re-run a scan.

Docker Image Content