Apple Bazel definitions - Common Information
Build limits
According to Apple's documentation, iOS and tvOS bundle sizes cannot exceed 4GB in size (limit for watchOS is 75MB). Because of this, rules_apple does not support building zipped archives that would be larger than 4GB. If your build outputs would be larger than 4GB (e.g. test bundles) you'll need to reduce the number of dependencies to fit this limit (e.g. for test bundles, you can split the targets so that output size is smaller than 4GB).
Build outputs
Most aspects of your builds should be controlled via the attributes you set on the rules. However there are some things where Bazel and/or the rules allow you to opt in/out of something on a per-build basis without the need to express it in a BUILD file.
Output Groups {#output_groups}
Output groups
are an interface provided by Bazel to signal which files are to be built. By
default, Bazel will build all files in the default
output group. In addition
to this, rules_apple supports other output groups that can be used to control
which files are requested:
dsyms
: This output group contains all dSYM files generated during the build, for the top level target and its embedded dependencies. To request this output group to be built, use the--output_groups=+dsyms
flag. In order to generate the dSYM files you still need to pass the--apple_generate_dsym
flag.linkmaps
: This output group contains all linkmap files generated during the build, for the top level target and its embedded dependencies. To request this output group to be built, use the--output_groups=+linkmaps
flag. In order to generate the linkmap files you still need to pass the--objc_generate_linkmap
flag.
dSYMs Generation {#apple_generate_dsym}
dSYMs are needed for debugging, decode crash logs, etc.; but they can take a
while to generate and aren't always needed. All of the Apple rules support
generating a dSYM bundle via --apple_generate_dsym
when doing a bazel build
.
bazel build --apple_generate_dsym //your/target
By default, only the top level dSYM bundles is built when this flag is
specified. If you require the dSYM bundles of the top level target dependencies,
you'll need to specify the --output_groups=+dsyms
flag.
Codesigning identity
When building for devices, by default the codesigning step will use the first
codesigning identity present in the given provisioning profile. This should
accommodate most cases, but there are certain scenarios when the provisioning
profile will have more than one allowed signing identity, and developers may
have different development certificates installed on their devices. For these
cases you can use the --ios_signing_cert_name
flag to force the signing
identity to be used when codesigning your app.
bazel build //your/target --ios_signing_cert_name="iPhone Developer: [CERT OWNER NAME]"
To make this easier to use, we recommend adding the following to the
~/.bazelrc
file, which will configure bazel to pass this flag to all
invocations:
build --ios_signing_cert_name="iPhone Developer: [CERT OWNER NAME]"
linkmap Generation {#objc_generate_linkmap}
Linkmaps can be useful for figuring out how the deps
going into a target are
contributing to the final size of the binary. Bazel will generate a link map
when linking by adding --objc_generate_linkmap
to a bazel build
.
bazel build --objc_generate_linkmap //your/target
By default, only the top level linkmap file is built when this flag is
specified. If you require the linkmap file of the top level target dependencies,
you'll need to specify the --output_groups=+linkmaps
flag.
Debugging Entitlement Support {#apple.add_debugger_entitlement}
Some Apple platforms require an entitlement (get-task-allow
) to support
debugging tools. The rules will auto add the entitlement for non optimized
builds (i.e. - anything that isn't -c opt
). However when looking at specific
issues (performance of a release build via Instruments), the entitlement is also
needed.
The rules support direct control over the inclusion/exclusion of any bundle
being built by
--define=apple.add_debugger_entitlement=(yes|true|1|no|false|0)
.
Add get-task-allow
entitlement:
bazel build --define=apple.add_debugger_entitlement=yes //your/target
Ensure get-task-allow
entitlement is not added (even if the default would
have added it):
bazel build --define=apple.add_debugger_entitlement=no //your/target
Force ipa compression {#apple.compress_ipa}
By default the final App.ipa
produced from building an app is uncompressed,
unless you're building with --compilation_mode=opt
. This flag allows you to
force compression if the size is more important than the CPU time for your
build. To use this pass --define=apple.compress_ipa=(yes|true|1)
to bazel build
.
Include Embedded Bundles in Rule Output {#apple.propagate_embedded_extra_outputs}
Deprecated: Please see the Output Groups section.
Some Apple bundles include other bundles within them (for example, an application extension inside an iOS application). When you build a top-level application target and ask for extra outputs such as linkmaps or dSYM bundles, Bazel typically only produces the extra outputs for the top-level application but not for the embedded extension.
In order to produce those extra outputs for all embedded bundles as well, you
can pass --define=apple.propagate_embedded_extra_outputs=(yes|true|1)
to
bazel build
.
bazel build --define=apple.propagate_embedded_extra_outputs=yes //your/target
Disable SwiftSupport
in ipas
The SwiftSupport directory in a final ipa is only necessary if you're shipping
the build to Apple. If you want to disable bundling SwiftSupport in your ipa for
other device or enterprise builds, you can pass
--define=apple.package_swift_support=no
to bazel build
Codesign Bundles for the Simulator {#apple.codesign_simulator_bundles}
The simulators are far more lax about a lot of things compared to working on real devices. One of these areas is the codesigning of bundles (applications, extensions, etc.). As of Xcode 9.3.x on macOS High Sierra, the Simulator will run any bundle just fine as long as its Frameworks are signed (if it has any), the main bundle does not appear to need to be signed.
However, if the binary makes use of entitlement-protected APIs, they may not work. The entitlements for a simulator build are added as a Mach-O segment, so they are still provided; but for some entitlements (at a minimum, Shared App Groups), the simulator appears to also require the bundle be signed for the APIs to work.
By default, the rules will do what Xcode would otherwise do and will sign the
main bundle (with an adhoc signature) when targeting the Simulator. However,
this --define
can be used to opt out of this if you are more concerned with
build speed vs. potential correctness.
Remember, at any time, Apple could do a macOS point release and/or an Xcode release that changes this and opting out of could mean your binary doesn't run under the simulator.
The rules support direct control over this signing via
--define=apple.codesign_simulator_bundles=(yes|true|1|no|false|0)
.
Disable the signing of simulator bundles:
bazel build --define=apple.codesign_simulator_bundles=no //your/target
One exception is XCTest bundles, those do need to be signed for the simulators
to load them. The above --define
does not change the behavior around signing
of these bundles as a result.
Localization Handling
The Apple bundling rules have two flags for limiting which *.lproj directories
are copied as part of your build. Without specifying either the
apple.locales_to_include
flag or the apple.trim_lproj_locales
flag, all
locales are copied.
Locale names are explicitly matched; for example pt
may not be sufficient as
pt_BR
or pt_PT
is likely the name of the lproj folder. Note that
Base.lproj
is always included if it exists.
Explicitly Listing Locales
Use --define "apple.locales_to_include=foo,bar,bam"
where foo,bar,bam
are
the exact names of the locales to be included.
This can be used to improve compile/debug/test cycles because most developers only work/test in one language.
Automatically Trimming Locales
Use --define "apple.trim_lproj_locales=(yes|true|1)"
to strip any .lproj
folders that don't have a matching .lproj
folder in the base of the resource
folder(e.g. bundles from Frameworks that are localized).
If a product is pulling in some other component(s) that support more localizations than the product does, this can be used to strip away those extra localizations thereby shrinking the final product sent to the end users.
Info.plist Handling
Merging
The infoplists
attribute on the Apple rules takes a list of files that will be
merged. This allows developers to provide fragments of the final Info.plist and
use select()
statements to pull in different bits based on different
configuration settings. For example, a select()
on some config_setting
could
allow the rule to pull in a file with a different CFBundleDisplayName
pair for
the TestFlight build.
The merging though is done only at the root level of these plists. If two files
being merged have the same key, their values must match. That means if two files
both have a value for something (e.g., CFBundleDisplayName
), then as long as
the values are the same, the build will succeed; but if they have different
values, then the build will fail. If a key's value is an array or a dictionary,
those values won't be merged and must match to avoid the failure.
Variable Substitution
As the files are merged, the values are recursively checked for variable
references and substitutions are made. A reference is made using ${NAME}
or
$(NAME)
notation. Similar to Xcode, the variable reference can get the
rfc1034identifier
qualifier (i.e., ${NAME:rfc1034identifier}
); this will
transform the value such that any characters besides 0-9A-Za-z.
will be
replaced with the -
character (i.e., Foo Bar
will become Foo-Bar
).
Variables Supported | |
---|---|
BUNDLE_NAME |
This is the same value as |
DEVELOPMENT_LANGUAGE |
This is currently hardcoded to |
EXECUTABLE_NAME |
The value of the rule's |
PRODUCT_BUNDLE_IDENTIFIER |
The value of the rule's |
PRODUCT_NAME |
If the rule supports a |
TARGET_NAME |
This is an alias for the same value as |
Variables Explicitly Not Supported | |
---|---|
PRODUCT_MODULE_NAME |
This is a variable commonly used in extensions to specify the
|
Tests
Runfiles location for test data
Most likely, test related resources will be bundled within the .xctest
bundle
itself, but there may be use cases where the test resources are not wanted in
the bundle, but instead are needed in the Bazel runfiles location. These
resources should be placed into the data
attribute of the
<platform>_unit_test
or <platform>_ui_test
targets.
Within the tests you can retrieve the runfiles resources through the
TEST_SRCDIR
environment variable following this template:
$TEST_SRCDIR/<workspace_name>/<workspace_relative_path_to_resource>
Take for example this BUILD
file:
# my/package/BUILD
...
ios_unit_test(
name = "MyTest",
...
data = ["my_test_resource.txt"],
...
)
To read this file from, for example, a Swift test, you'd get the path with something similar to:
// MyTest.swift
...
if let runfilesPath = ProcessInfo.processInfo.environment["TEST_SRCDIR"] {
let resourceFullPath = "\(runfilesPath)/\(workspaceName)/\(resourcePath)"
}
...