1// Copyright 2019 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 "errors" 9 "fmt" 10 "io" 11 "path/filepath" 12 "strings" 13 "testing" 14) 15 16func TestClangBasename(t *testing.T) { 17 withTestContext(t, func(ctx *testContext) { 18 var tests = []struct { 19 in string 20 out string 21 }{ 22 {"./x86_64-cros-linux-gnu-clang", ".*/clang"}, 23 {"./x86_64-cros-linux-gnu-clang++", ".*/clang\\+\\+"}, 24 } 25 26 for _, tt := range tests { 27 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 28 ctx.newCommand(tt.in, mainCc))) 29 if err := verifyPath(cmd, tt.out); err != nil { 30 t.Error(err) 31 } 32 } 33 }) 34} 35 36func TestClangPathGivenClangEnv(t *testing.T) { 37 withTestContext(t, func(ctx *testContext) { 38 ctx.env = []string{"CLANG=/a/b/clang"} 39 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 40 ctx.newCommand(clangX86_64, mainCc))) 41 if err := verifyPath(cmd, "/a/b/clang"); err != nil { 42 t.Error(err) 43 } 44 }) 45} 46 47func TestAbsoluteClangPathBasedOnRootPath(t *testing.T) { 48 withTestContext(t, func(ctx *testContext) { 49 ctx.cfg.clangRootRelPath = "somepath" 50 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 51 ctx.newCommand(filepath.Join(ctx.tempDir, clangX86_64), mainCc))) 52 if err := verifyPath(cmd, filepath.Join(ctx.tempDir, "somepath/usr/bin/clang")); err != nil { 53 t.Error(err) 54 } 55 }) 56} 57 58func TestRelativeClangPathBasedOnRootPath(t *testing.T) { 59 withTestContext(t, func(ctx *testContext) { 60 ctx.cfg.clangRootRelPath = "somepath" 61 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 62 ctx.newCommand(clangX86_64, mainCc))) 63 if err := verifyPath(cmd, "somepath/usr/bin/clang"); err != nil { 64 t.Error(err) 65 } 66 }) 67} 68 69func TestRelativeClangPathWithDirBasedOnRootPath(t *testing.T) { 70 withTestContext(t, func(ctx *testContext) { 71 ctx.cfg.clangRootRelPath = "somepath" 72 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 73 ctx.newCommand("test/x86_64-cros-linux-gnu-clang", mainCc))) 74 if err := verifyPath(cmd, "test/somepath/usr/bin/clang"); err != nil { 75 t.Error(err) 76 } 77 }) 78} 79 80func TestPathEnvClangPathBasedOnRootPath(t *testing.T) { 81 withTestContext(t, func(ctx *testContext) { 82 ctx.cfg.clangRootRelPath = "somepath" 83 ctx.env = []string{"PATH=" + filepath.Join(ctx.tempDir, "/pathenv")} 84 ctx.writeFile(filepath.Join(ctx.tempDir, "/pathenv/x86_64-cros-linux-gnu-clang"), "") 85 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 86 ctx.newCommand("x86_64-cros-linux-gnu-clang", mainCc))) 87 if err := verifyPath(cmd, filepath.Join(ctx.tempDir, "pathenv/somepath/usr/bin/clang")); err != nil { 88 t.Error(err) 89 } 90 }) 91} 92 93func TestClangPathForClangHostWrapper(t *testing.T) { 94 withTestContext(t, func(ctx *testContext) { 95 ctx.cfg.isHostWrapper = true 96 ctx.cfg.clangRootRelPath = "somepath" 97 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 98 ctx.newCommand(clangX86_64, mainCc))) 99 if err := verifyPath(cmd, filepath.Join(ctx.tempDir, "clang")); err != nil { 100 t.Error(err) 101 } 102 }) 103} 104 105func TestClangPathForAndroidWrapper(t *testing.T) { 106 withTestContext(t, func(ctx *testContext) { 107 ctx.cfg.isAndroidWrapper = true 108 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 109 ctx.newCommand("somedir/clang", mainCc))) 110 if err := verifyPath(cmd, "somedir/clang.real"); err != nil { 111 t.Error(err) 112 } 113 }) 114} 115 116func TestClangPathForAndroidWrapperWithSymlinks(t *testing.T) { 117 withTestContext(t, func(ctx *testContext) { 118 ctx.cfg.isAndroidWrapper = true 119 ctx.writeFile("base/come_clang", "") 120 ctx.symlink("base/some_clang", "linked/clang") 121 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 122 ctx.newCommand("linked/clang", mainCc))) 123 if err := verifyPath(cmd, "linked/some_clang.real"); err != nil { 124 t.Error(err) 125 } 126 }) 127} 128 129func TestUseXclangPathAndCalcResourceDirByNestedClangCall(t *testing.T) { 130 withTestContext(t, func(ctx *testContext) { 131 ctx.cfg.clangRootRelPath = "somepath" 132 ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 133 if ctx.cmdCount > 1 { 134 return nil 135 } 136 if err := verifyPath(cmd, "somepath/usr/bin/clang"); err != nil { 137 t.Error(err) 138 } 139 if err := verifyArgOrder(cmd, "--print-resource-dir"); err != nil { 140 t.Error(err) 141 } 142 fmt.Fprint(stdout, "someResourcePath\n") 143 return nil 144 } 145 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 146 ctx.newCommand(clangX86_64, "-Xclang-path=somedir", mainCc))) 147 if err := verifyPath(cmd, "somedir/clang"); err != nil { 148 t.Error(err) 149 } 150 if err := verifyArgOrder(cmd, "-resource-dir=someResourcePath", 151 "--gcc-toolchain=/usr", mainCc); err != nil { 152 t.Error(err) 153 } 154 }) 155} 156 157func TestXclangPathFailIfNestedClangCallFails(t *testing.T) { 158 withTestContext(t, func(ctx *testContext) { 159 ctx.cmdMock = func(cmd *command, stdin io.Reader, stdout io.Writer, stderr io.Writer) error { 160 fmt.Fprint(stderr, "someclangerror") 161 return errors.New("someerror") 162 } 163 stderr := ctx.mustFail(callCompiler(ctx, ctx.cfg, 164 ctx.newCommand(clangX86_64, "-Xclang-path=somedir", mainCc))) 165 if err := verifyInternalError(stderr); err != nil { 166 t.Fatal(err) 167 } 168 if !strings.Contains(stderr, "clang") { 169 t.Errorf("could not find compiler path on stderr. Got: %s", stderr) 170 } 171 if !strings.Contains(stderr, "someerror") { 172 t.Errorf("could not find original error on stderr. Got: %s", stderr) 173 } 174 if !strings.Contains(stderr, "someclangerror") { 175 t.Errorf("stderr was not forwarded. Got: %s", stderr) 176 } 177 }) 178} 179 180func TestConvertGccToClangFlags(t *testing.T) { 181 withTestContext(t, func(ctx *testContext) { 182 var tests = []struct { 183 in string 184 out string 185 }{ 186 {"-Wno-error=maybe-uninitialized", "-Wno-error=uninitialized"}, 187 {"-Wno-error=cpp", "-Wno-#warnings"}, 188 {"-Xclang-only=-abc=xyz", "-abc=xyz"}, 189 } 190 191 for _, tt := range tests { 192 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 193 ctx.newCommand(clangX86_64, tt.in, mainCc))) 194 if err := verifyArgCount(cmd, 0, tt.in); err != nil { 195 t.Error(err) 196 } 197 if err := verifyArgOrder(cmd, tt.out, mainCc); err != nil { 198 t.Error(err) 199 } 200 } 201 }) 202} 203 204func TestFilterUnsupportedClangFlags(t *testing.T) { 205 withTestContext(t, func(ctx *testContext) { 206 var tests = []struct { 207 compiler string 208 flag string 209 expectedCount int 210 }{ 211 {clangX86_64, "-Wstrict-aliasing=xyz", 0}, 212 {clangX86_64, "-finline-limit=xyz", 0}, 213 {"./armv7a-cros-linux-gnu-clang", "-ftrapv", 0}, 214 {"./armv7a-cros-win-gnu-clang", "-ftrapv", 1}, 215 {"./armv8a-cros-win-gnu-clang", "-ftrapv", 1}, 216 {clangX86_64, "-ftrapv", 1}, 217 } 218 219 for _, tt := range tests { 220 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 221 ctx.newCommand(tt.compiler, tt.flag, mainCc))) 222 if err := verifyArgCount(cmd, tt.expectedCount, tt.flag); err != nil { 223 t.Error(err) 224 } 225 } 226 }) 227} 228 229func TestClangArchFlags(t *testing.T) { 230 withTestContext(t, func(ctx *testContext) { 231 var tests = []struct { 232 compiler string 233 flags []string 234 }{ 235 {"./i686_64-cros-linux-gnu-clang", []string{mainCc, "-target", "i686_64-cros-linux-gnu"}}, 236 {"./x86_64-cros-linux-gnu-clang", []string{mainCc, "-target", "x86_64-cros-linux-gnu"}}, 237 } 238 for _, tt := range tests { 239 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 240 ctx.newCommand(tt.compiler, mainCc))) 241 if err := verifyArgOrder(cmd, tt.flags...); err != nil { 242 t.Error(err) 243 } 244 } 245 }) 246} 247 248func TestClangLinkerPathProbesBinariesOnPath(t *testing.T) { 249 withTestContext(t, func(ctx *testContext) { 250 linkerPath := filepath.Join(ctx.tempDir, "a/b/c") 251 ctx.writeFile(filepath.Join(linkerPath, "x86_64-cros-linux-gnu-ld.bfd"), "") 252 ctx.env = []string{"PATH=nonExistantPath:" + linkerPath} 253 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 254 ctx.newCommand("./x86_64-cros-linux-gnu-clang", mainCc))) 255 if err := verifyArgOrder(cmd, "-Ba/b/c"); err != nil { 256 t.Error(err) 257 } 258 if err := verifyArgOrder(cmd, "--prefix=a/b/c/x86_64-cros-linux-gnu-"); err != nil { 259 t.Error(err) 260 } 261 262 }) 263} 264 265func TestClangLinkerPathEvaluatesSymlinksForBinariesOnPath(t *testing.T) { 266 withTestContext(t, func(ctx *testContext) { 267 realLinkerPath := filepath.Join(ctx.tempDir, "a/original/path/somelinker") 268 ctx.writeFile(realLinkerPath, "") 269 firstLinkLinkerPath := filepath.Join(ctx.tempDir, "a/first/somelinker") 270 ctx.symlink(realLinkerPath, firstLinkLinkerPath) 271 secondLinkLinkerPath := filepath.Join(ctx.tempDir, "a/second/x86_64-cros-linux-gnu-ld.bfd") 272 ctx.symlink(firstLinkLinkerPath, secondLinkLinkerPath) 273 274 ctx.env = []string{"PATH=nonExistantPath:" + filepath.Dir(secondLinkLinkerPath)} 275 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 276 ctx.newCommand("./x86_64-cros-linux-gnu-clang", mainCc))) 277 if err := verifyArgOrder(cmd, "-Ba/first"); err != nil { 278 t.Error(err) 279 } 280 if err := verifyArgOrder(cmd, "--prefix=a/first/x86_64-cros-linux-gnu-"); err != nil { 281 t.Error(err) 282 } 283 284 }) 285} 286 287func TestClangFallbackLinkerPathRelativeToRootDir(t *testing.T) { 288 withTestContext(t, func(ctx *testContext) { 289 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 290 ctx.newCommand(clangX86_64, mainCc))) 291 if err := verifyArgOrder(cmd, "-Bbin"); err != nil { 292 t.Error(err) 293 } 294 if err := verifyArgOrder(cmd, "--prefix=bin/x86_64-cros-linux-gnu-"); err != nil { 295 t.Error(err) 296 } 297 }) 298} 299 300func TestClangLinkerPathRelativeToRootDir(t *testing.T) { 301 withTestContext(t, func(ctx *testContext) { 302 ctx.cfg.clangRootRelPath = "somepath" 303 cmd := ctx.must(callCompiler(ctx, ctx.cfg, 304 ctx.newCommand(clangX86_64, mainCc))) 305 if err := verifyArgOrder(cmd, "-Bsomepath/bin"); err != nil { 306 t.Error(err) 307 } 308 if err := verifyArgOrder(cmd, "--prefix=somepath/bin/x86_64-cros-linux-gnu-"); err != nil { 309 t.Error(err) 310 } 311 }) 312} 313