linkstatic = True is silently ignored under --dynamic_mode=fully
- Dominant language
- Java
- Stars
- 25.8k
- Forks
- 4.6k
- Avg merge
- 2d 20h
- Merged PRs (30d)
- 72
Description
### Description of the bug
A per-target `linkstatic = True` on a `cc_binary` is silently ignored when the build is run with `--dynamic_mode=fully`. The target's `cc_library` deps are linked dynamically anyway, with no warning or error.
I'd expect a per-target attribute like `linkstatic` to override a command-line *default* rather than be overridden by it, and that is exactly the behavior under `--dynamic_mode=default`: there, `linkstatic = True` and `linkstatic = False` on otherwise identical binaries produce static and dynamic linkage respectively. Only under `fully` does the flag win over the explicit per-target request.
If overriding really is intended, it should at least not be silent, since it means `--dynamic_mode=fully` can change the linkage of targets that have deliberately opted out of it.
(`--dynamic_mode=off` has the mirror-image behavior: `linkstatic = False` is silently ignored. That direction is at least suggested by the flag name; `fully` overriding an explicit `True` is the surprising one.)
### Which category does this issue belong to?
C++ Rules
### What's the simplest, easiest way to reproduce this bug?
`MODULE.bazel`:
```python
module(name = "linkstatic_repro")
bazel_dep(name = "rules_cc", version = "0.2.17")
```
`lib.h`:
```c++
int f();
```
`lib.cc`:
```c++
#include "lib.h"
int f() { return 42; }
```
`main.cc`:
```c++
#include "lib.h"
int main() { return f() == 42 ? 0 : 1; }
```
`BUILD`:
```python
load("@rules_cc//cc:cc_binary.bzl", "cc_binary")
load("@rules_cc//cc:cc_library.bzl", "cc_library")
cc_library(name = "lib", srcs = ["lib.cc"], hdrs = ["lib.h"])
cc_binary(name = "static_bin", srcs = ["main.cc"], deps = [":lib"], linkstatic = True)
cc_binary(name = "dynamic_bin", srcs = ["main.cc"], deps = [":lib"], linkstatic = False)
```
Then check whether each binary ends up with a `NEEDED` entry for the dep's shared library:
```bash
for mode in default fully off; do
bazel build --dynamic_mode=$mode //:static_bin //:dynamic_bin >/dev/null 2>&1
echo "--dynamic_mode=$mode"
for t in static_bin dynamic_bin; do
readelf -d bazel-bin/$t | grep -q libliblib.so && r=dynamic || r=static
echo " $t -> $r"
done
done
```
### What operating system are you running Bazel on?
Ubuntu 24.04, Linux 6.8.0 x86_64, gcc 13.3.0
### What is the output of `bazel info release`?
`release 9.2.0`
### Have you found anything relevant by searching the web?
I didn't find an existing issue for this; #16479 and #21301 touch `linkstatic` but not its interaction with `--dynamic_mode`.
### Any other information, logs, or outputs that you want to share?
Output of the script above:
```
--dynamic_mode=default
static_bin (linkstatic=True ) -> static
dynamic_bin (linkstatic=False) -> dynamic
--dynamic_mode=fully
static_bin (linkstatic=True ) -> dynamic <-- linkstatic=True ignored
dynamic_bin (linkstatic=False) -> dynamic
--dynamic_mode=off
static_bin (linkstatic=True ) -> static
dynamic_bin (linkstatic=False) -> static <-- linkstatic=False ignored
```
The `fully` build produces no diagnostic mentioning `linkstatic`.
Contributor guide
Research direction
Start with the MODULE.bazel and BUILD reproduction, then run the provided loop with readelf for default, fully, and off. Trace how the cc_binary linkstatic attribute is combined with --dynamic_mode; done means the precedence is intentional and documented by behavior, with no silent mismatch for an explicit per-target setting.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- build-system
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100