Skip to content

Instantly share code, notes, and snippets.

@shundhammer
Last active February 25, 2021 21:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shundhammer/b775dd4f8acd126be2166373e208e9bb to your computer and use it in GitHub Desktop.
Save shundhammer/b775dd4f8acd126be2166373e208e9bb to your computer and use it in GitHub Desktop.
Libyui Packaging

Libyui Packaging with the New CMake Build Environment

Current Situation

  • Lots of different libyui*.rpm packages from lots of individual, independent GitHub repos

  • Most of those packages have a -devel subpackage for the header files and a -doc subpackage for auto-generated doxygen documentation

  • The main package of each has a .spec file and a .changes file in the repo's package/ subdirectory

  • The -doc subpackage has its own -doc.spec file and its own .changes file

  • The -devel subpackage is defined in the main package's .spec file; there is no separate .changes file

  • Both the -devel and the -doc subpackage have the same version number as the main package

  • The version numbering of all the individual libyui*.rpm packages is wildly different between them.

Requirements in SUSE Distros

  • Each distro or product may or may not contain some of the libyui*.rpm packages

  • Each software pattern of a distro or product may or may not contain some of the libyui*.rpm packages

  • Upon package upgrade (zypper dup), the resulting system needs to be consistent so YaST can be started and used; i.e. dependencies need to be set up to ensure that only compatible libyui*.rpm packages are on the target system on any given time.

Requirements by OBS

  • No build dependency cycles; there needs to be a clear starting and end point for building a set of packages

Requirements by the YaST CI (Jenkins, Travis)

Some of those requirements are not fulfilled right now; that's why we are working on this topic.

  • When there are changes in libyui, the CI needs to be able to bootstrap itself.

    Currently, SO number changes introduce a vicious cycle: The docker image used for building libyui and dependent packages still contains an older version, so all packages other than libyui.rpm itself inevitably cause a ton of build problems, so both Travis and Jenkins keep failing until all libyui*.rpm packages make it to that docker image, but that doesn't happen until all PRs are test-built, reviewed and merged; but a successful Travis build is a prerequisite to review a PR. This cycle can only be broken by brute force and praying that everything will be alright.

  • Jenkins should be able to detect a change in the libyui source repo, check out the updated sources, test-build (at least) the changed packages and submit (at least) the changed packages to OBS if the test-build was successful.

    In an ideal world, Jenkins would only submit any libyui*.rpm packages to OBS when they ALL build without problems.

The Simplistic Approach: Keep Packaging as it Is

We could just merge all our libyui* GitHub repos to a single big one containing a subdirectory for each and iterate manually or with simple scripts over them:

libyui
├── libyui
│   ├── doc
│   ├── examples
│   ├── legacy-buildtools
│   ├── package
│   └── src
├── libyui-ncurses
│   ├── doc
│   ├── package
│   └── src
├── libyui-ncurses-pkg
│   ├── package
│   └── src
├── libyui-ncurses-rest-api
│   ├── package
│   └── src
├── libyui-qt
│   ├── doc
│   ├── package
│   └── src
├── libyui-qt-graph
│   ├── examples
│   ├── package
│   └── src
├── libyui-qt-pkg
│   ├── examples
│   ├── package
│   └── src
├── libyui-qt-rest-api
│   ├── package
│   └── src
└── libyui-rest-api
    ├── package
    └── src

Each libyui/libyui* directory would be a self-sufficient package in its own right with a .spec file and a .changes file in its package/ subdirectory (and possibly one more for its -doc subpackage if it has one).

We'd have to update the version number in each one separately (rake version:bump), write a separate change log entry in its packages/*.changes file, build each one separately (mkdir build; cd build; cmake ..; make), create a tarball in each one separately (rake tarball). After a PR is reviewed and merged, Jenkins would do a test build and, if successful, submit a new package to OBS.

A number of simple scripts at the toplevel directory (libyui/) could support that manual work for bulk operations: for dir in libyui*; rake version:bump; done.

Pros

  • Simple

  • Not very disruptive for our CI, OBS and the distros

  • Keep using existing tools and workflows

  • Atomic source code changes: One PR for all libyui* packages (because it's now one single GitHub repo)

Cons

  • Responsibility rests heavily on the shoulders of the developer: There is no way to make sure that everything builds without problem, so partial submissions would remain a problem: Some libyui* packages build fine and are submitted, some may fail to build and cannot be submitted, again causing a lot of disruption for the release manager, OBS and QA.

  • The vicious circle in Travis and Jenkins remains: The libyui docker image cannot be updated until all packages are submitted, and that's not possible because Travis keeps failing for each libyui* package except libyui.rpm.

The Complex Approach: One Huge .spec File Containing all libyui* Packages

Instead of having a separate package/ subdirectory for each libyui* package, we would have one huge unified libyui.spec file in a toplevel package/ subdirectory.

That libyui.spec file would contain all the libyui*rpm packages; all except libyui.rpm would be subpackages.

There would be one single build command that builds everything, and the created files would be packaged by separate file lists in the huge libyui.spec file into all the subpackages. There would be one single libyui.changes file and one common version number for each of the subpackages.

We would have to add all the Requires and BuildRequires to the huge libyui.spec file, separated by subpackages.

Pros

  • Atomic source code changes: One PR for all libyui* packages (because it's now one single GitHub repo)

  • Atomic package submission because now it's all or nothing.

Cons

  • The one unified libyui.spec file would be incredibly huge and incredibly complex, thus very hard to maintain.

  • There are zero chances to submit any libyui* package if any subpackage fails to build, no matter if that subpackage is relevant to the target distro. If it fails because of changes that are outside our responsibility, we'd have to come up with workarounds.

  • All target distros would need to provide all Requires and BuildRequires, even if they are intended to be minimalistic: If it doesn't contain, say, graphviz (for libyui-qt-graph), it couldn't build, so we'd have to come up with workarounds. Similar with libyui-*rest-api: Some target distros may not even want to provide them for security considerations.

  • We'd probably have to adapt our auto-submission logic in our Jenkins CI jobs.

One Single Big Fat Tarball, Individual .spec Files

This was first inspired by Qt5 packages; initially I had thought they all share the same large tarball. On closer inspection, that is not the case at all. Still, the idea has merit.

Most libqt5*.rpm packages in Factory and/or SLE use the single large tarball from the qt-everywhere repo. While that tarball contains all the sources, only a part of them is actually used for building the resulting libqt5*rpm binary packages; yet they all build with a consistent set of header files that ensure that everything is binary compatible.

All the libqt5* packages still have their own .spec file, and they are submitted individually.

We could do the same thing with the libyui* packages.

Pros

  • Atomic source code changes: One PR for all libyui* packages (because it's now one single GitHub repo)

  • We can simply add one layer of CMake CMakeLists.txt in the toplevel directory to enable or disable individual parts to be built (with or without Qt, with or without libzypp (libyui*-pkg)

  • Consistency / binary compatibility between libyui* packages: We can easily ensure that dependent packages use the headers from ../libyui/src in the same tarball (the proposed new CMake configuration in the huha-cmake branch already does this).

Cons

  • The source tarball becomes considerably bigger; it would be the same large one for each of the libyui* packages.

To Clarify

  • Enforce the same package version between our libyui* packages? (Not the community-maintained libyui-gtk*, libyui-mga*, of course!)

  • Enforce the exact same package number as Requires for all our libyui* packages, very much like between kernel and kernel module packages?

  • Automate common tasks like adding a new change log entry for each libyui* package when the version number changes? Provide a script that accepts a bsc# to be added along with the automated change log entry? E.g.

    ---------------------------------------------------------------
    Wed Nov 11 17:17:45 UTC 2020 - MyRealName <myusername@suse.com>
    
    - Related to bsc#4711
    - 4.42.1
    ---------------------------------------------------------------
    

    (The user name and mail address can be obtained with git config --show --no-show-origin user.name and git config --show --no-show-origin user.email)

@lslezak
Copy link

lslezak commented Dec 14, 2020

One Big Fat Tarball is fine for me. I was also thinking about One Big Spec File - with many *.spec files we will have some duplicated entries there.

RPM supports --with build option and %{with} macro so we could possibly limit which plugins should be built from the sources (to skip e.g. REST API) and limit the BuildRequires according to the requested output. But I do not know how to handle these options in OBS, if that's supported at all...

On the other hand multiple spec files are supported and we already use them in YaST or libyui (e.g. for the -doc subpackages).

@shundhammer
Copy link
Author

shundhammer commented Dec 14, 2020

IMHO it's much easier to do that with CMake:

https://github.com/libyui/libyui/blob/huha-cmake/CMakeLists.txt#L26 from https://github.com/libyui/libyui/pull/178

option( BUILD_DOC "Build class documentation" off )
...
...
if ( BUILD_DOC )
  add_subdirectory( doc )
endif()

And then in the .spec file

https://github.com/libyui/libyui/blob/huha-cmake/package/libyui-doc.spec#L62

cmake .. \
  -DBUILD_DOC=on \
  -DDOC_DESTDIR=$RPM_BUILD_ROOT

This has the added benefit that you can use it in the exact same way in the development environment:

mkdir build
cd build
cmake -DBUILD_DOC=on
make

We would simply do that one level higher, including build options such as

  • WITH_QT (libyui-qt*)
  • WITH_PKG (libyui*-pkg*)
  • WITH_GRAPH (libyui-qt-graph)
  • WITH_REST_API (libyui*-rest-api)

which makes this approach much more attractive for our libyui community since they can easily omit anything specific to our SUSE distros.

@joseivanlopez
Copy link

My opinion regarding the three different options proposed here (sorry if I missunderstood something):

  • The Simplistic Approach: Keep Packaging as it Is

    • Each package has its own .spec and .changes files
    • Caveats
      • Requires one SR to OBS per package
      • Temporary build failures because the package requires a docker image with the last libyui
      • Dependencies between libyui packages are manually maintained
      • So version is manually synchronized between all packages
      • Surplus SR only because bumping SO versions
    • Challenges
      • After merging, Jenkins should do a SR for every package
      • Ideally, Jenkins should only submit a package when it is modified
  • One Single Big Fat Tarball, Individual .spec Files

    • Each package has its own .spec and .changes files
    • Caveats
      • Requires one SR to OBS per package
      • Every package contains a fat tar
      • Dependencies between libyui packages are manually maintained
      • So version is manually synchronized between all packages (although it can be solved with script)
      • Surplus SR only because bumping SO versions
    • Challenges
      • After merging, Jenkins should do a SR for every package
      • Ideally, Jenkins should only submit a package when it is modified
  • The Complex Approach: One Huge .spec File Containing all libyui* Packages

    • Only one .spec and .changes file for all the packages/subpackages
    • Only one SR to OBS
    • Caveats
      • It generates all the subpackages, even if they are not needed for a specific product
      • It could force unnecessary requires and buildrequires

IMHO, the ideal packaging should have this features:

  • Only one SR to OBS
  • Only one .spec and .changes file for all the packages/subpackages
  • OBS project decides what subpackages have to be generated
  • SO version is automatically synchronized between packages
  • All packages have the very same version (in fact, there would be only one version, the libyui one)
  • A subpackage always requires libyui* packages with its same version (e.g., libyui-qt-4.1.2 requires
    libyui-4.1.2).
  • Challenges

I would try to see the subpackages only as a way to split libyui, allowing to generate only the needed
parts. But I don't see the real need of having different versions, changes files, etc for each subpackage.

@lslezak
Copy link

lslezak commented Feb 23, 2021

A subpackage always requires libyui* packages with its same version (e.g., libyui-qt-4.1.2 requires libyui-4.1.2).

That would make troubles for maintenance fixes. If we fix a bug in libyui-qt we would need to release an update for all libyui packages. The dependency cannot be that strict. 😟

@lslezak
Copy link

lslezak commented Feb 23, 2021

  • The Complex Approach: One Huge .spec File Containing all libyui* Packages
    • It generates all the subpackages, even if they are not needed for a specific product

We could use the %{with foo} RPM macros and make the subpackages optional. The project could enable/disable the required parts.

Also do not forget that the libyui library is also used outside the (open)SUSE world, we should not complicate packaging in other distributions.

@joseivanlopez
Copy link

joseivanlopez commented Feb 23, 2021

A subpackage always requires libyui* packages with its same version (e.g., libyui-qt-4.1.2 requires libyui-4.1.2).

That would make troubles for maintenance fixes. If we fix a bug in libyui-qt we would need to release an update for all libyui packages. The dependency cannot be that strict. worried

Ok. Could we generate a SR that only bumps the version of some subpackages?

@mvidner
Copy link

mvidner commented Feb 23, 2021

For merging the *.changes files I made this tool way back for the SUSE Cloud changelogs, it might be useful: https://github.com/mvidner/changelog-chew/blob/master/suse-changelog-merge

@joseivanlopez
Copy link

@shundhammer
Copy link
Author

We will probably need some changes to the CMake environment (as it is right now) as well:

  • One single VERSION.cmake in the toplevel directory
  • change the includes for it from ../VERSION.cmake to ../../VERSION.cmake in all the subdirectories
  • Add a new toplevel CMakeLists.txt with the config options mentioned above:
    • WITH_QT (default: "on")
    • WITH_PKG (default: "on")
    • WITH_GRAPH (default: "on")
    • WITH_REST_API (default: "off")
      Additionally, some existing ones from the subprojects:
    • BUILD_EXAMPLES (default: "on")
    • BUILD_DOC (default: "off")
    • WERROR (default: "on")
  • The toplevel CMakeLists.txt will then recurse into subdirectories in the correct sequence, depending on those options.
  • Notice that .spec files might override some of them as needed.

Nice to have: The subproject CMakeLists.txt files (libyui-qt/CMakeLists.txt) should remain usable stand-alone so a developer could choose to only work on any invididual part without having to rebuild everything every time.

@joseivanlopez
Copy link

joseivanlopez commented Feb 24, 2021

We have decided to go with the following packaging options:

  • Multiple .spec files (one per subpackage)
  • Only one .changes file
  • Synchronizing so versions via script ?
  • All subpackages have the same version (e.g., libyui-4.1.2, libyui-qt-4.1.2)
libyui
libyui-qt
libyui-ncurses
package
  libyui.changes
  libyui.spec
  libyui-qt.spec
  libyui-ncurses.spec

The idea is to have only one OBS package which generates several rpms from its set of spec files:

devel:libraries:libyui
    libyui.spec
    libyui-qt.spec
    libyui-ncurses.spec
    ...

But an OBS project should still be able to include only some subpackages. For example, let's say there is a openSUSE::Leap-Min project which does no provide a graphical UI. In that project, the subpackage libyui-qt should not be included.

This was easily solved with our current approach, where we have a separate OBS package for every libyui spec:

devel:libraries:libyui
    libyui.spec

devel:libraries:libyui-qt
    libyui-qt.spec

devel:libraries:libyui-ncurses
    libyui-ncurses.spec

So we can decide to not copy the package libyui-qt into openSUSE::Leap-Min and that's all. With the new approach, we only have an OBS package containing all the libyui spec files. But we still want to make possible to enable/disable subpackages in a OBS project. There are two possible options: with OBS linked packages and with the OBS multibuild option.

Linking packages is basically what we already do to generate the documentation packages. For example, in the libyui project we have two spec files, one for libyui itself and another one for its documentation:

devel:libraries:libyui
    libyui.spec
    libyui-doc.spec

OBS builds the spec file that matches with the name of the package. So, the package devel:libraries:libyui builds libyui.spec. In order to build the documentation, a second OBS package is created as a link to devel:libraries:libyui:

devel:libraries:libyui-doc (links to devel:libraries:libyui)
    libyui.spec
    libyui-doc.spec

Such devel:libraries:libyui-doc is a link to devel:libraries:libyui, so it has exactly the same content as the linked package and it also builds every time that devel:libraries:libyui is built. But, in this case, devel:libraries:libyui-doc will use the libyui-doc.spec file instead.

We can follow this same approach in order to enable a subpackage in certain OBS project. For example, we can create a openSUSE:Factory/libyui-qt package as a link to openSUSE:Factory/libyui, which would generate the libyui-qt subpackage in the openSUSE::Factory project.

And the second option for enabling/disabling subpackages would be to use the multibuild feature provided by OBS. In this case, we can include a _multibuild file into the OBS package to indicate the different flavors it should generate.

devel:libraries:libyui
    _multibuild
    libyui.spec
    libyui-qt.spec
    libyui-ncurses.spec
    ...

Such _multibuild file would contain something like:

<multibuild>
  <flavor>libyui-qt</flavor>
  <flavor>libyui-ncurses</flavor>
</multibuild>

This indicates to OBS that libyui-qt.spec and libyui-ncurses.spec should be built too. Following this approach, an OBS project only has to provide its own _multibuild file in order to indicate which subpackages to generate.

@joseivanlopez
Copy link

Having different versions for each subpackage (e.g., libyui-4.1.2, libyui-qt-4.1.5) could be problematic. Note that we will have only one big tar (e.g., libyui-4.1.2.tar.bz2). That tar file would have different content when the version is only bumped for a subpackage. For example, bumping libyui-qt to libyui-qt-4.1.6 would produce the same tar libyui-4.1.2.tar.bz2 but with different content.

@anaselli
Copy link

anaselli commented Feb 24, 2021

Having different versions for each subpackage (e.g., libyui-4.1.2, libyui-qt-4.1.5) could be problematic. Note that we will have only one big tar (e.g., libyui-4.1.2.tar.bz2). That tar file would have different content when the version is only bumped for a subpackage. For example, bumping libyui-qt to libyui-qt-4.1.6 would produce the same tar libyui-4.1.2.tar.bz2 but with different content.

Agreed, if you move for a single tarball you have to go for a single version, from a packager point of view means maintaining one package only, even if bigger. Moreover libyui version is simplified with community ones exceptions that cannot go same way of course.
Agreed also with WITH_FEATURE or BUILD_FEATURE approach to avoid building unwanted features such as pkg or graph for instance...
I also prefer the one spec only, but that could be managed by packagers for other distros...

Just one thing i'd like, to ask for a tag containing last old style stable that i probably need before start working on community packages adaptation... and maybe to consider adding things from libyui-mga when possible...
Thanks for your work

@joseivanlopez
Copy link

Agreed, if you move for a single tarball you have to go for a single version, from a packager point of view means maintaining one package only, even if bigger. Moreover libyui version is simplified with community ones exceptions that cannot go same way of course.
Agreed also with WITH_FEATURE or BUILD_FEATURE approach to avoid building unwanted features such as pkg or graph for instance...
I also prefer the one spec only, but that could be managed by packagers for other distros...

Thanks for your feedback!

Just one thing i'd like, to ask for a tag containing last old style stable that i probably need before start working on community packages adaptation... and maybe to consider adding things from libyui-mga when possible...
Thanks for your work

Sure, we can tag the last version of the current libyui* repos. Only take into account that after merging all libyui-* repos into the libyui one, we will emtpy the master branch in all those libyui-* repos.

@anaselli
Copy link

Right, that means i will move forward as fast as possible too...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment