clang/cmake.go (97 lines of code) (raw):
package main
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/JetBrains/qodana-cli/internal/platform/utils"
log "github.com/sirupsen/logrus"
)
type Command struct {
Directory string `json:"directory"`
Command string `json:"command"`
File string `json:"file"`
Output string `json:"output"`
Arguments []string `json:"arguments,omitempty"`
}
type FileWithHeaders struct {
File string
Headers []string
}
const (
SIS = "#include <...> search starts here:"
SIE = "End of search list."
)
func getHeaderType(file string) string {
extension := filepath.Ext(file)
nullDevice := os.DevNull
switch extension {
case ".c", ".h":
return fmt.Sprintf("-E -Wp,-v -xc %s", nullDevice)
default:
return fmt.Sprintf("-E -Wp,-v -xc++ %s", nullDevice)
}
}
// getFilesAndCompilers returns a list of files with their corresponding compiler's include directories
func getFilesAndCompilers(compileCommands string) ([]FileWithHeaders, error) {
data, err := os.ReadFile(compileCommands)
if err != nil {
return nil, err
}
var commands []Command
err = json.Unmarshal(data, &commands)
if err != nil {
return nil, err
}
var processList []FileWithHeaders
fileHeaderMap := make(map[string][]string)
for _, cmd := range commands {
var compiler string
trimmedCommand := strings.TrimSpace(cmd.Command)
if trimmedCommand == "" {
if len(cmd.Arguments) == 0 {
log.Warn("Empty command and arguments for file in compilation db: ", cmd.File)
continue
}
compiler = cmd.Arguments[0]
} else {
compiler = strings.Split(trimmedCommand, " ")[0]
}
headerType := getHeaderType(cmd.File)
if val, ok := fileHeaderMap[compiler+headerType]; ok {
processList = append(processList, FileWithHeaders{File: cmd.File, Headers: val})
} else {
headers, err := askCompiler(compiler, headerType)
if err != nil {
return nil, err
}
fileHeaderMap[compiler+headerType] = headers
processList = append(processList, FileWithHeaders{File: cmd.File, Headers: headers})
}
}
return processList, nil
}
// askCompiler asks the compiler for the include directories
func askCompiler(compiler string, headerType string) ([]string, error) {
args := []string{compiler, headerType}
_, stderr, _, err := utils.RunCmdRedirectOutput("", args...)
if err != nil {
return nil, err
}
startIndex := strings.Index(stderr, SIS)
endIndex := strings.Index(stderr, SIE)
var list []string
if startIndex != -1 && endIndex != -1 && endIndex > startIndex {
includes := strings.TrimSpace(stderr[startIndex+len(SIS) : endIndex])
re := regexp.MustCompile(`[\n\r]+`)
lines := re.Split(includes, -1)
for _, dir := range lines {
if strings.Contains(dir, "(") {
continue
}
list = append(list, "--extra-arg=-isystem"+strings.TrimSpace(dir))
}
}
log.Debug("Compiler: ", compiler, "Include dirs: ", list)
return list, nil
}