clang/clang.go (138 lines of code) (raw):

package main import ( "fmt" "os" "os/signal" "path" "runtime" "strings" "sync" "sync/atomic" "time" "github.com/JetBrains/qodana-cli/internal/platform" "github.com/JetBrains/qodana-cli/internal/platform/strutil" "github.com/JetBrains/qodana-cli/internal/platform/thirdpartyscan" "github.com/JetBrains/qodana-cli/internal/platform/utils" "github.com/briandowns/spinner" log "github.com/sirupsen/logrus" ) const spinnerIndex = 34 const spinnerInterval = 100 * time.Millisecond // runClangTidyUnderProgress runs clang-tidy for each file in filesAndCompilers and shows a progress bar. func runClangTidyUnderProgress(c thirdpartyscan.Context, filesAndCompilers []FileWithHeaders, checks string) { spin := initializeSpinner() stdoutChannel, stderrChannel := createFileLoggers(c.LogDir()) worker(c, filesAndCompilers, checks, spin, stdoutChannel, stderrChannel) } func initializeSpinner() *spinner.Spinner { spin := spinner.New(spinner.CharSets[9], spinnerInterval, spinner.WithWriter(os.Stderr)) spin.UpdateCharSet(spinner.CharSets[spinnerIndex]) return spin } func createFileLoggers(logDir string) (chan string, chan string) { stdout := make(chan string) stderr := make(chan string) go logToFile(path.Join(logDir, "clang-out.txt"), stdout) go logToFile(path.Join(logDir, "clang-err.txt"), stderr) return stdout, stderr } func logToFile(fileName string, logChannel chan string) { for logItem := range logChannel { if err := utils.AppendToFile(fileName, logItem); err != nil { log.Error(err) } } } func worker( c thirdpartyscan.Context, filesAndCompilers []FileWithHeaders, checks string, spin *spinner.Spinner, stdoutChannel, stderrChannel chan string, ) { spin.Start() spin.Suffix = fmt.Sprintf(" %d/%d", 0, len(filesAndCompilers)) defer close(stdoutChannel) defer close(stderrChannel) var wg sync.WaitGroup progressCounter := int32(0) sem := make(chan bool, runtime.NumCPU()) quit := make(chan os.Signal, 1) finished := make(chan bool) signal.Notify(quit, os.Interrupt) go func() { for counter, fileWithHeader := range filesAndCompilers { select { case <-quit: fmt.Println("Interrupt signal received. Exiting function.") spin.Stop() finished <- true return default: wg.Add(1) go func(counter int, input FileWithHeaders) { defer wg.Done() sem <- true spin.Suffix = fmt.Sprintf(" %d/%d", atomic.AddInt32(&progressCounter, 1), len(filesAndCompilers)) spin.Restart() defer func() { <-sem }() err := runClangTidy( counter, input, checks, c, platform.GetTmpResultsDir(c.ResultsDir()), stderrChannel, stdoutChannel, ) if err != nil { log.Errorf("Error running clang-tidy: %s", err) } }(counter, fileWithHeader) } } wg.Wait() spin.Stop() finished <- true }() select { case <-quit: return case <-finished: return } } // runClangTidy runs clang-tidy for a single file. func runClangTidy( counter int, input FileWithHeaders, checks string, c thirdpartyscan.Context, tmpResultsDir string, stderrChannel chan string, stdoutChannel chan string, ) error { args := []string{ strutil.QuoteIfSpace(c.ClangPath()), checks, "-p", strutil.QuoteIfSpace(c.ClangCompileCommands()), "--export-sarif", strutil.QuoteIfSpace(path.Join(tmpResultsDir, fmt.Sprintf("%d.sarif.json", counter))), } args = append(args, input.Headers...) args = append(args, input.File) args = append(args, "--quiet") args = append(args, strings.Split(c.ClangArgs(), " ")...) stdout, stderr, _, err := utils.RunCmdRedirectOutput( strutil.QuoteIfSpace(c.ProjectDir()), args..., ) if stderr != "" { log.Debug(stderr) stderrChannel <- fmt.Sprintf("File: %s\n%s\n", input.File, stderr) } if stdout != "" { log.Debug(stdout) stdoutChannel <- fmt.Sprintf("File: %s\n%s\n%s\n", input.File, stdout, stderr) } return err }