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 }