bazelbuild / bazelbuild/rules_apple
-ObjC removal / transition plan
- Dominant language
- Starlark
- Stars
- 593
- Forks
- 334
- Avg merge
- 16h 48m
- Merged PRs (30d)
- 9
Description
I'm creating this issue to track and document removing the `-ObjC` linker flag by default, and replacing that with enabling `alwayslink` by default on `swift_library`, `objc_library` and the other related rules.
## Background
For a long time in the iOS community folks have thrown `-ObjC` into the linker flags to avoid a common pattern where methods on an Objective-C category were not found at runtime. When using CocoaPods you've likely gotten this through any pods that expect to be included as static libraries / frameworks. The downside of this is that `-ObjC` may load things that need to be loaded, but it may also load static archives containing Objective-C that are actually unused, which leads to an increase in binary size.
## Solution
Since the build system cannot be 100% sure whether `-ObjC` is necessary or not in your project, in order to remove it without breaking any projects, we need to opt users into the same[^1] behavior, and let users decide how to back this off and reduce binary size if they're interested in doing so. To do this there will be a few changes in bazel itself, and the rules to support this.
1. Bazel has a new flag [`--incompatible_objc_alwayslink_by_default`](https://bazel.build/reference/command-line-reference#flag--incompatible_objc_alwayslink_by_default) in 7.x which is currently off, but will be flipped on before the 7.x release
2. The rules will respect this flag such that when it is enabled, `alwayslink` will be `True` regardless of what you pass for that attribute (note this unfortunately means during this transition you cannot set `alwayslink = False`, but practically speaking this is no different than today)
3. We will remove the `-ObjC` flag from all link invocations, because using `alwayslink` means the library will be passed to the linker with `-force_load` which applies the same[^1] behavior
4. We will remove the `--incompatible_objc_alwayslink_by_default` flag once it's stable, which will allow the use of `alwayslink = False`. This means if you are interested, you can go through libraries that have a significant impact on your binary size, and set `alwayslink = False`, to try to avoid loading things that are actually unused in this library. **This could break your code at runtime, so be sure you know that things being excluded when you do this are actually unused** (to see what setting this to `False` does, you can diff the output of `nm -j path/to/binary` with `alwayslink` enabled, then disabled.
## Notes
- The `-ObjC` flag also applies to Swift, in that if you have an extension on a type (similar to the category case) that conformance will only be loaded if the archive is pulled in for another reason. Because of this subtle behavior, it's unlikely that you should set `alwayslink = False` on `swift_library` targets
- If you would like to improve your binary size by using `alwayslink = False` on targets, but do need to load specific category methods, you can use an approach like this to force them to be loaded naturally: https://github.com/Instagram/IGListKit/pull/957
- If you would like to avoid this change, you can set `alwayslink = False` on your `swift_library` and `objc_library` targets, and pass `--linkopt=-ObjC` in your `.bazelrc` to retain the previous behavior.
[^1]: technically there are _slight_ differences in behavior here, but practically speaking they shouldn't be impactful. For example if you have an `objc_library` that only has C code, previously `-ObjC` would not load it, but now it will be loaded. This likely means that you will over-load things rather than drop things that are needed, which means it could increase binary size, but shouldn't impact runtime behavior. Whether or not you have this case is something you can verify with and without `--incompatible_objc_alwayslink_by_default` checking the impact on your binary size.
Contributor guide
Research direction
Start by reviewing the rules and link invocations that handle `alwayslink`, `-ObjC`, and `--incompatible_objc_alwayslink_by_default`; the issue does not name specific files or tests. Trace how `swift_library`, `objc_library`, and related rules pass these options. Done means removing the default `-ObjC` behavior while preserving the documented transition and flag behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- objective-c, swift
- Domain
- build-system, mobile-dev
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100