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

Support consuming autoconf/make external dependencies via a Bazel rule #2792

Closed
htuch opened this issue Apr 8, 2017 · 7 comments
Closed
Labels
P2 We'll consider working on this in future. (Assignee optional) type: feature request

Comments

@htuch
Copy link

htuch commented Apr 8, 2017

Having recently converted a mid-sized project from cmake to Bazel (https://github.com/lyft/envoy, envoyproxy/envoy#415), one of the very significant challenges was dealing with more than a dozen external dependencies (https://lyft.github.io/envoy/docs/install/requirements.html), almost all of which had no native Bazel support and were mostly autoconf/make based.

We had a requirement to consume these external dependencies in multiple ways. Some organizations (notably Lyft, the project owner) required the dependencies as prebuilts, essentially .a and .h files in a standard --prefix layout. This was also required by our CI system for performance reasons. Other developers wanted a way to have Bazel perform the build of the dependencies. To compose with other projects, allowing Envoy to be consumed as a git_repository, we wanted to avoid having the consuming project figure out its own dependencies, so we needed a self-contained Bazel build of the dependencies as well for this reason.

We settled on building the dependencies under a common recursive make both when creating the prebuilts and also ran the recursive make under a Bazel genrule for the Bazel build. Unfortunately, AFAICT, genrules need to know ahead of time all the headers and libraries in its outs, we can't just glob them. Explicitly declaring the libraries is fine, each dependency only had 1 or 2. The headers would be a nightmare though by hand, there are O(1k) of these.

We settled on performing an offline build outside of Bazel, then analyzing the build outputs to generate a .bzl with the smarts to provide a genrule for the dependencies. This .bzl then gets checked into the repository. See envoyproxy/envoy#716. This seems like a huge amount of engineering effort to allow for easy consumption of external deps that are non-Bazel based (Please let us know if there is a better way!).

I think Bazel should have an option in its git_repository for taking an external repository, performing a native build under autoconf/make (or whatever native build system it has) and discovering its generated files (perhaps with globs), that runs prior to building the dependency graph.

CC: @mattklein123 @lizan @tschroed

@steven-johnson
Copy link
Contributor

+1; this is a nontrivial blocker to C++ projects that use existing non-Bazel libraries (i.e.: most of them). (I had hoped that the "contributed Bazel BUILD files" repository would help solve this, but it appears to be mostly stagnant.)

@htuch
Copy link
Author

htuch commented Apr 9, 2017

I think what we're doing in envoyproxy/envoy#716 is actually not the best way to approach it. By defining a repository_rule (https://bazel.build/versions/master/docs/skylark/repository_rules.html) and performing the autoconf/make there rather than in a genrule, we can probably achieve the goal of having Bazel pick up on the make outputs prior to building the dependency graph.

@hlopko
Copy link
Member

hlopko commented Apr 10, 2017

Can you investigate if repository_rule solves your needs and then ping this thread?

@htuch
Copy link
Author

htuch commented Apr 12, 2017

@mhlopko It does, see envoyproxy/envoy#747. However, it's got a horrible UX because of #1289. I've filed an issue to improve documentation of best practices for consuming C++ autoconf/make dependencies at #2814. I'll close this issue out, since the machinery is there today.

@htuch htuch closed this as completed Apr 12, 2017
@htuch htuch reopened this Apr 13, 2017
@htuch
Copy link
Author

htuch commented Apr 13, 2017

Re-opening. As discussed in envoyproxy/envoy#747 (comment), Bazel could do more to support finer grained builds of autoconf/make dependencies under a repository_rule, by acting as a make job server. Otherwise, all external deps that build under autoconf/make really need to be built as a single monolithic dependency for performance reasons, which is bad for the reasons discussed in that PR. CC @lizan.

@htuch
Copy link
Author

htuch commented Apr 15, 2017

I think envoyproxy/envoy#747 (comment) provides some useful data on what the performance issues are. There's a foundational issue when it comes to dealing with multiple autoconf/make deps and how the repository_rule(s) should be structured. In particular, since repository_rule is sequential today, we're forced to group all deps under a single rule.

There's also an issue that hasn't been previously raised. If the the autoconf/make dependency (e.g. Lightstep in our case) wants to consume from something that can be cleanly imported as a git_repository (e.g. protobuf for Lightstep) as it has an existing BUILD file, there doesn't seem a clean way to plumb the location of the headers/libraries into the repository_rule for autoconf/make to "get" at the Bazel-side dependency. I.e. there's no way to express dependency relationship between repositories. For this reason, we're forced to fetch/build protobuf as an autoconf/make dependency rather than first-class Bazel.

It seems we need to either make repository_rule capable of handling these scenarios or introduce first-class support for autotools deps.

@hlopko hlopko added P2 We'll consider working on this in future. (Assignee optional) and removed under investigation labels Apr 18, 2017
htuch added a commit to htuch/envoy that referenced this issue Apr 18, 2017
* Build a recursive make cache in a parallel directory to where Bazel thinks the repository rule
  outputs are. E.g. if Bazel think the deps are in ~/.cache/bazel/89676793239ac96d94294e6c7a44597f/external/envoy_deps,
  the cache lives in ~/.cache/bazel/89676793239ac96d94294e6c7a44597f/external/envoy_deps_cache.
  This allow us to avoid Bazel falsely clobbering all the make build directories everytime it _thinks_
  there is a dependency change (e.g. a single recipe is modified). We then defer to make to figure
  out when it's time to rebuild these deps. If something gets really messed up, bazel clean
  --expunge still works with this cache.

  Underlying issue tracked in bazelbuild/bazel#2792.

* Always output stuff after the build of external deps, to signal what's happened. Otherwise, it
  appears mysterious to the user where all the time went as Bazel doesn't report progress.

  Underlying issue tracked in bazelbuild/bazel#1289.

* Add a `bazel fetch` step to the documentation so that the mystery hang when Bazel first builds
  the external deps is understandable.

  Underlying issue tracked in bazelbuild/bazel#1289.
mattklein123 pushed a commit to envoyproxy/envoy that referenced this issue Apr 20, 2017
* Build a recursive make cache in a parallel directory to where Bazel thinks the repository rule
  outputs are. E.g. if Bazel think the deps are in ~/.cache/bazel/89676793239ac96d94294e6c7a44597f/external/envoy_deps,
  the cache lives in ~/.cache/bazel/89676793239ac96d94294e6c7a44597f/external/envoy_deps_cache.
  This allow us to avoid Bazel falsely clobbering all the make build directories everytime it _thinks_
  there is a dependency change (e.g. a single recipe is modified). We then defer to make to figure
  out when it's time to rebuild these deps. If something gets really messed up, bazel clean
  --expunge still works with this cache.

  Underlying issue tracked in bazelbuild/bazel#2792.

* Always output stuff after the build of external deps, to signal what's happened. Otherwise, it
  appears mysterious to the user where all the time went as Bazel doesn't report progress.

  Underlying issue tracked in bazelbuild/bazel#1289.

* Add a `bazel fetch` step to the documentation so that the mystery hang when Bazel first builds
  the external deps is understandable.

  Underlying issue tracked in bazelbuild/bazel#1289.
@dslomov
Copy link
Contributor

dslomov commented Mar 21, 2019

Use https://github.com/bazelbuild/rules_foreign_cc

@dslomov dslomov closed this as completed Mar 21, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P2 We'll consider working on this in future. (Assignee optional) type: feature request
Projects
None yet
Development

No branches or pull requests

5 participants