mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
Merge topic 'per-importer-bmi'
5c00749c5dcmCxxModuleUsageEffects: Collect and apply BMI compatibility requirements5898c8d2e0cxxmodules: Generate per-importer BMIs for native targets Acked-by: Kitware Robot <kwrobot@kitware.com> Reviewed-by: Vito Gamberini <vito.gamberini@kitware.com> Reviewed-by: Ben Boeckel <ben.boeckel@kitware.com> Merge-request: !11561
This commit is contained in:
@@ -180,35 +180,90 @@ cmCommonTargetGenerator::GetLinkedTargetDirectories(
|
||||
|
||||
if (cmComputeLinkInformation* cli =
|
||||
this->GeneratorTarget->GetLinkInformation(config)) {
|
||||
auto addLinkedTarget =
|
||||
[this, &lang, &config, &dirs, &direct_emitted, &forward_emitted,
|
||||
gg](cmGeneratorTarget const* linkee, Forwarding forward) {
|
||||
if (linkee &&
|
||||
!linkee->IsImported()
|
||||
// Skip targets that build after this one in a static lib cycle.
|
||||
&& gg->TargetOrderIndexLess(linkee, this->GeneratorTarget)
|
||||
// We can ignore the INTERFACE_LIBRARY items because
|
||||
// Target->GetLinkInformation already processed their
|
||||
// link interface and they don't have any output themselves.
|
||||
&& (linkee->GetType() != cmStateEnums::INTERFACE_LIBRARY
|
||||
// Synthesized targets may have relevant rules.
|
||||
|| linkee->IsSynthetic()) &&
|
||||
((lang == "CXX"_s && linkee->HaveCxx20ModuleSources()) ||
|
||||
(lang == "Fortran"_s && linkee->HaveFortranSources(config)))) {
|
||||
cmLocalGenerator* lg = linkee->GetLocalGenerator();
|
||||
std::string di = linkee->GetSupportDirectory();
|
||||
if (lg->GetGlobalGenerator()->IsMultiConfig()) {
|
||||
di = cmStrCat(di, '/', config);
|
||||
}
|
||||
if (forward == Forwarding::Yes &&
|
||||
forward_emitted.insert(linkee).second) {
|
||||
dirs.Forward.push_back(di);
|
||||
}
|
||||
if (direct_emitted.insert(linkee).second) {
|
||||
dirs.Direct.emplace_back(di);
|
||||
|
||||
auto findSyntheticTarget =
|
||||
[this,
|
||||
&config](cmGeneratorTarget const* linkee) -> cmGeneratorTarget const* {
|
||||
if (!linkee) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check the map of direct synthetic dependencies for a substitute
|
||||
auto const& synthDeps = this->GeneratorTarget->GetSyntheticDeps(config);
|
||||
auto it = synthDeps.find(linkee);
|
||||
if (it != synthDeps.end() && !it->second.empty()) {
|
||||
return it->second.front();
|
||||
}
|
||||
|
||||
// Check linked targets to finding synthetic targets for transitive deps
|
||||
std::vector<cmGeneratorTarget const*> pending;
|
||||
std::set<cmGeneratorTarget const*> visited;
|
||||
for (auto const& dep : synthDeps) {
|
||||
for (auto const* synth : dep.second) {
|
||||
if (synth && visited.insert(synth).second) {
|
||||
pending.push_back(synth);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
while (!pending.empty()) {
|
||||
auto const* current = pending.back();
|
||||
pending.pop_back();
|
||||
auto const& transitiveSynthDeps = current->GetSyntheticDeps(config);
|
||||
auto itLinkeeSynth = transitiveSynthDeps.find(linkee);
|
||||
if (itLinkeeSynth != transitiveSynthDeps.end() &&
|
||||
!itLinkeeSynth->second.empty()) {
|
||||
return itLinkeeSynth->second.front();
|
||||
}
|
||||
for (auto const& entry : transitiveSynthDeps) {
|
||||
for (auto const* synth : entry.second) {
|
||||
if (synth && visited.insert(synth).second) {
|
||||
pending.push_back(synth);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
auto addLinkedTarget = [this, &lang, &config, &dirs, &direct_emitted,
|
||||
&forward_emitted, &findSyntheticTarget,
|
||||
gg](cmGeneratorTarget const* linkee,
|
||||
Forwarding forward) {
|
||||
// Check if the linkee has a synthetic target to use for importing
|
||||
cmGeneratorTarget const* mappedLinkee = linkee;
|
||||
if (auto const* synth = findSyntheticTarget(linkee)) {
|
||||
mappedLinkee = synth;
|
||||
}
|
||||
|
||||
if (mappedLinkee &&
|
||||
!mappedLinkee->IsImported()
|
||||
// Skip targets that build after this one in a static lib cycle.
|
||||
&& gg->TargetOrderIndexLess(mappedLinkee, this->GeneratorTarget)
|
||||
// We can ignore the INTERFACE_LIBRARY items because
|
||||
// Target->GetLinkInformation already processed their
|
||||
// link interface and they don't have any output themselves.
|
||||
&& (mappedLinkee->GetType() != cmStateEnums::INTERFACE_LIBRARY
|
||||
// Synthesized targets may have relevant rules.
|
||||
|| mappedLinkee->IsSynthetic()) &&
|
||||
((lang == "CXX"_s && mappedLinkee->HaveCxx20ModuleSources()) ||
|
||||
(lang == "Fortran"_s &&
|
||||
mappedLinkee->HaveFortranSources(config)))) {
|
||||
cmLocalGenerator* lg = mappedLinkee->GetLocalGenerator();
|
||||
std::string di = mappedLinkee->GetSupportDirectory();
|
||||
if (lg->GetGlobalGenerator()->IsMultiConfig()) {
|
||||
di = cmStrCat(di, '/', config);
|
||||
}
|
||||
if (forward == Forwarding::Yes &&
|
||||
forward_emitted.insert(mappedLinkee).second) {
|
||||
dirs.Forward.push_back(di);
|
||||
}
|
||||
if (direct_emitted.insert(mappedLinkee).second) {
|
||||
dirs.Direct.emplace_back(di);
|
||||
}
|
||||
}
|
||||
};
|
||||
for (auto const& item : cli->GetItems()) {
|
||||
if (item.Target) {
|
||||
addLinkedTarget(item.Target, Forwarding::No);
|
||||
|
||||
@@ -2,17 +2,28 @@
|
||||
file LICENSE.rst or https://cmake.org/licensing for details. */
|
||||
#include "cmCxxModuleUsageEffects.h"
|
||||
|
||||
cmCxxModuleUsageEffects::cmCxxModuleUsageEffects(
|
||||
cmGeneratorTarget const* /*gt*/)
|
||||
: Hash("0000000000000000000000000000000000000000")
|
||||
#include <cm/optional>
|
||||
|
||||
#include "cmCryptoHash.h"
|
||||
#include "cmGeneratorTarget.h"
|
||||
#include "cmTarget.h"
|
||||
|
||||
cmCxxModuleUsageEffects::cmCxxModuleUsageEffects(cmGeneratorTarget const* gt)
|
||||
{
|
||||
// TODO: collect information from the generator target as to what might
|
||||
// affect module consumption.
|
||||
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
|
||||
this->Hash = hasher.HashString(gt->GetName());
|
||||
|
||||
// Collect compile features from the consuming target.
|
||||
for (auto const& feature : gt->Target->GetCompileFeaturesEntries()) {
|
||||
this->CompileFeatures.emplace_back(feature);
|
||||
}
|
||||
}
|
||||
|
||||
void cmCxxModuleUsageEffects::ApplyToTarget(cmTarget* /*tgt*/)
|
||||
void cmCxxModuleUsageEffects::ApplyToTarget(cmTarget* tgt)
|
||||
{
|
||||
// TODO: apply the information collected in the constructor
|
||||
for (auto const& feature : this->CompileFeatures) {
|
||||
tgt->AppendProperty("COMPILE_FEATURES", feature.Value, feature.Backtrace);
|
||||
}
|
||||
}
|
||||
|
||||
std::string const& cmCxxModuleUsageEffects::GetHash() const
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "cmListFileCache.h"
|
||||
|
||||
class cmGeneratorTarget;
|
||||
class cmTarget;
|
||||
@@ -19,4 +22,5 @@ public:
|
||||
|
||||
private:
|
||||
std::string Hash;
|
||||
std::vector<BT<std::string>> CompileFeatures;
|
||||
};
|
||||
|
||||
@@ -108,6 +108,11 @@ TdiSourceInfo CollationInformationSources(cmGeneratorTarget const* gt,
|
||||
if (fs_type != "CXX_MODULES"_s) {
|
||||
continue;
|
||||
}
|
||||
// Synthetic (BMI-only) targets do not build private C++ modules.
|
||||
if (tgt->IsSynthetic() &&
|
||||
file_set->GetVisibility() == cmFileSetVisibility::Private) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto fileEntries = file_set->CompileFileEntries();
|
||||
auto directoryEntries = file_set->CompileDirectoryEntries();
|
||||
|
||||
+101
-84
@@ -5309,118 +5309,135 @@ bool cmGeneratorTarget::ApplyCXXStdTargets()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool cmGeneratorTarget::DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
|
||||
std::string const& config)
|
||||
bool cmGeneratorTarget::DiscoverSyntheticTargets(
|
||||
cmSyntheticTargetCache& cache, std::string const& config,
|
||||
cmGeneratorTarget const* bmiConsumer)
|
||||
{
|
||||
std::vector<std::string> allConfigs =
|
||||
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
|
||||
cmOptionalLinkImplementation impl;
|
||||
this->ComputeLinkImplementationLibraries(config, impl, UseTo::Link);
|
||||
|
||||
cmCxxModuleUsageEffects usage(this);
|
||||
if (!bmiConsumer) {
|
||||
bmiConsumer = this;
|
||||
}
|
||||
|
||||
cmCxxModuleUsageEffects usage(bmiConsumer);
|
||||
|
||||
auto& SyntheticDeps = this->Configs[config].SyntheticDeps;
|
||||
|
||||
for (auto const& entry : impl.Libraries) {
|
||||
auto const* gt = entry.Target;
|
||||
if (!gt || !gt->IsImported()) {
|
||||
if (!gt || !gt->HaveCxx20ModuleSources()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (gt->HaveCxx20ModuleSources()) {
|
||||
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
|
||||
constexpr size_t HASH_TRUNCATION = 12;
|
||||
auto dirhash = hasher.HashString(
|
||||
gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
|
||||
std::string safeName = gt->GetName();
|
||||
cmSystemTools::ReplaceString(safeName, ":", "_");
|
||||
auto targetIdent =
|
||||
hasher.HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash()));
|
||||
std::string targetName =
|
||||
cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION));
|
||||
// Visual Studio generators do not currently support BMI-only compilation,
|
||||
// so they can't generate synthetic targets. For consuming native modules,
|
||||
// skip so that the native target is used directly. For imported modules,
|
||||
// create the synth target anyway and diagnose later, in the VS generator.
|
||||
if (!gt->IsImported() && this->GlobalGenerator->IsVisualStudio()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check the cache to see if this instance of the imported target has
|
||||
// already been created.
|
||||
auto cached = cache.CxxModuleTargets.find(targetName);
|
||||
cmGeneratorTarget const* synthDep = nullptr;
|
||||
if (cached == cache.CxxModuleTargets.end()) {
|
||||
auto const* model = gt->Target;
|
||||
auto* mf = gt->Makefile;
|
||||
auto* lg = gt->GetLocalGenerator();
|
||||
auto* tgt = mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY,
|
||||
targetName);
|
||||
cmCryptoHash hasher(cmCryptoHash::AlgoSHA3_512);
|
||||
constexpr size_t HASH_TRUNCATION = 12;
|
||||
auto dirhash =
|
||||
hasher.HashString(gt->GetLocalGenerator()->GetCurrentBinaryDirectory());
|
||||
std::string safeName = gt->GetName();
|
||||
cmSystemTools::ReplaceString(safeName, ":", "_");
|
||||
auto targetIdent =
|
||||
hasher.HashString(cmStrCat("@d_", dirhash, "@u_", usage.GetHash()));
|
||||
std::string targetName =
|
||||
cmStrCat(safeName, "@synth_", targetIdent.substr(0, HASH_TRUNCATION));
|
||||
|
||||
// Copy relevant information from the existing IMPORTED target.
|
||||
// Check the cache to see if this instance of the target has
|
||||
// already been created.
|
||||
auto cached = cache.CxxModuleTargets.find(targetName);
|
||||
cmGeneratorTarget const* synthDep = nullptr;
|
||||
if (cached == cache.CxxModuleTargets.end()) {
|
||||
auto const* model = gt->Target;
|
||||
auto* mf = gt->Makefile;
|
||||
auto* lg = gt->GetLocalGenerator();
|
||||
auto* tgt =
|
||||
mf->AddSynthesizedTarget(cmStateEnums::INTERFACE_LIBRARY, targetName);
|
||||
|
||||
// Copy policies to the target.
|
||||
tgt->CopyPolicyStatuses(model);
|
||||
// Copy relevant information from the existing target.
|
||||
|
||||
// Copy file sets.
|
||||
{
|
||||
auto fsNames = model->GetAllFileSetNames();
|
||||
for (auto const& fsName : fsNames) {
|
||||
auto const* fs = model->GetFileSet(fsName);
|
||||
if (!fs) {
|
||||
mf->IssueMessage(MessageType::INTERNAL_ERROR,
|
||||
cmStrCat("Failed to find file set named '",
|
||||
fsName, "' on target '",
|
||||
tgt->GetName(), '\''));
|
||||
continue;
|
||||
}
|
||||
auto* newFs = tgt
|
||||
->GetOrCreateFileSet(fs->GetName(), fs->GetType(),
|
||||
fs->GetVisibility())
|
||||
.first;
|
||||
newFs->CopyEntries(fs);
|
||||
// Copy policies to the target.
|
||||
tgt->CopyPolicyStatuses(model);
|
||||
|
||||
// Copy file sets.
|
||||
{
|
||||
auto fsNames = model->GetAllFileSetNames();
|
||||
for (auto const& fsName : fsNames) {
|
||||
auto const* fs = model->GetFileSet(fsName);
|
||||
if (!fs) {
|
||||
mf->IssueMessage(MessageType::INTERNAL_ERROR,
|
||||
cmStrCat("Failed to find file set named '",
|
||||
fsName, "' on target '", tgt->GetName(),
|
||||
'\''));
|
||||
continue;
|
||||
}
|
||||
auto* newFs = tgt
|
||||
->GetOrCreateFileSet(fs->GetName(), fs->GetType(),
|
||||
fs->GetVisibility())
|
||||
.first;
|
||||
newFs->CopyEntries(fs);
|
||||
}
|
||||
|
||||
// Copy imported C++ module properties.
|
||||
tgt->CopyImportedCxxModulesEntries(model);
|
||||
|
||||
// Copy other properties which may affect the C++ module BMI
|
||||
// generation.
|
||||
tgt->CopyImportedCxxModulesProperties(model);
|
||||
|
||||
tgt->AddLinkLibrary(*mf,
|
||||
cmStrCat("$<COMPILE_ONLY:", model->GetName(), '>'),
|
||||
GENERAL_LibraryType);
|
||||
|
||||
// Apply usage requirements to the target.
|
||||
usage.ApplyToTarget(tgt);
|
||||
|
||||
// Create the generator target and attach it to the local generator.
|
||||
auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg);
|
||||
|
||||
synthDep = gtp.get();
|
||||
cache.CxxModuleTargets[targetName] = synthDep;
|
||||
|
||||
// See `localGen->ComputeTargetCompileFeatures()` call in
|
||||
// `cmGlobalGenerator::Compute` for where non-synthetic targets resolve
|
||||
// this.
|
||||
for (auto const& innerConfig : allConfigs) {
|
||||
gtp->ComputeCompileFeatures(innerConfig);
|
||||
}
|
||||
// See `cmGlobalGenerator::ApplyCXXStdTargets` in
|
||||
// `cmGlobalGenerator::Compute` for non-synthetic target resolutions.
|
||||
if (!gtp->ApplyCXXStdTargets()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gtp->DiscoverSyntheticTargets(cache, config);
|
||||
|
||||
lg->AddGeneratorTarget(std::move(gtp));
|
||||
} else {
|
||||
synthDep = cached->second;
|
||||
}
|
||||
|
||||
SyntheticDeps[gt].push_back(synthDep);
|
||||
// Copy C++ module properties.
|
||||
tgt->CopyCxxModulesEntries(model);
|
||||
|
||||
// Copy other properties which may affect the C++ module BMI
|
||||
// generation.
|
||||
tgt->CopyCxxModulesProperties(model);
|
||||
|
||||
tgt->AddLinkLibrary(*mf,
|
||||
cmStrCat("$<COMPILE_ONLY:", model->GetName(), '>'),
|
||||
GENERAL_LibraryType);
|
||||
|
||||
// Apply usage requirements to the target.
|
||||
usage.ApplyToTarget(tgt);
|
||||
|
||||
// Create the generator target and attach it to the local generator.
|
||||
auto gtp = cm::make_unique<cmGeneratorTarget>(tgt, lg);
|
||||
|
||||
synthDep = gtp.get();
|
||||
cache.CxxModuleTargets[targetName] = synthDep;
|
||||
|
||||
// See `localGen->ComputeTargetCompileFeatures()` call in
|
||||
// `cmGlobalGenerator::Compute` for where non-synthetic targets resolve
|
||||
// this.
|
||||
for (auto const& innerConfig : allConfigs) {
|
||||
gtp->ComputeCompileFeatures(innerConfig);
|
||||
}
|
||||
// See `cmGlobalGenerator::ApplyCXXStdTargets` in
|
||||
// `cmGlobalGenerator::Compute` for non-synthetic target resolutions.
|
||||
if (!gtp->ApplyCXXStdTargets()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gtp->DiscoverSyntheticTargets(cache, config, bmiConsumer);
|
||||
|
||||
lg->AddGeneratorTarget(std::move(gtp));
|
||||
} else {
|
||||
synthDep = cached->second;
|
||||
}
|
||||
|
||||
SyntheticDeps[gt].push_back(synthDep);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
cmGeneratorTarget::SyntheticDepsMap const& cmGeneratorTarget::GetSyntheticDeps(
|
||||
std::string const& config) const
|
||||
{
|
||||
return this->Configs[config].SyntheticDeps;
|
||||
}
|
||||
|
||||
bool cmGeneratorTarget::HasPackageReferences() const
|
||||
{
|
||||
return this->IsInBuildSystem() &&
|
||||
|
||||
@@ -1118,8 +1118,13 @@ public:
|
||||
std::string GetImportedXcFrameworkPath(std::string const& config) const;
|
||||
|
||||
bool ApplyCXXStdTargets();
|
||||
bool DiscoverSyntheticTargets(cmSyntheticTargetCache& cache,
|
||||
std::string const& config);
|
||||
bool DiscoverSyntheticTargets(
|
||||
cmSyntheticTargetCache& cache, std::string const& config,
|
||||
cmGeneratorTarget const* bmiConsumer = nullptr);
|
||||
|
||||
using SyntheticDepsMap =
|
||||
std::map<cmGeneratorTarget const*, std::vector<cmGeneratorTarget const*>>;
|
||||
SyntheticDepsMap const& GetSyntheticDeps(std::string const& config) const;
|
||||
|
||||
class CustomTransitiveProperty : public TransitiveProperty
|
||||
{
|
||||
|
||||
@@ -2587,8 +2587,8 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
|
||||
std::string const& module_dir,
|
||||
std::vector<std::string> const& linked_target_dirs,
|
||||
std::vector<std::string> const& forward_modules_from_target_dirs,
|
||||
std::string const& arg_lang, std::string const& arg_modmapfmt,
|
||||
cmCxxModuleExportInfo const& export_info)
|
||||
std::string const& native_target_dir, std::string const& arg_lang,
|
||||
std::string const& arg_modmapfmt, cmCxxModuleExportInfo const& export_info)
|
||||
{
|
||||
// Setup path conversions.
|
||||
{
|
||||
@@ -2748,6 +2748,47 @@ bool cmGlobalNinjaGenerator::WriteDyndepFile(
|
||||
}
|
||||
}
|
||||
|
||||
// If this is a synthetic target for a non-imported target, read PRIVATE
|
||||
// module info from the native target
|
||||
if (!native_target_dir.empty()) {
|
||||
std::string const modules_info_path =
|
||||
cmStrCat(native_target_dir, '/', arg_lang, "Modules.json");
|
||||
Json::Value native_modules_info;
|
||||
cmsys::ifstream modules_file(modules_info_path.c_str(),
|
||||
std::ios::in | std::ios::binary);
|
||||
if (!modules_file) {
|
||||
cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to open ",
|
||||
modules_info_path,
|
||||
" for module information"));
|
||||
return false;
|
||||
}
|
||||
Json::Reader reader;
|
||||
if (!reader.parse(modules_file, native_modules_info, false)) {
|
||||
cmSystemTools::Error(cmStrCat("-E cmake_ninja_dyndep failed to parse ",
|
||||
modules_info_path,
|
||||
reader.getFormattedErrorMessages()));
|
||||
return false;
|
||||
}
|
||||
if (native_modules_info.isObject()) {
|
||||
Json::Value const& native_target_modules =
|
||||
native_modules_info["modules"];
|
||||
if (native_target_modules.isObject()) {
|
||||
for (auto i = native_target_modules.begin();
|
||||
i != native_target_modules.end(); ++i) {
|
||||
Json::Value const& visible_module = *i;
|
||||
if (visible_module.isObject()) {
|
||||
auto is_private = visible_module["is-private"].asBool();
|
||||
// Only add private modules since others are discovered by the
|
||||
// synthetic target's own scan rules
|
||||
if (is_private) {
|
||||
target_modules[i.key().asString()] = visible_module;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmGeneratedFileStream ddf(arg_dd);
|
||||
ddf << "ninja_dyndep_version = 1.0\n";
|
||||
|
||||
@@ -3041,6 +3082,7 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
|
||||
tdi_forward_modules_from_target_dir.asString());
|
||||
}
|
||||
}
|
||||
std::string const native_target_dir = tdi["native-target-dir"].asString();
|
||||
std::string const compilerId = tdi["compiler-id"].asString();
|
||||
std::string const simulateId = tdi["compiler-simulate-id"].asString();
|
||||
std::string const compilerFrontendVariant =
|
||||
@@ -3064,8 +3106,9 @@ int cmcmd_cmake_ninja_dyndep(std::vector<std::string>::const_iterator argBeg,
|
||||
# endif
|
||||
return gg.WriteDyndepFile(dir_top_src, dir_top_bld, dir_cur_src, dir_cur_bld,
|
||||
arg_dd, arg_ddis, module_dir, linked_target_dirs,
|
||||
forward_modules_from_target_dirs, arg_lang,
|
||||
arg_modmapfmt, *export_info)
|
||||
forward_modules_from_target_dirs,
|
||||
native_target_dir, arg_lang, arg_modmapfmt,
|
||||
*export_info)
|
||||
? 0
|
||||
: 1;
|
||||
}
|
||||
|
||||
@@ -433,7 +433,8 @@ public:
|
||||
std::string const& module_dir,
|
||||
std::vector<std::string> const& linked_target_dirs,
|
||||
std::vector<std::string> const& forward_modules_from_target_dirs,
|
||||
std::string const& arg_lang, std::string const& arg_modmapfmt,
|
||||
std::string const& native_target_dir, std::string const& arg_lang,
|
||||
std::string const& arg_modmapfmt,
|
||||
cmCxxModuleExportInfo const& export_info);
|
||||
|
||||
virtual std::string BuildAlias(std::string const& alias,
|
||||
|
||||
@@ -1228,6 +1228,23 @@ void cmNinjaTargetGenerator::WriteObjectBuildStatements(
|
||||
|
||||
this->WriteTargetDependInfo(language, config);
|
||||
|
||||
// Non-imported synthetic targets read module info from their native target
|
||||
// Add as implicit dependency.
|
||||
if (this->GeneratorTarget->IsSynthetic()) {
|
||||
if (cmGeneratorTarget const* native_gt =
|
||||
this->LocalGenerator->FindGeneratorTargetToUse(
|
||||
this->GeneratorTarget->Target->GetTemplateName())) {
|
||||
if (!native_gt->IsImported()) {
|
||||
std::string native_dir = native_gt->GetSupportDirectory();
|
||||
if (this->GetGlobalGenerator()->IsMultiConfig()) {
|
||||
native_dir = cmStrCat(native_dir, '/', config);
|
||||
}
|
||||
build.ImplicitDeps.emplace_back(this->ConvertToNinjaPath(
|
||||
cmStrCat(native_dir, '/', language, "Modules.json")));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto const linked_directories =
|
||||
this->GetLinkedTargetDirectories(language, config);
|
||||
for (std::string const& l : linked_directories.Direct) {
|
||||
@@ -2155,6 +2172,22 @@ void cmNinjaTargetGenerator::WriteTargetDependInfo(std::string const& lang,
|
||||
tdi_forward_modules_from_target_dirs.append(l);
|
||||
}
|
||||
|
||||
// Record the native target support directory for non-imported synthetic
|
||||
// targets
|
||||
if (this->GeneratorTarget->IsSynthetic()) {
|
||||
if (cmGeneratorTarget* nativeGT =
|
||||
this->LocalGenerator->FindGeneratorTargetToUse(
|
||||
this->GeneratorTarget->Target->GetTemplateName())) {
|
||||
if (!nativeGT->IsImported()) {
|
||||
std::string nativeDir = nativeGT->GetSupportDirectory();
|
||||
if (this->GetGlobalGenerator()->IsMultiConfig()) {
|
||||
nativeDir = cmStrCat(nativeDir, '/', config);
|
||||
}
|
||||
tdi["native-target-dir"] = nativeDir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmDyndepGeneratorCallbacks cb;
|
||||
cb.ObjectFilePath = [this](cmSourceFile const* sf, std::string const& cnf) {
|
||||
return this->GetObjectFilePath(sf, cnf);
|
||||
|
||||
+33
-18
@@ -1721,37 +1721,52 @@ void cmTarget::CopyPolicyStatuses(cmTarget const* tgt)
|
||||
assert(!this->IsNormal());
|
||||
// Imported targets cannot be the target of a copy.
|
||||
assert(!this->IsImported());
|
||||
// Only imported targets can be the source of a copy.
|
||||
assert(tgt->IsImported());
|
||||
|
||||
// Only imported or normal targets can be the source of a copy.
|
||||
assert(tgt->IsImported() || tgt->IsNormal());
|
||||
|
||||
this->impl->PolicyMap = tgt->impl->PolicyMap;
|
||||
this->impl->TemplateTarget = tgt;
|
||||
}
|
||||
|
||||
void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt)
|
||||
void cmTarget::CopyCxxModulesEntries(cmTarget const* tgt)
|
||||
{
|
||||
// Normal targets cannot be the target of a copy.
|
||||
assert(!this->IsNormal());
|
||||
// Imported targets cannot be the target of a copy.
|
||||
assert(!this->IsImported());
|
||||
// Only imported targets can be the source of a copy.
|
||||
assert(tgt->IsImported());
|
||||
// Only imported or normal targets can be the source of a copy.
|
||||
assert(tgt->IsImported() || tgt->IsNormal());
|
||||
|
||||
this->impl->IncludeDirectories.Entries.clear();
|
||||
this->impl->IncludeDirectories.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries));
|
||||
this->impl->CompileDefinitions.Entries.clear();
|
||||
this->impl->CompileDefinitions.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries));
|
||||
this->impl->CompileFeatures.Entries.clear();
|
||||
this->impl->CompileFeatures.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries));
|
||||
this->impl->CompileOptions.Entries.clear();
|
||||
this->impl->CompileOptions.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries));
|
||||
this->impl->LinkLibraries.Entries.clear();
|
||||
this->impl->LinkLibraries.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesLinkLibraries.Entries));
|
||||
|
||||
if (tgt->IsImported()) {
|
||||
this->impl->IncludeDirectories.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesIncludeDirectories.Entries));
|
||||
this->impl->CompileDefinitions.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesCompileDefinitions.Entries));
|
||||
this->impl->CompileFeatures.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesCompileFeatures.Entries));
|
||||
this->impl->CompileOptions.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesCompileOptions.Entries));
|
||||
this->impl->LinkLibraries.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->ImportedCxxModulesLinkLibraries.Entries));
|
||||
} else {
|
||||
this->impl->IncludeDirectories.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->IncludeDirectories.Entries));
|
||||
this->impl->CompileDefinitions.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->CompileDefinitions.Entries));
|
||||
this->impl->CompileFeatures.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->CompileFeatures.Entries));
|
||||
this->impl->CompileOptions.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->CompileOptions.Entries));
|
||||
this->impl->LinkLibraries.CopyFromEntries(
|
||||
cmMakeRange(tgt->impl->LinkLibraries.Entries));
|
||||
}
|
||||
|
||||
// Copy the C++ module fileset entries from `tgt`'s `INTERFACE` to this
|
||||
// target's `PRIVATE`.
|
||||
@@ -1760,14 +1775,14 @@ void cmTarget::CopyImportedCxxModulesEntries(cmTarget const* tgt)
|
||||
tgt->impl->CxxModulesFileSets.InterfaceEntries.Entries;
|
||||
}
|
||||
|
||||
void cmTarget::CopyImportedCxxModulesProperties(cmTarget const* tgt)
|
||||
void cmTarget::CopyCxxModulesProperties(cmTarget const* tgt)
|
||||
{
|
||||
// Normal targets cannot be the target of a copy.
|
||||
assert(!this->IsNormal());
|
||||
// Imported targets cannot be the target of a copy.
|
||||
assert(!this->IsImported());
|
||||
// Only imported targets can be the source of a copy.
|
||||
assert(tgt->IsImported());
|
||||
// Only imported or normal targets can be the source of a copy.
|
||||
assert(tgt->IsImported() || tgt->IsNormal());
|
||||
|
||||
// The list of properties that are relevant here include:
|
||||
// - compilation-specific properties for any language or platform
|
||||
|
||||
+2
-2
@@ -321,8 +321,8 @@ public:
|
||||
cmBTStringRange GetLinkInterfaceDirectExcludeEntries() const;
|
||||
|
||||
void CopyPolicyStatuses(cmTarget const* tgt);
|
||||
void CopyImportedCxxModulesEntries(cmTarget const* tgt);
|
||||
void CopyImportedCxxModulesProperties(cmTarget const* tgt);
|
||||
void CopyCxxModulesEntries(cmTarget const* tgt);
|
||||
void CopyCxxModulesProperties(cmTarget const* tgt);
|
||||
|
||||
cmBTStringRange GetHeaderSetsEntries() const;
|
||||
cmBTStringRange GetCxxModuleSetsEntries() const;
|
||||
|
||||
@@ -177,6 +177,13 @@ if ("named" IN_LIST CMake_TEST_MODULE_COMPILATION)
|
||||
run_cxx_module_test(scan_props)
|
||||
run_cxx_module_test(target-objects)
|
||||
|
||||
# mixed-bmi-compatibility requires a generator that implements per-importer
|
||||
# BMI generation
|
||||
if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND
|
||||
RunCMake_GENERATOR MATCHES "Ninja")
|
||||
run_cxx_module_test(mixed-bmi-compatibility)
|
||||
endif()
|
||||
|
||||
if ("cxx_std_23" IN_LIST CMAKE_CXX_COMPILE_FEATURES AND
|
||||
"import_std23" IN_LIST CMake_TEST_MODULE_COMPILATION)
|
||||
run_cxx_module_test(imp-std)
|
||||
@@ -251,6 +258,7 @@ endif ()
|
||||
# Tests which use named modules in shared libraries.
|
||||
if ("shared" IN_LIST CMake_TEST_MODULE_COMPILATION)
|
||||
run_cxx_module_test(library library-shared -DBUILD_SHARED_LIBS=ON)
|
||||
run_cxx_module_test(shared-library-symbol-visibility)
|
||||
endif ()
|
||||
|
||||
# Tests which use partitions.
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(dep_modules_json_path "CMakeFiles/depchain_modules_json_file.dir/Debug/CXX.dd")
|
||||
set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/Debug/CXXModules.json")
|
||||
else ()
|
||||
set(dep_modules_json_path "CMakeFiles/depchain_modules_json_file.dir/CXX.dd")
|
||||
set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/CXXModules.json")
|
||||
file(GLOB synth_dirs
|
||||
"${RunCMake_TEST_BINARY_DIR}/CMakeFiles/depchain_with_modules_json_file@synth_*.dir")
|
||||
|
||||
list(LENGTH synth_dirs synth_dirs_len)
|
||||
if (NOT synth_dirs_len EQUAL 1)
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Expected exactly one synthetic target for consuming 'depchain_with_modules_json_file' but found ${synth_dirs_len}: ${synth_dirs}")
|
||||
endif ()
|
||||
|
||||
if ("${RunCMake_TEST_BINARY_DIR}/${modules_json_path}" IS_NEWER_THAN "${RunCMake_TEST_BINARY_DIR}/${dep_modules_json_path}")
|
||||
list(GET synth_dirs 0 synth_dir)
|
||||
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(dep_modules_json_path "CMakeFiles/depchain_modules_json_file.dir/Debug/CXX.dd")
|
||||
set(modules_json_path "${synth_dir}/Debug/CXXModules.json")
|
||||
else ()
|
||||
set(dep_modules_json_path "CMakeFiles/depchain_modules_json_file.dir/CXX.dd")
|
||||
set(modules_json_path "${synth_dir}/CXXModules.json")
|
||||
endif ()
|
||||
|
||||
|
||||
if ("${modules_json_path}" IS_NEWER_THAN "${RunCMake_TEST_BINARY_DIR}/${dep_modules_json_path}")
|
||||
cmake_path(RELATIVE_PATH modules_json_path
|
||||
BASE_DIRECTORY "${RunCMake_TEST_BINARY_DIR}")
|
||||
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Object '${dep_modules_json_path}' should have recompiled if '${modules_json_path}' changed.")
|
||||
endif ()
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
file(GLOB synth_dirs
|
||||
"${RunCMake_TEST_BINARY_DIR}/CMakeFiles/depchain_with_modules_json_file@synth_*.dir")
|
||||
|
||||
list(LENGTH synth_dirs synth_dirs_len)
|
||||
if (NOT synth_dirs_len EQUAL 1)
|
||||
return()
|
||||
endif()
|
||||
|
||||
list(GET synth_dirs 0 synth_dir)
|
||||
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/Debug/CXXModules.json")
|
||||
set(modules_json_path "${synth_dir}/Debug/CXXModules.json")
|
||||
else ()
|
||||
set(modules_json_path "CMakeFiles/depchain_with_modules_json_file.dir/CXXModules.json")
|
||||
set(modules_json_path "${synth_dir}/CXXModules.json")
|
||||
endif ()
|
||||
|
||||
file(TOUCH_NOCREATE "${RunCMake_TEST_BINARY_DIR}/${modules_json_path}")
|
||||
file(TOUCH_NOCREATE "${modules_json_path}")
|
||||
|
||||
@@ -18,7 +18,9 @@ target_sources(library
|
||||
BASE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
FILES
|
||||
importable.cxx)
|
||||
importable.ixx
|
||||
PRIVATE
|
||||
importable.cxx)
|
||||
target_compile_features(library PUBLIC cxx_std_20)
|
||||
|
||||
add_executable(exe)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
export module importable;
|
||||
module importable;
|
||||
|
||||
#include "library_export.h"
|
||||
|
||||
export LIBRARY_EXPORT int from_import()
|
||||
int from_import()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
export module importable;
|
||||
|
||||
#include "library_export.h"
|
||||
|
||||
export LIBRARY_EXPORT int from_import();
|
||||
@@ -0,0 +1,111 @@
|
||||
# Verify the build system generated a synthetic target/BMI for each unique importer
|
||||
set(expected_consumers consumer20 consumer23)
|
||||
set(linked_dir_keys "")
|
||||
set(linked_dir_names "")
|
||||
|
||||
if (DEFINED RunCMake_TEST_CONFIG)
|
||||
set(config_dir "${RunCMake_TEST_CONFIG}")
|
||||
else ()
|
||||
set(config_dir "Debug")
|
||||
endif ()
|
||||
|
||||
# Get and check linked-target-dirs for each consumer
|
||||
foreach (consumer IN LISTS expected_consumers)
|
||||
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(output_dir "${consumer}.dir/${config_dir}")
|
||||
else ()
|
||||
set(output_dir "${consumer}.dir")
|
||||
endif ()
|
||||
|
||||
set(depend_info_file "${RunCMake_TEST_BINARY_DIR}/CMakeFiles/${output_dir}/CXXDependInfo.json")
|
||||
if (NOT EXISTS "${depend_info_file}")
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Could not find CXXDependInfo.json for consumer ${consumer}: checked ${depend_info_file}")
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
file(READ "${depend_info_file}" depend_info_json)
|
||||
|
||||
# Extract linked-target-dirs array length and first element
|
||||
string(JSON linked_dirs_len LENGTH "${depend_info_json}" "linked-target-dirs")
|
||||
string(JSON linked_dirs GET "${depend_info_json}" "linked-target-dirs")
|
||||
|
||||
if (NOT linked_dirs_len GREATER 0)
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Consumer '${consumer}' has no linked-target-dirs but expected synthetic target for 'importable'")
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
# For this test, expect exactly one linked target dir per consumer
|
||||
if (NOT linked_dirs_len EQUAL 1)
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Expected 1 linked-target-dir for '${consumer}' but found ${linked_dirs_len}: ${linked_dirs}")
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
string(JSON linked_dir GET "${depend_info_json}" "linked-target-dirs" 0)
|
||||
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
cmake_path(GET linked_dir PARENT_PATH linked_tgt_root)
|
||||
else ()
|
||||
set(linked_tgt_root "${linked_dir}")
|
||||
endif ()
|
||||
|
||||
cmake_path(GET linked_tgt_root FILENAME linked_tgt_name)
|
||||
|
||||
# Verify it is a synthetic target for the 'importable' library
|
||||
if (NOT linked_tgt_name MATCHES "^importable@synth_[A-Za-z0-9_]+\.dir$")
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Consumer '${consumer}' should link to synthetic target dir for 'importable' but found ${linked_dir}")
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
if (RunCMake_GENERATOR_IS_MULTI_CONFIG)
|
||||
set(linked_output_dir "${linked_dir}/${config_dir}")
|
||||
else ()
|
||||
set(linked_output_dir "${linked_dir}")
|
||||
endif ()
|
||||
|
||||
# Verify the synthetic target dir exists and contains a BMI
|
||||
if (NOT EXISTS "${linked_dir}")
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Consumer '${consumer}' links to synthetic target directory that does not exist: ${linked_dir}")
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
file(GLOB_RECURSE bmi_files "${linked_dir}/*.bmi")
|
||||
if (NOT bmi_files)
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"No BMI files found in synthetic target directory: ${linked_dir}")
|
||||
continue()
|
||||
endif ()
|
||||
|
||||
# Record the consumers of each linked dir to verify uniqueness
|
||||
string(REGEX REPLACE "[^A-Za-z0-9_]" "_" linked_dir_key "${linked_tgt_name}")
|
||||
set(linked_dir_consumers_var "linked_dir_consumers_${linked_dir_key}")
|
||||
if (NOT DEFINED ${linked_dir_consumers_var})
|
||||
list(APPEND linked_dir_keys "${linked_dir_key}")
|
||||
list(APPEND linked_dir_names "${linked_tgt_name}")
|
||||
endif ()
|
||||
|
||||
list(APPEND ${linked_dir_consumers_var} ${consumer})
|
||||
|
||||
endforeach ()
|
||||
|
||||
# Verify each synthetic target is consumed exactly once
|
||||
foreach (linked_dir_key IN LISTS linked_dir_keys)
|
||||
set(consumers "${linked_dir_consumers_${linked_dir_key}}")
|
||||
list(LENGTH consumers linked_dir_consumer_count)
|
||||
|
||||
if (linked_dir_consumer_count GREATER 1)
|
||||
list(FIND linked_dir_keys ${linked_dir_key} linked_dir_index)
|
||||
list(GET linked_dir_names ${linked_dir_index} linked_tgt_name)
|
||||
|
||||
string(JOIN ", " linked_dir_consumers_joined ${consumers})
|
||||
list(APPEND RunCMake_TEST_FAILED
|
||||
"Expected per-importer BMI generation, but '${linked_tgt_name}' is linked to by multiple targets: ${linked_dir_consumers_joined}")
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
string(REPLACE ";" "\n " RunCMake_TEST_FAILED "${RunCMake_TEST_FAILED}")
|
||||
@@ -0,0 +1,21 @@
|
||||
cmake_minimum_required(VERSION 3.24...3.28)
|
||||
project(cxx_modules_mixed_bmi_compatibility CXX)
|
||||
|
||||
include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
|
||||
|
||||
add_library(importable)
|
||||
target_sources(importable
|
||||
PUBLIC FILE_SET CXX_MODULES FILES importable.cxx)
|
||||
|
||||
target_compile_features(importable PUBLIC cxx_std_20)
|
||||
|
||||
add_executable(consumer20 consumer20.cxx)
|
||||
target_compile_features(consumer20 PRIVATE cxx_std_20)
|
||||
target_link_libraries(consumer20 PRIVATE importable)
|
||||
|
||||
add_executable(consumer23 consumer23.cxx)
|
||||
target_compile_features(consumer23 PRIVATE cxx_std_23)
|
||||
target_link_libraries(consumer23 PRIVATE importable)
|
||||
|
||||
add_test(NAME consumer20 COMMAND consumer20)
|
||||
add_test(NAME consumer23 COMMAND consumer23)
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <iostream>
|
||||
|
||||
import importable;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << "from_import() = " << from_import() << '\n';
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
#include <print>
|
||||
|
||||
import importable;
|
||||
|
||||
int main()
|
||||
{
|
||||
std::println("from_import() = {}", from_import());
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export module importable;
|
||||
|
||||
export int from_import()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
cmake_minimum_required(VERSION 3.24...3.28)
|
||||
project(cxx_modules_library CXX)
|
||||
|
||||
include("${CMAKE_SOURCE_DIR}/../cxx-modules-rules.cmake")
|
||||
|
||||
include(GenerateExportHeader)
|
||||
|
||||
add_library(library SHARED)
|
||||
generate_export_header(library)
|
||||
target_sources(library
|
||||
PUBLIC
|
||||
FILE_SET HEADERS
|
||||
BASE_DIRS
|
||||
"${CMAKE_CURRENT_BINARY_DIR}"
|
||||
FILES
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/library_export.h"
|
||||
FILE_SET CXX_MODULES
|
||||
BASE_DIRS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}"
|
||||
FILES
|
||||
importable.ixx
|
||||
PRIVATE
|
||||
importable.cxx)
|
||||
target_compile_features(library PUBLIC cxx_std_20)
|
||||
target_compile_definitions(library INTERFACE CHECK_IMPORT_INTERFACE)
|
||||
|
||||
add_executable(exe)
|
||||
target_link_libraries(exe PRIVATE library)
|
||||
target_sources(exe
|
||||
PRIVATE
|
||||
main.cxx)
|
||||
|
||||
add_test(NAME exe COMMAND exe)
|
||||
@@ -0,0 +1,6 @@
|
||||
module importable;
|
||||
|
||||
int from_import()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export module importable;
|
||||
|
||||
#include "library_export.h"
|
||||
|
||||
#ifdef CHECK_IMPORT_INTERFACE
|
||||
# ifdef library_EXPORTS
|
||||
# error "library_EXPORTS defined but should NOT be defined when importing module"
|
||||
# endif
|
||||
#else
|
||||
# ifndef library_EXPORTS
|
||||
# error "library_EXPORTS NOT defined but should be defined when building module"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
export LIBRARY_EXPORT int from_import();
|
||||
@@ -0,0 +1,6 @@
|
||||
import importable;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
return from_import();
|
||||
}
|
||||
Reference in New Issue
Block a user