Skip to content
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

Fix issue #1417 - Group deps by platform triple rather than cfg(...) string. #1647

Merged
merged 1 commit into from
Nov 18, 2022

Conversation

wmatthews-google
Copy link
Contributor

Group deps and aliases by platform triple rather than by cfg string when generating BUILD files. This avoid Bazel errors due to duplicate keys/deps. See #1417.

This is done purely at render time by adding a new Tera filter which will remap the conditions in the deps SelectList from "cfg(...)" strings to platform triple strings.

This is my first PR here, so please bear with me if I'm doing things wrong!

@wmatthews-google wmatthews-google changed the title Fix issue #1417. Fix issue #1417 - Group deps by platform triple rather than cfg(...) string. Nov 10, 2022
Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Could you show an example of what these files render out to? One of the reason cfg strings were used was to try and make it clearer what was happening in the select statements so that if a dependency was unexpectedly missing or present, we can see what Cargo says and how that maps to rust. Just wanna make sure that's still clear.

@wmatthews-google
Copy link
Contributor Author

@UebelAndre - thanks for taking a look! Right now it's not clear at all in the output how deps map to cfg blocks...

e.g. for errno whose crate has

[target."cfg(target_os=\"dragonfly\")".dependencies.errno-dragonfly]
version = "0.1.1"
[target."cfg(target_os=\"hermit\")".dependencies.libc]
version = "0.2"
[target."cfg(target_os=\"wasi\")".dependencies.libc]
version = "0.2"
[target."cfg(unix)".dependencies.libc]
version = "0.2"
[target."cfg(windows)".dependencies.winapi]
version = "0.3"

we now generate

    deps = [
    ] + select({
        "@rules_rust//rust/platform:aarch64-apple-darwin": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:aarch64-apple-ios": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:aarch64-apple-ios-sim": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:aarch64-linux-android": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:aarch64-unknown-linux-gnu": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:arm-unknown-linux-gnueabi": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:armv7-linux-androideabi": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:armv7-unknown-linux-gnueabi": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:i686-apple-darwin": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:i686-linux-android": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:i686-pc-windows-msvc": [
            "@crates_vendor__winapi-0.3.9//:winapi",
        ],
        "@rules_rust//rust/platform:i686-unknown-freebsd": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:i686-unknown-linux-gnu": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:powerpc-unknown-linux-gnu": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:s390x-unknown-linux-gnu": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:wasm32-wasi": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:x86_64-apple-darwin": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:x86_64-apple-ios": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:x86_64-linux-android": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:x86_64-pc-windows-msvc": [
            "@crates_vendor__winapi-0.3.9//:winapi",
        ],
        "@rules_rust//rust/platform:x86_64-unknown-freebsd": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        "//conditions:default": [
        ],
    }),

... which makes Bazel happy, but I agree is less clear to humans.

A possible idea:

Suppose I make remap_conditions return SelectList<WithOriginalConditions<T>> where (roughly)

struct WithOriginalConditions<T> {
    value: T,
    original_conditions: BTreeSet<Option<String>>
}

then render the original_conditions (Some("cfg(...)") etc., or None for default) as comments above each dep.

... then the output would be something like

        # ...
        "@rules_rust//rust/platform:x86_64-unknown-linux-gnu": [
            # cfg(unix)
            "@crates_vendor__libc-0.2.137//:libc",
        ],
        # ...

... somewhat long and verbose but at least explicit. (And hopefully humans rarely read these generated files anyway).

There is still a down side that conditions which don't match any platform triples end up silently dropped in remap_conditions. If this is a concern, remap_conditions could return an additional BTreeSet<String> of unmapped conditions which could be rendered as comments too.

WDYT?

Related question: Should I regenerate (at the same locked version) all BUILD files under crate_universe/3rdparty as part of this PR?

Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re: #1647 (comment)

Thanks for showing a rendering, the change is way easier to comprehend now! My concern about readability is more for debugability. Humans likely won't look at it unless there's a problem and in developing crate_universe I've learned that dependency management concepts can be difficult to keep paged into my brain. I think it'd be a good thing to make sure the future version of maintainers and new developers have an easier time figuring out what's going on. So I like the proposal to retain info for commenting the original configurations. 👍

Related question: Should I regenerate (at the same locked version) all BUILD files under crate_universe/3rdparty as part of this PR?

It would be helpful if you had a commit with those refreshed but it does make PRs harder to comprehend post-merge. Your call 😄

crate_universe/src/rendering/template_engine.rs Outdated Show resolved Hide resolved
wmatthews-google added a commit to wmatthews-google/rules_rust that referenced this pull request Nov 13, 2022
@wmatthews-google
Copy link
Contributor Author

Updated the code as discussed. The implementation is a big uglier than before (WithOriginalConfigurations<T> isn't a great value type inside SelectList), however we can now render all the original cfg strings in the resulting build files.

See #1655 for a re-rendering of crate_universe/3rdparty BUILD files after this CL. We can choose to merge it too or not as you deem best.

Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is looking good! There's some error which I'm not entirely sure isn't an infrastructure issue so you may want to push a new commit to force CI to retrigger.

One last thought is it might be good to have some kind of test for the renderer that ensures a crate with a conditional dependency renders a build file that contains a comment for the original config and the expected triple somewhere in the output. There aren't a ton of render tests so this might be more scope increase than I thought but I think that'd be a really good test to have. But to be clear, the requested change is just to investigate CI.

@wmatthews-google
Copy link
Contributor Author

Thanks!

Added a build file rendering test. LMK if you think it'd be better to frame the assertion in terms of regexes. As-is it's quite clear how the comments map to dependencies, but this comes at the expense of being a bit brittle.

Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great! One last request but looks good to me otherwise 😄

crate_universe/src/rendering/template_engine.rs Outdated Show resolved Hide resolved
…hen generating BUILD files. This avoid bazel errors due to duplicate keys/deps.
Copy link
Collaborator

@UebelAndre UebelAndre left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome work! Thank you so much for fixing this issue 🎉

@UebelAndre UebelAndre merged commit 44c7e15 into bazelbuild:main Nov 18, 2022
UebelAndre added a commit that referenced this pull request Nov 18, 2022
* Group deps and aliases by platform triple rather than by cfg string when generating BUILD files. This avoid bazel errors due to duplicate keys/deps.

* Re-render crate BUILD files after #1647

* Re-render additional BUILD files

Co-authored-by: UebelAndre <github@uebelandre.com>
@wmatthews-google
Copy link
Contributor Author

@UebelAndre thanks for the review!

@wmatthews-google wmatthews-google deleted the fix-1417 branch November 18, 2022 17:04
illicitonion pushed a commit that referenced this pull request Jan 4, 2023
Context: see #1734.

This PR converts all of `crate_build_file.j2`, `partials/crate/*.j2` and `partials/starlark/*.j2` to serde_starlark.

### Remarks

- I kept the output of `bazel run //crate_universe/3rdparty:crates_vendor` out of this PR to keep GitHub's code review interface usable. You can find the output generated code in 44c264a.

- I tried to keep the conversion 1-to-1 to the largest extent possible. As an example, build_script.j2 duplicated most of the content of common_attrs.j2 even though it probably could have used that template via include, and I have mirrored the same here by not using `#[serde(flatten)] common: CommonAttrs` inside of `CargoBuildScript`. There is probably plenty of opportunity for cleaning up things in followup PRs.

- I came across several bugs in the existing code. Again, I mostly stayed away from these to keep the PR bounded in scope. For example the fact that `rustc_flags` is stored as a BTreeSet\<String\> is not correct; compiler flags are ordered and sorting them is not correct. For example `-C debuginfo=2`...

    https://github.com/bazelbuild/rules_rust/blob/532e60ff0ee3ac318333b3ae05392c220b17ce28/crate_universe/src/context/crate_context.rs#L178

- One bug that I fixed because in the new implementation it was easier to fix than to not fix, is that all existing uses of selectable_list.j2 and selectable_dict.j2 were broken because they all had the same failure mode as #1417. The generated code using `selects.with_or` could end up generating a map with duplicate keys if multiple distinct Cargo cfg expressions mapped to the same subset of supported platform triples. My implementation follows the same approach as #1647, but that PR only applied it to `deps` and `aliases`, not e.g. `rustc_env` and everything else.

- Anecdotally the new implementation has strong "if it compiles, it works" property. I mostly typed out the implementation of this PR top to bottom without running `crates_vendor`, and as soon as the whole thing was written and the compiler was okay with it, it was generating the output I wanted. I have not done much work in the templates in the past but I do not imagine they have this property.

### Remaining work

- `select_with_or` in private/selects.bzl is no longer used by the code generator's output. I had to keep it in this PR because all the generated BUILD files still include calls to it. It can be deleted after all those are regenerated.

- One of the tera templates still uses the old serialization of `SelectList` so the starlark serialization for it isn't a normal `Serialize` impl but instead an associated function. This causes there to be a bunch of `serialize_with = "SelectList::serialize_starlark"` attributes needed. This can be cleaned up after the last tera template is converted.

- ~~A lot of the contents of `crate::context` can be cleaned up after they're no longer used by tera. Tera needs those data structures to be serializable, whereas with serde_starlark we're not serializing anything from `crate::context`, only from `crate::utils::starlark`. For now I've just added a bunch of `serde(skip)` attributes in `crate::context` on all the parts tera is no longer using.~~ *Edit: apparently we still need to serialize all the fields in `Context` so that they end up in cargo-bazel-lock.json.*

- There are 2 remaining tera templates after this PR, module_bzl.j2 and vendor_module.j2.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants