1061da546Spatrick //===-- CppModuleConfiguration.cpp ----------------------------------------===// 2061da546Spatrick // 3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information. 5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6061da546Spatrick // 7061da546Spatrick //===----------------------------------------------------------------------===// 8061da546Spatrick 9061da546Spatrick #include "CppModuleConfiguration.h" 10061da546Spatrick 11061da546Spatrick #include "ClangHost.h" 12061da546Spatrick #include "lldb/Host/FileSystem.h" 13061da546Spatrick 14061da546Spatrick using namespace lldb_private; 15061da546Spatrick 16061da546Spatrick bool CppModuleConfiguration::SetOncePath::TrySet(llvm::StringRef path) { 17061da546Spatrick // Setting for the first time always works. 18061da546Spatrick if (m_first) { 19061da546Spatrick m_path = path.str(); 20061da546Spatrick m_valid = true; 21061da546Spatrick m_first = false; 22061da546Spatrick return true; 23061da546Spatrick } 24061da546Spatrick // Changing the path to the same value is fine. 25061da546Spatrick if (m_path == path) 26061da546Spatrick return true; 27061da546Spatrick 28061da546Spatrick // Changing the path after it was already set is not allowed. 29061da546Spatrick m_valid = false; 30061da546Spatrick return false; 31061da546Spatrick } 32061da546Spatrick 33061da546Spatrick bool CppModuleConfiguration::analyzeFile(const FileSpec &f) { 34061da546Spatrick using namespace llvm::sys::path; 35061da546Spatrick // Convert to slashes to make following operations simpler. 36061da546Spatrick std::string dir_buffer = convert_to_slash(f.GetDirectory().GetStringRef()); 37061da546Spatrick llvm::StringRef posix_dir(dir_buffer); 38061da546Spatrick 39061da546Spatrick // Check for /c++/vX/ that is used by libc++. 40061da546Spatrick static llvm::Regex libcpp_regex(R"regex(/c[+][+]/v[0-9]/)regex"); 41*be691f3bSpatrick // If the path is in the libc++ include directory use it as the found libc++ 42*be691f3bSpatrick // path. Ignore subdirectories such as /c++/v1/experimental as those don't 43*be691f3bSpatrick // need to be specified in the header search. 44*be691f3bSpatrick if (libcpp_regex.match(f.GetPath()) && 45*be691f3bSpatrick parent_path(posix_dir, Style::posix).endswith("c++")) { 46061da546Spatrick return m_std_inc.TrySet(posix_dir); 47061da546Spatrick } 48061da546Spatrick 49061da546Spatrick // Check for /usr/include. On Linux this might be /usr/include/bits, so 50061da546Spatrick // we should remove that '/bits' suffix to get the actual include directory. 51061da546Spatrick if (posix_dir.endswith("/usr/include/bits")) 52061da546Spatrick posix_dir.consume_back("/bits"); 53061da546Spatrick if (posix_dir.endswith("/usr/include")) 54061da546Spatrick return m_c_inc.TrySet(posix_dir); 55061da546Spatrick 56061da546Spatrick // File wasn't interesting, continue analyzing. 57061da546Spatrick return true; 58061da546Spatrick } 59061da546Spatrick 60*be691f3bSpatrick /// Utility function for just appending two paths. 61*be691f3bSpatrick static std::string MakePath(llvm::StringRef lhs, llvm::StringRef rhs) { 62*be691f3bSpatrick llvm::SmallString<256> result(lhs); 63*be691f3bSpatrick llvm::sys::path::append(result, rhs); 64*be691f3bSpatrick return std::string(result); 65*be691f3bSpatrick } 66*be691f3bSpatrick 67061da546Spatrick bool CppModuleConfiguration::hasValidConfig() { 68*be691f3bSpatrick // We need to have a C and C++ include dir for a valid configuration. 69*be691f3bSpatrick if (!m_c_inc.Valid() || !m_std_inc.Valid()) 70*be691f3bSpatrick return false; 71*be691f3bSpatrick 72*be691f3bSpatrick // Do some basic sanity checks on the directories that we don't activate 73*be691f3bSpatrick // the module when it's clear that it's not usable. 74*be691f3bSpatrick const std::vector<std::string> files_to_check = { 75*be691f3bSpatrick // * Check that the C library contains at least one random C standard 76*be691f3bSpatrick // library header. 77*be691f3bSpatrick MakePath(m_c_inc.Get(), "stdio.h"), 78*be691f3bSpatrick // * Without a libc++ modulemap file we can't have a 'std' module that 79*be691f3bSpatrick // could be imported. 80*be691f3bSpatrick MakePath(m_std_inc.Get(), "module.modulemap"), 81*be691f3bSpatrick // * Check for a random libc++ header (vector in this case) that has to 82*be691f3bSpatrick // exist in a working libc++ setup. 83*be691f3bSpatrick MakePath(m_std_inc.Get(), "vector"), 84*be691f3bSpatrick }; 85*be691f3bSpatrick 86*be691f3bSpatrick for (llvm::StringRef file_to_check : files_to_check) { 87*be691f3bSpatrick if (!FileSystem::Instance().Exists(file_to_check)) 88*be691f3bSpatrick return false; 89*be691f3bSpatrick } 90*be691f3bSpatrick 91*be691f3bSpatrick return true; 92061da546Spatrick } 93061da546Spatrick 94061da546Spatrick CppModuleConfiguration::CppModuleConfiguration( 95061da546Spatrick const FileSpecList &support_files) { 96061da546Spatrick // Analyze all files we were given to build the configuration. 97061da546Spatrick bool error = !llvm::all_of(support_files, 98061da546Spatrick std::bind(&CppModuleConfiguration::analyzeFile, 99061da546Spatrick this, std::placeholders::_1)); 100061da546Spatrick // If we have a valid configuration at this point, set the 101061da546Spatrick // include directories and module list that should be used. 102061da546Spatrick if (!error && hasValidConfig()) { 103061da546Spatrick // Calculate the resource directory for LLDB. 104061da546Spatrick llvm::SmallString<256> resource_dir; 105061da546Spatrick llvm::sys::path::append(resource_dir, GetClangResourceDir().GetPath(), 106061da546Spatrick "include"); 107dda28197Spatrick m_resource_inc = std::string(resource_dir.str()); 108061da546Spatrick 109061da546Spatrick // This order matches the way Clang orders these directories. 110*be691f3bSpatrick m_include_dirs = {m_std_inc.Get().str(), m_resource_inc, 111*be691f3bSpatrick m_c_inc.Get().str()}; 112061da546Spatrick m_imported_modules = {"std"}; 113061da546Spatrick } 114061da546Spatrick } 115