Index: clang-tools-extra/clangd/SystemIncludeExtractor.cpp =================================================================== --- clang-tools-extra/clangd/SystemIncludeExtractor.cpp +++ clang-tools-extra/clangd/SystemIncludeExtractor.cpp @@ -135,11 +135,10 @@ return std::move(Info); } -llvm::Optional -extractSystemIncludesAndTarget(llvm::SmallString<128> Driver, - llvm::StringRef Lang, - llvm::ArrayRef CommandLine, - const llvm::Regex &QueryDriverRegex) { +llvm::Optional extractSystemIncludesAndTarget( + llvm::SmallString<128> Driver, llvm::StringRef Lang, + llvm::ArrayRef AdditionalDriverArgs, + const llvm::Regex &QueryDriverRegex) { trace::Span Tracer("Extract system includes and target"); if (!llvm::sys::path::is_absolute(Driver)) { @@ -180,33 +179,7 @@ llvm::SmallVector Args = {Driver, "-E", "-x", Lang, "-", "-v"}; - // These flags will be preserved - const llvm::StringRef FlagsToPreserve[] = { - "-nostdinc", "--no-standard-includes", "-nostdinc++", "-nobuiltininc"}; - // Preserves these flags and their values, either as separate args or with an - // equalsbetween them - const llvm::StringRef ArgsToPreserve[] = {"--sysroot", "-isysroot"}; - - for (size_t I = 0, E = CommandLine.size(); I < E; ++I) { - llvm::StringRef Arg = CommandLine[I]; - if (llvm::is_contained(FlagsToPreserve, Arg)) { - Args.push_back(Arg); - } else { - const auto *Found = - llvm::find_if(ArgsToPreserve, [&Arg](llvm::StringRef S) { - return Arg.startswith(S); - }); - if (Found == std::end(ArgsToPreserve)) - continue; - Arg = Arg.drop_front(Found->size()); - if (Arg.empty() && I + 1 < E) { - Args.push_back(CommandLine[I]); - Args.push_back(CommandLine[++I]); - } else if (Arg.startswith("=")) { - Args.push_back(CommandLine[I]); - } - } - } + llvm::append_range(Args, AdditionalDriverArgs); std::string ErrMsg; if (int RC = llvm::sys::ExecuteAndWait(Driver, Args, /*Env=*/llvm::None, @@ -325,12 +298,34 @@ return; llvm::StringRef Lang; + llvm::SmallVector AdditionalDriverArgs; + + // These flags will be preserved + const llvm::StringRef FlagsToPreserve[] = { + "-nostdinc", "--no-standard-includes", "-nostdinc++", "-nobuiltininc"}; + // Preserve these flags and their values when provided as separate args + const llvm::StringRef TwoPartArgsToPreserve[] = {"--sysroot", "-isysroot"}; + // Preserve these flags and their values when provided as a single arg + const llvm::StringRef ArgPrefixesToPreserve[] = { + "-isysroot", "--sysroot=", "-specs=", "--specs="}; + for (size_t I = 0, E = Cmd.CommandLine.size(); I < E; ++I) { llvm::StringRef Arg = Cmd.CommandLine[I]; - if (Arg == "-x" && I + 1 < E) - Lang = Cmd.CommandLine[I + 1]; - else if (Arg.startswith("-x")) + if (Arg == "-x" && I + 1 < E) { + Lang = Cmd.CommandLine[++I]; + } else if (Arg.startswith("-x")) { Lang = Arg.drop_front(2).trim(); + } else if (llvm::is_contained(FlagsToPreserve, Arg)) { + AdditionalDriverArgs.push_back(Arg); + } else if (llvm::is_contained(TwoPartArgsToPreserve, Arg) && I + 1 < E) { + AdditionalDriverArgs.push_back(Arg); + AdditionalDriverArgs.push_back(Cmd.CommandLine[++I]); + } else if (llvm::any_of(ArgPrefixesToPreserve, + [&Arg](const llvm::StringRef Prefix) { + return Arg.starts_with(Prefix); + })) { + AdditionalDriverArgs.push_back(Arg); + } } if (Lang.empty()) { llvm::StringRef Ext = llvm::sys::path::extension(File).trim('.'); @@ -349,11 +344,14 @@ // relative or absolute). llvm::sys::fs::make_absolute(Cmd.Directory, Driver); - if (auto Info = - QueriedDrivers.get(/*Key=*/(Driver + ":" + Lang).str(), [&] { - return extractSystemIncludesAndTarget( - Driver, Lang, Cmd.CommandLine, QueryDriverRegex); - })) { + std::string CacheKey = (Driver + ":" + Lang).str(); + if (!AdditionalDriverArgs.empty()) + CacheKey += ":" + llvm::join(AdditionalDriverArgs, ":"); + + if (auto Info = QueriedDrivers.get(/*Key=*/CacheKey, [&] { + return extractSystemIncludesAndTarget( + Driver, Lang, AdditionalDriverArgs, QueryDriverRegex); + })) { setTarget(addSystemIncludes(Cmd, Info->SystemIncludes), Info->Target); } } Index: clang-tools-extra/clangd/test/system-include-extractor.test =================================================================== --- clang-tools-extra/clangd/test/system-include-extractor.test +++ clang-tools-extra/clangd/test/system-include-extractor.test @@ -14,13 +14,17 @@ # Check that clangd preserves certain flags like `-nostdinc` from # original invocation in compile_commands.json. # RUN: echo '[ -z "${args##*"-nostdinc"*}" ] || exit' >> %t.dir/bin/my_driver.sh -# RUN: echo '[ -z "${args##*"-isysroot=/isysroot"*}" ] || exit' >> %t.dir/bin/my_driver.sh -# RUN: echo '[ -z "${args##*"--sysroot /my/sysroot/path"*}" ] || exit' >> %t.dir/bin/my_driver.sh +# RUN: echo '[ -z "${args##*"-isysroot/isysroot1"*}" ] || exit' >> %t.dir/bin/my_driver.sh +# RUN: echo '[ -z "${args##*"-isysroot /isysroot2"*}" ] || exit' >> %t.dir/bin/my_driver.sh +# RUN: echo '[ -z "${args##*"--sysroot /my/sysroot1/path"*}" ] || exit' >> %t.dir/bin/my_driver.sh +# RUN: echo '[ -z "${args##*"--sysroot=/my/sysroot2/path"*}" ] || exit' >> %t.dir/bin/my_driver.sh # RUN: echo 'echo line to ignore >&2' >> %t.dir/bin/my_driver.sh # RUN: echo 'printf "Target: arm-linux-gnueabihf\r\n" >&2' >> %t.dir/bin/my_driver.sh # RUN: echo 'printf "#include <...> search starts here:\r\n" >&2' >> %t.dir/bin/my_driver.sh # RUN: echo 'echo %t.dir/my/dir/ >&2' >> %t.dir/bin/my_driver.sh # RUN: echo 'echo %t.dir/my/dir2/ >&2' >> %t.dir/bin/my_driver.sh +# Append an alternate includes path iff we were given a specs file +# RUN: echo '[ -z "${args##*"-specs=nano.specs"*}" ] && echo %t.dir/my/dir3/ >&2' >> %t.dir/bin/my_driver.sh # RUN: echo 'printf "End of search list.\r\n" >&2' >> %t.dir/bin/my_driver.sh # RUN: chmod +x %t.dir/bin/my_driver.sh @@ -29,10 +33,12 @@ # RUN: touch %t.dir/my/dir/a.h # RUN: mkdir -p %t.dir/my/dir2 # RUN: touch %t.dir/my/dir2/b.h +# RUN: mkdir -p %t.dir/my/dir3 +# RUN: touch %t.dir/my/dir3/c.h # Generate a compile_commands.json that will query the mock driver we've # created. Which should add a.h and b.h into include search path. -# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp -nostdinc --sysroot /my/sysroot/path -isysroot=/isysroot", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json +# RUN: echo '[{"directory": "%/t.dir", "command": "my_driver.sh the-file.cpp -nostdinc --sysroot /my/sysroot1/path --sysroot=/my/sysroot2/path -isysroot/isysroot1 -isysroot /isysroot2 -specs=nano.specs", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json # RUN: sed -e "s|INPUT_DIR|%/t.dir|g" %s > %t.test.1 # On Windows, we need the URI in didOpen to look like "uri":"file:///C:/..." @@ -53,7 +59,7 @@ "uri": "file://INPUT_DIR/the-file.cpp", "languageId":"cpp", "version":1, - "text":"#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif" + "text":"#include \n#include \n#include \n#if !defined(__ARM_ARCH) || !defined(__gnu_linux__)\n#error \"Invalid target\"\n#endif" } } } @@ -70,7 +76,7 @@ {"jsonrpc":"2.0","method":"exit"} # Generate a different compile_commands.json which does not point to the mock driver -# RUN: echo '[{"directory": "%/t.dir", "command": "gcc the-file.cpp -nostdinc --sysroot /my/sysroot/path -isysroot=/isysroot", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json +# RUN: echo '[{"directory": "%/t.dir", "command": "gcc the-file.cpp -nostdinc --sysroot /my/sysroot1/path --sysroot=/my/sysroot2/path -isysroot/isysroot1 -isysroot /isysroot2 -specs=nano.specs", "file": "the-file.cpp"}]' > %t.dir/compile_commands.json # Generate a clangd config file which points to the mock driver instead # RUN: echo 'CompileFlags:' > %t.dir/.clangd