-
Notifications
You must be signed in to change notification settings - Fork 198
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
Use cxx-rs for core.rs #2336
Use cxx-rs for core.rs #2336
Conversation
c816c67
to
bd0cd00
Compare
Looks like CI fails here because Rust is too old in f32. Hopefully we get coreos/coreos-assembler#1881 going soon. |
glnx_autofd int fd = -1; | ||
try { | ||
fd = rpmostreecxx::download_to_fd(*pkg); | ||
} catch (std::exception& e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one is a really interesting topic. GError
is closer to Rust's explicit Result
than C++ exceptions are. However...for most of the usage of GError
in rpm-ostree we are effectively just using it to unwind up the stack - i.e. we don't explicitly catch errors.
The only exceptions¹ I can think of are:
main()
(obviously)- GDBus method handlers
And so what I'd probably argue for is that rather than doing try/catch-to-gerror at places like this we just go with the C++ exception flow, and install toplevel try/catch at those places that need it.
(Also in rpm-ostree the usage of g_error_matches()
is something like 90% for ENOENT...which totally validates https://github.com/cgwalters/openat-ext/blob/26324991dd1648bab64ce1f2e6471186e6ed21f9/src/lib.rs#L36 )
¹ 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that main()
has a try/catch
, I think can drop this one, right?
OK I looked at this a bit more. Definitely a whole mixed bag of tradeoffs here. Idiomatic C++ strings and (not to mention Rust String) do totally clash with our pervasive use of C NUL-terminated strings; see e.g. https://www.murrayc.com/permalink/2017/06/20/c-stdstring_view-not-so-useful-when-calling-c-functions/ We are already using CUtf8Buf, so...we could use that more Rust side. Hmm, and perhaps a viable path here is adding support for that crate into cxx-rs as an optional feature (or perhaps just move it into cxx-rs?). The other thing I haven't tried here but is probably going to be annoying is supporting passing GObjects. Hmm. So one option I mentioned in our chat is we could fork cxx and add custom stuff. Since some problem domains here are kind of unique to us it's not totally crazy IMO. Particularly since eventually we wouldn't need it. |
9afd13f
to
73e7405
Compare
OK right, currently our Prow job is using |
And the Jenkins/CoreOS CI is blocked on coreos/coreos-assembler#1881 because the Fedora team isn't backporting newer Rust to 32, and it looks like cxxbridge gained a dep on very recent Rust for some |
OK cool after filing dtolnay/cxx#544 I figured out how to pass through GObjects, which I think was the biggest blocker to this. |
OK I reworked this to have a better demo; I converted @lucab 's work in core.rs. Note how all the |
/// Remove the temporary /etc, and destroy the guard. | ||
pub fn redo_usretc(self) -> anyhow::Result<()> { | ||
pub fn undo(&self) -> anyhow::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From an out-of-band chat: this seems to be a cxx
limitation, not being able to consume the ownership of the object itself.
/// Remove the temporary /etc, and destroy the guard. | ||
pub fn redo_usretc(self) -> anyhow::Result<()> { | ||
pub fn undo(&self) -> anyhow::Result<()> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be cool if we could we make the undo
happen in Drop::drop
so we don't have to explicitly call it. This matches some other guards we have in C which used autocleanups to "unguard".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do that actually! But I explicitly chose not to because there's no way to check for errors in drop()
. (This gets into the whole rationale behind things like https://docs.rs/drop_bomb/0.1.5/drop_bomb/ that we use in https://docs.rs/openat-ext/0.1.9/openat_ext/struct.FileWriter.html )
@@ -4576,8 +4575,7 @@ rpmostree_context_assemble (RpmOstreeContext *self, | |||
} | |||
|
|||
/* Undo the /etc move above */ | |||
if (!ror_tempetc_redo_usretc (etc_guard, error)) | |||
return FALSE; | |||
etc_guard->undo(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to emphasize how representative this example will be I think of future code. Notice that we start using C++ method invocation syntax on a struct, and we use C++ exceptions instead of GError.
Personally I think the first is a notable win (on top of how we didn't need to manually create unsafe C wrappers). I'm less happy with C++ exceptions but I think on balance if we can greatly accelerate oxidation it will be worth the short term hit.
OK this needs #2381 |
OK, lifting draft on this finally! |
OK, fixed the thinko I introduced when pulling the utils.rs code out of another branch; now passing CI! |
<!-- Generated with: fakeroot /bin/sh -c 'cd dracut-urandom && find . -print0 | sort -z | (mknod dev/random c 1 8 && mknod dev/urandom c 1 9 && cpio -o --null -H newc -R 0:0 --reproducible --quiet -D . -O /tmp/dracut-urandom.cpio)' --> | ||
<file>dracut-random.cpio.gz</file> | ||
</gresource> | ||
</gresources> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
100% less XML!
if (lseek (tmpf.fd, 0, SEEK_END) < 0) | ||
return glnx_throw_errno_prefix (error, "lseek"); | ||
if (glnx_loop_write (tmpf.fd, random_cpio_data_p, random_cpio_data_len) < 0) | ||
if (glnx_loop_write (tmpf.fd, random_cpio_data.data(), random_cpio_data.length()) < 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know the parenthesis spacing is inconsistent. It's a challenge mentally for me at least because the methods being shared with Rust pulls towards Rust style. And in particular for method invocations there's a strong "binding" between the identifier and its arguments. Most C++ projects don't use a space between method name and open paren; there isn't an offical GNU coding style for C++ AFAICS.
Overall this kind of fits in with a theme of having the C/C++ get a bit uglier in some cases in return for making it way easier to bridge to Rust.
Also, perhaps at some point we can investigate using clang-format.
In the previous buildsytem rework we disabled the unit tests because of linking problems. Now I realized that a simple solution is to continue to build one big object, just make it an internal static library and have a tiny "stub main" that delegates to an entrypoint. That's basically what the C unit tests are - an alternative `main()` with some extra code.
We need to cleanly split off "test dependencies" that we install inside the cosa pod from builds (where we won't have `cargo`) from the build time where we use the cosa buildroot image. Prep for using https://cxx.rs
This is much better than bindgen because it's fully safe. It's much more ergonomic too: - Invoke Rust methods-on-structs just like C++ methods-on-structs - Rust `Result<>` is translated automatically to exceptions See https://cxx.rs/context.html for more.
A step towards converting all of utils.
The way gresources work using a constructor function started failing when I was refactoring the build system, and I couldn't figure it out. It's just easier to use Rust for this which has nice toolchain-integrated functionality for this.
Rebased 🏄 this one, not because it really requires it, but because I have further branches on top of this and they benefit from the rest of the C++ porting. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool stuff, approach LGTM overall!
@@ -10,11 +10,23 @@ | |||
mod ffiutil; | |||
mod includes; | |||
|
|||
#[cxx::bridge(namespace = "rpmostreecxx")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't the namespace name be e.g. rpmostreers
to reflect the fact that it's actually calling into Rust code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah but see it's bidirectional, cxx supports Rust calling C++ too and I just submitted #2413 to demo that.
Concretely anything listed in extern "C++"
is expected to be in the target namespace too.
That said it could totally use a better name.
ror::
following theror_
naming for bindgen?rpmostree::
- Unimaginative but obvious- Some made up name based on...a bridge or something? skytrain:: ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OTOH we can also change the name later; there's not a lot of code using this right now. It'll just get slightly more expensive to do so as usage increases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked at this a bit more. It's an interesting topic; partly comes down to whether we see the "cxxrs" part as separate from the C/C++ part. And the core question there is, do we stick everything in the C++ side into the same rpmostree
namespace? That'd mean that it'd just be e.g. download_to_fd
and not rpmostreecxx::download_to_fd()
when called.
This also conceptually would lead towards dropping the C function prefixing, e.g. instead of rpmostree_syscore_bump_mtime()
it'd be rpmostree::syscore_bump_mtime()
. My feeling right now is that'd be way too much churn; the high level goal here is to sprint towards thinning out the C/C++ side into Rust, not to spend time rewriting into "modern C++". But there will likely be some of the latter.
So I think for now let's just keep rpmostreecxx::
(and not stick any C++ code into a new namespace) because it highlights the C++/Rust-bridged code in our C-in-C++ code which just works differently for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right OK, got it. WDYT about cxxrs::
? Anyway, yeah we can revisit later.
Also, definitely agreed re. not trying to make too much use of namespaces outside of cxxrs.
glnx_autofd int fd = -1; | ||
try { | ||
fd = rpmostreecxx::download_to_fd(*pkg); | ||
} catch (std::exception& e) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now that main()
has a try/catch
, I think can drop this one, right?
This one is still necessary to print |
BTW here's a recent blog on cxx in a KDE project. |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: cgwalters, jlebon The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
ci/install-extra-builddeps.sh
Outdated
# Install build dependencies not in the cosa buildroot already | ||
set -xeuo pipefail | ||
if ! command -v cxxbridge; then | ||
cargo install --root=/usr cxxbridge-cmd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this actually need to be in /usr
BTW? Running this script locally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I guess we could dispatch on $(id -u)
?
Seeing the following errors on a fresh container and master: src/app/rpmostree-dbus-helpers.cxx: In function ‘gboolean rpmostree_sort_pkgs_strv(const char* const*, GUnixFDList*, GPtrArray**, GVariant**, GError**)’:
src/app/rpmostree-dbus-helpers.cxx:1069:18: error: ‘rpmostreecxx’ has not been declared
1069 | fd = rpmostreecxx::download_to_fd (pkg);
| ^~~~~~~~~~~~
src/app/rpmostree-dbus-helpers.cxx:1070:25: error: ‘exception’ in namespace ‘std’ does not name a type
1070 | } catch (std::exception& e) {
| ^~~~~~~~~
make[2]: *** [Makefile:3398: src/app/librpmostreeinternals_la-rpmostree-dbus-helpers.lo] Error 1 Don't know the codebase well enough for (1). EDIT: Fixing (1) yields more errors of the same nature. Is master/or me missing a common include? src/libpriv/rpmostree-core.cxx: In function ‘gboolean rpmostree_context_assemble(RpmOstreeContext*, GCancellable*, GError**)’:
src/libpriv/rpmostree-core.cxx:4326:20: error: ‘rpmostreecxx’ has not been declared
4326 | auto etc_guard = rpmostreecxx::prepare_tempetc_guard (tmprootfs_dfd);
| ^~~~~~~~~~~~
src/libpriv/rpmostree-core.cxx:4372:36: error: ‘rpmostreecxx’ has not been declared
4372 | auto systemctl_wrapper = rpmostreecxx::get_systemctl_wrapper ();
| |
In some cases you may need to |
Thanks for your suggestion @cgwalters. I've ran Commands issued in a F33 build container: git clone https://github.com/coreos/rpm-ostree.git
cd rpm-ostree/
git submodule update --init
env NOCONFIGURE=1 ./autogen.sh
./configure --prefix=/usr --libdir=/usr/lib64 --sysconfdir=/etc
make clean
make Side suggestion: Let's add |
This is much better than bindgen because it's fully safe. It's
much more ergonomic too:
Result<>
is translated automatically to exceptionsSee https://cxx.rs/context.html for more.