Skip to content

[ClangImporter] NFC: extract Clang include path mapping logic into a separate file #59810

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions lib/ClangImporter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ add_swift_host_library(swiftClangImporter STATIC
ClangDiagnosticConsumer.cpp
ClangImporter.cpp
ClangImporterRequests.cpp
ClangIncludePaths.cpp
ClangModuleDependencyScanner.cpp
ClangSourceBufferImporter.cpp
SwiftDeclSynthesizer.cpp
Expand Down
153 changes: 1 addition & 152 deletions lib/ClangImporter/ClangImporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//
#include "swift/ClangImporter/ClangImporter.h"
#include "ClangDiagnosticConsumer.h"
#include "ClangIncludePaths.h"
#include "ImporterImpl.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/Builtins.h"
Expand All @@ -38,21 +39,17 @@
#include "swift/Basic/Version.h"
#include "swift/ClangImporter/ClangImporterRequests.h"
#include "swift/ClangImporter/ClangModule.h"
#include "swift/Config.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Parse/Lexer.h"
#include "swift/Parse/Parser.h"
#include "swift/Strings.h"
#include "swift/Subsystems.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Mangle.h"
#include "clang/Basic/CharInfo.h"
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/Module.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/CodeGen/ObjectFilePCHContainerOperations.h"
#include "clang/Driver/Driver.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/Utils.h"
#include "clang/Index/IndexingAction.h"
Expand All @@ -74,7 +71,6 @@
#include "llvm/Support/Path.h"
#include "llvm/Support/VirtualFileSystem.h"
#include "llvm/Support/YAMLParser.h"
#include "llvm/Support/YAMLTraits.h"
#include <algorithm>
#include <string>
#include <memory>
Expand Down Expand Up @@ -444,60 +440,6 @@ ClangImporter::~ClangImporter() {

#pragma mark Module loading

static Optional<StringRef> getModuleMapFilePath(StringRef name,
SearchPathOptions &Opts,
llvm::Triple triple,
SmallVectorImpl<char> &buffer) {
StringRef platform = swift::getPlatformNameForTriple(triple);
StringRef arch = swift::getMajorArchitectureName(triple);

StringRef SDKPath = Opts.getSDKPath();
if (!SDKPath.empty()) {
buffer.clear();
buffer.append(SDKPath.begin(), SDKPath.end());
llvm::sys::path::append(buffer, "usr", "lib", "swift");
llvm::sys::path::append(buffer, platform, arch, name);

// Only specify the module map if that file actually exists. It may not;
// for example in the case that `swiftc -target x86_64-unknown-linux-gnu
// -emit-ir` is invoked using a Swift compiler not built for Linux targets.
if (llvm::sys::fs::exists(buffer))
return StringRef(buffer.data(), buffer.size());
}

if (!Opts.RuntimeResourcePath.empty()) {
buffer.clear();
buffer.append(Opts.RuntimeResourcePath.begin(),
Opts.RuntimeResourcePath.end());
llvm::sys::path::append(buffer, platform, arch, name);

// Only specify the module map if that file actually exists. It may not;
// for example in the case that `swiftc -target x86_64-unknown-linux-gnu
// -emit-ir` is invoked using a Swift compiler not built for Linux targets.
if (llvm::sys::fs::exists(buffer))
return StringRef(buffer.data(), buffer.size());
}

return None;
}

/// Finds the glibc.modulemap file relative to the provided resource dir.
///
/// Note that the module map used for Glibc depends on the target we're
/// compiling for, and is not included in the resource directory with the other
/// implicit module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
static Optional<StringRef>
getGlibcModuleMapPath(SearchPathOptions &Opts, llvm::Triple triple,
SmallVectorImpl<char> &buffer) {
return getModuleMapFilePath("glibc.modulemap", Opts, triple, buffer);
}

static Optional<StringRef>
getLibStdCxxModuleMapPath(SearchPathOptions &opts, llvm::Triple triple,
SmallVectorImpl<char> &buffer) {
return getModuleMapFilePath("libstdcxx.modulemap", opts, triple, buffer);
}

static bool clangSupportsPragmaAttributeWithSwiftAttr() {
clang::AttributeCommonInfo swiftAttrInfo(clang::SourceRange(),
clang::AttributeCommonInfo::AT_SwiftAttr,
Expand Down Expand Up @@ -882,99 +824,6 @@ importer::addCommonInvocationArguments(
}
}

/// On Linux, some platform libraries (glibc, libstdc++) are not modularized.
/// We inject modulemaps for those libraries into their include directories
/// to allow using them from Swift.
static SmallVector<std::pair<std::string, std::string>, 16>
getClangInvocationFileMapping(ASTContext &ctx) {
using Path = SmallString<128>;

const llvm::Triple &triple = ctx.LangOpts.Target;
// We currently only need this when building for Linux.
if (!triple.isOSLinux())
return {};
// Android uses libc++.
if (triple.isAndroid())
return {};

// Extract the libstdc++ installation path from Clang driver.
auto clangDiags = clang::CompilerInstance::createDiagnostics(
new clang::DiagnosticOptions());
clang::driver::Driver clangDriver(ctx.ClangImporterOpts.clangPath,
triple.str(), *clangDiags);
// Flags passed to Swift with `-Xcc` might affect include paths.
unsigned unused1, unused2;
std::vector<const char *> clangArgs;
for (const auto &each : ctx.ClangImporterOpts.ExtraArgs) {
clangArgs.push_back(each.c_str());
}
llvm::opt::InputArgList clangDriverArgs =
clangDriver.getOpts().ParseArgs(clangArgs, unused1, unused2);
// If an SDK path was explicitly passed to Swift, make sure to pass it to
// Clang driver as well. It affects the resulting include paths.
auto sdkPath = ctx.SearchPathOpts.getSDKPath();
if (!sdkPath.empty()) {
unsigned argIndex = clangDriverArgs.MakeIndex("--sysroot", sdkPath);
clangDriverArgs.append(new llvm::opt::Arg(
clangDriver.getOpts().getOption(clang::driver::options::OPT__sysroot),
sdkPath, argIndex));
}
auto cxxStdlibDirs =
clangDriver.getLibStdCxxIncludePaths(clangDriverArgs, triple);
if (cxxStdlibDirs.empty()) {
ctx.Diags.diagnose(SourceLoc(), diag::libstdcxx_not_found, triple.str());
return {};
}
Path cxxStdlibDir(cxxStdlibDirs.front());
// VFS does not allow mapping paths that contain `../` or `./`.
llvm::sys::path::remove_dots(cxxStdlibDir, /*remove_dot_dot=*/true);

// Currently only a modulemap for libstdc++ is injected.
if (!ctx.LangOpts.EnableCXXInterop)
return {};

Path actualModuleMapPath;
Path buffer;
if (auto path = getLibStdCxxModuleMapPath(ctx.SearchPathOpts, triple, buffer))
actualModuleMapPath = path.getValue();
else
return {};

// Only inject the module map if it actually exists. It may not, for example
// if `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
// a Swift compiler not built for Linux targets.
if (!llvm::sys::fs::exists(actualModuleMapPath))
// FIXME: emit a warning of some kind.
return {};

// TODO: remove the libstdcxx.h header and reference all libstdc++ headers
// directly from the modulemap.
Path actualHeaderPath = actualModuleMapPath;
llvm::sys::path::remove_filename(actualHeaderPath);
llvm::sys::path::append(actualHeaderPath, "libstdcxx.h");

// Inject a modulemap into VFS for the libstdc++ directory.
// Only inject the module map if the module does not already exist at
// {sysroot}/usr/include/module.{map,modulemap}.
Path injectedModuleMapLegacyPath(cxxStdlibDir);
llvm::sys::path::append(injectedModuleMapLegacyPath, "module.map");
if (llvm::sys::fs::exists(injectedModuleMapLegacyPath))
return {};

Path injectedModuleMapPath(cxxStdlibDir);
llvm::sys::path::append(injectedModuleMapPath, "module.modulemap");
if (llvm::sys::fs::exists(injectedModuleMapPath))
return {};

Path injectedHeaderPath(cxxStdlibDir);
llvm::sys::path::append(injectedHeaderPath, "libstdcxx.h");

return {
{std::string(injectedModuleMapPath), std::string(actualModuleMapPath)},
{std::string(injectedHeaderPath), std::string(actualHeaderPath)},
};
}

bool ClangImporter::canReadPCH(StringRef PCHFilename) {
if (!llvm::sys::fs::exists(PCHFilename))
return false;
Expand Down
160 changes: 160 additions & 0 deletions lib/ClangImporter/ClangIncludePaths.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//===--- ClangIncludePaths.cpp --------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "ClangIncludePaths.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsClangImporter.h"
#include "swift/Basic/Platform.h"
#include "clang/Driver/Driver.h"
#include "clang/Frontend/CompilerInstance.h"

using namespace swift;

static Optional<StringRef> getModuleMapFilePath(StringRef name,
SearchPathOptions &Opts,
llvm::Triple triple,
SmallVectorImpl<char> &buffer) {
StringRef platform = swift::getPlatformNameForTriple(triple);
StringRef arch = swift::getMajorArchitectureName(triple);

StringRef SDKPath = Opts.getSDKPath();
if (!SDKPath.empty()) {
buffer.clear();
buffer.append(SDKPath.begin(), SDKPath.end());
llvm::sys::path::append(buffer, "usr", "lib", "swift");
llvm::sys::path::append(buffer, platform, arch, name);

// Only specify the module map if that file actually exists. It may not;
// for example in the case that `swiftc -target x86_64-unknown-linux-gnu
// -emit-ir` is invoked using a Swift compiler not built for Linux targets.
if (llvm::sys::fs::exists(buffer))
return StringRef(buffer.data(), buffer.size());
}

if (!Opts.RuntimeResourcePath.empty()) {
buffer.clear();
buffer.append(Opts.RuntimeResourcePath.begin(),
Opts.RuntimeResourcePath.end());
llvm::sys::path::append(buffer, platform, arch, name);

// Only specify the module map if that file actually exists. It may not;
// for example in the case that `swiftc -target x86_64-unknown-linux-gnu
// -emit-ir` is invoked using a Swift compiler not built for Linux targets.
if (llvm::sys::fs::exists(buffer))
return StringRef(buffer.data(), buffer.size());
}

return None;
}

Optional<StringRef>
swift::getGlibcModuleMapPath(SearchPathOptions &Opts, llvm::Triple triple,
SmallVectorImpl<char> &buffer) {
return getModuleMapFilePath("glibc.modulemap", Opts, triple, buffer);
}

static Optional<StringRef>
getLibStdCxxModuleMapPath(SearchPathOptions &opts, llvm::Triple triple,
SmallVectorImpl<char> &buffer) {
return getModuleMapFilePath("libstdcxx.modulemap", opts, triple, buffer);
}

SmallVector<std::pair<std::string, std::string>, 16>
swift::getClangInvocationFileMapping(ASTContext &ctx) {
using Path = SmallString<128>;

const llvm::Triple &triple = ctx.LangOpts.Target;
// We currently only need this when building for Linux.
if (!triple.isOSLinux())
return {};
// Android uses libc++.
if (triple.isAndroid())
return {};

// Extract the libstdc++ installation path from Clang driver.
auto clangDiags = clang::CompilerInstance::createDiagnostics(
new clang::DiagnosticOptions());
clang::driver::Driver clangDriver(ctx.ClangImporterOpts.clangPath,
triple.str(), *clangDiags);
// Flags passed to Swift with `-Xcc` might affect include paths.
unsigned unused1, unused2;
std::vector<const char *> clangArgs;
for (const auto &each : ctx.ClangImporterOpts.ExtraArgs) {
clangArgs.push_back(each.c_str());
}
llvm::opt::InputArgList clangDriverArgs =
clangDriver.getOpts().ParseArgs(clangArgs, unused1, unused2);
// If an SDK path was explicitly passed to Swift, make sure to pass it to
// Clang driver as well. It affects the resulting include paths.
auto sdkPath = ctx.SearchPathOpts.getSDKPath();
if (!sdkPath.empty()) {
unsigned argIndex = clangDriverArgs.MakeIndex("--sysroot", sdkPath);
clangDriverArgs.append(new llvm::opt::Arg(
clangDriver.getOpts().getOption(clang::driver::options::OPT__sysroot),
sdkPath, argIndex));
}
auto cxxStdlibDirs =
clangDriver.getLibStdCxxIncludePaths(clangDriverArgs, triple);
if (cxxStdlibDirs.empty()) {
ctx.Diags.diagnose(SourceLoc(), diag::libstdcxx_not_found, triple.str());
return {};
}
Path cxxStdlibDir(cxxStdlibDirs.front());
// VFS does not allow mapping paths that contain `../` or `./`.
llvm::sys::path::remove_dots(cxxStdlibDir, /*remove_dot_dot=*/true);

// Currently only a modulemap for libstdc++ is injected.
if (!ctx.LangOpts.EnableCXXInterop)
return {};

Path actualModuleMapPath;
Path buffer;
if (auto path = getLibStdCxxModuleMapPath(ctx.SearchPathOpts, triple, buffer))
actualModuleMapPath = path.getValue();
else
return {};

// Only inject the module map if it actually exists. It may not, for example
// if `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
// a Swift compiler not built for Linux targets.
if (!llvm::sys::fs::exists(actualModuleMapPath))
// FIXME: emit a warning of some kind.
return {};

// TODO: remove the libstdcxx.h header and reference all libstdc++ headers
// directly from the modulemap.
Path actualHeaderPath = actualModuleMapPath;
llvm::sys::path::remove_filename(actualHeaderPath);
llvm::sys::path::append(actualHeaderPath, "libstdcxx.h");

// Inject a modulemap into VFS for the libstdc++ directory.
// Only inject the module map if the module does not already exist at
// {sysroot}/usr/include/module.{map,modulemap}.
Path injectedModuleMapLegacyPath(cxxStdlibDir);
llvm::sys::path::append(injectedModuleMapLegacyPath, "module.map");
if (llvm::sys::fs::exists(injectedModuleMapLegacyPath))
return {};

Path injectedModuleMapPath(cxxStdlibDir);
llvm::sys::path::append(injectedModuleMapPath, "module.modulemap");
if (llvm::sys::fs::exists(injectedModuleMapPath))
return {};

Path injectedHeaderPath(cxxStdlibDir);
llvm::sys::path::append(injectedHeaderPath, "libstdcxx.h");

return {
{std::string(injectedModuleMapPath), std::string(actualModuleMapPath)},
{std::string(injectedHeaderPath), std::string(actualHeaderPath)},
};
}
Loading