1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
//===-- CppModuleConfiguration.cpp ----------------------------------------===//
//
// 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 "CppModuleConfiguration.h"
#include "ClangHost.h"
#include "lldb/Host/FileSystem.h"
#include "llvm/TargetParser/Triple.h"
#include <optional>
using namespace lldb_private;
bool CppModuleConfiguration::SetOncePath::TrySet(llvm::StringRef path) {
// Setting for the first time always works.
if (m_first) {
m_path = path.str();
m_valid = true;
m_first = false;
return true;
}
// Changing the path to the same value is fine.
if (m_path == path)
return true;
// Changing the path after it was already set is not allowed.
m_valid = false;
return false;
}
static llvm::SmallVector<std::string, 2>
getTargetIncludePaths(const llvm::Triple &triple) {
llvm::SmallVector<std::string, 2> paths;
if (!triple.str().empty()) {
paths.push_back("/usr/include/" + triple.str());
if (!triple.getArchName().empty() ||
triple.getOSAndEnvironmentName().empty())
paths.push_back(("/usr/include/" + triple.getArchName() + "-" +
triple.getOSAndEnvironmentName())
.str());
}
return paths;
}
/// Returns the include path matching the given pattern for the given file
/// path (or std::nullopt if the path doesn't match the pattern).
static std::optional<llvm::StringRef>
guessIncludePath(llvm::StringRef path_to_file, llvm::StringRef pattern) {
if (pattern.empty())
return std::nullopt;
size_t pos = path_to_file.find(pattern);
if (pos == llvm::StringRef::npos)
return std::nullopt;
return path_to_file.substr(0, pos + pattern.size());
}
bool CppModuleConfiguration::analyzeFile(const FileSpec &f,
const llvm::Triple &triple) {
using namespace llvm::sys::path;
// Convert to slashes to make following operations simpler.
std::string dir_buffer = convert_to_slash(f.GetDirectory().GetStringRef());
llvm::StringRef posix_dir(dir_buffer);
// Check for /c++/vX/ that is used by libc++.
static llvm::Regex libcpp_regex(R"regex(/c[+][+]/v[0-9]/)regex");
// If the path is in the libc++ include directory use it as the found libc++
// path. Ignore subdirectories such as /c++/v1/experimental as those don't
// need to be specified in the header search.
if (libcpp_regex.match(f.GetPath()) &&
parent_path(posix_dir, Style::posix).endswith("c++")) {
if (!m_std_inc.TrySet(posix_dir))
return false;
if (triple.str().empty())
return true;
posix_dir.consume_back("c++/v1");
// Check if this is a target-specific libc++ include directory.
return m_std_target_inc.TrySet(
(posix_dir + triple.str() + "/c++/v1").str());
}
std::optional<llvm::StringRef> inc_path;
// Target specific paths contains /usr/include, so we check them first
for (auto &path : getTargetIncludePaths(triple)) {
if ((inc_path = guessIncludePath(posix_dir, path)))
return m_c_target_inc.TrySet(*inc_path);
}
if ((inc_path = guessIncludePath(posix_dir, "/usr/include")))
return m_c_inc.TrySet(*inc_path);
// File wasn't interesting, continue analyzing.
return true;
}
/// Utility function for just appending two paths.
static std::string MakePath(llvm::StringRef lhs, llvm::StringRef rhs) {
llvm::SmallString<256> result(lhs);
llvm::sys::path::append(result, rhs);
return std::string(result);
}
bool CppModuleConfiguration::hasValidConfig() {
// We need to have a C and C++ include dir for a valid configuration.
if (!m_c_inc.Valid() || !m_std_inc.Valid())
return false;
// Do some basic sanity checks on the directories that we don't activate
// the module when it's clear that it's not usable.
const std::vector<std::string> files_to_check = {
// * Check that the C library contains at least one random C standard
// library header.
MakePath(m_c_inc.Get(), "stdio.h"),
// * Without a libc++ modulemap file we can't have a 'std' module that
// could be imported.
MakePath(m_std_inc.Get(), "module.modulemap"),
// * Check for a random libc++ header (vector in this case) that has to
// exist in a working libc++ setup.
MakePath(m_std_inc.Get(), "vector"),
};
for (llvm::StringRef file_to_check : files_to_check) {
if (!FileSystem::Instance().Exists(file_to_check))
return false;
}
return true;
}
CppModuleConfiguration::CppModuleConfiguration(
const FileSpecList &support_files, const llvm::Triple &triple) {
// Analyze all files we were given to build the configuration.
bool error = !llvm::all_of(support_files,
std::bind(&CppModuleConfiguration::analyzeFile,
this, std::placeholders::_1, triple));
// If we have a valid configuration at this point, set the
// include directories and module list that should be used.
if (!error && hasValidConfig()) {
// Calculate the resource directory for LLDB.
llvm::SmallString<256> resource_dir;
llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(),
"include");
m_resource_inc = std::string(resource_dir.str());
// This order matches the way Clang orders these directories.
m_include_dirs = {m_std_inc.Get().str(), m_resource_inc,
m_c_inc.Get().str()};
if (m_c_target_inc.Valid())
m_include_dirs.push_back(m_c_target_inc.Get().str());
if (m_std_target_inc.Valid())
m_include_dirs.push_back(m_std_target_inc.Get().str());
m_imported_modules = {"std"};
}
}
|