forked from OSchip/llvm-project
180 lines
6.3 KiB
C++
180 lines
6.3 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/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/CompilerInvocation.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Lex/HeaderSearch.h"
|
|
#include "llvm/ADT/SmallString.h"
|
|
#include "llvm/Support/FileSystem.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");
|
|
}
|
|
};
|
|
|
|
TEST_F(ModuleCacheTest, CachedModuleNewPath) {
|
|
addDuplicateFrameworks();
|
|
|
|
SmallString<256> MCPArg("-fmodules-cache-path=");
|
|
MCPArg.append(ModuleCachePath);
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
|
|
CompilerInstance::createDiagnostics(new DiagnosticOptions());
|
|
|
|
// 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 =
|
|
createInvocationFromCommandLine(Args, Diags);
|
|
ASSERT_TRUE(Invocation);
|
|
CompilerInstance Instance;
|
|
Instance.setDiagnostics(Diags.get());
|
|
Instance.setInvocation(Invocation);
|
|
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 =
|
|
createInvocationFromCommandLine(Args2, Diags);
|
|
ASSERT_TRUE(Invocation2);
|
|
CompilerInstance Instance2(Instance.getPCHContainerOperations(),
|
|
&Instance.getModuleCache());
|
|
Instance2.setDiagnostics(Diags.get());
|
|
Instance2.setInvocation(Invocation2);
|
|
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);
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
|
|
CompilerInstance::createDiagnostics(new DiagnosticOptions());
|
|
|
|
// 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 =
|
|
createInvocationFromCommandLine(Args, Diags);
|
|
ASSERT_TRUE(Invocation);
|
|
CompilerInstance Instance;
|
|
Instance.setDiagnostics(Diags.get());
|
|
Instance.setInvocation(Invocation);
|
|
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 =
|
|
createInvocationFromCommandLine(Args2, Diags);
|
|
ASSERT_TRUE(Invocation2);
|
|
CompilerInstance Instance2(Instance.getPCHContainerOperations(),
|
|
&Instance.getModuleCache());
|
|
Instance2.setDiagnostics(Diags.get());
|
|
Instance2.setInvocation(Invocation2);
|
|
SyntaxOnlyAction Action2;
|
|
ASSERT_FALSE(Instance2.ExecuteAction(Action2));
|
|
ASSERT_TRUE(Diags->hasErrorOccurred());
|
|
}
|
|
|
|
} // anonymous namespace
|