This removes the dependency on clangDriver from clangFrontend and flangFrontend. This refactoring is part of a broader effort to support driver-managed builds for compilations using C++ named modules and/or Clang modules. It is required for linking the dependency scanning tooling against the driver without introducing cyclic dependencies, which would otherwise cause build failures when dynamic linking is enabled. In particular, clangFrontend must no longer depend on clangDriver for this to be possible. This change was discussed in the following RFC: https://discourse.llvm.org/t/rfc-new-clangoptions-library-remove-dependency-on-clangdriver-from-clangfrontend-and-flangfrontend/88773
204 lines
7.2 KiB
C++
204 lines
7.2 KiB
C++
//===- unittests/Serialization/ModuleCacheTest.cpp - CI tests -------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Basic/FileManager.h"
|
|
#include "clang/Driver/CreateInvocationFromArgs.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Frontend/Utils.h"
|
|
#include "clang/Lex/HeaderSearch.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/VirtualFileSystem.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace llvm;
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
|
|
class ModuleCacheTest : public ::testing::Test {
|
|
void SetUp() override {
|
|
ASSERT_FALSE(sys::fs::createUniqueDirectory("modulecache-test", TestDir));
|
|
|
|
ModuleCachePath = SmallString<256>(TestDir);
|
|
sys::path::append(ModuleCachePath, "mcp");
|
|
ASSERT_FALSE(sys::fs::create_directories(ModuleCachePath));
|
|
}
|
|
|
|
void TearDown() override { sys::fs::remove_directories(TestDir); }
|
|
|
|
public:
|
|
SmallString<256> TestDir;
|
|
SmallString<256> ModuleCachePath;
|
|
|
|
void addFile(StringRef Path, StringRef Contents) {
|
|
ASSERT_FALSE(sys::path::is_absolute(Path));
|
|
|
|
SmallString<256> AbsPath(TestDir);
|
|
sys::path::append(AbsPath, Path);
|
|
|
|
std::error_code EC;
|
|
ASSERT_FALSE(
|
|
sys::fs::create_directories(llvm::sys::path::parent_path(AbsPath)));
|
|
llvm::raw_fd_ostream OS(AbsPath, EC);
|
|
ASSERT_FALSE(EC);
|
|
OS << Contents;
|
|
}
|
|
|
|
void addDuplicateFrameworks() {
|
|
addFile("test.m", R"cpp(
|
|
@import Top;
|
|
)cpp");
|
|
|
|
addFile("frameworks/Top.framework/Headers/top.h", R"cpp(
|
|
@import M;
|
|
)cpp");
|
|
addFile("frameworks/Top.framework/Modules/module.modulemap", R"cpp(
|
|
framework module Top [system] {
|
|
header "top.h"
|
|
export *
|
|
}
|
|
)cpp");
|
|
|
|
addFile("frameworks/M.framework/Headers/m.h", R"cpp(
|
|
void foo();
|
|
)cpp");
|
|
addFile("frameworks/M.framework/Modules/module.modulemap", R"cpp(
|
|
framework module M [system] {
|
|
header "m.h"
|
|
export *
|
|
}
|
|
)cpp");
|
|
|
|
addFile("frameworks2/M.framework/Headers/m.h", R"cpp(
|
|
void foo();
|
|
)cpp");
|
|
addFile("frameworks2/M.framework/Modules/module.modulemap", R"cpp(
|
|
framework module M [system] {
|
|
header "m.h"
|
|
export *
|
|
}
|
|
)cpp");
|
|
}
|
|
|
|
std::unique_ptr<CompilerInvocation>
|
|
createInvocationAndEnableFree(ArrayRef<const char *> Args,
|
|
CreateInvocationOptions Opts) {
|
|
std::unique_ptr<CompilerInvocation> Invocation =
|
|
createInvocation(Args, Opts);
|
|
if (Invocation)
|
|
Invocation->getFrontendOpts().DisableFree = false;
|
|
|
|
return Invocation;
|
|
}
|
|
};
|
|
|
|
TEST_F(ModuleCacheTest, CachedModuleNewPath) {
|
|
addDuplicateFrameworks();
|
|
|
|
SmallString<256> MCPArg("-fmodules-cache-path=");
|
|
MCPArg.append(ModuleCachePath);
|
|
CreateInvocationOptions CIOpts;
|
|
CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
|
|
DiagnosticOptions DiagOpts;
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
|
|
CompilerInstance::createDiagnostics(*CIOpts.VFS, DiagOpts);
|
|
CIOpts.Diags = Diags;
|
|
|
|
// First run should pass with no errors
|
|
const char *Args[] = {"clang", "-fmodules", "-Fframeworks",
|
|
MCPArg.c_str(), "-working-directory", TestDir.c_str(),
|
|
"test.m"};
|
|
std::shared_ptr<CompilerInvocation> Invocation =
|
|
createInvocationAndEnableFree(Args, CIOpts);
|
|
ASSERT_TRUE(Invocation);
|
|
CompilerInstance Instance(std::move(Invocation));
|
|
Instance.setVirtualFileSystem(CIOpts.VFS);
|
|
Instance.setDiagnostics(Diags);
|
|
SyntaxOnlyAction Action;
|
|
ASSERT_TRUE(Instance.ExecuteAction(Action));
|
|
ASSERT_FALSE(Diags->hasErrorOccurred());
|
|
|
|
// Now add `frameworks2` to the search path. `Top.pcm` will have a reference
|
|
// to the `M` from `frameworks`, but a search will find the `M` from
|
|
// `frameworks2` - causing a mismatch and it to be considered out of date.
|
|
//
|
|
// Normally this would be fine - `M` and the modules it depends on would be
|
|
// rebuilt. However, since we have a shared module cache and thus an already
|
|
// finalized `Top`, recompiling `Top` will cause the existing module to be
|
|
// removed from the cache, causing possible crashed if it is ever used.
|
|
//
|
|
// Make sure that an error occurs instead.
|
|
const char *Args2[] = {"clang", "-fmodules", "-Fframeworks2",
|
|
"-Fframeworks", MCPArg.c_str(), "-working-directory",
|
|
TestDir.c_str(), "test.m"};
|
|
std::shared_ptr<CompilerInvocation> Invocation2 =
|
|
createInvocationAndEnableFree(Args2, CIOpts);
|
|
ASSERT_TRUE(Invocation2);
|
|
CompilerInstance Instance2(std::move(Invocation2),
|
|
Instance.getPCHContainerOperations(),
|
|
&Instance.getModuleCache());
|
|
Instance2.setVirtualFileSystem(CIOpts.VFS);
|
|
Instance2.setDiagnostics(Diags);
|
|
SyntaxOnlyAction Action2;
|
|
ASSERT_FALSE(Instance2.ExecuteAction(Action2));
|
|
ASSERT_TRUE(Diags->hasErrorOccurred());
|
|
}
|
|
|
|
TEST_F(ModuleCacheTest, CachedModuleNewPathAllowErrors) {
|
|
addDuplicateFrameworks();
|
|
|
|
SmallString<256> MCPArg("-fmodules-cache-path=");
|
|
MCPArg.append(ModuleCachePath);
|
|
CreateInvocationOptions CIOpts;
|
|
CIOpts.VFS = llvm::vfs::createPhysicalFileSystem();
|
|
DiagnosticOptions DiagOpts;
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
|
|
CompilerInstance::createDiagnostics(*CIOpts.VFS, DiagOpts);
|
|
CIOpts.Diags = Diags;
|
|
|
|
// First run should pass with no errors
|
|
const char *Args[] = {"clang", "-fmodules", "-Fframeworks",
|
|
MCPArg.c_str(), "-working-directory", TestDir.c_str(),
|
|
"test.m"};
|
|
std::shared_ptr<CompilerInvocation> Invocation =
|
|
createInvocationAndEnableFree(Args, CIOpts);
|
|
ASSERT_TRUE(Invocation);
|
|
CompilerInstance Instance(std::move(Invocation));
|
|
Instance.setVirtualFileSystem(CIOpts.VFS);
|
|
Instance.setDiagnostics(Diags);
|
|
SyntaxOnlyAction Action;
|
|
ASSERT_TRUE(Instance.ExecuteAction(Action));
|
|
ASSERT_FALSE(Diags->hasErrorOccurred());
|
|
|
|
// Same as `CachedModuleNewPath` but while allowing errors. This is a hard
|
|
// failure where the module wasn't created, so it should still fail.
|
|
const char *Args2[] = {
|
|
"clang", "-fmodules", "-Fframeworks2",
|
|
"-Fframeworks", MCPArg.c_str(), "-working-directory",
|
|
TestDir.c_str(), "-Xclang", "-fallow-pcm-with-compiler-errors",
|
|
"test.m"};
|
|
std::shared_ptr<CompilerInvocation> Invocation2 =
|
|
createInvocationAndEnableFree(Args2, CIOpts);
|
|
ASSERT_TRUE(Invocation2);
|
|
CompilerInstance Instance2(std::move(Invocation2),
|
|
Instance.getPCHContainerOperations(),
|
|
&Instance.getModuleCache());
|
|
Instance2.setVirtualFileSystem(CIOpts.VFS);
|
|
Instance2.setDiagnostics(Diags);
|
|
SyntaxOnlyAction Action2;
|
|
ASSERT_FALSE(Instance2.ExecuteAction(Action2));
|
|
ASSERT_TRUE(Diags->hasErrorOccurred());
|
|
}
|
|
|
|
} // anonymous namespace
|