1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmCxxModuleMapper.h"
13 #include <cm/string_view>
14 #include <cmext/string_view>
16 #include "cmScanDepFormat.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
20 CxxBmiLocation::CxxBmiLocation() = default;
22 CxxBmiLocation::CxxBmiLocation(std::string path
)
23 : BmiLocation(std::move(path
))
27 CxxBmiLocation
CxxBmiLocation::Unknown()
32 CxxBmiLocation
CxxBmiLocation::Private()
34 return { std::string
{} };
37 CxxBmiLocation
CxxBmiLocation::Known(std::string path
)
39 return { std::move(path
) };
42 bool CxxBmiLocation::IsKnown() const
44 return this->BmiLocation
.has_value();
47 bool CxxBmiLocation::IsPrivate() const
49 if (auto const& loc
= this->BmiLocation
) {
55 std::string
const& CxxBmiLocation::Location() const
57 if (auto const& loc
= this->BmiLocation
) {
60 static std::string empty
;
64 CxxBmiLocation
CxxModuleLocations::BmiGeneratorPathForModule(
65 std::string
const& logical_name
) const
67 auto bmi_loc
= this->BmiLocationForModule(logical_name
);
68 if (bmi_loc
.IsKnown() && !bmi_loc
.IsPrivate()) {
70 CxxBmiLocation::Known(this->PathForGenerator(bmi_loc
.Location()));
77 struct TransitiveUsage
79 TransitiveUsage(std::string name
, std::string location
, LookupMethod method
)
80 : LogicalName(std::move(name
))
81 , Location(std::move(location
))
86 std::string LogicalName
;
91 std::vector
<TransitiveUsage
> GetTransitiveUsages(
92 CxxModuleLocations
const& loc
, std::vector
<cmSourceReqInfo
> const& required
,
93 CxxModuleUsage
const& usages
)
95 std::set
<std::string
> transitive_usage_directs
;
96 std::set
<std::string
> transitive_usage_names
;
98 std::vector
<TransitiveUsage
> all_usages
;
100 for (auto const& r
: required
) {
101 auto bmi_loc
= loc
.BmiGeneratorPathForModule(r
.LogicalName
);
102 if (bmi_loc
.IsKnown()) {
103 all_usages
.emplace_back(r
.LogicalName
, bmi_loc
.Location(), r
.Method
);
104 transitive_usage_directs
.insert(r
.LogicalName
);
106 // Insert transitive usages.
107 auto transitive_usages
= usages
.Usage
.find(r
.LogicalName
);
108 if (transitive_usages
!= usages
.Usage
.end()) {
109 transitive_usage_names
.insert(transitive_usages
->second
.begin(),
110 transitive_usages
->second
.end());
115 for (auto const& transitive_name
: transitive_usage_names
) {
116 if (transitive_usage_directs
.count(transitive_name
)) {
120 auto module_ref
= usages
.Reference
.find(transitive_name
);
121 if (module_ref
!= usages
.Reference
.end()) {
122 all_usages
.emplace_back(transitive_name
, module_ref
->second
.Path
,
123 module_ref
->second
.Method
);
130 std::string
CxxModuleMapContentClang(CxxModuleLocations
const& loc
,
131 cmScanDepInfo
const& obj
,
132 CxxModuleUsage
const& usages
)
134 std::stringstream mm
;
136 // Clang's command line only supports a single output. If more than one is
137 // expected, we cannot make a useful module map file.
138 if (obj
.Provides
.size() > 1) {
142 // A series of flags which tell the compiler where to look for modules.
144 for (auto const& p
: obj
.Provides
) {
145 auto bmi_loc
= loc
.BmiGeneratorPathForModule(p
.LogicalName
);
146 if (bmi_loc
.IsKnown()) {
147 // Force the TU to be considered a C++ module source file regardless of
149 mm
<< "-x c++-module\n";
151 mm
<< "-fmodule-output=" << bmi_loc
.Location() << '\n';
156 auto all_usages
= GetTransitiveUsages(loc
, obj
.Requires
, usages
);
157 for (auto const& usage
: all_usages
) {
158 mm
<< "-fmodule-file=" << usage
.LogicalName
<< '=' << usage
.Location
165 std::string
CxxModuleMapContentGcc(CxxModuleLocations
const& loc
,
166 cmScanDepInfo
const& obj
)
168 std::stringstream mm
;
170 // Documented in GCC's documentation. The format is a series of
171 // lines with a module name and the associated filename separated
172 // by spaces. The first line may use `$root` as the module name
173 // to specify a "repository root". That is used to anchor any
174 // relative paths present in the file (CMake should never
177 // Write the root directory to use for module paths.
178 mm
<< "$root " << loc
.RootDirectory
<< '\n';
180 for (auto const& p
: obj
.Provides
) {
181 auto bmi_loc
= loc
.BmiGeneratorPathForModule(p
.LogicalName
);
182 if (bmi_loc
.IsKnown()) {
183 mm
<< p
.LogicalName
<< ' ' << bmi_loc
.Location() << '\n';
186 for (auto const& r
: obj
.Requires
) {
187 auto bmi_loc
= loc
.BmiGeneratorPathForModule(r
.LogicalName
);
188 if (bmi_loc
.IsKnown()) {
189 mm
<< r
.LogicalName
<< ' ' << bmi_loc
.Location() << '\n';
196 std::string
CxxModuleMapContentMsvc(CxxModuleLocations
const& loc
,
197 cmScanDepInfo
const& obj
,
198 CxxModuleUsage
const& usages
)
200 std::stringstream mm
;
202 // A response file of `-reference NAME=PATH` arguments.
204 // MSVC's command line only supports a single output. If more than one is
205 // expected, we cannot make a useful module map file.
206 if (obj
.Provides
.size() > 1) {
210 auto flag_for_method
= [](LookupMethod method
) -> cm::static_string_view
{
212 case LookupMethod::ByName
:
213 return "-reference"_s
;
214 case LookupMethod::IncludeAngle
:
215 return "-headerUnit:angle"_s
;
216 case LookupMethod::IncludeQuote
:
217 return "-headerUnit:quote"_s
;
219 assert(false && "unsupported lookup method");
223 for (auto const& p
: obj
.Provides
) {
225 mm
<< "-interface\n";
227 mm
<< "-internalPartition\n";
230 auto bmi_loc
= loc
.BmiGeneratorPathForModule(p
.LogicalName
);
231 if (bmi_loc
.IsKnown()) {
232 mm
<< "-ifcOutput " << bmi_loc
.Location() << '\n';
236 auto all_usages
= GetTransitiveUsages(loc
, obj
.Requires
, usages
);
237 for (auto const& usage
: all_usages
) {
238 auto flag
= flag_for_method(usage
.Method
);
240 mm
<< flag
<< ' ' << usage
.LogicalName
<< '=' << usage
.Location
<< '\n';
247 bool CxxModuleUsage::AddReference(std::string
const& logical
,
248 std::string
const& loc
, LookupMethod method
)
250 auto r
= this->Reference
.find(logical
);
251 if (r
!= this->Reference
.end()) {
252 auto& ref
= r
->second
;
254 if (ref
.Path
== loc
&& ref
.Method
== method
) {
258 auto method_name
= [](LookupMethod m
) -> cm::static_string_view
{
260 case LookupMethod::ByName
:
262 case LookupMethod::IncludeAngle
:
263 return "include-angle"_s
;
264 case LookupMethod::IncludeQuote
:
265 return "include-quote"_s
;
267 assert(false && "unsupported lookup method");
271 cmSystemTools::Error(cmStrCat("Disagreement of the location of the '",
275 ref
.Path
, "' via ", method_name(ref
.Method
),
278 loc
, "' via ", method_name(method
), "."));
282 auto& ref
= this->Reference
[logical
];
289 cm::static_string_view
CxxModuleMapExtension(
290 cm::optional
<CxxModuleMapFormat
> format
)
294 case CxxModuleMapFormat::Clang
:
296 case CxxModuleMapFormat::Gcc
:
298 case CxxModuleMapFormat::Msvc
:
306 std::set
<std::string
> CxxModuleUsageSeed(
307 CxxModuleLocations
const& loc
, std::vector
<cmScanDepInfo
> const& objects
,
308 CxxModuleUsage
& usages
, bool& private_usage_found
)
310 // Track inner usages to populate usages from internal bits.
312 // This is a map of modules that required some other module that was not
313 // found to those that were not found.
314 std::map
<std::string
, std::set
<std::string
>> internal_usages
;
315 std::set
<std::string
> unresolved
;
317 for (cmScanDepInfo
const& object
: objects
) {
318 // Add references for each of the provided modules.
319 for (auto const& p
: object
.Provides
) {
320 auto bmi_loc
= loc
.BmiGeneratorPathForModule(p
.LogicalName
);
321 if (bmi_loc
.IsKnown()) {
322 // XXX(cxx-modules): How to support header units?
323 usages
.AddReference(p
.LogicalName
, bmi_loc
.Location(),
324 LookupMethod::ByName
);
328 // For each requires, pull in what is required.
329 for (auto const& r
: object
.Requires
) {
330 // Find the required name in the current target.
331 auto bmi_loc
= loc
.BmiGeneratorPathForModule(r
.LogicalName
);
332 if (bmi_loc
.IsPrivate()) {
333 cmSystemTools::Error(
334 cmStrCat("Unable to use module '", r
.LogicalName
,
335 "' as it is 'PRIVATE' and therefore not accessible outside "
336 "of its owning target."));
337 private_usage_found
= true;
341 // Find transitive usages.
342 auto transitive_usages
= usages
.Usage
.find(r
.LogicalName
);
344 for (auto const& p
: object
.Provides
) {
345 auto& this_usages
= usages
.Usage
[p
.LogicalName
];
347 // Add the direct usage.
348 this_usages
.insert(r
.LogicalName
);
350 if (transitive_usages
== usages
.Usage
.end() ||
351 internal_usages
.find(r
.LogicalName
) != internal_usages
.end()) {
352 // Mark that we need to update transitive usages later.
353 if (bmi_loc
.IsKnown()) {
354 internal_usages
[p
.LogicalName
].insert(r
.LogicalName
);
357 // Add the transitive usage.
358 this_usages
.insert(transitive_usages
->second
.begin(),
359 transitive_usages
->second
.end());
363 if (bmi_loc
.IsKnown()) {
364 usages
.AddReference(r
.LogicalName
, bmi_loc
.Location(), r
.Method
);
369 // While we have internal usages to manage.
370 while (!internal_usages
.empty()) {
371 size_t starting_size
= internal_usages
.size();
373 // For each internal usage.
374 for (auto usage
= internal_usages
.begin(); usage
!= internal_usages
.end();
375 /* see end of loop */) {
376 auto& this_usages
= usages
.Usage
[usage
->first
];
378 for (auto use
= usage
->second
.begin(); use
!= usage
->second
.end();
379 /* see end of loop */) {
380 // Check if this required module uses other internal modules; defer
382 if (internal_usages
.count(*use
)) {
383 // Advance the iterator.
388 auto transitive_usages
= usages
.Usage
.find(*use
);
389 if (transitive_usages
!= usages
.Usage
.end()) {
390 this_usages
.insert(transitive_usages
->second
.begin(),
391 transitive_usages
->second
.end());
394 // Remove the entry and advance the iterator.
395 use
= usage
->second
.erase(use
);
398 // Erase the entry if it doesn't have any remaining usages.
399 if (usage
->second
.empty()) {
400 usage
= internal_usages
.erase(usage
);
406 // Check that at least one usage was resolved.
407 if (starting_size
== internal_usages
.size()) {
408 // Nothing could be resolved this loop; we have a cycle, so record the
410 for (auto const& usage
: internal_usages
) {
411 unresolved
.insert(usage
.first
);
420 std::string
CxxModuleMapContent(CxxModuleMapFormat format
,
421 CxxModuleLocations
const& loc
,
422 cmScanDepInfo
const& obj
,
423 CxxModuleUsage
const& usages
)
426 case CxxModuleMapFormat::Clang
:
427 return CxxModuleMapContentClang(loc
, obj
, usages
);
428 case CxxModuleMapFormat::Gcc
:
429 return CxxModuleMapContentGcc(loc
, obj
);
430 case CxxModuleMapFormat::Msvc
:
431 return CxxModuleMapContentMsvc(loc
, obj
, usages
);
438 CxxModuleMapMode
CxxModuleMapOpenMode(CxxModuleMapFormat format
)
441 case CxxModuleMapFormat::Gcc
:
442 return CxxModuleMapMode::Binary
;
443 case CxxModuleMapFormat::Clang
:
444 case CxxModuleMapFormat::Msvc
:
445 return CxxModuleMapMode::Default
;
449 return CxxModuleMapMode::Default
;