From c6a940761c664a2e776b3d6105b71afc10e218ce Mon Sep 17 00:00:00 2001 From: Craig Scott Date: Sat, 10 Jan 2026 00:15:54 +1100 Subject: fileapi: Handle unused imported libraries with missing IMPORTED_IMPLIB CMake 4.1 and earlier did not issue an error if an imported shared library target was missing an IMPORTED_IMPLIB property and nothing used that imported library. There was no code path checking for the CMP0111 NEW behavior. Since b626843d71 (fileAPI: Output all INTERFACE and IMPORTED targets, 2025-09-13), we now include all imported targets in the file API replies, and that does trigger that check. We need to tolerate such imported targets to preserve backward compatibility, and to avoid issuing errors for problems in targets likely to be coming from outside the project and beyond the developer's control. Fixes: #27496 --- Source/cmFileAPICodemodel.cxx | 33 +++++++++++--- Source/cmTarget.cxx | 5 ++- Source/cmTarget.h | 11 ++++- Tests/RunCMake/FileAPI/codemodel-v2-check.py | 1 + .../codemodel-v2-data/directories/imported.json | 3 +- .../codemodel-v2-data/projects/imported.json | 3 +- .../targets/unused_imported_shared_lib.json | 50 ++++++++++++++++++++++ Tests/RunCMake/FileAPI/imported/CMakeLists.txt | 7 +++ 8 files changed, 101 insertions(+), 12 deletions(-) create mode 100644 Tests/RunCMake/FileAPI/codemodel-v2-data/targets/unused_imported_shared_lib.json diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index b4b60f6..35afd91 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -2049,13 +2049,34 @@ Json::Value Target::DumpArtifacts() } // Add Windows-specific artifacts produced by the linker. + // NOTE: HasImportLibrary() only checks if the target SHOULD have an import + // library, not whether it has one set. if (this->GT->HasImportLibrary(this->Config)) { - Json::Value artifact = Json::objectValue; - artifact["path"] = - RelativeIfUnder(this->TopBuild, - this->GT->GetFullPath( - this->Config, cmStateEnums::ImportLibraryArtifact)); - artifacts.append(std::move(artifact)); // NOLINT(*) + std::string fullPath; + if (this->GT->IsImported()) { + // This imported target might not be well-formed. For Windows, it should + // have its IMPORTED_IMPLIB property set, and CMP0111's NEW behavior is + // intended to catch and report that. But if nothing uses the imported + // target, there won't have been any opportunity to detect that property + // being missing before here. Therefore, we tell ImportedGetFullPath() + // not to raise that CMP0111 error if it sees the problem. We don't want + // to trigger an error for a target that nothing uses, as that would be a + // regression compared to CMake 4.1 and earlier behavior. + fullPath = this->GT->Target->ImportedGetFullPath( + this->Config, cmStateEnums::ImportLibraryArtifact, + cmTarget::ImportArtifactMissingOk::Yes); + if (cmHasLiteralSuffix(fullPath, "-NOTFOUND")) { + fullPath.clear(); + } + } else { + fullPath = this->GT->NormalGetFullPath( + this->Config, cmStateEnums::ImportLibraryArtifact, false); + } + if (!fullPath.empty()) { + Json::Value artifact = Json::objectValue; + artifact["path"] = RelativeIfUnder(this->TopBuild, fullPath); + artifacts.append(std::move(artifact)); // NOLINT(*) + } } if (this->GT->IsDLLPlatform() && this->GT->GetType() != cmStateEnums::STATIC_LIBRARY) { diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx index 6c0cd8b..ad37fe0 100644 --- a/Source/cmTarget.cxx +++ b/Source/cmTarget.cxx @@ -2993,7 +2993,8 @@ char const* cmTarget::GetPrefixVariableInternal( } std::string cmTarget::ImportedGetFullPath( - std::string const& config, cmStateEnums::ArtifactType artifact) const + std::string const& config, cmStateEnums::ArtifactType artifact, + ImportArtifactMissingOk missingOk) const { assert(this->IsImported()); @@ -3074,7 +3075,7 @@ std::string cmTarget::ImportedGetFullPath( } } - if (result.empty()) { + if (result.empty() && missingOk != ImportArtifactMissingOk::Yes) { if (this->GetType() != cmStateEnums::INTERFACE_LIBRARY) { auto message = [&]() -> std::string { std::string unset; diff --git a/Source/cmTarget.h b/Source/cmTarget.h index 1eed100..96a067b 100644 --- a/Source/cmTarget.h +++ b/Source/cmTarget.h @@ -328,8 +328,15 @@ public: cmBTStringRange GetInterfaceHeaderSetsEntries() const; cmBTStringRange GetInterfaceCxxModuleSetsEntries() const; - std::string ImportedGetFullPath(std::string const& config, - cmStateEnums::ArtifactType artifact) const; + enum class ImportArtifactMissingOk + { + No, + Yes + }; + + std::string ImportedGetFullPath( + std::string const& config, cmStateEnums::ArtifactType artifact, + ImportArtifactMissingOk missingOk = ImportArtifactMissingOk::No) const; struct StrictTargetComparison { diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 8b2e1c3..8d140ab 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -1224,6 +1224,7 @@ def gen_check_abstract_targets(c, g, inSource): read_codemodel_json_data("targets/imported_object_lib.json"), read_codemodel_json_data("targets/imported_shared_lib.json"), read_codemodel_json_data("targets/imported_static_lib.json"), + read_codemodel_json_data("targets/unused_imported_shared_lib.json"), read_codemodel_json_data("targets/iface_none.json"), read_codemodel_json_data("targets/iface_symbolic.json"), diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json index bbb77ec..6dd03a6 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/imported.json @@ -20,7 +20,8 @@ "^imported_lib::@ba7eb709d0b48779c6c8$", "^imported_object_lib::@ba7eb709d0b48779c6c8$", "^imported_shared_lib::@ba7eb709d0b48779c6c8$", - "^imported_static_lib::@ba7eb709d0b48779c6c8$" + "^imported_static_lib::@ba7eb709d0b48779c6c8$", + "^unused_imported_shared_lib::@ba7eb709d0b48779c6c8$" ], "projectName": "Imported", "minimumCMakeVersion": "3.13", diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/imported.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/imported.json index 5673418..80d2417 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/imported.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/projects/imported.json @@ -22,6 +22,7 @@ "^imported_interface_symbolic_lib::@ba7eb709d0b48779c6c8$", "^imported_object_lib::@ba7eb709d0b48779c6c8$", "^imported_shared_lib::@ba7eb709d0b48779c6c8$", - "^imported_static_lib::@ba7eb709d0b48779c6c8$" + "^imported_static_lib::@ba7eb709d0b48779c6c8$", + "^unused_imported_shared_lib::@ba7eb709d0b48779c6c8$" ] } diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/unused_imported_shared_lib.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/unused_imported_shared_lib.json new file mode 100644 index 0000000..133c78f --- /dev/null +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/unused_imported_shared_lib.json @@ -0,0 +1,50 @@ +{ + "name": "unused_imported_shared_lib", + "id": "^unused_imported_shared_lib::@ba7eb709d0b48779c6c8$", + "directorySource": "^imported$", + "projectName": "Imported", + "type": "SHARED_LIBRARY", + "imported": true, + "local": true, + "abstract": true, + "symbolic": null, + "isGeneratorProvided": null, + "fileSets": null, + "sources": [], + "sourceGroups": null, + "compileGroups": null, + "backtrace": [ + { + "file": "^imported/CMakeLists\\.txt$", + "line": 48, + "command": "add_library", + "hasParent": true + }, + { + "file": "^imported/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ], + "folder": null, + "nameOnDisk": "^(lib|cyg|msys-)?unused_imported_shared\\.(so|dylib|dll)$", + "artifacts": [ + { + "path": "^((Debug|Release|RelWithDebInfo|MinSizeRel)/)?(lib|cyg|msys-)?unused_imported_shared\\.(so|dylib|dll)$", + "_dllExtra": false + } + ], + "build": "^imported$", + "source": "^imported$", + "install": null, + "link": null, + "archive": null, + "dependencies": null, + "linkLibraries": null, + "interfaceLinkLibraries": null, + "compileDependencies": null, + "interfaceCompileDependencies": null, + "objectDependencies": null, + "orderDependencies": null +} diff --git a/Tests/RunCMake/FileAPI/imported/CMakeLists.txt b/Tests/RunCMake/FileAPI/imported/CMakeLists.txt index d9b5902..9d6ec39 100644 --- a/Tests/RunCMake/FileAPI/imported/CMakeLists.txt +++ b/Tests/RunCMake/FileAPI/imported/CMakeLists.txt @@ -42,3 +42,10 @@ install(IMPORTED_RUNTIME_ARTIFACTS imported_shared_lib install(IMPORTED_RUNTIME_ARTIFACTS imported_shared_lib DESTINATION lib2 OPTIONAL ) + +# This is deliberately missing the IMPORTED_IMPLIB property. +# The file API needs to tolerate this for unused imported targets. +add_library(unused_imported_shared_lib SHARED IMPORTED) +set_target_properties(unused_imported_shared_lib PROPERTIES + IMPORTED_LOCATION "unused_imported_shared${CMAKE_SHARED_LIBRARY_SUFFIX}" +) -- cgit v0.12