No more PYTHON_TARGETS in single-r1

Since its inception in 2012, python-single-r1 has been haunting users with two sets of USE flags: PYTHON_TARGETS and PYTHON_SINGLE_TARGET. While this initially seemed a necessary part of the grand design, today I know we could have done better. Today this chymera is disappearing for real, and python-single-r1 are going to use PYTHON_SINGLE_TARGET flags only.

I would like to take this opportunity to explain why the eclass has been designed this way in the first place, and what has been done to change that.

Why PYTHON_SINGLE_TARGET?

Why did we need a second variable in the first place? After all, we could probably get away with using PYTHON_TARGETS everywhere, and adding an appropriate REQUIRED_USE constraint.

Back in the day we have established that for users’ convenience we need to default to enabling one version of Python 2 and one version of Python 3. If we enabled only one of them, the users would end up having to enable the other for a lot of packages. On the other had, if we combined both with using PT for single-r1 packages, the users would have to disable the extra implementation for a lot of them. Neither option was good.

The primary purpose of PYTHON_SINGLE_TARGET was to provide a parallel sensible setting for those packages. It was not only to make the default work out of the box but also to let users change it in one step.

Today, with the demise of Python 2 and the effort to remove Python 2 from default PT, it may seem less important to keep the distinction. Nevertheless, a number of developers and at least some users keep multiple versions of Python in PT to test their packages. Having PST is still helpful to them.

Why additional PYTHON_TARGETS then?

PST is only half of the story. What I explained above does not justify having PYTHON_TARGETS on those packages as well, and a REQUIRED_USE constraint to make them superset of enabled PST. Why did we need to have two flag sets then?

The answer is: PYTHON_USEDEP. The initial design goal was that both python-r1 eclasses would use the same approach to declaring USE dependencies between packages. This also meant that this variable must work alike on dependencies that are multi-impl and single-r1 packages. In the end, this meant a gross hack.

Without getting into details, the currently available USE dependency syntax does not permit directly depending on PT flags based on PST-based conditions. This needs to be done using the more verbose expanded syntax:

pst2_7? ( foo[pt2_7] )
pst3_7? ( foo[pt3_7] )

While this was doable back in the day, it was not possible with PYTHON_USEDEP-based approach. Hence, all single-r1 packages gained additional set of flags merely to construct dependencies conveniently.

What is the problem with that?

I suppose some of you see the problem already. Nevertheless, let’s list them explicitly.

Firstly, enabling additional implementations is inconvenient. Whenever you need to do that, you need to add both PST and PT flags.

Secondly, the PT flags are entirely redundant and meaningless for the package in question. Whenever your value of PT changes, all single-r1 packages trigger rebuilds even if their PST value stays the same.

Thirdly, the PT flags overspecify dependencies. If your PT flags specify multiple implementations (which is normally the case), all dependencies will also have to be built for those interpreters even though PST requires only one of them.

The solution

The user-visible part of the solution is that PYTHON_TARGETS are disappearing from single-r1 packages. From now on, only PYTHON_SINGLE_TARGET will be necessary. Furthermore, PT enforcement on dependencies (if necessary) will be limited to the single implementation selected by PST rather than all of PT.

The developer-oriented part is that PYTHON_USEDEP is no longer valid in single-r1 packages. Instead, PYTHON_SINGLE_USEDEP is provided for dependencies on other single-r1 packages, and PYTHON_MULTI_USEDEP placeholder is used for multi-impl packages. The former is available as a global variable, the latter only as a placeholder in python_gen_cond_dep (the name is a bit of misnomer now but I’ve decided not to introduce additional function).

All existing uses have been converted, and the eclasses will now fail if someone tries to use the old logic. The conversion of existing ebuilds is rather simple:

  1. Replace all ${PYTHON_USEDEP}s with ${PYTHON_SINGLE_USEDEP} when the dep is single-r1, or with ${PYTHON_MULTI_USEDEP} otherwise.
  2. Wrap all dependencies containing ${PYTHON_MULTI_USEDEP} in a python_gen_cond_dep. Remember that the variable must be a literal placeholder, i.e. use single quotes.

An example of the new logic follows:

RDEPEND="
  dev-libs/libfoo[python,${PYTHON_SINGLE_USEDEP}]
  $(python_gen_cond_dep '
    dev-python/foo[${PYTHON_MULTI_USEDEP}]
    dev-python/bar[${PYTHON_MULTI_USEDEP}]
  ')
"

If you get the dependency type wrong, repoman/pkgcheck will complain about bad dependency.