Skip to content

Commit af7b83d

Browse files
committed
[cxx-interop] Use VFS to inject modulemap into libstdc++ installation
Previously the modulemap for the C++ stdlib on Linux was provided via `-fmodule-map-file=` Clang argument pointing to the modulemap file within the Swift toolchain. The modulemap file could not reference the stdlib headers directly, since the exact stdlib include directory varies across Linux versions (it generally looks like `/usr/include/c++/{gcc_version}`). So the modulemap file instead referenced a local header, which `#include <>`-ed the stdlib headers, relying on Clang include resolution. Unfortunately this did not work properly in the presence of another C++ module which included the stdlib headers: sometimes decls from the stdlib were hijacked by the other module, and were not treated as a part of the stdlib by Clang. This caused compile errors in Swift. This change uses LLVM VFS to inject the modulemap file into the libstdc++ directory. The modulemap file is now able to reference the stdlib headers directly, which fixes the issue. Credits to Rintaro Ishizaki for proposing a similar idea for SwiftGlibc back in 2016.
1 parent 223d73c commit af7b83d

File tree

2 files changed

+159
-77
lines changed

2 files changed

+159
-77
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@
7373
#include "llvm/Support/Path.h"
7474
#include "llvm/Support/YAMLParser.h"
7575
#include "llvm/Support/YAMLTraits.h"
76+
#include "llvm/Support/VirtualFileSystem.h"
7677
#include <algorithm>
7778
#include <memory>
7879

@@ -693,14 +694,6 @@ importer::getNormalInvocationArguments(
693694
} else {
694695
// FIXME: Emit a warning of some kind.
695696
}
696-
697-
if (EnableCXXInterop) {
698-
if (auto path =
699-
getLibStdCxxModuleMapPath(searchPathOpts, triple, buffer)) {
700-
invocationArgStrs.push_back(
701-
(Twine("-fmodule-map-file=") + *path).str());
702-
}
703-
}
704697
}
705698

706699
if (searchPathOpts.getSDKPath().empty()) {
@@ -870,6 +863,92 @@ importer::addCommonInvocationArguments(
870863
}
871864
}
872865

866+
/// On Linux, some platform libraries (glibc, libstdc++) are not modularized.
867+
/// We inject modulemaps for those libraries into their include directories
868+
/// to allow using them from Swift.
869+
static SmallVector<std::pair<std::string, std::string>>
870+
getClangInvocationFileMapping(ASTContext &ctx) {
871+
using Path = SmallString<128>;
872+
873+
const llvm::Triple &triple = ctx.LangOpts.Target;
874+
// We currently only need this when building for Linux.
875+
if (!triple.isOSLinux())
876+
return {};
877+
878+
SearchPathOptions &searchPathOpts = ctx.SearchPathOpts;
879+
880+
Path SDKPath(searchPathOpts.getSDKPath());
881+
if (SDKPath.empty())
882+
SDKPath = "/";
883+
884+
// Currently only a modulemap for libstdc++ is injected.
885+
if (!ctx.LangOpts.EnableCXXInterop)
886+
return {};
887+
888+
Path actualModuleMapPath;
889+
Path buffer;
890+
if (auto path = getLibStdCxxModuleMapPath(searchPathOpts, triple, buffer)) {
891+
actualModuleMapPath = path.getValue();
892+
} else
893+
return {};
894+
895+
// Only inject the module map if it actually exists. It may not, for example
896+
// if `swiftc -target x86_64-unknown-linux-gnu -emit-ir` is invoked using
897+
// a Swift compiler not built for Linux targets.
898+
if (!llvm::sys::fs::exists(actualModuleMapPath))
899+
// FIXME: emit a warning of some kind.
900+
return {};
901+
902+
// TODO: remove the libstdcxx.h header and reference all libstdc++ headers
903+
// directly from the modulemap.
904+
Path actualHeaderPath = actualModuleMapPath;
905+
llvm::sys::path::remove_filename(actualHeaderPath);
906+
llvm::sys::path::append(actualHeaderPath, "libstdcxx.h");
907+
908+
Path cxxStdlibsRoot(SDKPath);
909+
llvm::sys::path::append(cxxStdlibsRoot, "usr", "include", "c++");
910+
if (!llvm::sys::fs::exists(cxxStdlibsRoot))
911+
return {};
912+
913+
// Collect all installed versions of libstdc++. We currently have no way to
914+
// know which libstdc++ version will be used for this Clang invocation.
915+
// TODO: extract this information from the Clang driver.
916+
SmallVector<Path, 1> cxxStdlibDirs;
917+
std::error_code errorCode;
918+
for (llvm::vfs::directory_iterator
919+
iter = ctx.SourceMgr.getFileSystem()->dir_begin(cxxStdlibsRoot,
920+
errorCode),
921+
endIter;
922+
!errorCode && iter != endIter; iter = iter.increment(errorCode)) {
923+
cxxStdlibDirs.push_back(SmallString<128>(iter->path()));
924+
}
925+
926+
SmallVector<std::pair<std::string, std::string>> result;
927+
// Inject a modulemap into the VFS for each of the libstdc++ versions.
928+
for (const auto &cxxStdlibDir : cxxStdlibDirs) {
929+
// Only inject the module map if the module does not already exist at
930+
// {sysroot}/usr/include/module.{map,modulemap}.
931+
Path injectedModuleMapLegacyPath(cxxStdlibDir);
932+
llvm::sys::path::append(injectedModuleMapLegacyPath, "module.map");
933+
if (llvm::sys::fs::exists(injectedModuleMapLegacyPath))
934+
continue;
935+
936+
Path injectedModuleMapPath = cxxStdlibDir;
937+
llvm::sys::path::append(injectedModuleMapPath, "module.modulemap");
938+
if (llvm::sys::fs::exists(injectedModuleMapPath))
939+
continue;
940+
941+
Path injectedHeaderPath = cxxStdlibDir;
942+
llvm::sys::path::append(injectedHeaderPath, "libstdcxx.h");
943+
944+
result.push_back(
945+
{std::string(injectedModuleMapPath), std::string(actualModuleMapPath)});
946+
result.push_back(
947+
{std::string(injectedHeaderPath), std::string(actualHeaderPath)});
948+
}
949+
return result;
950+
}
951+
873952
bool ClangImporter::canReadPCH(StringRef PCHFilename) {
874953
if (!llvm::sys::fs::exists(PCHFilename))
875954
return false;
@@ -1122,9 +1201,10 @@ ClangImporter::create(ASTContext &ctx,
11221201
}
11231202
}
11241203

1204+
auto fileMapping = getClangInvocationFileMapping(ctx);
11251205
// Wrap Swift's FS to allow Clang to override the working directory
11261206
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS =
1127-
llvm::vfs::RedirectingFileSystem::create({}, true,
1207+
llvm::vfs::RedirectingFileSystem::create(fileMapping, true,
11281208
*ctx.SourceMgr.getFileSystem());
11291209

11301210
// Create a new Clang compiler invocation.

stdlib/public/Cxx/libstdcxx.h

Lines changed: 70 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,81 @@
1-
#include <algorithm>
2-
#include <bitset>
3-
#include <complex>
4-
#include <deque>
5-
#include <exception>
6-
#include <fstream>
7-
#include <functional>
8-
#include <iomanip>
9-
#include <ios>
10-
#include <iosfwd>
11-
#include <iostream>
12-
#include <istream>
13-
#include <iterator>
14-
#include <limits>
15-
#include <list>
16-
#include <locale>
17-
#include <map>
18-
#include <memory>
19-
#include <new>
20-
#include <numeric>
21-
#include <ostream>
22-
#include <queue>
23-
#include <set>
24-
#include <sstream>
25-
#include <stack>
26-
#include <stdexcept>
27-
#include <streambuf>
28-
#include <string>
29-
#include <utility>
30-
#include <typeinfo>
31-
#include <valarray>
32-
#include <vector>
33-
#include <array>
34-
#include <atomic>
35-
#include <chrono>
36-
#include <codecvt>
37-
#include <condition_variable>
38-
#include <forward_list>
39-
#include <future>
40-
#include <initializer_list>
41-
#include <mutex>
42-
#include <random>
43-
#include <ratio>
44-
#include <regex>
45-
#include <scoped_allocator>
46-
#include <system_error>
47-
#include <thread>
48-
#include <tuple>
49-
#include <typeindex>
50-
#include <type_traits>
51-
#include <unordered_map>
52-
#include <unordered_set>
1+
#include "algorithm"
2+
#include "bitset"
3+
#include "complex"
4+
#include "deque"
5+
#include "exception"
6+
#include "fstream"
7+
#include "functional"
8+
#include "iomanip"
9+
#include "ios"
10+
#include "iosfwd"
11+
#include "iostream"
12+
#include "istream"
13+
#include "iterator"
14+
#include "limits"
15+
#include "list"
16+
#include "locale"
17+
#include "map"
18+
#include "memory"
19+
#include "new"
20+
#include "numeric"
21+
#include "ostream"
22+
#include "queue"
23+
#include "set"
24+
#include "sstream"
25+
#include "stack"
26+
#include "stdexcept"
27+
#include "streambuf"
28+
#include "string"
29+
#include "utility"
30+
#include "typeinfo"
31+
#include "valarray"
32+
#include "vector"
33+
//#include "cassert"
34+
#include "cmath"
35+
#include "array"
36+
#include "atomic"
37+
#include "chrono"
38+
#include "codecvt"
39+
#include "condition_variable"
40+
#include "forward_list"
41+
#include "future"
42+
#include "initializer_list"
43+
#include "mutex"
44+
#include "random"
45+
#include "ratio"
46+
#include "regex"
47+
#include "scoped_allocator"
48+
#include "system_error"
49+
#include "thread"
50+
#include "tuple"
51+
#include "typeindex"
52+
#include "type_traits"
53+
#include "unordered_map"
54+
#include "unordered_set"
5355

5456
// C++17 and newer:
5557

56-
#if __has_include(<any>)
57-
#include <any>
58+
#if __has_include("any")
59+
#include "any"
5860
#endif
59-
#if __has_include(<charconv>)
60-
#include <charconv>
61+
#if __has_include("charconv")
62+
#include "charconv"
6163
#endif
62-
#if __has_include(<execution>)
63-
#include <execution>
64+
#if __has_include("execution")
65+
#include "execution"
6466
#endif
65-
#if __has_include(<filesystem>)
66-
#include <filesystem>
67+
#if __has_include("filesystem")
68+
#include "filesystem"
6769
#endif
68-
#if __has_include(<memory_resource>)
69-
#include <memory_resource>
70+
#if __has_include("memory_resource")
71+
#include "memory_resource"
7072
#endif
71-
#if __has_include(<optional>)
72-
#include <optional>
73+
#if __has_include("optional")
74+
#include "optional"
7375
#endif
74-
#if __has_include(<string_view>)
75-
#include <string_view>
76+
#if __has_include("string_view")
77+
#include "string_view"
7678
#endif
77-
#if __has_include(<variant>)
78-
#include <variant>
79+
#if __has_include("variant")
80+
#include "variant"
7981
#endif

0 commit comments

Comments
 (0)