cps-org / cps

Common Package Specification — A cross-tool mechanism for locating software dependencies
https://cps-org.github.io/cps/
Other
99 stars 8 forks source link

Case Study: absl_time_zone #36

Open bretbrownjr opened 7 months ago

bretbrownjr commented 7 months ago

Completion Criteria

Documentation about how to practically convert existing CMake and/or pkg-config projects to CPS should be started. It can be iterated on as we gain experience, but sharing lessons learned in discoverable ways is important to new adopters and contributors.

Background

Structure

It seems like absl_time_zone is an implementation library that installs headers but expects only other absl libraries to make use of them, absl_time especially. It needs to provide build systems information for discovering headers and libraries to link, of course, but end-users shouldn't be directly referencing this library, so it's an example of a "private" library.

CMake Metadata

On my Linux machine, find_package(absl) in CMake imports dozens and dozens of IMPORTED library targets with absl:: namespace scopes.

For instance, absl::time_zone has a RelWithDebInfo IMPORTED SHARED library located at ${_IMPORT_PREFIX}/lib/libabsl_time_zone.so.2308.0.0.

pkg-config metadata

The relevant pkg-config metadata in /usr/lib/pkgconfig/absl_time_zone.pc is as follows:

prefix=/usr
exec_prefix=${prefix}
libdir=/usr/lib
includedir=/usr/include

Name: absl_time_zone
Description: Abseil time_zone library
URL: https://abseil.io/
Version: 20230802
Requires:
Libs: -L${libdir}  -labsl_time_zone 
Cflags: -I${includedir} -DNOMINMAX

Questions

Do we recommend absl_time_zone.cps or do we recommend an omnibus absl.cps that includes models for each library shipped by the absl project?

Should CPS files adopt RelWithDebInfo as an explicit default or should it be unqualified like the pkg-config metadata?

Is there currently support for "private" libraries like this? If not, should we add a tracking issue?

dcbaker commented 7 months ago

Here's my thoughts (because LLVM is such a pain, and always on my mind): I think it makes sense to encourage one CPS file per conceptual project (it's possible a single repo contains multiple conceptual projects, like libLLVM and clang, which live in the same repo but are conceptually different projects, even if clang requires LLVM), and then use components for representing, well, components.

So absl would have a single CPS file, which might (with redactions look like):

{
  "components": {
    "time": {"requires": [":time_zone"]},
    "time_zone": {}
  }
}

This makes sense in that it prevents odd situations where components from one version of a library accidentally get mixed with another one; it should also speed up load time since an implementation does less I/O. It also would allow exploiting private components (which I like more and more) to hide implementation details which having separate CPS files would not

mwoehlke commented 7 months ago

Do we recommend absl_time_zone.cps or do we recommend an omnibus absl.cps that includes models for each library shipped by the absl project?

I don't know that it's our place to make such a blanket recommendation. Rather, what is the project trying to accomplish?

Qt, for example, enforces that you can't mix and match components (e.g. core, gui, widgets, xmlpatterns, webengine). A project like that should ship as a single CPS "project". I suspect llvm/clang should, or at least could, ship as multiple projects.

More generally, the "rule" is that the project is a tool for ensuring that a set of components all comes from the same build. It's even possible for multiple repos to ship as a single CPS project (one has to be "primary").

Another rule is that if users expect to consume A without also consuming B (again, e.g. llvm without clang), then you should ship as separate projects.

it prevents odd situations where components from one version of a library accidentally get mixed with another one

Exactly; that is a major purpose, if not the purpose, of projects.

bretbrownjr commented 7 months ago

Let's keep in mind the implications for build systems going forward. If we expect projectname::libraryname [1] will be an ecosystem-wide name for a given library, we should note that as a primary design decision and explain our reasoning. It should be a fairly prominent part of the documentation, for instance in conceptual guides and maybe even quick start guides.

This fits naturally for some things like CMake, Meson, and bazel (maybe substituting :: for a / to make the name path-like). I'm not sure it's as natural a fit for pkg-config unless we set some standard mapping rules. Though likely we'll also have to design some recommended or standard coping mechanisms when projectname_libraryname.pc is inappropriate or unavailable for whatever reason.

[1] Maybe the delimiter for :: can be played with depending on the syntax of the build system.

mwoehlke commented 7 months ago

Note: the name according to CPS is project:library with one :. (I know I often will write two out of habit, but the official spec is one.) It's assumed/intended that build systems will parse this as a 2-tuple and map that to their own internal convention as needed. It's also assumed that users will write these using their build tool's convention, at least when they appear in e.g. a CMakeLists.txt. (For example, in Bazel you'd likely write @project//:library.)

In other words, the canonical name isn't a string that contains one or more :s, it is a 2-tuple. How that 2-tuple is written as a string is expected to vary depending on the tool.

dcbaker commented 7 months ago

This fits naturally for some things like CMake, Meson, and bazel (maybe substituting :: for a / to make the name path-like). I'm not sure it's as natural a fit for pkg-config unless we set some standard mapping rules.

Meson has as syntax for handling these kind of dependencies, qt for example:

dependency('qt', version : ['>=6', '<7'], modules : ['core', 'widgets'])

Which the meson implementation would transform into ['qt:core', 'qt:widgets'], and would then call cps-config qt --component core --component widgets

As such I frankly don't care what the divider is :)

Though likely we'll also have to design some recommended or standard coping mechanisms when projectname_libraryname.pc is inappropriate or unavailable for whatever reason.

pkg-config naming is such a mess because there are no recommendations and various projects have different solutions:

dcbaker commented 7 months ago

Should CPS files adopt RelWithDebInfo as an explicit default or should it be unqualified like the pkg-config metadata?

This probably deserves a separate discussion. On Linux at least it is common for the debug info to be split out of the package provided by the distro.

mwoehlke commented 7 months ago

Should CPS files adopt RelWithDebInfo as an explicit default or should it be unqualified like the pkg-config metadata?

Configurations are... complicated, and right now CPS isn't trying to say what they look like, just that they're a thing that can exist. In practice, you're more likely to encounter static/shared from anything a distro ships, as distros don't usually ship separate release and debug libraries. (Also, to a consumer, there is little to no practical difference between release and relwithdebinfo.)

Is there currently support for "private" libraries like this? If not, should we add a tracking issue?

There is not currently support. I'd suggested that #29 should metamorphose into said issue.