Skip to content

Commit 1e7489c

Browse files
committed
[cxx-interop] Import libstdc++ as a Clang module
This change adds a module map for libstdc++, which allows it to be properly imported into Swift as a module. The module map is installed into `usr/lib/swift/linux/{arch}` similarly to `glibc.modulemap`, and is passed to Clang as `-fmodule-map-file`. That means it is now possible to import std directly from Swift on Linux, when C++ interop is enabled. The module map currently declares a single `std` module without splitting the headers into submodules. This is going to change in the near future. rdar://87654514
1 parent 27ad938 commit 1e7489c

File tree

8 files changed

+244
-19
lines changed

8 files changed

+244
-19
lines changed

lib/ClangImporter/ClangImporter.cpp

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -430,14 +430,10 @@ ClangImporter::~ClangImporter() {
430430

431431
#pragma mark Module loading
432432

433-
/// Finds the glibc.modulemap file relative to the provided resource dir.
434-
///
435-
/// Note that the module map used for Glibc depends on the target we're
436-
/// compiling for, and is not included in the resource directory with the other
437-
/// implicit module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
438-
static Optional<StringRef>
439-
getGlibcModuleMapPath(SearchPathOptions& Opts, llvm::Triple triple,
440-
SmallVectorImpl<char> &buffer) {
433+
static Optional<StringRef> getModuleMapFilePath(StringRef name,
434+
SearchPathOptions &Opts,
435+
llvm::Triple triple,
436+
SmallVectorImpl<char> &buffer) {
441437
StringRef platform = swift::getPlatformNameForTriple(triple);
442438
StringRef arch = swift::getMajorArchitectureName(triple);
443439

@@ -446,7 +442,7 @@ getGlibcModuleMapPath(SearchPathOptions& Opts, llvm::Triple triple,
446442
buffer.clear();
447443
buffer.append(SDKPath.begin(), SDKPath.end());
448444
llvm::sys::path::append(buffer, "usr", "lib", "swift");
449-
llvm::sys::path::append(buffer, platform, arch, "glibc.modulemap");
445+
llvm::sys::path::append(buffer, platform, arch, name);
450446

451447
// Only specify the module map if that file actually exists. It may not;
452448
// for example in the case that `swiftc -target x86_64-unknown-linux-gnu
@@ -459,7 +455,7 @@ getGlibcModuleMapPath(SearchPathOptions& Opts, llvm::Triple triple,
459455
buffer.clear();
460456
buffer.append(Opts.RuntimeResourcePath.begin(),
461457
Opts.RuntimeResourcePath.end());
462-
llvm::sys::path::append(buffer, platform, arch, "glibc.modulemap");
458+
llvm::sys::path::append(buffer, platform, arch, name);
463459

464460
// Only specify the module map if that file actually exists. It may not;
465461
// for example in the case that `swiftc -target x86_64-unknown-linux-gnu
@@ -471,6 +467,23 @@ getGlibcModuleMapPath(SearchPathOptions& Opts, llvm::Triple triple,
471467
return None;
472468
}
473469

470+
/// Finds the glibc.modulemap file relative to the provided resource dir.
471+
///
472+
/// Note that the module map used for Glibc depends on the target we're
473+
/// compiling for, and is not included in the resource directory with the other
474+
/// implicit module maps. It's at {freebsd|linux}/{arch}/glibc.modulemap.
475+
static Optional<StringRef>
476+
getGlibcModuleMapPath(SearchPathOptions &Opts, llvm::Triple triple,
477+
SmallVectorImpl<char> &buffer) {
478+
return getModuleMapFilePath("glibc.modulemap", Opts, triple, buffer);
479+
}
480+
481+
static Optional<StringRef>
482+
getLibStdCxxModuleMapPath(SearchPathOptions &opts, llvm::Triple triple,
483+
SmallVectorImpl<char> &buffer) {
484+
return getModuleMapFilePath("libstdcxx.modulemap", opts, triple, buffer);
485+
}
486+
474487
static bool clangSupportsPragmaAttributeWithSwiftAttr() {
475488
clang::AttributeCommonInfo swiftAttrInfo(clang::SourceRange(),
476489
clang::AttributeCommonInfo::AT_SwiftAttr,
@@ -680,6 +693,14 @@ importer::getNormalInvocationArguments(
680693
} else {
681694
// FIXME: Emit a warning of some kind.
682695
}
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+
}
683704
}
684705

685706
if (searchPathOpts.getSDKPath().empty()) {

stdlib/public/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ endif()
153153

154154
if(SWIFT_BUILD_SDK_OVERLAY OR SWIFT_BUILD_TEST_SUPPORT_MODULES)
155155
add_subdirectory(Platform)
156+
add_subdirectory(Cxx)
156157
endif()
157158

158159
if(SWIFT_BUILD_SDK_OVERLAY)

stdlib/public/Cxx/CMakeLists.txt

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
set(libstdcxx_modulemap_target_list)
2+
foreach(sdk ${SWIFT_SDKS})
3+
if(NOT "${sdk}" STREQUAL "LINUX" AND
4+
NOT "${sdk}" STREQUAL "FREEBSD" AND
5+
NOT "${sdk}" STREQUAL "OPENBSD" AND
6+
NOT "${sdk}" STREQUAL "ANDROID" AND
7+
NOT "${sdk}" STREQUAL "CYGWIN" AND
8+
NOT "${sdk}" STREQUAL "HAIKU")
9+
continue()
10+
endif()
11+
12+
foreach(arch ${SWIFT_SDK_${sdk}_ARCHITECTURES})
13+
set(arch_suffix "${SWIFT_SDK_${sdk}_LIB_SUBDIR}-${arch}")
14+
set(arch_subdir "${SWIFT_SDK_${sdk}_LIB_SUBDIR}/${arch}")
15+
16+
set(module_dir "${SWIFTLIB_DIR}/${arch_subdir}")
17+
set(module_dir_static "${SWIFTSTATICLIB_DIR}/${arch_subdir}")
18+
19+
set(libstdcxx_header "libstdcxx.h")
20+
set(libstdcxx_header_out "${module_dir}/libstdcxx.h")
21+
set(libstdcxx_header_out_static "${module_dir_static}/libstdcxx.h")
22+
set(libstdcxx_modulemap "libstdcxx.modulemap")
23+
set(libstdcxx_modulemap_out "${module_dir}/libstdcxx.modulemap")
24+
set(libstdcxx_modulemap_out_static "${module_dir_static}/libstdcxx.modulemap")
25+
26+
add_custom_command_target(
27+
copy_libstdcxx_modulemap
28+
COMMAND
29+
"${CMAKE_COMMAND}" "-E" "make_directory" ${module_dir}
30+
COMMAND
31+
"${CMAKE_COMMAND}" "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/${libstdcxx_modulemap}" "${libstdcxx_modulemap_out}"
32+
OUTPUT ${libstdcxx_modulemap_out}
33+
DEPENDS ${libstdcxx_modulemap}
34+
COMMENT "Copying libstdcxx modulemap to resources")
35+
list(APPEND libstdcxx_modulemap_target_list ${copy_libstdcxx_modulemap})
36+
add_dependencies(swift-stdlib-${arch_suffix} ${copy_libstdcxx_modulemap})
37+
38+
add_custom_command_target(
39+
copy_libstdcxx_header
40+
COMMAND
41+
"${CMAKE_COMMAND}" "-E" "make_directory" ${module_dir}
42+
COMMAND
43+
"${CMAKE_COMMAND}" "-E" "copy" "${CMAKE_CURRENT_SOURCE_DIR}/${libstdcxx_header}" "${libstdcxx_header_out}"
44+
OUTPUT ${libstdcxx_header_out}
45+
DEPENDS ${libstdcxx_header}
46+
COMMENT "Copying libstdcxx header to resources")
47+
list(APPEND libstdcxx_modulemap_target_list ${copy_libstdcxx_header})
48+
add_dependencies(swift-stdlib-${arch_suffix} ${copy_libstdcxx_header})
49+
50+
if(SWIFT_BUILD_STATIC_STDLIB)
51+
add_custom_command_target(
52+
copy_libstdcxx_modulemap_static
53+
COMMAND
54+
"${CMAKE_COMMAND}" "-E" "make_directory" ${module_dir_static}
55+
COMMAND
56+
"${CMAKE_COMMAND}" "-E" "copy"
57+
"${libstdcxx_modulemap_out}" "${libstdcxx_modulemap_out_static}"
58+
OUTPUT ${libstdcxx_modulemap_out_static}
59+
DEPENDS ${copy_libstdcxx_modulemap}
60+
COMMENT "Copying libstdcxx modulemap to static resources")
61+
list(APPEND libstdcxx_modulemap_target_list ${copy_libstdcxx_modulemap_static})
62+
add_dependencies(swift-stdlib-${arch_suffix} ${copy_libstdcxx_modulemap_static})
63+
64+
add_custom_command_target(
65+
copy_libstdcxx_header_static
66+
COMMAND
67+
"${CMAKE_COMMAND}" "-E" "make_directory" ${module_dir_static}
68+
COMMAND
69+
"${CMAKE_COMMAND}" "-E" "copy"
70+
"${libstdcxx_header_out}" "${libstdcxx_header_out_static}"
71+
OUTPUT ${libstdcxx_header_out_static}
72+
DEPENDS ${copy_libstdcxx_header}
73+
COMMENT "Copying libstdcxx header to static resources")
74+
list(APPEND libstdcxx_modulemap_target_list ${copy_libstdcxx_header_static})
75+
add_dependencies(swift-stdlib-${arch_suffix} ${copy_libstdcxx_header_static})
76+
endif()
77+
78+
swift_install_in_component(FILES "${libstdcxx_modulemap_out}"
79+
DESTINATION "lib/swift/${arch_subdir}"
80+
COMPONENT sdk-overlay)
81+
swift_install_in_component(FILES "${libstdcxx_header_out}"
82+
DESTINATION "lib/swift/${arch_subdir}"
83+
COMPONENT sdk-overlay)
84+
85+
if(SWIFT_BUILD_STATIC_STDLIB)
86+
swift_install_in_component(FILES "${libstdcxx_modulemap_out_static}"
87+
DESTINATION "lib/swift_static/${arch_subdir}"
88+
COMPONENT sdk-overlay)
89+
swift_install_in_component(FILES "${libstdcxx_header_out_static}"
90+
DESTINATION "lib/swift_static/${arch_subdir}"
91+
COMPONENT sdk-overlay)
92+
endif()
93+
endforeach()
94+
endforeach()
95+
add_custom_target(libstdcxx-modulemap DEPENDS ${libstdcxx_modulemap_target_list})
96+
set_property(TARGET libstdcxx-modulemap PROPERTY FOLDER "Miscellaneous")
97+
add_dependencies(sdk-overlay libstdcxx-modulemap)

stdlib/public/Cxx/libstdcxx.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
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>
53+
#include <any>
54+
#include <charconv>
55+
#include <execution>
56+
#include <filesystem>
57+
#include <memory_resource>
58+
#include <optional>
59+
#include <string_view>
60+
#include <variant>

stdlib/public/Cxx/libstdcxx.modulemap

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
//===--- libstdcxx.modulemap ----------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// In order to use a C++ stdlib from Swift, the stdlib needs to have a Clang
14+
// module map. Currently libstdc++ does not have a module map. To work around
15+
// this, Swift provides its own module map for libstdc++.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
module std {
20+
header "libstdcxx.h"
21+
requires cplusplus
22+
export *
23+
}

test/Interop/Cxx/stdlib/std-module-interface.swift renamed to test/Interop/Cxx/stdlib/libcxx-module-interface.swift

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,8 @@
22
// RUN: %target-swift-ide-test -print-module -module-to-print=std.iosfwd -source-filename=x -enable-cxx-interop -tools-directory=%llvm_obj_root/bin -module-cache-path %t | %FileCheck %s -check-prefix=CHECK-IOSFWD
33
// RUN: %target-swift-ide-test -print-module -module-to-print=std.string -source-filename=x -enable-cxx-interop -tools-directory=%llvm_obj_root/bin -module-cache-path %t | %FileCheck %s -check-prefix=CHECK-STRING
44

5-
// Clang driver on Windows doesn't support -stdlib=libc++
6-
// UNSUPPORTED: OS=windows-msvc
7-
8-
// libstdc++ cannot currently be imported
9-
// UNSUPPORTED: OS=linux-gnu
10-
// UNSUPPORTED: OS=linux-androideabi
11-
// UNSUPPORTED: OS=linux-android
5+
// This test is specific to libc++ and therefore only runs on Darwin platforms.
6+
// REQUIRES: OS=macosx || OS=ios
127

138
// REQUIRES: rdar84036022
149

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-swift-ide-test -print-module -module-to-print=std -source-filename=x -enable-cxx-interop -module-cache-path %t | %FileCheck %s -check-prefix=CHECK-STD
2+
3+
// This test is specific to libstdc++ and only runs on platforms where libstdc++ is used.
4+
// REQUIRES: OS=linux-gnu
5+
6+
// CHECK-STD: enum std {
7+
// CHECK-STD: enum __cxx11 {
8+
// CHECK-STD: struct __CxxTemplateInstNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE {
9+
// CHECK-STD: typealias value_type = std.__CxxTemplateInstSt11char_traitsIcE.char_type
10+
// CHECK-STD: }
11+
// CHECK-STD: struct __CxxTemplateInstNSt7__cxx1112basic_stringIwSt11char_traitsIwESaIwEEE {
12+
// CHECK-STD: typealias value_type = std.__CxxTemplateInstSt11char_traitsIwE.char_type
13+
// CHECK-STD: }
14+
// CHECK-STD: }
15+
16+
// CHECK-STD: static func to_string(_ __val: Int32) -> std.string
17+
// CHECK-STD: static func to_wstring(_ __val: Int32) -> std.wstring
18+
19+
// CHECK-STD: typealias size_t = Int
20+
21+
// CHECK-STD: typealias string = std.__cxx11.__CxxTemplateInstNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
22+
// CHECK-STD: typealias wstring = std.__cxx11.__CxxTemplateInstNSt7__cxx1112basic_stringIwSt11char_traitsIwESaIwEEE
23+
// CHECK-STD: }

test/Interop/Cxx/stdlib/use-std-string.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,17 @@
22
//
33
// REQUIRES: executable_test
44
//
5-
// Enable this everywhere once we have a solution for modularizing libstdc++: rdar://87654514
6-
// REQUIRES: OS=macosx
5+
// Enable this everywhere once we have a solution for modularizing other C++ stdlibs: rdar://87654514
6+
// REQUIRES: OS=macosx || OS=linux-gnu
77

88
import StdlibUnittest
99
import StdString
10+
#if os(Linux)
11+
import std
12+
// FIXME: import std.string once libstdc++ is split into submodules.
13+
#else
1014
import std.string
15+
#endif
1116

1217
var StdStringTestSuite = TestSuite("StdString")
1318

0 commit comments

Comments
 (0)