-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFE] Rewrite rpmautospec in a compiled language #217
Comments
Wouldn't a memeory-safe language be a better choice for a new project in 2025? Even if it is in Rust and uses several dependencies, those are statically compiled and the tools would still have no (or very little) runtime dependencies. There is a precedence for using Rust for such things, https://github.com/keszybz/add-determinism. There's https://src.fedoraproject.org/rpms/rust-git2 and possibly https://github.com/rpm-software-management/librpm.rs (which does not seem quite there yet) |
Alternatively, if this thing would be written as a single no-dependency Python script possibly using ctypes to interact with libgit2 and librpm, it would still solve the problem + it might be easier to hack on by casual contributors. |
Maybe easier to hack on, sure. I don't think it entirely solves the original problem, however. It still creates the potential for a bootstrapping issue with Python itself (or we disallow the python interpreter from using rpmautospec). Regarding the use of rust: while I'm not necessarily opposed to it, there's a huge learning curve there; I'm currently volunteering to do this rewrite, but I am not versed in rust. I cannot commit to having the necessary cycles to both learn a new language and completely rewrite this project in it. |
It doesn't. When we build Python 3.N+1 as python3 for the first time, we still have Python 3.N as python3.
That was not obvious to me from I propose that we make a plan for "rpmautospec 1.0" to be written in C...
I understand this argument. However, it also means you will be the only one available to maintain this1 . While if we actually choose the Python route, perhaps more folks can contribute (I know I certainly can). Footnotes
|
Either way, we should probably also figure out how to provide the importable Python module that is now available in rpmautospec-core and used by various Fedora Python projects (mock, rpkg, copr) trough the Python interface. Would it continue to exist (and not be used by the new "standalone" rpmautospec at all)? Similarly, would this "standalone" rpmautospec offer perks for packagers (such as the |
Sorry, I meant there's still a bootstrapping loop with Python 3.N+1 and python-rpmautospec. While it's not the end of the world, it would be nice if we could use
Yeah, sorry. That information was lost in rewrites as I reworked the initial description. |
I am sorry, but I still don't see it.
We could. |
rpmautospec-core is already pure-python and should have no other dependencies. All it does is, basically, run a regex across the specfile to see if
Its behavior is so trivial that I'd probably reimplement it in the compiled rpmautospec and leave the python module there for convenience. Good questions!
I'll confess that I haven't made a final determination on that yet; it may come down to the available time I have. I may just write librpmautospec in C (or, I guess Rust if I'm feeling ambitious...) and then split off the existing CLI functionality into a python-rpmautospec-cli tool to avoid rewriting it. That split was already being considered to trim dependencies, so it might be best to do that first in any case. |
python-rpmautospec has a runtime requirement on |
Yes, it currently has. If we rewrite it to standalone Python script to be installed in
Yes, this is in essence what I reported as #169 We are trying to find a solution. You said "rewrite it in C", I said "rewrite it in Rust or a standalone Python script". All of the three mentioned solutions remove the python(abi) requirement, solving #169 |
OK, I think I see the missing piece of the puzzle. I didn't understand the term "standalone Python script" to mean "it doesn't need the interpreter". I wasn't actually aware that was an option. Definitely worth considering. |
I think we still don't understand each other. A standalone Python script still needs the interpreter. It just doesn't need |
FWIW I had previously semi-seriously offered to work on a Rust port, when a similar problem was discussed regarding EPEL 10. I don't know how much time I could devote to this, but it would be an option. |
@hroncok I think I need a more concrete explanation of what you are suggesting. Do you mean that we should disable automatic I could certainly do that for We still have to figure out what to do about the dependencies:
|
No disabling of any kind of automatic
This would no longer be called (
Agreed.
Or, it could be replaced by the standard library
There is value: No dependency on |
Hmm, I think I figured out why Babel is in use here; |
That could not happen if it was just a standalone script in |
So, a lot of discussion has happened in various places in the last ten days and I want to capture as much of a summary as I can before disappearing on PTO at the end of the week (at least in part so I can refresh my own memory when I come back). Important feedback, in no particular order:
With that all in mind, I pivoted to seeing what options we have for mitigating our issues with Python rather than completely rebuilding rpmautospec in a new language. So the next things to explore are solutions to the main issues with bootstrapping rpmautospec with new Python releases: Problem 1) rpmautospec itself and several of its Python dependencies rely on a functional rpmautospec in the build environment. Problem 2) rpmautospec's dependency chain is of moderate length; it's not enormous, but neither is it tightly contained (and dependencies have a tendency to grow more dependencies over time). Each of these dependencies that also use rpmautospec adds to the complexity of bootstrapping.
Of these dependencies, we are working to drop several of them and potentially rework others. We've already dropped python3-babel upstream as it was being used for a single feature. We are also planning to drop the python3-click and python3-click-plugins dependency for the main functionality of rpmautospec. Those core pieces needed for build operations will be provided with Python standard library argument handling only. We may implement a new rpmautospec-cli package for the more fully-featured CLI tool, potentially using Click, but this won't need to be part of the bootstrap set. We are also exploring the possibility of replacing python3-pygit2 with a pure-Python implementation that we can embed in the rpmautospec script. This would allow us to extricate rpmautospec from the /usr/lib/python3.X filesystem hierarchy and remove the bootstrap-impeding dependency on a matching version of Python in the buildroot. It would be able to run against whatever /usr/bin/python3 binary calls it. |
FWIW this is how we could replace the rpm module: import ctypes
from ctypes.util import find_library
class RPMError(RuntimeError):
pass
# all the RPM state is global and not thread safe
# if need to be, we could create a class that holds rpmMacroContext
_library_name = find_library("rpm")
if _library_name:
LIBRPM = ctypes.cdll.LoadLibrary(_library_name)
else:
raise RPMError("could not locate librpm")
LIBRPM.rpmExpandMacros.restype = ctypes.c_int
LIBRPM.rpmlogSetFile.argtypes = [ctypes.c_void_p]
LIBC = ctypes.cdll.LoadLibrary(find_library("c"))
LIBC.fdopen.argtypes = [ctypes.c_int, ctypes.c_char_p]
LIBC.fdopen.restype = ctypes.c_void_p
def rpm_reload_config():
LIBRPM.rpmFreeMacros(None)
LIBRPM.rpmFreeRpmrc()
if LIBRPM.rpmReadConfigFiles(LIBRPM.rpmcliRcfile, None) != 0:
raise RPMError("could not rpmReadConfigFiles")
# we call this immediately
rpm_reload_config()
def rpm_expand(source):
source_b = source.encode("utf-8")
result_c = ctypes.c_char_p()
if (rc := LIBRPM.rpmExpandMacros(None, source_b, ctypes.byref(result_c), 0)) < 0:
raise RPMError(f"could not expand: {source!r}, got {rc}")
result = result_c.value.decode("utf-8")
LIBC.free(result_c)
return result
def rpm_define(macro, value):
LIBRPM.rpmPushMacro(None, macro.encode("utf-8"), None, value.encode("utf-8"), -1)
def rpm_set_log_file(fileobj=None):
if fileobj is None:
LIBRPM.rpmlogSetFile(None)
else:
LIBRPM.rpmlogSetFile(LIBC.fdopen(fileobj.fileno(), b"a")) Replacing |
rpmautospec was rewritten to run inside the
mock
environment for several good reasons, not least of which being that it's the only realistic way to ensure that the version of rpm that it's using for parsing matches the version on the target OS. However, as things have progressed, we've discovered several problems.This is largely solvable by rewriting rpmautospec in a compiled language, consuming libgit2 (possibly bundled, to avoid bootstrapping issues due to its erratic backwards-compatibility history) and librpm.
I propose that we make a plan for "rpmautospec 1.0" to be written in C, since that will require no additional bindings for libgit2 or librpm and aim to implement this no later than May 2025 (so as to avoid the Python 3.14 bootstrapping problem).
cc @hroncok @praiskup
The text was updated successfully, but these errors were encountered: