brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.7 KiB · 4523af3 Raw
388 lines · cpp
1//===- DependencyScannerTest.cpp ------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/AST/ASTConsumer.h"10#include "clang/AST/DeclCXX.h"11#include "clang/AST/DeclGroup.h"12#include "clang/Frontend/ASTUnit.h"13#include "clang/Frontend/CompilerInstance.h"14#include "clang/Frontend/FrontendAction.h"15#include "clang/Frontend/FrontendActions.h"16#include "clang/Tooling/CompilationDatabase.h"17#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"18#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"19#include "clang/Tooling/Tooling.h"20#include "llvm/ADT/STLExtras.h"21#include "llvm/MC/TargetRegistry.h"22#include "llvm/Support/FormatVariadic.h"23#include "llvm/Support/Path.h"24#include "llvm/Support/TargetSelect.h"25#include "llvm/Testing/Support/Error.h"26#include "gtest/gtest.h"27#include <algorithm>28#include <string>29 30using namespace clang;31using namespace tooling;32using namespace dependencies;33 34namespace {35 36/// Prints out all of the gathered dependencies into a string.37class TestFileCollector : public DependencyFileGenerator {38public:39  TestFileCollector(DependencyOutputOptions &Opts,40                    std::vector<std::string> &Deps)41      : DependencyFileGenerator(Opts), Deps(Deps) {}42 43  void finishedMainFile(DiagnosticsEngine &Diags) override {44    auto NewDeps = getDependencies();45    llvm::append_range(Deps, NewDeps);46  }47 48private:49  std::vector<std::string> &Deps;50};51 52// FIXME: Use the regular Service/Worker/Collector APIs instead of53//        reimplementing the action.54class TestDependencyScanningAction : public tooling::ToolAction {55public:56  TestDependencyScanningAction(std::vector<std::string> &Deps) : Deps(Deps) {}57 58  bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,59                     FileManager *FileMgr,60                     std::shared_ptr<PCHContainerOperations> PCHContainerOps,61                     DiagnosticConsumer *DiagConsumer) override {62    CompilerInstance Compiler(std::move(Invocation),63                              std::move(PCHContainerOps));64    Compiler.setVirtualFileSystem(FileMgr->getVirtualFileSystemPtr());65    Compiler.setFileManager(FileMgr);66 67    Compiler.createDiagnostics(DiagConsumer, /*ShouldOwnClient=*/false);68    if (!Compiler.hasDiagnostics())69      return false;70 71    Compiler.createSourceManager();72    Compiler.addDependencyCollector(std::make_shared<TestFileCollector>(73        Compiler.getInvocation().getDependencyOutputOpts(), Deps));74 75    auto Action = std::make_unique<PreprocessOnlyAction>();76    return Compiler.ExecuteAction(*Action);77  }78 79private:80  std::vector<std::string> &Deps;81};82 83} // namespace84 85TEST(DependencyScanner, ScanDepsReuseFilemanager) {86  std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};87  StringRef CWD = "/root";88  FixedCompilationDatabase CDB(CWD, Compilation);89 90  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();91  VFS->setCurrentWorkingDirectory(CWD);92  auto Sept = llvm::sys::path::get_separator();93  std::string HeaderPath =94      std::string(llvm::formatv("{0}root{0}header.h", Sept));95  std::string SymlinkPath =96      std::string(llvm::formatv("{0}root{0}symlink.h", Sept));97  std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));98 99  VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));100  VFS->addHardLink(SymlinkPath, HeaderPath);101  VFS->addFile(TestPath, 0,102               llvm::MemoryBuffer::getMemBuffer(103                   "#include \"symlink.h\"\n#include \"header.h\"\n"));104 105  ClangTool Tool(CDB, {"test.cpp"}, std::make_shared<PCHContainerOperations>(),106                 VFS);107  Tool.clearArgumentsAdjusters();108  std::vector<std::string> Deps;109  TestDependencyScanningAction Action(Deps);110  Tool.run(&Action);111  using llvm::sys::path::convert_to_slash;112  // The first invocation should return dependencies in order of access.113  ASSERT_EQ(Deps.size(), 3u);114  EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");115  EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");116  EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");117 118  // The file manager should still have two FileEntries, as one file is a119  // hardlink.120  FileManager &Files = Tool.getFiles();121  EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);122 123  Deps.clear();124  Tool.run(&Action);125  // The second invocation should have the same order of dependencies.126  ASSERT_EQ(Deps.size(), 3u);127  EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");128  EXPECT_EQ(convert_to_slash(Deps[1]), "/root/symlink.h");129  EXPECT_EQ(convert_to_slash(Deps[2]), "/root/header.h");130 131  EXPECT_EQ(Files.getNumUniqueRealFiles(), 2u);132}133 134TEST(DependencyScanner, ScanDepsReuseFilemanagerSkippedFile) {135  std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};136  StringRef CWD = "/root";137  FixedCompilationDatabase CDB(CWD, Compilation);138 139  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();140  VFS->setCurrentWorkingDirectory(CWD);141  auto Sept = llvm::sys::path::get_separator();142  std::string HeaderPath =143      std::string(llvm::formatv("{0}root{0}header.h", Sept));144  std::string SymlinkPath =145      std::string(llvm::formatv("{0}root{0}symlink.h", Sept));146  std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));147  std::string Test2Path =148      std::string(llvm::formatv("{0}root{0}test2.cpp", Sept));149 150  VFS->addFile(HeaderPath, 0,151               llvm::MemoryBuffer::getMemBuffer("#pragma once\n"));152  VFS->addHardLink(SymlinkPath, HeaderPath);153  VFS->addFile(TestPath, 0,154               llvm::MemoryBuffer::getMemBuffer(155                   "#include \"header.h\"\n#include \"symlink.h\"\n"));156  VFS->addFile(Test2Path, 0,157               llvm::MemoryBuffer::getMemBuffer(158                   "#include \"symlink.h\"\n#include \"header.h\"\n"));159 160  ClangTool Tool(CDB, {"test.cpp", "test2.cpp"},161                 std::make_shared<PCHContainerOperations>(), VFS);162  Tool.clearArgumentsAdjusters();163  std::vector<std::string> Deps;164  TestDependencyScanningAction Action(Deps);165  Tool.run(&Action);166  using llvm::sys::path::convert_to_slash;167  ASSERT_EQ(Deps.size(), 6u);168  EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");169  EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");170  EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");171  EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test2.cpp");172  EXPECT_EQ(convert_to_slash(Deps[4]), "/root/symlink.h");173  EXPECT_EQ(convert_to_slash(Deps[5]), "/root/header.h");174}175 176TEST(DependencyScanner, ScanDepsReuseFilemanagerHasInclude) {177  std::vector<std::string> Compilation = {"-c", "-E", "-MT", "test.cpp.o"};178  StringRef CWD = "/root";179  FixedCompilationDatabase CDB(CWD, Compilation);180 181  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();182  VFS->setCurrentWorkingDirectory(CWD);183  auto Sept = llvm::sys::path::get_separator();184  std::string HeaderPath =185      std::string(llvm::formatv("{0}root{0}header.h", Sept));186  std::string SymlinkPath =187      std::string(llvm::formatv("{0}root{0}symlink.h", Sept));188  std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));189 190  VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));191  VFS->addHardLink(SymlinkPath, HeaderPath);192  VFS->addFile(193      TestPath, 0,194      llvm::MemoryBuffer::getMemBuffer("#if __has_include(\"header.h\") && "195                                       "__has_include(\"symlink.h\")\n#endif"));196 197  ClangTool Tool(CDB, {"test.cpp", "test.cpp"},198                 std::make_shared<PCHContainerOperations>(), VFS);199  Tool.clearArgumentsAdjusters();200  std::vector<std::string> Deps;201  TestDependencyScanningAction Action(Deps);202  Tool.run(&Action);203  using llvm::sys::path::convert_to_slash;204  ASSERT_EQ(Deps.size(), 6u);205  EXPECT_EQ(convert_to_slash(Deps[0]), "/root/test.cpp");206  EXPECT_EQ(convert_to_slash(Deps[1]), "/root/header.h");207  EXPECT_EQ(convert_to_slash(Deps[2]), "/root/symlink.h");208  EXPECT_EQ(convert_to_slash(Deps[3]), "/root/test.cpp");209  EXPECT_EQ(convert_to_slash(Deps[4]), "/root/header.h");210  EXPECT_EQ(convert_to_slash(Deps[5]), "/root/symlink.h");211}212 213TEST(DependencyScanner, ScanDepsWithFS) {214  std::vector<std::string> CommandLine = {"clang",215                                          "-target",216                                          "x86_64-apple-macosx10.7",217                                          "-c",218                                          "test.cpp",219                                          "-o"220                                          "test.cpp.o"};221  StringRef CWD = "/root";222 223  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();224  VFS->setCurrentWorkingDirectory(CWD);225  auto Sept = llvm::sys::path::get_separator();226  std::string HeaderPath =227      std::string(llvm::formatv("{0}root{0}header.h", Sept));228  std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));229 230  VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));231  VFS->addFile(TestPath, 0,232               llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));233 234  DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,235                                    ScanningOutputFormat::Make);236  DependencyScanningTool ScanTool(Service, VFS);237 238  std::string DepFile;239  ASSERT_THAT_ERROR(240      ScanTool.getDependencyFile(CommandLine, CWD).moveInto(DepFile),241      llvm::Succeeded());242  using llvm::sys::path::convert_to_slash;243  EXPECT_EQ(convert_to_slash(DepFile),244            "test.cpp.o: /root/test.cpp /root/header.h\n");245}246 247TEST(DependencyScanner, ScanDepsWithModuleLookup) {248  std::vector<std::string> CommandLine = {249      "clang",250      "-target",251      "x86_64-apple-macosx10.7",252      "-c",253      "test.m",254      "-o"255      "test.m.o",256      "-fmodules",257      "-I/root/SomeSources",258  };259  StringRef CWD = "/root";260 261  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();262  VFS->setCurrentWorkingDirectory(CWD);263  auto Sept = llvm::sys::path::get_separator();264  std::string OtherPath =265      std::string(llvm::formatv("{0}root{0}SomeSources{0}other.h", Sept));266  std::string TestPath = std::string(llvm::formatv("{0}root{0}test.m", Sept));267 268  VFS->addFile(OtherPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));269  VFS->addFile(TestPath, 0, llvm::MemoryBuffer::getMemBuffer("@import Foo;\n"));270 271  struct InterceptorFS : llvm::vfs::ProxyFileSystem {272    std::vector<std::string> StatPaths;273    std::vector<std::string> ReadFiles;274 275    InterceptorFS(IntrusiveRefCntPtr<FileSystem> UnderlyingFS)276        : ProxyFileSystem(UnderlyingFS) {}277 278    llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {279      StatPaths.push_back(Path.str());280      return ProxyFileSystem::status(Path);281    }282 283    llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>284    openFileForRead(const Twine &Path) override {285      ReadFiles.push_back(Path.str());286      return ProxyFileSystem::openFileForRead(Path);287    }288  };289 290  auto InterceptFS = llvm::makeIntrusiveRefCnt<InterceptorFS>(VFS);291 292  DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,293                                    ScanningOutputFormat::Make);294  DependencyScanningTool ScanTool(Service, InterceptFS);295 296  // This will fail with "fatal error: module 'Foo' not found" but it doesn't297  // matter, the point of the test is to check that files are not read298  // unnecessarily.299  std::string DepFile;300  ASSERT_THAT_ERROR(301      ScanTool.getDependencyFile(CommandLine, CWD).moveInto(DepFile),302      llvm::Failed());303 304  EXPECT_TRUE(!llvm::is_contained(InterceptFS->StatPaths, OtherPath));305  EXPECT_EQ(InterceptFS->ReadFiles, std::vector<std::string>{"test.m"});306}307 308TEST(DependencyScanner, ScanDepsWithDiagConsumer) {309  StringRef CWD = "/root";310 311  auto VFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();312  VFS->setCurrentWorkingDirectory(CWD);313  auto Sept = llvm::sys::path::get_separator();314  std::string HeaderPath =315      std::string(llvm::formatv("{0}root{0}header.h", Sept));316  std::string TestPath = std::string(llvm::formatv("{0}root{0}test.cpp", Sept));317  std::string AsmPath = std::string(llvm::formatv("{0}root{0}test.s", Sept));318 319  VFS->addFile(HeaderPath, 0, llvm::MemoryBuffer::getMemBuffer("\n"));320  VFS->addFile(TestPath, 0,321               llvm::MemoryBuffer::getMemBuffer("#include \"header.h\"\n"));322  VFS->addFile(AsmPath, 0, llvm::MemoryBuffer::getMemBuffer(""));323 324  DependencyScanningService Service(ScanningMode::DependencyDirectivesScan,325                                    ScanningOutputFormat::Make);326  DependencyScanningWorker Worker(Service, VFS);327 328  llvm::DenseSet<ModuleID> AlreadySeen;329  FullDependencyConsumer DC(AlreadySeen);330  CallbackActionController AC(nullptr);331 332  struct EnsureFinishedConsumer : public DiagnosticConsumer {333    bool Finished = false;334    void finish() override { Finished = true; }335  };336 337  {338    // Check that a successful scan calls DiagConsumer.finish().339    std::vector<std::string> Args = {"clang",340                                     "-target",341                                     "x86_64-apple-macosx10.7",342                                     "-c",343                                     "test.cpp",344                                     "-o"345                                     "test.cpp.o"};346 347    EnsureFinishedConsumer DiagConsumer;348    bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);349 350    EXPECT_TRUE(Success);351    EXPECT_EQ(DiagConsumer.getNumErrors(), 0u);352    EXPECT_TRUE(DiagConsumer.Finished);353  }354 355  {356    // Check that an invalid command-line, which never enters the scanning357    // action calls DiagConsumer.finish().358    std::vector<std::string> Args = {"clang", "-invalid-arg"};359    EnsureFinishedConsumer DiagConsumer;360    bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);361 362    EXPECT_FALSE(Success);363    EXPECT_GE(DiagConsumer.getNumErrors(), 1u);364    EXPECT_TRUE(DiagConsumer.Finished);365  }366 367  {368    // Check that a valid command line that produces no scanning jobs calls369    // DiagConsumer.finish().370    std::vector<std::string> Args = {"clang",371                                     "-target",372                                     "x86_64-apple-macosx10.7",373                                     "-c",374                                     "-x",375                                     "assembler",376                                     "test.s",377                                     "-o"378                                     "test.cpp.o"};379 380    EnsureFinishedConsumer DiagConsumer;381    bool Success = Worker.computeDependencies(CWD, Args, DC, AC, DiagConsumer);382 383    EXPECT_FALSE(Success);384    EXPECT_EQ(DiagConsumer.getNumErrors(), 1u);385    EXPECT_TRUE(DiagConsumer.Finished);386  }387}388