Skip to content

[bug] CPS reader ignores a component's inline configurations map, so multi-config descriptors from export(PACKAGE_INFO) yield empty libraries #20284

Description

@mikirothman

Describe the bug

Environment

  • Conan version: 2.29.1 (mechanism also present on develop2 as of 2026-08)
  • OS: Windows (generator-agnostic; the CPS parsing is platform-independent)
  • Generator: CMakeConfigDeps (experimental CPS consumption)
  • Producer: CMake 4.4 export(PACKAGE_INFO) / install(PACKAGE_INFO), multi-config
    generator (Ninja Multi-Config)

Summary

When a dependency's CPS descriptor stores per-configuration artifact locations in the
inline per-component configurations map (a valid CPS representation, and what CMake's
export(PACKAGE_INFO) emits for a multi-config build), Conan's CPS reader does not read it.
CPS.to_conan() therefore produces components with no libraries (libs/libdirs
empty), so a consumer fails to link (unresolved external / Cannot obtain 'location').

How to reproduce it

Reproduction

  1. A multi-component package built with a multi-config generator that emits a CPS via
    export(PACKAGE_INFO) (e.g. consuming it as a Conan editable).
  2. Consume with CMakeConfigDeps and self.cpp_info = CPS.load(cps_path).to_conan().
  3. Inspect the generated dependency data: every component's *_LIBS_<CONFIG> and
    *_LIB_DIRS_<CONFIG> are empty; linking the consumer fails with unresolved externals.

Inspecting the descriptor shows the data is present, just inline:

{
  "components": {
    "MyLib": {
      "type": "dylib",
      "configurations": {
        "Debug":   { "link_location": ".../lib/Debug/MyLib.lib",   "location": ".../bin/Debug/MyLib.dll" },
        "Release": { "link_location": ".../lib/Release/MyLib.lib", "location": ".../bin/Release/MyLib.dll" }
      }
    }
  }
}

Consumer-side workaround

Hoist the active build type's inline config data to the component top level before
deserializing:

import json
from conan.cps.cps import CPS

with open(cps_path, encoding="utf-8") as fh:
    document = json.load(fh)
components = document.get("components", {})
if any("configurations" in c for c in components.values()):
    build_type = str(self.settings.build_type)
    for comp in components.values():
        cfg = (comp.get("configurations") or {}).get(build_type) or {}
        for key in ("location", "link_location", "link_libraries", "link_languages"):
            if not comp.get(key) and cfg.get(key):
                comp[key] = cfg[key]
    cpp_info = CPS.deserialize(document).to_conan()
else:
    cpp_info = CPS.load(cps_path).to_conan()

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions