1// Copyright 2022 The ChromiumOS Authors 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package main 6 7import ( 8 "bufio" 9 "bytes" 10 "fmt" 11 "path/filepath" 12 "strings" 13) 14 15type useIWYUMode int 16 17const iwyuCrashSubstring = "PLEASE submit a bug report" 18 19const ( 20 iwyuModeNone useIWYUMode = iota 21 iwyuModeAll 22 iwyuModeError 23) 24 25var srcFileSuffixes = []string{ 26 ".c", 27 ".cc", 28 ".cpp", 29 ".C", 30 ".cxx", 31 ".c++", 32} 33 34func findWithIWYUFlag(args []builderArg) (string, []builderArg) { 35 for i := range args { 36 if args[i].value == "--with-iwyu" { 37 args = append(args[:i], args[i+1:]...) 38 return "1", args 39 } 40 } 41 return "", args 42} 43 44func processIWYUFlags(builder *commandBuilder) (cSrcFile string, iwyuFlags []string, mode useIWYUMode) { 45 builder.transformArgs(func(arg builderArg) string { 46 const prefix = "-iwyu-flag=" 47 if !strings.HasPrefix(arg.value, prefix) { 48 return arg.value 49 } 50 51 iwyuFlags = append(iwyuFlags, arg.value[len(prefix):]) 52 return "" 53 }) 54 55 cSrcFile = "" 56 lastArg := "" 57 for _, arg := range builder.args { 58 if lastArg != "-o" { 59 for _, suffix := range srcFileSuffixes { 60 if strings.HasSuffix(arg.value, suffix) { 61 cSrcFile = arg.value 62 break 63 } 64 } 65 } 66 lastArg = arg.value 67 } 68 69 if cSrcFile == "" { 70 return "", iwyuFlags, iwyuModeNone 71 } 72 73 withIWYU, _ := builder.env.getenv("WITH_IWYU") 74 if withIWYU == "" { 75 withIWYU, builder.args = findWithIWYUFlag(builder.args) 76 if withIWYU == "" { 77 return cSrcFile, iwyuFlags, iwyuModeNone 78 } 79 } 80 81 if withIWYU != "1" { 82 return cSrcFile, iwyuFlags, iwyuModeError 83 } 84 85 return cSrcFile, iwyuFlags, iwyuModeAll 86} 87 88func calcIWYUInvocation(env env, clangCmd *command, cSrcFile string, iwyuFlags ...string) (*command, error) { 89 resourceDir, err := getClangResourceDir(env, clangCmd.Path) 90 if err != nil { 91 return nil, err 92 } 93 94 iwyuPath := filepath.Join(filepath.Dir(clangCmd.Path), "include-what-you-use") 95 args := append([]string{}, iwyuFlags...) 96 args = append(args, "-resource-dir="+resourceDir) 97 args = append(args, clangCmd.Args...) 98 99 for i := 0; i < len(args); i++ { 100 for j := 0; j < len(srcFileSuffixes); j++ { 101 if strings.HasSuffix(args[i], srcFileSuffixes[j]) { 102 args = append(args[:i], args[i+1:]...) 103 break 104 } 105 } 106 } 107 args = append(args, cSrcFile) 108 109 return &command{ 110 Path: iwyuPath, 111 Args: args, 112 EnvUpdates: clangCmd.EnvUpdates, 113 }, nil 114} 115 116func runIWYU(env env, clangCmd *command, cSrcFile string, extraIWYUFlags []string) error { 117 extraIWYUFlags = append(extraIWYUFlags, "-Xiwyu", "--mapping_file=/usr/share/include-what-you-use/libcxx.imp", "-Xiwyu", "--no_fwd_decls") 118 iwyuCmd, err := calcIWYUInvocation(env, clangCmd, cSrcFile, extraIWYUFlags...) 119 if err != nil { 120 return fmt.Errorf("calculating include-what-you-use invocation: %v", err) 121 } 122 123 // Note: We pass nil as stdin as we checked before that the compiler 124 // was invoked with a source file argument. 125 var stderr bytes.Buffer 126 stderrWriter := bufio.NewWriter(&stderr) 127 exitCode, err := wrapSubprocessErrorWithSourceLoc(iwyuCmd, 128 env.run(iwyuCmd, nil, nil, stderrWriter)) 129 stderrMessage := stderr.String() 130 fmt.Fprintln(env.stderr(), stderrMessage) 131 132 if err == nil && exitCode != 0 { 133 // Note: We continue on purpose when include-what-you-use fails 134 // to maintain compatibility with the previous wrapper. 135 fmt.Fprintln(env.stderr(), "include-what-you-use failed") 136 } 137 138 var path strings.Builder 139 path.WriteString(strings.TrimSuffix(iwyuCmd.Path, "include-what-you-use")) 140 path.WriteString("fix_includes.py") 141 fixIncludesCmd := &command{ 142 Path: path.String(), 143 Args: []string{"--nocomment"}, 144 EnvUpdates: clangCmd.EnvUpdates, 145 } 146 147 exitCode, err = wrapSubprocessErrorWithSourceLoc(fixIncludesCmd, 148 env.run(fixIncludesCmd, strings.NewReader(stderrMessage), env.stdout(), env.stderr())) 149 if err == nil && exitCode != 0 { 150 // Note: We continue on purpose when include-what-you-use fails 151 // to maintain compatibility with the previous wrapper. 152 fmt.Fprint(env.stderr(), "include-what-you-use failed") 153 } 154 155 return err 156} 157