clang/run.go (110 lines of code) (raw):
package main
import (
"bytes"
"encoding/hex"
"fmt"
"os"
"path/filepath"
"runtime"
_ "embed"
"github.com/JetBrains/qodana-cli/internal/platform"
"github.com/JetBrains/qodana-cli/internal/platform/thirdpartyscan"
"github.com/JetBrains/qodana-cli/internal/platform/utils"
log "github.com/sirupsen/logrus"
)
type ClangLinter struct {
}
func (l ClangLinter) RunAnalysis(c thirdpartyscan.Context) error {
checks, err := processConfig(c)
if err != nil {
return err
}
filesAndCompilers, err := getFilesAndCompilers(c.ClangCompileCommands())
if err != nil {
return err
}
runClangTidyUnderProgress(c, filesAndCompilers, checks)
_, err = mergeSarifReports(c)
if err != nil {
log.Errorf("Error merging SARIF reports: %s", err)
return err
}
sarifPath := platform.GetSarifPath(c.ResultsDir())
if err = fixupClangLinterTaxa(sarifPath); err != nil {
return err
}
return nil
}
//go:generate go run scripts/prepare-clang-tidy-binary.go
//goland:noinspection GoRedundantElseInIf
func (l ClangLinter) MountTools(path string) (map[string]string, error) {
clang := thirdpartyscan.Clang
val := make(map[string]string)
val[clang] = getBinaryPath(path)
_, err := os.Stat(val[clang])
var isBinaryOk bool
if os.IsNotExist(err) {
isBinaryOk = false
} else if err != nil {
return nil, err
} else {
hash, err := utils.GetFileSha256(val[clang])
if err != nil {
log.Warningf("getting sha256 of %q failed: %s", val[clang], err)
isBinaryOk = false
} else {
isBinaryOk = bytes.Equal(hash[:], ClangTidySha256)
if !isBinaryOk {
log.Warningf(
"failed to verify sha256 checksum of %q: expected %s, got %s", val[clang],
hex.EncodeToString(ClangTidySha256),
hex.EncodeToString(hash[:]),
)
}
}
}
if !isBinaryOk {
extension := ".tar.gz"
if runtime.GOOS == "windows" {
extension = ".zip"
}
clangArchive := clang + extension
clangArchivePath := platform.ProcessAuxiliaryTool(clangArchive, clang, path, ClangTidyArchive)
if err := platform.Decompress(clangArchivePath, path); err != nil {
return nil, fmt.Errorf("failed to decompress clang archive: %w", err)
}
val[clang] = getBinaryPath(path)
}
return val, nil
}
func getBinaryPath(toolsPath string) string {
binaryName := "clang-tidy"
binaryPath := filepath.Join(toolsPath, binaryName)
//goland:noinspection GoBoolExpressions
if runtime.GOOS == "windows" {
binaryPath += ".exe"
}
if _, err := os.Stat(binaryPath); os.IsNotExist(err) {
binaryPath = filepath.Join(toolsPath, "bin", binaryName)
//goland:noinspection GoBoolExpressions
if runtime.GOOS == "windows" {
binaryPath += ".exe"
}
}
return binaryPath
}
func fixupClangLinterTaxa(sarifPath string) error {
r, err := platform.ReadReport(sarifPath)
if err != nil {
log.Errorf("Error reading SARIF reports: %s", err)
return err
}
for _, taxa := range r.Runs[0].Tool.Driver.Taxa {
if len(taxa.Relationships) == 1 &&
taxa.Relationships[0].Target != nil && taxa.Relationships[0].Target.Id == taxa.Id {
taxa.Relationships[0].Target.Id = r.Runs[0].Tool.Driver.Taxa[0].Id
}
}
err = platform.WriteReport(sarifPath, r)
if err != nil {
log.Errorf("Error writing SARIF reports: %s", err)
return err
}
return nil
}