bazelbuild / rules_go

Go rules for Bazel
Apache License 2.0
1.35k stars 635 forks source link

Cross-compilation to Linux on MacOS sets CGO_ENABLED=0 even though cgo=True in go_library and platform set to linux_amd64_cgo #3954

Closed akesling closed 2 weeks ago

akesling commented 3 weeks ago

What version of rules_go are you using?

0.48.0

What version of gazelle are you using?

0.37.0

What version of Bazel are you using?

7.1.2

Does this issue reproduce with the latest releases of all the above?

Yes

What operating system and processor architecture are you using?

MacOS 14.4.1 x86_64

What did you do?

See https://gist.github.com/akesling/8e0cdfa8ab2d680fec9573a14428744d for output of bazel build --subcommands //repro --platforms=@io_bazel_rules_go//go/toolchain:linux_amd64_cgo --toolchain_resolution_debug='@@bazel_tools//tools/cpp:toolchain_type' in https://github.com/akesling/cgo_repro repo.

See https://github.com/akesling/cgo_repro for local reproduction steps on MacOS using docker.

What did you expect to see?

CGO_ENABLED=1 or an error that cgo is configured/required but could not be enabled (preferably with steps to fix).

What did you see instead?

Binary is compiled with CGO_ENABLED=0

derekperkins commented 2 weeks ago

do you have either of these flags set in .bazelrc?

build --features=pure --features=static
fmeum commented 2 weeks ago

What I don't see in the reproducer is a C++ toolchain that runs on macOS and targets Linux, such as https://github.com/bazel-contrib/musl-toolchain. Without such a toolchain, I don't see how you could compile CGO for Linux, but maybe I am misunderstanding what you are trying to achieve.

fmeum commented 2 weeks ago

In fact, when I add this to WORKSPACE:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "musl_toolchains",
    sha256 = "79c3dab39eb52a9c0927f6a222e2a10b733fd875ccdad59feadd5b30fd4518fb",
    url = "https://github.com/bazel-contrib/musl-toolchain/releases/download/v0.1.9/musl_toolchain-v0.1.9.tar.gz",
)

load("@musl_toolchains//:repositories.bzl", "load_musl_toolchains")

load_musl_toolchains()

load("@musl_toolchains//:toolchains.bzl", "register_musl_toolchains")

register_musl_toolchains()

and this to .bazelrc:

common --features=fully_static_link

it just works:

Read text from DB: Hello SQLite!

@illicitonion A great occasion to say "nice job"! Are you still planning to add Bazel support to musl-toolchain? ;-)

akesling commented 2 weeks ago

@derekperkins neither of those were set.

@fmeum Thank you! It looks like this works (the added musl-toolchain plus --features=fully_static_link).

A few follow-up questions: 1) Looks like y'all just added support for bzlmod loading musl-toolchain (https://github.com/bazel-contrib/musl-toolchain/releases/tag/v0.1.10). When do you expect this will be available on the Bazel Central Registry so it can be used? In the monorepo I was reproducing, we've moved wholly to bzlmod and would like to keep it that way. 2) Is there any way to set --features=fully_static_link from Starlark so it's always run for a target and doesn't need to be set on the command line? I can't set it in .bazelrc as I have other targets which should never be run with this flag. I'd also rather it not be a surprise in the future (as someone will invariably forget to add the flag if it's not encoded in the build directly). 3) Is there any way to force a failure to build with cgo to cause a build failure? My binary absolutely requires SQLite to run correctly and it's very surprising that the same target can give wildly different runtime behavior (working vs. very much not) without any form of feedback. I'd rather avoid having a place where a platform transition causes a semantically different binary to be built. A perfect world would involve a successful build to give high confidence that runtime will work as expected.

fmeum commented 2 weeks ago
  1. There are some problems with the BCR setup that we are iterating on and release take pretty long to build due to musl building from source, but soon.
  2. I will enable this feature in the toolchain by default in a follow-up release.
  3. Failing at runtime instead of build time is a conscious choice by go-sqlite3. Without that file, rules_go would fail at build time if CGo isn't available.

Since the remaining issues are unrelated to rules_go (and will be addressed soon in musl-toolchain), I will close this issue.

akesling commented 2 weeks ago

@fmeum While I agree that go-sqlite3 choosing to be compilable in both fashions isn't the responsibility of rules_go, #3 (above) is specifically about my ability to mark a go_binary or go_library as requiring cgo compilation and failing when it can't be configured.

How can I make it so that there is a human-readable error which directly signals the Bazel target which required cgo, that cgo was not available, and plausibly some information about why it wasn't available? If this isn't available in rules_go right now (as I expect it isn't from this thread), then do you have any recommendations for how to hack some target which will fail in a reasonably readable fashion?

fmeum commented 2 weeks ago

How can I make it so that there is a human-readable error which directly signals the Bazel target which required cgo, that cgo was not available, and plausibly some information about why it wasn't available? If this isn't available in rules_go right now (as I expect it isn't from this thread), then do you have any recommendations for how to hack some target which will fail in a reasonably readable fashion?

This is directly available in Go itself and should also work with rules_go: The usual way how you would have something fail without cgo is by having it depend on something that is only there with cgo. Most Go packages provide both cgo and non-cgo definitions specifically to prevent this from happening, but if you want it, you could add a file with a !cgo or purego build tag and have it fail compilation or, alternatively, have a dependency on something in a cgo tagged file.