cling/lib/Interpreter/CIFactory.cpp
Jonas Hahnfeld 7695c7302c Turn off delayed template parsing on Windows
It is "a deprecated technique".

Co-authored-by: Bertrand Bellenot <Bertrand.Bellenot@cern.ch>
2024-08-14 13:14:06 +02:00

1790 lines
71 KiB
C++

//--------------------------------------------------------------------*- C++ -*-
// CLING - the C++ LLVM-based InterpreterG :)
// author: Axel Naumann <axel@cern.ch>
//
// This file is dual-licensed: you can choose to license it under the University
// of Illinois Open Source License or the GNU Lesser General Public License. See
// LICENSE.TXT for details.
//------------------------------------------------------------------------------
#include "ClingUtils.h"
#include <cling-compiledata.h>
#include "cling/Interpreter/CIFactory.h"
#include "cling/Interpreter/InvocationOptions.h"
#include "cling/Utils/Output.h"
#include "cling/Utils/Paths.h"
#include "cling/Utils/Platform.h"
#include "cling/Utils/Utils.h"
#include "clang/AST/ASTContext.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/Driver/Compilation.h"
#include "clang/Driver/Driver.h"
#include "clang/Driver/Job.h"
#include "clang/Driver/Tool.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/FrontendPluginRegistry.h"
#include "clang/Frontend/MultiplexConsumer.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "clang/Frontend/VerifyDiagnosticConsumer.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PreprocessorOptions.h"
#include "clang/Sema/Sema.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "clang/Serialization/ASTReader.h"
#include "clang/Serialization/ASTWriter.h"
#include "clang/Serialization/SerializationDiagnostic.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetOptions.h"
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <limits>
#include <memory>
using namespace clang;
using namespace cling;
namespace {
static constexpr unsigned CxxStdCompiledWith() {
// The value of __cplusplus in GCC < 5.0 (e.g. 4.9.3) when
// either -std=c++1y or -std=c++14 is specified is 201300L, which fails
// the test for C++14 or more (201402L) as previously specified.
// I would claim that the check should be relaxed to:
#if __cplusplus > 201703L
return 20;
#elif __cplusplus > 201402L
return 17;
#elif __cplusplus > 201103L || (defined(_WIN32) && _MSC_VER >= 1900)
return 14;
#elif __cplusplus >= 201103L
return 11;
#else
#error "Unknown __cplusplus version"
#endif
}
// This function isn't referenced outside its translation unit, but it
// can't use the "static" keyword because its address is used for
// GetMainExecutable (since some platforms don't support taking the
// address of main, and some platforms can't implement GetMainExecutable
// without being given the address of a function in the main executable).
std::string GetExecutablePath(const char *Argv0) {
// This just needs to be some symbol in the binary; C++ doesn't
// allow taking the address of ::main however.
void *MainAddr = (void*) intptr_t(GetExecutablePath);
return llvm::sys::fs::getMainExecutable(Argv0, MainAddr);
}
class AdditionalArgList {
typedef std::vector< std::pair<const char*,std::string> > container_t;
container_t m_Saved;
public:
void addArgument(const char* arg, std::string value = std::string()) {
m_Saved.push_back(std::make_pair(arg,std::move(value)));
}
container_t::const_iterator begin() const { return m_Saved.begin(); }
container_t::const_iterator end() const { return m_Saved.end(); }
bool empty() const { return m_Saved.empty(); }
};
#ifndef _MSC_VER
static void ReadCompilerIncludePaths(const char* Compiler,
llvm::SmallVectorImpl<char>& Buf,
AdditionalArgList& Args,
bool Verbose) {
std::string CppInclQuery("LC_ALL=C ");
CppInclQuery.append(Compiler);
CppInclQuery.append(" -xc++ -E -v /dev/null 2>&1 |"
" sed -n -e '/^.include/,${' -e '/^ \\/.*++/p' -e '}'");
if (Verbose)
cling::log() << "Looking for C++ headers with:\n " << CppInclQuery << "\n";
if (FILE *PF = ::popen(CppInclQuery.c_str(), "r")) {
Buf.resize(Buf.capacity_in_bytes());
while (fgets(&Buf[0], Buf.capacity_in_bytes(), PF) && Buf[0]) {
llvm::StringRef Path(&Buf[0]);
// Skip leading and trailing whitespace
Path = Path.trim();
if (!Path.empty()) {
if (!llvm::sys::fs::is_directory(Path)) {
if (Verbose)
cling::utils::LogNonExistantDirectory(Path);
}
else
Args.addArgument("-cxx-isystem", Path.str());
}
}
::pclose(PF);
} else {
::perror("popen failure");
// Don't be overly verbose, we already printed the command
if (!Verbose)
cling::errs() << " for '" << CppInclQuery << "'\n";
}
// Return the query in Buf on failure
if (Args.empty()) {
Buf.resize(0);
Buf.insert(Buf.begin(), CppInclQuery.begin(), CppInclQuery.end());
} else if (Verbose) {
cling::log() << "Found:\n";
for (const auto& Arg : Args)
cling::log() << " " << Arg.second << "\n";
}
}
static bool AddCxxPaths(llvm::StringRef PathStr, AdditionalArgList& Args,
bool Verbose) {
if (Verbose)
cling::log() << "Looking for C++ headers in \"" << PathStr << "\"\n";
llvm::SmallVector<llvm::StringRef, 6> Paths;
if (!utils::SplitPaths(PathStr, Paths, utils::kFailNonExistant,
platform::kEnvDelim, Verbose))
return false;
if (Verbose) {
cling::log() << "Found:\n";
for (llvm::StringRef Path : Paths)
cling::log() << " " << Path << "\n";
}
for (llvm::StringRef Path : Paths)
Args.addArgument("-cxx-isystem", Path.str());
return true;
}
#endif
static std::string getResourceDir(const char* llvmdir) {
if (!llvmdir) {
// FIXME: The first arg really does need to be argv[0] on FreeBSD.
//
// Note: The second arg is not used for Apple, FreeBSD, Linux,
// or cygwin, and can only be used on systems which support
// the use of dladdr().
//
// Note: On linux and cygwin this uses /proc/self/exe to find the path
// Note: On Apple it uses _NSGetExecutablePath().
// Note: On FreeBSD it uses getprogpath().
// Note: Otherwise it uses dladdr().
//
return CompilerInvocation::GetResourcesPath(
"cling", (void*)intptr_t(GetExecutablePath));
} else {
std::string resourcePath;
llvm::SmallString<512> tmp(llvmdir);
llvm::sys::path::append(tmp, "lib", "clang", CLANG_VERSION_MAJOR_STRING);
resourcePath.assign(&tmp[0], tmp.size());
return resourcePath;
}
}
///\brief Adds standard library -I used by whatever compiler is found in PATH.
static void AddHostArguments(llvm::StringRef clingBin,
std::vector<const char*>& args,
const char* llvmdir, const CompilerOptions& opts) {
(void)clingBin;
static AdditionalArgList sArguments;
if (sArguments.empty()) {
const bool Verbose = opts.Verbose;
#ifdef _MSC_VER
// When built with access to the proper Windows APIs, try to actually find
// the correct include paths first. Init for UnivSDK.empty check below.
std::string VSDir, WinSDK,
UnivSDK(opts.NoBuiltinInc ? "" : CLING_UCRT_VERSION);
if (platform::GetVisualStudioDirs(VSDir,
opts.NoBuiltinInc ? nullptr : &WinSDK,
opts.NoBuiltinInc ? nullptr : &UnivSDK,
Verbose)) {
if (!opts.NoCXXInc) {
// The Visual Studio 2017 path is very different than the previous
// versions (see also GetVisualStudioDirs() in PlatformWin.cpp)
const std::string VSIncl = VSDir + "\\include";
if (Verbose)
cling::log() << "Adding VisualStudio SDK: '" << VSIncl << "'\n";
sArguments.addArgument("-I", std::move(VSIncl));
}
if (!opts.NoBuiltinInc) {
if (!WinSDK.empty()) {
WinSDK.append("\\include");
if (Verbose)
cling::log() << "Adding Windows SDK: '" << WinSDK << "'\n";
sArguments.addArgument("-I", std::move(WinSDK));
} else {
// Since Visual Studio 2017, this is not valid anymore...
VSDir.append("\\VC\\PlatformSDK\\Include");
if (Verbose)
cling::log() << "Adding Platform SDK: '" << VSDir << "'\n";
sArguments.addArgument("-I", std::move(VSDir));
}
}
}
#if LLVM_MSC_PREREQ(1900)
if (!UnivSDK.empty()) {
if (Verbose)
cling::log() << "Adding UniversalCRT SDK: '" << UnivSDK << "'\n";
sArguments.addArgument("-I", std::move(UnivSDK));
}
#endif
// Windows headers use '__declspec(dllexport) __cdecl' for most funcs
// causing a lot of warnings for different redeclarations (eg. coming from
// the test suite).
// Do not warn about such cases.
sArguments.addArgument("-Wno-dll-attribute-on-redeclaration");
sArguments.addArgument("-Wno-inconsistent-dllimport");
// Assume Windows.h might be included, and don't spew a ton of warnings
sArguments.addArgument("-Wno-ignored-attributes");
sArguments.addArgument("-Wno-nonportable-include-path");
sArguments.addArgument("-Wno-microsoft-enum-value");
sArguments.addArgument("-Wno-expansion-to-defined");
// silent many warnings (mostly during ROOT compilation)
sArguments.addArgument("-Wno-constant-conversion");
sArguments.addArgument("-Wno-unknown-escape-sequence");
sArguments.addArgument("-Wno-microsoft-unqualified-friend");
sArguments.addArgument("-Wno-deprecated-declarations");
//sArguments.addArgument("-fno-threadsafe-statics");
//sArguments.addArgument("-Wno-dllimport-static-field-def");
//sArguments.addArgument("-Wno-microsoft-template");
#else // _MSC_VER
// Skip LLVM_CXX execution if -nostdinc++ was provided.
if (!opts.NoCXXInc) {
// Need sArguments.empty as a check condition later
assert(sArguments.empty() && "Arguments not empty");
SmallString<2048> buffer;
#ifdef _LIBCPP_VERSION
// Try to use a version of clang that is located next to cling
// in case cling was built with a new/custom libc++
std::string clang = llvm::sys::path::parent_path(clingBin).str();
buffer.assign(clang);
llvm::sys::path::append(buffer, "clang");
clang.assign(&buffer[0], buffer.size());
if (llvm::sys::fs::is_regular_file(clang)) {
if (!opts.StdLib) {
#if defined(_LIBCPP_VERSION)
clang.append(" -stdlib=libc++");
#elif defined(__GLIBCXX__)
clang.append(" -stdlib=libstdc++");
#endif
}
ReadCompilerIncludePaths(clang.c_str(), buffer, sArguments, Verbose);
}
#endif // _LIBCPP_VERSION
// First try the relative path 'g++'
#ifdef CLING_CXX_RLTV
if (sArguments.empty())
ReadCompilerIncludePaths(CLING_CXX_RLTV, buffer, sArguments, Verbose);
#endif
// Then try the include directory cling was built with
#ifdef CLING_CXX_INCL
if (sArguments.empty())
AddCxxPaths(CLING_CXX_INCL, sArguments, Verbose);
#endif
// Finally try the absolute path i.e.: '/usr/bin/g++'
#ifdef CLING_CXX_PATH
if (sArguments.empty())
ReadCompilerIncludePaths(CLING_CXX_PATH, buffer, sArguments, Verbose);
#endif
if (sArguments.empty()) {
// buffer is a copy of the query string that failed
cling::errs() << "ERROR in cling::CIFactory::createCI(): cannot extract"
" standard library include paths!\n";
#if defined(CLING_CXX_PATH) || defined(CLING_CXX_RLTV)
// Only when ReadCompilerIncludePaths called do we have the command
// Verbose has already printed the command
if (!Verbose)
cling::errs() << "Invoking:\n " << buffer.c_str() << "\n";
cling::errs() << "Results was:\n";
const int ExitCode = system(buffer.c_str());
cling::errs() << "With exit code " << ExitCode << "\n";
#elif !defined(CLING_CXX_INCL)
// Technically a valid configuration that just wants to use libClangs
// internal header detection, but for now give a hint about why.
cling::errs() << "CLING_CXX_INCL, CLING_CXX_PATH, and CLING_CXX_RLTV"
" are undefined, there was probably an error during"
" configuration.\n";
#endif
} else
sArguments.addArgument("-nostdinc++");
}
#ifdef CLING_OSX_SYSROOT
sArguments.addArgument("-isysroot", CLING_OSX_SYSROOT);
#endif
#endif // _MSC_VER
if (!opts.ResourceDir && !opts.NoBuiltinInc) {
std::string resourcePath = getResourceDir(llvmdir);
// FIXME: Handle cases, where the cling is part of a library/framework.
// There we can't rely on the find executable logic.
if (!llvm::sys::fs::is_directory(resourcePath)) {
cling::errs()
<< "ERROR in cling::CIFactory::createCI():\n resource directory "
<< resourcePath << " not found!\n";
resourcePath = "";
} else {
sArguments.addArgument("-resource-dir", std::move(resourcePath));
}
}
}
for (auto& arg : sArguments) {
args.push_back(arg.first);
args.push_back(arg.second.c_str());
}
}
static void SetClingCustomLangOpts(LangOptions& Opts,
const CompilerOptions& CompilerOpts) {
Opts.EmitAllDecls = 0; // Otherwise if PCH attached will codegen all decls.
#ifdef _MSC_VER
#ifdef _DEBUG
// FIXME: This requires bufferoverflowu.lib, but adding:
// #pragma comment(lib, "bufferoverflowu.lib") still gives errors!
// Opts.setStackProtector(clang::LangOptions::SSPStrong);
#endif // _DEBUG
#ifdef _CPPRTTI
Opts.RTTIData = 1;
#else
Opts.RTTIData = 0;
#endif // _CPPRTTI
Opts.MSVolatile = 1;
Opts.Trigraphs = 0;
Opts.setDefaultCallingConv(clang::LangOptions::DCC_CDecl);
#else // !_MSC_VER
Opts.Trigraphs = 1;
// Opts.RTTIData = 1;
// Opts.DefaultCallingConventions = 1;
// Opts.StackProtector = 0;
#endif // _MSC_VER
Opts.IncrementalExtensions = 1;
Opts.Exceptions = 1;
if (Opts.CPlusPlus) {
Opts.CXXExceptions = 1;
}
//Opts.Modules = 1;
// See test/CodeUnloading/PCH/VTables.cpp which implicitly compares clang
// to cling lang options. They should be the same, we should not have to
// give extra lang options to their invocations on any platform.
// Except -fexceptions -fcxx-exceptions.
Opts.Deprecated = 1;
#ifdef __APPLE__
Opts.Blocks = 1;
Opts.MathErrno = 0;
#endif
#ifndef _WIN32
Opts.POSIXThreads = 1;
#endif
#ifdef __FAST_MATH__
Opts.FastMath = 1;
#endif
if (CompilerOpts.DefaultLanguage(&Opts)) {
#ifdef __STRICT_ANSI__
Opts.GNUMode = 0;
#else
Opts.GNUMode = 1;
#endif
Opts.GNUKeywords = 0;
}
}
static void SetClingTargetLangOpts(LangOptions& Opts,
const TargetInfo& Target,
const CompilerOptions& CompilerOpts) {
if (Target.getTriple().getOS() == llvm::Triple::Win32) {
Opts.MicrosoftExt = 1;
#ifdef _MSC_VER
Opts.MSCompatibilityVersion = (_MSC_VER * 100000);
Opts.MSVCCompat = 1;
Opts.ThreadsafeStatics = 0; // FIXME: this is removing the thread guard around static init!
#endif
// Disable delayed template parsing, it is "a deprecated technique".
Opts.DelayedTemplateParsing = 0;
} else {
Opts.MicrosoftExt = 0;
}
if (CompilerOpts.DefaultLanguage(&Opts)) {
#if _GLIBCXX_USE_FLOAT128
// We are compiling with libstdc++ with __float128 enabled.
if (!Target.hasFloat128Type()) {
// clang currently supports native __float128 only on few targets, and
// this target does not have it. The most visible consequence of this is
// a specialization
// __is_floating_point_helper<__float128>
// in include/c++/6.3.0/type_traits:344 that clang then rejects. The
// specialization is protected by !if _GLIBCXX_USE_FLOAT128 (which is
// unconditionally set in c++config.h) and #if !__STRICT_ANSI__. Tweak
// the latter by disabling GNUMode.
// the nvptx backend doesn't support 128 bit float
// a error message is not necessary
if(!CompilerOpts.CUDADevice) {
cling::errs()
<< "Disabling gnu++: "
"clang has no __float128 support on this target!\n";
}
Opts.GNUMode = 0;
}
#endif //_GLIBCXX_USE_FLOAT128
}
// Set char signedness to match how this file is built, i.e. flags like
// -f(no-)(un)signed-char.
Opts.CharIsSigned = std::numeric_limits<char>::is_signed;
}
// This must be a copy of clang::getClangToolFullVersion(). Luckily
// we'll notice quickly if it ever changes! :-)
static std::string CopyOfClanggetClangToolFullVersion(StringRef ToolName) {
cling::stdstrstream OS;
#ifdef CLANG_VENDOR
OS << CLANG_VENDOR;
#endif
OS << ToolName << " version " CLANG_VERSION_STRING;
std::string repo = getClangFullRepositoryVersion();
if (!repo.empty()) {
OS << " " << repo;
}
// If vendor supplied, include the base LLVM version as well.
#ifdef CLANG_VENDOR
OS << " (based on LLVM " << PACKAGE_VERSION << ")";
#endif
return OS.str();
}
///\brief Check the compile-time clang version vs the run-time clang version,
/// a mismatch could cause havoc. Reports if clang versions differ.
static void CheckClangCompatibility() {
if (clang::getClangToolFullVersion("cling")
!= CopyOfClanggetClangToolFullVersion("cling"))
cling::errs()
<< "Warning in cling::CIFactory::createCI():\n "
"Using incompatible clang library! "
"Please use the one provided by cling!\n";
return;
}
/// \brief Retrieves the clang CC1 specific flags out of the compilation's
/// jobs. Returns NULL on error.
static const llvm::opt::ArgStringList*
GetCC1Arguments(clang::driver::Compilation *Compilation,
clang::DiagnosticsEngine* = nullptr) {
// We expect to get back exactly one Command job, if we didn't something
// failed. Extract that job from the Compilation.
const clang::driver::JobList &Jobs = Compilation->getJobs();
if (!Jobs.size() || !isa<clang::driver::Command>(*Jobs.begin())) {
// diagnose this better...
cling::errs() << "No Command jobs were built.\n";
return nullptr;
}
// The one job we find should be to invoke clang again.
const clang::driver::Command *Cmd
= cast<clang::driver::Command>(&(*Jobs.begin()));
if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") {
// diagnose this better...
cling::errs() << "Clang wasn't the first job.\n";
return nullptr;
}
return &Cmd->getArguments();
}
/// \brief Splits the given environment variable by the path separator.
/// Can be used to extract the paths from LD_LIBRARY_PATH.
static SmallVector<StringRef, 4> getPathsFromEnv(const char* EnvVar) {
if (!EnvVar) return {};
SmallVector<StringRef, 4> Paths;
StringRef(EnvVar).split(Paths, llvm::sys::EnvPathSeparator, -1, false);
return Paths;
}
/// \brief Adds all the paths to the prebuilt module paths of the given
/// HeaderSearchOptions.
static void addPrebuiltModulePaths(clang::HeaderSearchOptions& Opts,
const SmallVectorImpl<StringRef>& Paths) {
for (StringRef ModulePath : Paths)
Opts.AddPrebuiltModulePath(ModulePath);
}
static std::string getIncludePathForHeader(const clang::HeaderSearch& HS,
llvm::StringRef header) {
for (auto Dir = HS.search_dir_begin(), E = HS.search_dir_end();
Dir != E; ++Dir) {
llvm::SmallString<512> headerPath(Dir->getName());
llvm::sys::path::append(headerPath, header);
if (llvm::sys::fs::exists(headerPath.str()))
return Dir->getName().str();
}
return {};
}
static void collectModuleMaps(clang::CompilerInstance& CI,
llvm::SmallVectorImpl<std::string> &ModuleMapFiles) {
assert(CI.getLangOpts().Modules && "Using overlay without -fmodules");
const clang::HeaderSearch& HS = CI.getPreprocessor().getHeaderSearchInfo();
clang::HeaderSearchOptions& HSOpts = CI.getHeaderSearchOpts();
// We can't use "assert.h" because it is defined in the resource dir, too.
#ifdef _WIN32
llvm::SmallString<256> vcIncLoc(getIncludePathForHeader(HS, "vcruntime.h"));
llvm::SmallString<256> servIncLoc(getIncludePathForHeader(HS, "windows.h"));
#endif
llvm::SmallString<128> cIncLoc(getIncludePathForHeader(HS, "time.h"));
// FIXME: Diagnose this until we teach cling how to work without libc.
if (!llvm::sys::fs::exists(cIncLoc))
llvm::errs()
<< "C system headers (glibc/Xcode/Windows SDK) must be installed.\n";
llvm::SmallString<256> stdIncLoc(getIncludePathForHeader(HS, "cassert"));
llvm::SmallString<256> boostIncLoc(getIncludePathForHeader(HS, "boost/version.hpp"));
llvm::SmallString<256> tinyxml2IncLoc(getIncludePathForHeader(HS, "tinyxml2.h"));
llvm::SmallString<256> cudaIncLoc(getIncludePathForHeader(HS, "cuda.h"));
llvm::SmallString<256> vcVcIncLoc(getIncludePathForHeader(HS, "Vc/Vc"));
llvm::SmallString<256> clingIncLoc(getIncludePathForHeader(HS,
"cling/Interpreter/RuntimeUniverse.h"));
// Re-add cling as the modulemap are in cling/*modulemap
llvm::sys::path::append(clingIncLoc, "cling");
// Construct a column of modulemap overlay file if needed.
auto maybeAppendOverlayEntry
= [&HSOpts, &ModuleMapFiles](llvm::StringRef SystemDir,
const std::string& Filename,
const std::string& Location,
std::string& overlay,
bool RegisterModuleMap,
bool AllowModulemapOverride)
-> void {
assert(llvm::sys::fs::exists(SystemDir) && "Must exist!");
std::string modulemapFilename = "module.modulemap";
llvm::SmallString<512> systemLoc(SystemDir);
llvm::sys::path::append(systemLoc, modulemapFilename);
// Check if we need to mount a custom modulemap. We may have it, for
// instance when we are on osx or using libc++.
if (AllowModulemapOverride && llvm::sys::fs::exists(systemLoc.str())) {
if (HSOpts.Verbose)
cling::log() << "Loading '" << systemLoc.str() << "'\n";
// If the library had its own modulemap file, use it. This should handle
// the case where we use libc++ on Unix.
if (!HSOpts.ImplicitModuleMaps)
ModuleMapFiles.push_back(systemLoc.str().str());
return;
}
llvm::SmallString<512> originalLoc(Location);
assert(llvm::sys::fs::exists(originalLoc.str()) && "Must exist!");
llvm::sys::path::append(originalLoc, Filename);
assert(llvm::sys::fs::exists(originalLoc.str()));
if (HSOpts.Verbose)
cling::log() << "'" << systemLoc << "' does not exist. Mounting '"
<< originalLoc.str() << "' as '" << systemLoc << "'\n";
if (!HSOpts.ImplicitModuleMaps) {
modulemapFilename = Filename;
llvm::sys::path::remove_filename(systemLoc);
llvm::sys::path::append(systemLoc, modulemapFilename);
}
if (!overlay.empty())
overlay += ",\n";
overlay += "{ 'name': '" + SystemDir.str() + "', 'type': 'directory',\n";
overlay += "'contents': [\n { 'name': '" + modulemapFilename + "', ";
overlay += "'type': 'file',\n 'external-contents': '";
overlay += originalLoc.str().str() + "'\n";
overlay += "}\n ]\n }";
if (HSOpts.ImplicitModuleMaps)
return;
if (RegisterModuleMap)
ModuleMapFiles.push_back(systemLoc.str().str());
};
const llvm::Triple &Triple = CI.getTarget().getTriple();
if (!HSOpts.ImplicitModuleMaps) {
// Register the modulemap files.
llvm::SmallString<512> resourceDirLoc(HSOpts.ResourceDir);
llvm::sys::path::append(resourceDirLoc, "include", "module.modulemap");
ModuleMapFiles.push_back(resourceDirLoc.str().str());
llvm::SmallString<512> clingModuleMap(clingIncLoc);
llvm::sys::path::append(clingModuleMap, "module.modulemap");
ModuleMapFiles.push_back(clingModuleMap.str().str());
if (Triple.isMacOSX()) {
llvm::SmallString<512> libcModuleMap(cIncLoc);
llvm::sys::path::append(libcModuleMap, "module.modulemap");
ModuleMapFiles.push_back(libcModuleMap.str().str());
if (CI.getTarget().getSDKVersion() < VersionTuple(14, 4)) {
llvm::SmallString<512> stdModuleMap(stdIncLoc);
llvm::sys::path::append(stdModuleMap, "module.modulemap");
ModuleMapFiles.push_back(stdModuleMap.str().str());
}
}
}
std::string MOverlay;
#ifdef _WIN32
maybeAppendOverlayEntry(vcIncLoc.str(), "vcruntime.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
maybeAppendOverlayEntry(servIncLoc.str(), "services_msvc.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
maybeAppendOverlayEntry(cIncLoc.str(), "libc_msvc.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
maybeAppendOverlayEntry(stdIncLoc.str(), "std_msvc.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
#elif __APPLE__
if (Triple.isMacOSX() && CI.getTarget().getSDKVersion() >= VersionTuple(14, 4))
maybeAppendOverlayEntry(stdIncLoc.str(), "std_darwin.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
#else
maybeAppendOverlayEntry(cIncLoc.str(), "libc.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/true);
maybeAppendOverlayEntry(stdIncLoc.str(), "std.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/true);
#endif // _WIN32
if (!tinyxml2IncLoc.empty())
maybeAppendOverlayEntry(tinyxml2IncLoc.str(), "tinyxml2.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ false,
/*AllowModulemapOverride=*/ false);
if (!cudaIncLoc.empty())
maybeAppendOverlayEntry(cudaIncLoc.str(), "cuda.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
if (!vcVcIncLoc.empty())
maybeAppendOverlayEntry(vcVcIncLoc.str(), "vc.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ true,
/*AllowModulemapOverride=*/ false);
if (!boostIncLoc.empty()) {
// Add the modulemap in the include/boost folder not in include.
llvm::sys::path::append(boostIncLoc, "boost");
maybeAppendOverlayEntry(boostIncLoc.str(), "boost.modulemap",
clingIncLoc.str().str(), MOverlay,
/*RegisterModuleMap=*/ false,
/*AllowModulemapOverride=*/ false);
}
if (/*needsOverlay*/!MOverlay.empty()) {
// Virtual modulemap overlay file
MOverlay.insert(0, "{\n 'version': 0,\n 'roots': [\n");
MOverlay += "]\n }\n ]\n }\n";
const std::string VfsOverlayFileName = "modulemap.overlay.yaml";
if (HSOpts.Verbose)
cling::log() << VfsOverlayFileName << "\n" << MOverlay;
// Set up the virtual modulemap overlay file
std::unique_ptr<llvm::MemoryBuffer> Buffer =
llvm::MemoryBuffer::getMemBuffer(MOverlay);
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
llvm::vfs::getVFSFromYAML(std::move(Buffer), nullptr,
VfsOverlayFileName);
if (!FS.get())
llvm::errs() << "Error in modulemap.overlay!\n";
// Load virtual modulemap overlay file - we set up an OverlayFileSystem
// when calling createFileManager.
auto& OverlayVFS =
static_cast<llvm::vfs::OverlayFileSystem&>(CI.getVirtualFileSystem());
OverlayVFS.pushOverlay(FS);
}
}
static void setupCxxModules(clang::CompilerInstance& CI) {
assert(CI.getLangOpts().Modules);
clang::HeaderSearchOptions& HSOpts = CI.getHeaderSearchOpts();
// Register prebuilt module paths where we will lookup module files.
addPrebuiltModulePaths(HSOpts,
getPathsFromEnv(getenv("CLING_PREBUILT_MODULE_PATH")));
// Register all modulemaps necessary for cling to run. If we have specified
// -fno-implicit-module-maps then we have to add them explicitly to the list
// of modulemap files to load.
llvm::SmallVector<std::string, 4> ModuleMaps;
collectModuleMaps(CI, ModuleMaps);
assert(HSOpts.ImplicitModuleMaps == ModuleMaps.empty() &&
"We must have register the modulemaps by hand!");
// Prepend the modulemap files we attached so that they will be loaded.
if (!HSOpts.ImplicitModuleMaps) {
FrontendOptions& FrontendOpts = CI.getInvocation().getFrontendOpts();
FrontendOpts.ModuleMapFiles.insert(FrontendOpts.ModuleMapFiles.begin(),
ModuleMaps.begin(), ModuleMaps.end());
}
}
#if defined(_MSC_VER)
static void stringifyPreprocSetting(PreprocessorOptions& PPOpts,
const std::string &Name, int Val) {
smallstream Strm;
Strm << Name << "=" << Val;
if (std::find(PPOpts.Macros.begin(), PPOpts.Macros.end(),
std::make_pair(Name, true))
== PPOpts.Macros.end()
&& std::find(PPOpts.Macros.begin(), PPOpts.Macros.end(),
std::make_pair(Name, false))
== PPOpts.Macros.end())
PPOpts.addMacroDef(Strm.str());
}
#define STRINGIFY_PREPROC_SETTING(PP, name) \
stringifyPreprocSetting(PP, #name, name)
#endif
/// Set cling's preprocessor defines to match the cling binary.
static void SetPreprocessorFromBinary(PreprocessorOptions& PPOpts) {
#ifdef _MSC_VER
// FIXME: Stay consistent with the _HAS_EXCEPTIONS flag settings in SetClingCustomLangOpts
// STRINGIFY_PREPROC_SETTING(PPOpts, _HAS_EXCEPTIONS);
#ifdef _DEBUG
STRINGIFY_PREPROC_SETTING(PPOpts, _DEBUG);
#endif
#endif
// cling wants to JIT O1 by default. Might want to revisit once we have
// debug symbols.
PPOpts.addMacroDef("NDEBUG=1");
// Since cling, uses clang instead, macros always sees __CLANG__ defined
// In addition, clang also defined __GNUC__, we add the following two macros
// to allow scripts, and more important, dictionary generation to know which
// of the two is the underlying compiler.
#ifdef __clang__
PPOpts.addMacroDef("__CLING__clang__=" ClingStringify(__clang__));
#elif defined(__GNUC__)
PPOpts.addMacroDef("__CLING__GNUC__=" ClingStringify(__GNUC__));
PPOpts.addMacroDef("__CLING__GNUC_MINOR__=" ClingStringify(__GNUC_MINOR__));
#elif defined(_MSC_VER)
PPOpts.addMacroDef("__CLING__MSVC__=" ClingStringify(_MSC_VER));
#endif
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html
#ifdef _GLIBCXX_USE_CXX11_ABI
PPOpts.addMacroDef("_GLIBCXX_USE_CXX11_ABI="
ClingStringify(_GLIBCXX_USE_CXX11_ABI));
#endif
#if defined(_WIN32)
PPOpts.addMacroDef("CLING_EXPORT=__declspec(dllimport)");
// prevent compilation error G47C585C4: STL1000: Unexpected compiler
// version, expected Clang 6 or newer.
PPOpts.addMacroDef("_ALLOW_COMPILER_AND_STL_VERSION_MISMATCH");
#else
PPOpts.addMacroDef("CLING_EXPORT=");
#endif
}
/// Set target-specific preprocessor defines.
static void SetPreprocessorFromTarget(PreprocessorOptions& PPOpts,
const llvm::Triple& TTriple) {
if (TTriple.getEnvironment() == llvm::Triple::Cygnus) {
// clang "forgets" the basic arch part needed by winnt.h:
if (TTriple.getArch() == llvm::Triple::x86) {
PPOpts.addMacroDef("_X86_=1");
} else if (TTriple.getArch() == llvm::Triple::x86_64) {
PPOpts.addMacroDef("__x86_64=1");
} else {
cling::errs() << "Warning in cling::CIFactory::createCI():\n"
"unhandled target architecture "
<< TTriple.getArchName() << '\n';
}
}
}
template <class CONTAINER>
static void insertBehind(CONTAINER& To, const CONTAINER& From) {
To.insert(To.end(), From.begin(), From.end());
}
static void AddRuntimeIncludePaths(llvm::StringRef ClingBin,
clang::HeaderSearchOptions& HOpts) {
if (HOpts.Verbose)
cling::log() << "Adding runtime include paths:\n";
// Add configuration paths to interpreter's include files.
#ifdef CLING_INCLUDE_PATHS
if (HOpts.Verbose)
cling::log() << " \"" CLING_INCLUDE_PATHS "\"\n";
utils::AddIncludePaths(CLING_INCLUDE_PATHS, HOpts);
#endif
llvm::SmallString<512> P(ClingBin);
if (!P.empty()) {
// Remove /cling from foo/bin/clang
llvm::StringRef ExeIncl = llvm::sys::path::parent_path(P);
// Remove /bin from foo/bin
ExeIncl = llvm::sys::path::parent_path(ExeIncl);
P.resize(ExeIncl.size());
// Get foo/include
llvm::sys::path::append(P, "include");
if (llvm::sys::fs::is_directory(P.str())) {
utils::AddIncludePaths(P.str(), HOpts, nullptr);
llvm::sys::path::append(P, "clang");
if (!llvm::sys::fs::is_directory(P.str())) {
// LLVM is not installed. Try resolving clang from its usual location.
llvm::SmallString<512> PParent = llvm::sys::path::parent_path(P);
P = PParent;
llvm::sys::path::append(P, "..", "tools", "clang", "include");
if (llvm::sys::fs::is_directory(P.str()))
utils::AddIncludePaths(P.str(), HOpts, nullptr);
}
}
}
}
static llvm::IntrusiveRefCntPtr<DiagnosticsEngine>
SetupDiagnostics(DiagnosticOptions& DiagOpts, const std::string& ExeName) {
// The compiler invocation is the owner of the diagnostic options.
// Everything else points to them.
llvm::IntrusiveRefCntPtr<clang::DiagnosticIDs> DiagIDs(new DiagnosticIDs());
std::unique_ptr<TextDiagnosticPrinter>
DiagnosticPrinter(new TextDiagnosticPrinter(cling::errs(), &DiagOpts));
DiagnosticPrinter->setPrefix(ExeName);
llvm::IntrusiveRefCntPtr<DiagnosticsEngine>
Diags(new DiagnosticsEngine(DiagIDs, &DiagOpts,
DiagnosticPrinter.get(), /*Owns it*/ true));
DiagnosticPrinter.release();
return Diags;
}
static bool
SetupCompiler(CompilerInstance* CI, const CompilerOptions& CompilerOpts,
bool Lang = true, bool Targ = true) {
LangOptions& LangOpts = CI->getLangOpts();
// Set the language options, which cling needs.
// This may have already been done via a precompiled header
if (Lang)
SetClingCustomLangOpts(LangOpts, CompilerOpts);
PreprocessorOptions& PPOpts = CI->getInvocation().getPreprocessorOpts();
SetPreprocessorFromBinary(PPOpts);
// Sanity check that clang delivered the language standard requested
if (CompilerOpts.DefaultLanguage(&LangOpts)) {
switch (CxxStdCompiledWith()) {
case 20: assert(LangOpts.CPlusPlus20 && "Language version mismatch");
LLVM_FALLTHROUGH;
case 17: assert(LangOpts.CPlusPlus17 && "Language version mismatch");
LLVM_FALLTHROUGH;
case 14: assert(LangOpts.CPlusPlus14 && "Language version mismatch");
LLVM_FALLTHROUGH;
case 11: assert(LangOpts.CPlusPlus11 && "Language version mismatch");
break;
default: assert(false && "You have an unhandled C++ standard!");
}
}
PPOpts.addMacroDef("__CLING__");
if (LangOpts.CPlusPlus11 == 1)
PPOpts.addMacroDef("__CLING__CXX11");
if (LangOpts.CPlusPlus14 == 1)
PPOpts.addMacroDef("__CLING__CXX14");
DiagnosticsEngine& Diags = CI->getDiagnostics();
if (Diags.hasErrorOccurred()) {
cling::errs() << "Compiler error too early in initialization.\n";
return false;
}
CI->setTarget(TargetInfo::CreateTargetInfo(Diags,
CI->getInvocation().TargetOpts));
if (!CI->hasTarget()) {
cling::errs() << "Could not determine compiler target.\n";
return false;
}
CI->getTarget().adjust(Diags, LangOpts);
// This may have already been done via a precompiled header
if (Targ)
SetClingTargetLangOpts(LangOpts, CI->getTarget(), CompilerOpts);
SetPreprocessorFromTarget(PPOpts, CI->getTarget().getTriple());
return true;
}
class ActionScan {
std::set<const clang::driver::Action*> m_Visited;
llvm::SmallVector<clang::driver::Action::ActionClass, 2> m_Kinds;
bool find (const clang::driver::Action* A) {
if (A && !m_Visited.count(A)) {
if (std::find(m_Kinds.begin(), m_Kinds.end(), A->getKind()) !=
m_Kinds.end())
return true;
m_Visited.insert(A);
return find(*A->input_begin());
}
return false;
}
public:
ActionScan(clang::driver::Action::ActionClass a, int b = -1) {
m_Kinds.push_back(a);
if (b != -1)
m_Kinds.push_back(clang::driver::Action::ActionClass(b));
}
bool find (clang::driver::Compilation* C) {
for (clang::driver::Action* A : C->getActions()) {
if (find(A))
return true;
}
return false;
}
};
static void HandleProgramActions(CompilerInstance &CI) {
const clang::FrontendOptions& FrontendOpts = CI.getFrontendOpts();
if (FrontendOpts.ProgramAction == clang::frontend::ModuleFileInfo) {
// Copied from FrontendActions.cpp
// FIXME: Remove when we switch to the new driver.
class DumpModuleInfoListener : public ASTReaderListener {
llvm::raw_ostream &Out;
public:
DumpModuleInfoListener(llvm::raw_ostream &OS) : Out(OS) { }
#define DUMP_BOOLEAN(Value, Text) \
Out.indent(4) << Text << ": " << (Value? "Yes" : "No") << "\n"
bool ReadFullVersionInformation(StringRef FullVersion) override {
Out.indent(2)
<< "Generated by "
<< (FullVersion == getClangFullRepositoryVersion()? "this"
: "a different")
<< " Clang: " << FullVersion << "\n";
return ASTReaderListener::ReadFullVersionInformation(FullVersion);
}
void ReadModuleName(StringRef ModuleName) override {
Out.indent(2) << "Module name: " << ModuleName << "\n";
}
void ReadModuleMapFile(StringRef ModuleMapPath) override {
Out.indent(2) << "Module map file: " << ModuleMapPath << "\n";
}
bool ReadLanguageOptions(const LangOptions &LangOpts, bool /*Complain*/,
bool /*AllowCompatibleDifferences*/) override {
Out.indent(2) << "Language options:\n";
#define LANGOPT(Name, Bits, Default, Description) \
DUMP_BOOLEAN(LangOpts.Name, Description);
#define ENUM_LANGOPT(Name, Type, Bits, Default, Description) \
Out.indent(4) << Description << ": " \
<< static_cast<unsigned>(LangOpts.get##Name()) << "\n";
#define VALUE_LANGOPT(Name, Bits, Default, Description) \
Out.indent(4) << Description << ": " << LangOpts.Name << "\n";
#define BENIGN_LANGOPT(Name, Bits, Default, Description)
#define BENIGN_ENUM_LANGOPT(Name, Type, Bits, Default, Description)
#include "clang/Basic/LangOptions.def"
if (!LangOpts.ModuleFeatures.empty()) {
Out.indent(4) << "Module features:\n";
for (StringRef Feature : LangOpts.ModuleFeatures)
Out.indent(6) << Feature << "\n";
}
return false;
}
bool ReadTargetOptions(const TargetOptions &TargetOpts,
bool /*Complain*/,
bool /*AllowCompatibleDifferences*/) override {
Out.indent(2) << "Target options:\n";
Out.indent(4) << " Triple: " << TargetOpts.Triple << "\n";
Out.indent(4) << " CPU: " << TargetOpts.CPU << "\n";
Out.indent(4) << " ABI: " << TargetOpts.ABI << "\n";
if (!TargetOpts.FeaturesAsWritten.empty()) {
Out.indent(4) << "Target features:\n";
for (unsigned I = 0, N = TargetOpts.FeaturesAsWritten.size();
I != N; ++I) {
Out.indent(6) << TargetOpts.FeaturesAsWritten[I] << "\n";
}
}
return false;
}
bool ReadDiagnosticOptions(IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts,
bool /*Complain*/) override {
Out.indent(2) << "Diagnostic options:\n";
#define DIAGOPT(Name, Bits, Default) DUMP_BOOLEAN(DiagOpts->Name, #Name);
#define ENUM_DIAGOPT(Name, Type, Bits, Default) \
Out.indent(4) << #Name << ": " << DiagOpts->get##Name() << "\n";
#define VALUE_DIAGOPT(Name, Bits, Default) \
Out.indent(4) << #Name << ": " << DiagOpts->Name << "\n";
#include "clang/Basic/DiagnosticOptions.def"
Out.indent(4) << "Diagnostic flags:\n";
for (const std::string &Warning : DiagOpts->Warnings)
Out.indent(6) << "-W" << Warning << "\n";
for (const std::string &Remark : DiagOpts->Remarks)
Out.indent(6) << "-R" << Remark << "\n";
return false;
}
bool ReadHeaderSearchOptions(const HeaderSearchOptions &HSOpts,
StringRef SpecificModuleCachePath,
bool /*Complain*/) override {
Out.indent(2) << "Header search options:\n";
Out.indent(4) << "System root [-isysroot=]: '"
<< HSOpts.Sysroot << "'\n";
Out.indent(4) << "Resource dir [ -resource-dir=]: '"
<< HSOpts.ResourceDir << "'\n";
Out.indent(4) << "Module Cache: '" << SpecificModuleCachePath
<< "'\n";
DUMP_BOOLEAN(HSOpts.UseBuiltinIncludes,
"Use builtin include directories [-nobuiltininc]");
DUMP_BOOLEAN(HSOpts.UseStandardSystemIncludes,
"Use standard system include directories [-nostdinc]");
DUMP_BOOLEAN(HSOpts.UseStandardCXXIncludes,
"Use standard C++ include directories [-nostdinc++]");
DUMP_BOOLEAN(HSOpts.UseLibcxx,
"Use libc++ (rather than libstdc++) [-stdlib=]");
return false;
}
bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
bool /*Complain*/,
std::string &/*SuggestedPredefines*/) override {
Out.indent(2) << "Preprocessor options:\n";
DUMP_BOOLEAN(PPOpts.UsePredefines,
"Uses compiler/target-specific predefines [-undef]");
DUMP_BOOLEAN(PPOpts.DetailedRecord,
"Uses detailed preprocessing record (for indexing)");
if (!PPOpts.Macros.empty()) {
Out.indent(4) << "Predefined macros:\n";
}
for (std::vector<std::pair<std::string, bool/*isUndef*/> >::const_iterator
I = PPOpts.Macros.begin(), IEnd = PPOpts.Macros.end();
I != IEnd; ++I) {
Out.indent(6);
if (I->second)
Out << "-U";
else
Out << "-D";
Out << I->first << "\n";
}
return false;
}
/// Indicates that a particular module file extension has been read.
void readModuleFileExtension(
const ModuleFileExtensionMetadata &Metadata) override {
Out.indent(2) << "Module file extension '"
<< Metadata.BlockName << "' " << Metadata.MajorVersion
<< "." << Metadata.MinorVersion;
if (!Metadata.UserInfo.empty()) {
Out << ": ";
Out.write_escaped(Metadata.UserInfo);
}
Out << "\n";
}
/// Tells the \c ASTReaderListener that we want to receive the
/// input files of the AST file via \c visitInputFile.
bool needsInputFileVisitation() override { return true; }
/// Tells the \c ASTReaderListener that we want to receive the
/// input files of the AST file via \c visitInputFile.
bool needsSystemInputFileVisitation() override { return true; }
/// Indicates that the AST file contains particular input file.
///
/// \returns true to continue receiving the next input file, false to stop.
bool visitInputFile(StringRef Filename, bool isSystem,
bool isOverridden, bool isExplicitModule) override {
Out.indent(2) << "Input file: " << Filename;
if (isSystem || isOverridden || isExplicitModule) {
Out << " [";
if (isSystem) {
Out << "System";
if (isOverridden || isExplicitModule)
Out << ", ";
}
if (isOverridden) {
Out << "Overridden";
if (isExplicitModule)
Out << ", ";
}
if (isExplicitModule)
Out << "ExplicitModule";
Out << "]";
}
Out << "\n";
return true;
}
#undef DUMP_BOOLEAN
};
std::unique_ptr<llvm::raw_fd_ostream> OutFile;
StringRef OutputFileName = FrontendOpts.OutputFile;
if (!OutputFileName.empty() && OutputFileName != "-") {
std::error_code EC;
OutFile.reset(new llvm::raw_fd_ostream(OutputFileName.str(), EC,
llvm::sys::fs::OF_Text));
}
llvm::raw_ostream &Out = OutFile.get()? *OutFile.get() : llvm::outs();
StringRef CurInput = FrontendOpts.Inputs[0].getFile();
Out << "Information for module file '" << CurInput << "':\n";
auto &FileMgr = CI.getFileManager();
auto Buffer = FileMgr.getBufferForFile(CurInput);
StringRef Magic = (*Buffer)->getMemBufferRef().getBuffer();
bool IsRaw = (Magic.size() >= 4 && Magic[0] == 'C' && Magic[1] == 'P' &&
Magic[2] == 'C' && Magic[3] == 'H');
Out << " Module format: " << (IsRaw ? "raw" : "obj") << "\n";
Preprocessor &PP = CI.getPreprocessor();
DumpModuleInfoListener Listener(Out);
HeaderSearchOptions &HSOpts =
PP.getHeaderSearchInfo().getHeaderSearchOpts();
ASTReader::readASTFileControlBlock(CurInput, FileMgr, CI.getModuleCache(),
CI.getPCHContainerReader(),
/*FindModuleFileExtensions=*/true,
Listener,
HSOpts.ModulesValidateDiagnosticOptions);
}
}
static CompilerInstance*
createCIImpl(std::unique_ptr<llvm::MemoryBuffer> Buffer,
const CompilerOptions& COpts,
const char* LLVMDir,
std::unique_ptr<clang::ASTConsumer> customConsumer,
const CIFactory::ModuleFileExtensions& moduleExtensions,
bool OnlyLex, bool HasInput = false) {
// Follow clang -v convention of printing version on first line
if (COpts.Verbose)
cling::log() << "cling version " << ClingStringify(CLING_VERSION) << '\n';
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargets();
llvm::InitializeAllAsmParsers();
llvm::InitializeAllAsmPrinters();
llvm::InitializeAllTargetMCs();
// Create an instance builder, passing the LLVMDir and arguments.
//
CheckClangCompatibility();
const size_t argc = COpts.Remaining.size();
const char* const* argv = &COpts.Remaining[0];
std::vector<const char*> argvCompile(argv, argv+1);
argvCompile.reserve(argc+32);
bool debuggingEnabled =
cling::utils::ConvertEnvValueToBool(std::getenv("CLING_DEBUG"));
bool profilingEnabled =
cling::utils::ConvertEnvValueToBool(std::getenv("CLING_PROFILE"));
#if __APPLE__ && __arm64__
argvCompile.push_back("--target=arm64-apple-darwin20.3.0");
#endif
#if __aarch64__
// Disable outline-atomics on AArch64; the routines __aarch64_* are defined
// in the static library libgcc.a and not necessarily included in libCling
// or otherwise present in the process, so the interpreter has a hard time
// finding them.
argvCompile.push_back("-mno-outline-atomics");
#endif
// Variables for storing the memory of the C-string arguments.
// FIXME: We shouldn't use C-strings in the first place, but just use
// std::string for clang arguments.
std::string overlayArg;
std::string cacheArg;
// If user has enabled C++ modules we add some special module flags to the
// compiler invocation.
if (COpts.CxxModules) {
// Enables modules in clang.
argvCompile.push_back("-fmodules");
argvCompile.push_back("-fcxx-modules");
// We want to use modules in local-submodule-visibility mode. This mode
// will probably be the future default mode for C++ modules in clang, so
// we want to start using right now.
// Maybe we have to remove this flag in the future when clang makes this
// mode the default and removes this internal flag.
argvCompile.push_back("-Xclang");
argvCompile.push_back("-fmodules-local-submodule-visibility");
// If we got a cache path, then we are supposed to place any modules
// we have to build in this directory.
if (!COpts.CachePath.empty()) {
cacheArg = std::string("-fmodules-cache-path=") + COpts.CachePath;
argvCompile.push_back(cacheArg.c_str());
}
// Disable the module hash. This gives us a flat file layout in the
// modules cache directory. In clang this is used to prevent modules from
// different compiler invocations to not collide, but we only have one
// compiler invocation in cling, so we don't need this.
argvCompile.push_back("-Xclang");
argvCompile.push_back("-fdisable-module-hash");
// Disable the warning when we import a module from extern C. Some headers
// from the STL are doing this and we can't really do anything about this.
argvCompile.push_back("-Wno-module-import-in-extern-c");
// Disable the warning when we import a module in a function body. This
// is a ROOT-specific issue tracked by ROOT-9088.
// FIXME: Remove after merging ROOT's PR1306.
argvCompile.push_back("-Wno-modules-import-nested-redundant");
// FIXME: We get an error "'cling/module.modulemap' from the precompiled
// header has been overridden". This comes from a bug that rootcling
// introduces by adding a lot of garbage in the PCH/PCM files because it
// essentially serializes its current state of the AST. That usually
// includes a few memory buffers which override their own contents.
// We know how to remove this: just implement a callback in clang
// which calls back the interpreter when a module file is built. This is
// a lot of work as it needs fixing rootcling. See RE-0003.
argvCompile.push_back("-Xclang");
argvCompile.push_back("-fno-validate-pch");
}
if (!COpts.Language) {
// We do C++ by default; append right after argv[0] if no "-x" given
argvCompile.push_back("-x");
argvCompile.push_back( "c++");
}
if (COpts.DefaultLanguage()) {
// By default, set the standard to what cling was compiled with.
// clang::driver::Compilation will do various things while initializing
// and by enforcing the std version now cling is telling clang what to
// do, rather than after clang has dedcuded a default.
switch (CxxStdCompiledWith()) {
case 20: argvCompile.emplace_back("-std=c++20"); break;
case 17: argvCompile.emplace_back("-std=c++17"); break;
case 14: argvCompile.emplace_back("-std=c++14"); break;
case 11: argvCompile.emplace_back("-std=c++11"); break;
default: llvm_unreachable("Unrecognized C++ version");
}
}
// This argument starts the cling instance with the x86 target. Otherwise,
// the first job in the joblist starts the cling instance with the nvptx
// target.
if(COpts.CUDAHost)
argvCompile.push_back("--cuda-host-only");
// argv[0] already inserted, get the rest
argvCompile.insert(argvCompile.end(), argv+1, argv + argc);
#ifdef __linux__
// Keep frame pointer to make JIT stack unwinding reliable for profiling
if (profilingEnabled)
argvCompile.push_back("-fno-omit-frame-pointer");
#endif
#ifdef __cpp_sized_deallocation
// Propagate the setting of the compiler to the interpreter
argvCompile.push_back("-fsized-deallocation");
#endif
// Disable optimizations and keep frame pointer when debugging, overriding
// other optimization options that might be in argv
if (debuggingEnabled) {
argvCompile.push_back("-O0");
argvCompile.push_back("-fno-omit-frame-pointer");
}
// Add host specific includes, -resource-dir if necessary, and -isysroot
std::string ClingBin = GetExecutablePath(argv[0]);
AddHostArguments(ClingBin, argvCompile, LLVMDir, COpts);
// Be explicit about the stdlib on OS X
// Would be nice on Linux but will warn 'argument unused during compilation'
// when -nostdinc++ is passed
#ifdef __APPLE__
if (!COpts.StdLib) {
#ifdef _LIBCPP_VERSION
argvCompile.push_back("-stdlib=libc++");
#elif defined(__GLIBCXX__)
argvCompile.push_back("-stdlib=libstdc++");
#endif
}
#endif
if (!COpts.HasOutput || !HasInput) {
// suppress the warning "argument unused during compilation: -c" of the
// device interpreter instance
if (!COpts.CUDADevice)
argvCompile.push_back("-c");
argvCompile.push_back("-");
}
auto InvocationPtr = std::make_shared<clang::CompilerInvocation>();
// The compiler invocation is the owner of the diagnostic options.
// Everything else points to them.
DiagnosticOptions& DiagOpts = InvocationPtr->getDiagnosticOpts();
// add prefix to diagnostic messages if second compiler instance is existing
// e.g. in CUDA mode
std::string ExeName = "";
if (COpts.CUDAHost)
ExeName = "cling";
if (COpts.CUDADevice)
ExeName = "cling-ptx";
llvm::IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
SetupDiagnostics(DiagOpts, ExeName);
if (!Diags) {
cling::errs() << "Could not setup diagnostic engine.\n";
return nullptr;
}
llvm::Triple TheTriple(llvm::sys::getProcessTriple());
clang::driver::Driver Drvr(argv[0], TheTriple.getTriple(), *Diags);
//Drvr.setWarnMissingInput(false);
Drvr.setCheckInputsExist(false); // think foo.C(12)
llvm::ArrayRef<const char*>RF(&(argvCompile[0]), argvCompile.size());
std::unique_ptr<clang::driver::Compilation>
Compilation(Drvr.BuildCompilation(RF));
if (!Compilation) {
cling::errs() << "Couldn't create clang::driver::Compilation.\n";
return nullptr;
}
const llvm::opt::ArgStringList* CC1Args = GetCC1Arguments(Compilation.get());
if (!CC1Args) {
cling::errs() << "Could not get cc1 arguments.\n";
return nullptr;
}
clang::CompilerInvocation::CreateFromArgs(*InvocationPtr, *CC1Args, *Diags);
// We appreciate the error message about an unknown flag (or do we? if not
// we should switch to a different DiagEngine for parsing the flags).
// But in general we'll happily go on.
Diags->Reset();
// Create and setup a compiler instance.
std::unique_ptr<CompilerInstance> CI(new CompilerInstance());
CI->setInvocation(InvocationPtr);
CI->setDiagnostics(Diags.get()); // Diags is ref-counted
if (!OnlyLex)
CI->getDiagnosticOpts().ShowColors =
llvm::sys::Process::StandardOutIsDisplayed() ||
llvm::sys::Process::StandardErrIsDisplayed();
// Copied from CompilerInstance::createDiagnostics:
// Chain in -verify checker, if requested.
if (DiagOpts.VerifyDiagnostics)
Diags->setClient(new clang::VerifyDiagnosticConsumer(*Diags));
// Configure our handling of diagnostics.
ProcessWarningOptions(*Diags, DiagOpts);
if (COpts.HasOutput && !OnlyLex) {
ActionScan scan(clang::driver::Action::PrecompileJobClass,
clang::driver::Action::PreprocessJobClass);
if (!scan.find(Compilation.get())) {
cling::errs() << "Only precompiled header or preprocessor "
"output is supported.\n";
return nullptr;
}
if (!SetupCompiler(CI.get(), COpts))
return nullptr;
ProcessWarningOptions(*Diags, DiagOpts);
return CI.release();
}
IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> Overlay =
new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem());
CI->createFileManager(Overlay);
clang::CompilerInvocation& Invocation = CI->getInvocation();
std::string& PCHFile = Invocation.getPreprocessorOpts().ImplicitPCHInclude;
bool InitLang = true, InitTarget = true;
if (!PCHFile.empty()) {
if (cling::utils::LookForFile(argvCompile, PCHFile,
&CI->getFileManager(),
COpts.Verbose ? "Precompiled header" : nullptr)) {
// Load target options etc from PCH.
struct PCHListener: public ASTReaderListener {
CompilerInvocation& m_Invocation;
bool m_ReadLang, m_ReadTarget;
PCHListener(CompilerInvocation& I) :
m_Invocation(I), m_ReadLang(false), m_ReadTarget(false) {}
bool ReadLanguageOptions(const LangOptions &LangOpts,
bool /*Complain*/,
bool /*AllowCompatibleDifferences*/) override {
*m_Invocation.getLangOpts() = LangOpts;
m_ReadLang = true;
return false;
}
bool ReadTargetOptions(const TargetOptions &TargetOpts,
bool /*Complain*/,
bool /*AllowCompatibleDifferences*/) override {
m_Invocation.getTargetOpts() = TargetOpts;
m_ReadTarget = true;
return false;
}
bool ReadPreprocessorOptions(const PreprocessorOptions &PPOpts,
bool /*Complain*/,
std::string &/*SuggestedPredefines*/) override {
// Import selected options, e.g. don't overwrite ImplicitPCHInclude.
PreprocessorOptions& myPP = m_Invocation.getPreprocessorOpts();
insertBehind(myPP.Macros, PPOpts.Macros);
insertBehind(myPP.Includes, PPOpts.Includes);
insertBehind(myPP.MacroIncludes, PPOpts.MacroIncludes);
return false;
}
};
PCHListener listener(Invocation);
if (ASTReader::readASTFileControlBlock(PCHFile,
CI->getFileManager(),
CI->getModuleCache(),
CI->getPCHContainerReader(),
false /*FindModuleFileExt*/,
listener,
/*ValidateDiagnosticOptions=*/false)) {
// When running interactively pass on the info that the PCH
// has failed so that IncrmentalParser::Initialize won't try again.
if (!HasInput && llvm::sys::Process::StandardInIsUserInput()) {
const unsigned ID = Diags->getCustomDiagID(
clang::DiagnosticsEngine::Level::Error,
"Problems loading PCH: '%0'.");
Diags->Report(ID) << PCHFile;
// If this was the only error, then don't let it stop anything
if (Diags->getClient()->getNumErrors() == 1)
Diags->Reset(true);
// Clear the include so no one else uses it.
std::string().swap(PCHFile);
}
}
// All we care about is if Language and Target options were successful.
InitLang = !listener.m_ReadLang;
InitTarget = !listener.m_ReadTarget;
}
}
FrontendOptions& FrontendOpts = Invocation.getFrontendOpts();
// Register the externally constructed extensions.
assert(FrontendOpts.ModuleFileExtensions.empty() && "Extensions exist!");
for (auto& E : moduleExtensions)
FrontendOpts.ModuleFileExtensions.push_back(E);
FrontendOpts.DisableFree = true;
// Set up compiler language and target
if (!SetupCompiler(CI.get(), COpts, InitLang, InitTarget))
return nullptr;
// Set up source managers
SourceManager* SM = new SourceManager(CI->getDiagnostics(),
CI->getFileManager(),
/*UserFilesAreVolatile*/ true);
CI->setSourceManager(SM); // CI now owns SM
if (CI->getCodeGenOpts().TimePasses)
CI->createFrontendTimer();
if (FrontendOpts.ModulesEmbedAllFiles)
CI->getSourceManager().setAllFilesAreTransient(true);
// As main file we want
// * a virtual file that is claiming to be huge
// * with an empty memory buffer attached (to bring the content)
FileManager& FM = SM->getFileManager();
// When asking for the input file below (which does not have a directory
// name), clang will call $PWD "." which is terrible if we ever change
// directories (see ROOT-7114). By asking for $PWD (and not ".") it will
// be registered as $PWD instead, which is stable even after chdirs.
FM.getDirectory(platform::GetCwd());
// Build the virtual file, Give it a name that's likely not to ever
// be #included (so we won't get a clash in clang's cache).
const char* Filename = "<<< cling interactive line includer >>>";
const FileEntry* FE = FM.getVirtualFile(Filename, 1U << 15U, time(0));
// Tell ASTReader to create a FileID even if this file does not exist:
SM->setFileIsTransient(FE);
FileID MainFileID = SM->createFileID(FE, SourceLocation(), SrcMgr::C_User);
SM->setMainFileID(MainFileID);
const SrcMgr::SLocEntry& MainFileSLocE = SM->getSLocEntry(MainFileID);
const SrcMgr::FileInfo& MainFileFI = MainFileSLocE.getFile();
SrcMgr::ContentCache& MainFileCC
= const_cast<SrcMgr::ContentCache&>(MainFileFI.getContentCache());
if (!Buffer)
Buffer = llvm::MemoryBuffer::getMemBuffer("/*CLING DEFAULT MEMBUF*/;\n");
MainFileCC.setBuffer(std::move(Buffer));
// Create TargetInfo for the other side of CUDA and OpenMP compilation.
if ((CI->getLangOpts().CUDA || CI->getLangOpts().OpenMPIsDevice) &&
!CI->getFrontendOpts().AuxTriple.empty()) {
auto TO = std::make_shared<TargetOptions>();
TO->Triple = CI->getFrontendOpts().AuxTriple;
TO->HostTriple = CI->getTarget().getTriple().str();
CI->setAuxTarget(TargetInfo::CreateTargetInfo(CI->getDiagnostics(), TO));
}
// Set up the preprocessor
auto TUKind = COpts.ModuleName.empty() ? TU_Complete : TU_Module;
CI->createPreprocessor(TUKind);
// With modules, we now start adding prebuilt module paths to the CI.
// Modules from those paths are treated like they are never out of date
// and we don't update them on demand.
// This mostly helps ROOT where we can't just recompile any out of date
// modules because we would miss the annotations that rootcling creates.
if (COpts.CxxModules) {
setupCxxModules(*CI);
}
Preprocessor& PP = CI->getPreprocessor();
PP.getBuiltinInfo().initializeBuiltins(PP.getIdentifierTable(),
PP.getLangOpts());
// Set up the ASTContext
CI->createASTContext();
std::vector<std::unique_ptr<ASTConsumer>> Consumers;
if (!OnlyLex) {
assert(customConsumer && "Need to specify a custom consumer"
" when not in OnlyLex mode");
Consumers.push_back(std::move(customConsumer));
}
// With C++ modules, we now attach the consumers that will handle the
// generation of the PCM file itself in case we want to generate
// a C++ module with the current interpreter instance.
if (COpts.CxxModules && !COpts.ModuleName.empty()) {
// Code below from the (private) code in the GenerateModuleAction class.
llvm::SmallVector<char, 256> Output;
llvm::sys::path::append(Output, COpts.CachePath,
COpts.ModuleName + ".pcm");
StringRef ModuleOutputFile = StringRef(Output.data(), Output.size());
std::unique_ptr<raw_pwrite_stream> OS =
CI->createOutputFile(ModuleOutputFile, /*Binary=*/true,
/*RemoveFileOnSignal=*/false,
/*useTemporary=*/true,
/*CreateMissingDirectories=*/true);
assert(OS);
std::string Sysroot;
auto PCHBuff = std::make_shared<PCHBuffer>();
Consumers.push_back(std::make_unique<PCHGenerator>(
CI->getPreprocessor(), CI->getModuleCache(), ModuleOutputFile,
Sysroot, PCHBuff, CI->getFrontendOpts().ModuleFileExtensions,
/*AllowASTWithErrors=*/false,
/*IncludeTimestamps=*/
+CI->getFrontendOpts().BuildingImplicitModule));
Consumers.push_back(
CI->getPCHContainerWriter().CreatePCHContainerGenerator(
*CI, "", ModuleOutputFile.str(), std::move(OS), PCHBuff));
// Set the current module name for clang. With that clang doesn't start
// to build the current module on demand when we include a header
// from the current module.
CI->getLangOpts().CurrentModule = COpts.ModuleName;
CI->getLangOpts().setCompilingModule(LangOptions::CMK_ModuleMap);
// Push the current module to the build stack so that clang knows when
// we have a cyclic dependency.
SM->pushModuleBuildStack(COpts.ModuleName,
FullSourceLoc(SourceLocation(), *SM));
}
std::unique_ptr<clang::MultiplexConsumer> multiConsumer(
new clang::MultiplexConsumer(std::move(Consumers)));
CI->setASTConsumer(std::move(multiConsumer));
// Set up Sema
CodeCompleteConsumer* CCC = 0;
// Make sure we inform Sema we compile a Module.
CI->createSema(TUKind, CCC);
// Set CodeGen options.
CodeGenOptions& CGOpts = CI->getCodeGenOpts();
#ifdef _MSC_VER
CGOpts.RelaxedAliasing = 1;
CGOpts.EmitCodeView = 1;
CGOpts.CXXCtorDtorAliases = 1;
#endif
// Taken from a -O2 run of clang:
CGOpts.DiscardValueNames = 1;
CGOpts.setFramePointer(CodeGenOptions::FramePointerKind::All);
CGOpts.UnrollLoops = 1;
CGOpts.VectorizeLoop = 1;
CGOpts.VectorizeSLP = 1;
CGOpts.DisableO0ImplyOptNone = 1; // Enable dynamic opt level switching.
// Set up inlining, even if we switch to O0 later: some transactions' code
// might pass `#pragma cling optimize` levels that require it. This is
// adjusted per transaction in IncrementalParser::codeGenTransaction().
CGOpts.setInlining(CodeGenOptions::NormalInlining);
// Add debugging info when debugging or profiling
if (debuggingEnabled || profilingEnabled)
CGOpts.setDebugInfo(codegenoptions::FullDebugInfo);
// CGOpts.EmitDeclMetadata = 1; // For unloading, for later
// aliasing the complete ctor to the base ctor causes the JIT to crash
CGOpts.CXXCtorDtorAliases = 0;
CGOpts.VerifyModule = 0; // takes too long
if (!OnlyLex) {
// -nobuiltininc
clang::HeaderSearchOptions& HOpts = CI->getHeaderSearchOpts();
if (CI->getHeaderSearchOpts().UseBuiltinIncludes)
AddRuntimeIncludePaths(ClingBin, HOpts);
// Write a marker to know the rest of the output is from clang
if (COpts.Verbose)
cling::log() << "Setting up system headers with clang:\n";
// ### FIXME:
// Want to update LLVM to 3.9 realease and better testing first, but
// ApplyHeaderSearchOptions shouldn't even be called here:
// 1. It's already been called via CI->createPreprocessor(TU_Complete)
// 2. It could corrupt clang's directory cache
// HeaderSearchOptions.::AddSearchPath is a better alternative
clang::ApplyHeaderSearchOptions(PP.getHeaderSearchInfo(), HOpts,
PP.getLangOpts(),
PP.getTargetInfo().getTriple());
}
// Tell the diagnostic client that we are entering file parsing mode as the
// handling of modulemap files may issue diagnostics.
// FIXME: Consider moving in SetupDiagnostics.
DiagnosticConsumer& DClient = CI->getDiagnosticClient();
DClient.BeginSourceFile(CI->getLangOpts(), &PP);
for (const auto& ModuleMapFile : FrontendOpts.ModuleMapFiles) {
auto File = FM.getFile(ModuleMapFile);
if (!File) {
CI->getDiagnostics().Report(diag::err_module_map_not_found)
<< ModuleMapFile;
continue;
}
PP.getHeaderSearchInfo().loadModuleMapFile(*File, /*IsSystem*/ false);
}
HandleProgramActions(*CI);
return CI.release(); // Passes over the ownership to the caller.
}
} // unnamed namespace
namespace cling {
CompilerInstance*
CIFactory::createCI(llvm::StringRef Code, const InvocationOptions& Opts,
const char* LLVMDir,
std::unique_ptr<clang::ASTConsumer> consumer,
const ModuleFileExtensions& moduleExtensions) {
return createCIImpl(llvm::MemoryBuffer::getMemBuffer(Code), Opts.CompilerOpts,
LLVMDir, std::move(consumer), moduleExtensions,
false /*OnlyLex*/,
!Opts.IsInteractive());
}
CompilerInstance* CIFactory::createCI(
MemBufPtr_t Buffer, int argc, const char* const* argv, const char* LLVMDir,
std::unique_ptr<clang::ASTConsumer> consumer,
const ModuleFileExtensions& moduleExtensions, bool OnlyLex /*false*/) {
return createCIImpl(std::move(Buffer), CompilerOptions(argc, argv), LLVMDir,
std::move(consumer), moduleExtensions, OnlyLex);
}
} // namespace cling