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

Avoid cmake_external for likely transitive dependencies #8141

Closed
troshko111 opened this issue Sep 4, 2019 · 8 comments
Closed

Avoid cmake_external for likely transitive dependencies #8141

troshko111 opened this issue Sep 4, 2019 · 8 comments
Assignees
Labels
question Questions that are neither investigations, bugs, nor enhancements stale stalebot believes this issue/PR has not been touched recently

Comments

@troshko111
Copy link
Contributor

Title: cmake_external-style dependencies in Bazel universe are hard to consume transitively as they have a completely distinct reference style.

Description: Even though cmake_external-style dependencies are very tempting, they are rather painful to use transitively, imagine Envoy itself is a submodule of project Parent, say Parent depends on another Bazel-native Child library, say Child depends on c-ares. Now since Envoy already has that, it'd make sense to use the same version when building Child and Envoy. Issue is that Bazel-native and cmake_external use different reference style (//external:<lib> vs @<lib>) so now Child needs to have something like:

config_setting(
    name = "c_ares_via_cmake",
    values = {"define": "c_ares_via_cmake=enabled"},
)

and do a conditional dependency

select({
               "//conditions:default": [
                   "@com_github_c_ares_c_ares//:ares",
               ],
               "@child//:c_ares_via_cmake": [
                   "//external:ares",
               ],
           })

For every dependency basically. So not only we now need to support

def child_dependencies(
        omit_curl = False,
        omit_com_github_c_ares_c_ares = False,
        omit_boringssl = False,
        omit_net_zlib = False,
...

But also Bazel config options for each on top of that so they can be used as a cmake_external dependency.

Note that omit_xxx and the reverse FQDN dependency naming are not part of Bazel standard/conventions and are rather a best practice in the community / answer to aligning transitive dependency versions.

Example of this is TensorFlow which uses locally-authored BUILD files for most dependencies https://github.com/tensorflow/tensorflow/tree/master/third_party.

@htuch
Copy link
Member

htuch commented Sep 4, 2019

@troshko111 if an external dependency has a native Bazel build, we will use this directly. We explicitly are not in the business though of writing and maintaining BUILD files for external dependencies that don't have this, unless they are totally trivial (e.g. a simple header-only library). We also don't want to vendor/snapshot some random BUILD file, as that way lies bit rot and maintenance overhead. This was a project-level decision we made long ago.

I think TensorFlow is an anti-pattern in this regard; it's hard to imagine a useful build tool that tells you that you need to go ahead and write a build system for every transitive dependency that you have. At the end of the day, we're in the business of building proxies, not writing and maintaining build systems for other projects.

The issues around cmake_external and naming should be taken up with the Bazel team; I think we all want to see Bazel succeed at working well with external dependencies, many of which are quite content with an autoconf or cmake build.

Please reach out to @irengrig at https://github.com/bazelbuild/bazel for further discussion.

CC @jmillikin-stripe @lizan

@htuch htuch added the question Questions that are neither investigations, bugs, nor enhancements label Sep 4, 2019
@htuch htuch self-assigned this Sep 4, 2019
@troshko111
Copy link
Contributor Author

if an external dependency has a native Bazel build, we will use this directly.

Taking this one step further, what if said dependency has its own Bazel-native dependency, already present in Envoy? Surely we'd want to reuse it making sure we don't compile independent version statically linked together. Precisely this situation has got me starting this conversation. I've been toying with a submoduled Envoy and some custom extensions which rely on first/third party Bazel-native dependencies, said dependencies have their own dependencies like rapidjson, spdlog, curl, zlib, the usual suspects. When including the top level dependency, I load and call a function like project_dependencies() and pass various skip_xxx flags so it does not load rapidjson, curl and so on, only the ones missing in Envoy. This is rather error-prone as the names must match (reverse-FQDN) and with the addition of cmake_external now each of them has to be select-ed via a config setting since the dependency string has two different formats, the @ or the //external.

So even though in this case a Bazel-native dependency was added, it only works as long as the common dependencies have matching names and is severely complicated if any non-Bazel-native ones are present (especially cmake_native).

Example above would be something like:

RootProject

  • Envoy submodule
  • Custom envoy extension code
  • DependencyA

DependencyA has 10 direct dependencies like json libs, curl, and so on. Some have BUILD files written for them (header libs), some more complex. Many are already defined in Envoy. When compiling RootProject it is desired to have only a single version of any given library statically linked into the resulting binary for obvious reasons. DependencyA defines a commonly used function dependency_a_dependencies accepting various omit_xxx bools so we don't copy-paste all transitive dependencies into RootProject WORKSPACE file and have an ability to skip the ones we already declare. This is rather cumberstone but seems to be the best one can do with Bazel atm. It works alright but making sure the names align, cmake_external are explicitly supported via config settings, it all adds up and makes the experience a bit rough.

I think TensorFlow is an anti-pattern in this regard; it's hard to imagine a useful build tool that tells you that you need to go ahead and write a build system for every transitive dependency that you have. At the end of the day, we're in the business of building proxies, not writing and maintaining build systems for other projects.

I personally agree with you and this is not an issue with Envoy per se, it's really the build system shortcoming, I wonder why TF went this way but seems to be working fine for them so far. They would be better to talk to various pros/cons but it seemed to me they wanted more control over dependencies of their dependencies, like in #8140, if you add curl as a direct dependency and you already have zlib, it's not even obvious curl needs one and can reuse what you have as all you do is call cmake_external and Bazel makes it happen. With a catch that that cmake could have compiled code you have, a different version of it worst case. These are hard to catch and can result in nasty crashes should objects get shared between different versions.

The issues around cmake_external and naming should be taken up with the Bazel team; I think we all want to see Bazel succeed at working well with external dependencies, many of which are quite content with an autoconf or cmake build.

Thanks I'll try to start a discussion at Bazel, I probably should have in the first place.

@lizan
Copy link
Member

lizan commented Sep 4, 2019

Issue is that Bazel-native and cmake_external use different reference style (//external: vs @) so now Child needs to have something like:

Just bind in your WORKSPACE? Many project_dependencies() checks if there's already a dependency with existing_rules().keys(), so you don't really need a select based on that.

@troshko111
Copy link
Contributor Author

bazelbuild/bazel#9325

@htuch feel free to close in favor of the linked issue, I'm still curios what you think of cmake_external blackbox problem potentially compiling something you already have compiled, this would not be a problem should all dependencies have a BUILD file. Maintaining a BUILD file is however very expensive and not feasible after some project size threshold.

Just bind in your WORKSPACE? Many project_dependencies() checks if there's already a dependency with existing_rules().keys(), so you don't really need a select based on that.

Bind is basically getting deprecated is it not? Also AFAIK one cannot rebind a cmake_external dependency i a way it can be referenced using @dependency syntax unconditionally?

@lizan
Copy link
Member

lizan commented Sep 4, 2019

Bind is basically getting deprecated is it not? Also AFAIK one cannot rebind a cmake_external dependency i a way it can be referenced using @dependency syntax unconditionally?

Right it is not recommended though no alternative yet (without nested workspace, etc). With bind you can reference using //external:dependency syntax unconditionally.

@htuch
Copy link
Member

htuch commented Sep 4, 2019

@lizan yeah, but the issue is that this is viral IMHO; you basically force bind style on anyone who depends on the project. I think there is a major gap from a usability perspective unless the whole world adapts bind naming rather than direct repository naming. I think that if cmake_external could synthesize a pseudo repository with arbitrary name, that might get us to a good place.

@stale
Copy link

stale bot commented Oct 4, 2019

This issue has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in the next 7 days unless it is tagged "help wanted" or other activity occurs. Thank you for your contributions.

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Oct 4, 2019
@stale
Copy link

stale bot commented Oct 11, 2019

This issue has been automatically closed because it has not had activity in the last 37 days. If this issue is still valid, please ping a maintainer and ask them to label it as "help wanted". Thank you for your contributions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Questions that are neither investigations, bugs, nor enhancements stale stalebot believes this issue/PR has not been touched recently
Projects
None yet
Development

No branches or pull requests

3 participants