// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.rules.objc; import static com.google.devtools.build.lib.packages.Attribute.ANY_RULE; import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; import static com.google.devtools.build.lib.packages.BuildType.LABEL; import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; import static com.google.devtools.build.lib.packages.ImplicitOutputsFunction.fromTemplates; import static com.google.devtools.build.lib.syntax.Type.BOOLEAN; import static com.google.devtools.build.lib.syntax.Type.STRING; import static com.google.devtools.build.lib.syntax.Type.STRING_LIST; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.actions.ExecutionRequirements; import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.RuleDefinition; import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; import com.google.devtools.build.lib.analysis.Runfiles; import com.google.devtools.build.lib.analysis.RunfilesProvider; import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.config.BuildConfiguration; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Attribute.LateBoundLabel; import com.google.devtools.build.lib.packages.AttributeMap; import com.google.devtools.build.lib.packages.BuildType; import com.google.devtools.build.lib.packages.ImplicitOutputsFunction.SafeImplicitOutputsFunction; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; import com.google.devtools.build.lib.rules.apple.AppleConfiguration; import com.google.devtools.build.lib.rules.apple.ApplePlatform; import com.google.devtools.build.lib.rules.apple.ApplePlatform.PlatformType; import com.google.devtools.build.lib.rules.apple.AppleToolchain; import com.google.devtools.build.lib.rules.apple.AppleToolchain.RequiresXcodeConfigRule; import com.google.devtools.build.lib.rules.cpp.CcToolchain; import com.google.devtools.build.lib.rules.cpp.CppConfiguration; import com.google.devtools.build.lib.rules.cpp.CppModuleMap.UmbrellaHeaderStrategy; import com.google.devtools.build.lib.rules.proto.ProtoSourceFileBlacklist; import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.util.FileTypeSet; /** * Shared rule classes and associated utility code for Objective-C rules. */ public class ObjcRuleClasses { /** * Name of the attribute used for implicit dependency on the libtool wrapper. */ public static final String LIBTOOL_ATTRIBUTE = "$libtool"; /** Name of the attribute used for implicit dependency on the header_scanner tool. */ public static final String HEADER_SCANNER_ATTRIBUTE = ":header_scanner"; /** Name of attribute used for implicit dependency on the apple SDKs. */ public static final String APPLE_SDK_ATTRIBUTE = ":apple_sdk"; static final String CLANG = "clang"; static final String CLANG_PLUSPLUS = "clang++"; static final String DSYMUTIL = "dsymutil"; static final String LIPO = "lipo"; static final String STRIP = "strip"; private ObjcRuleClasses() { throw new UnsupportedOperationException("static-only"); } /** * Creates and returns an {@link IntermediateArtifacts} object, using the given rule context * for fetching current-rule attributes, and using the given build configuration to determine * the appropriate output directory in which to root artifacts. */ public static IntermediateArtifacts intermediateArtifacts(RuleContext ruleContext, BuildConfiguration buildConfiguration) { return new IntermediateArtifacts(ruleContext, /*archiveFileNameSuffix*/ "", /*outputPrefix*/ "", buildConfiguration); } /** * Creates and returns an {@link IntermediateArtifacts} object, using the given rule context * for fetching current-rule attributes and the current rule's configuration for determining the * appropriate output output directory in which to root artifacts. */ public static IntermediateArtifacts intermediateArtifacts(RuleContext ruleContext) { return new IntermediateArtifacts(ruleContext, /*archiveFileNameSuffix=*/ ""); } /** * Attribute name for a dummy target in a child configuration. */ static final String CHILD_CONFIG_ATTR = ":child_configuration_dummy"; /** * Returns a {@link IntermediateArtifacts} to be used to compile and link the ObjC source files * generated by J2ObjC. */ static IntermediateArtifacts j2objcIntermediateArtifacts(RuleContext ruleContext) { // We need to append "_j2objc" to the name of the generated archive file to distinguish it from // the C/C++ archive file created by proto_library targets with attribute cc_api_version // specified. // Generate an umbrella header for the module map. The headers declared in module maps are // compiled using the #import directives which are incompatible with J2ObjC segmented headers. // We need to #iclude all the headers in an umbrella header and then declare the umbrella header // in the module map. return new IntermediateArtifacts( ruleContext, /*archiveFileNameSuffix=*/"_j2objc", UmbrellaHeaderStrategy.GENERATE); } public static Artifact artifactByAppendingToBaseName(RuleContext context, String suffix) { return context.getPackageRelativeArtifact( context.getLabel().getName() + suffix, context.getBinOrGenfilesDirectory()); } public static ObjcConfiguration objcConfiguration(RuleContext ruleContext) { return ruleContext.getFragment(ObjcConfiguration.class); } /** * Returns true if the source file can be instrumented for coverage. */ public static boolean isInstrumentable(Artifact sourceArtifact) { return !ASSEMBLY_SOURCES.matches(sourceArtifact.getFilename()); } /** * Label of a filegroup that contains all crosstool and grte files for all configurations, * as specified on the command-line. * *
Since this is the loading-phase default for the :cc_toolchain attribute of rules
* using the crosstool, it must contain in its transitive closure the computer value
* of that attribute under the default configuration.
*/
public static final String CROSSTOOL_LABEL = "//tools/defaults:crosstool";
/**
* Late-bound attribute giving the CcToolchain for CROSSTOOL_LABEL.
*
* TODO(cpeyser): Use AppleCcToolchain instead of CcToolchain once released.
*/
public static final LateBoundLabel
Note that for the generated Xcode project, directory paths specified using "-I" flags in
copts are parsed out, prepended with "$(WORKSPACE_ROOT)/" if they are relative paths, and
added to the header search paths for the associated Xcode target.
*/
.add(attr("copts", STRING_LIST))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_opts_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Attributes for {@code objc_*} rules that can link in SDK frameworks.
*/
public static class SdkFrameworksDependerRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) {
return builder
/*
Names of SDK frameworks to link with.
For instance, "XCTest" or "Cocoa". "UIKit" and "Foundation" are always
included and do not mean anything if you include them.
When linking a library, only those frameworks named in that library's
sdk_frameworks attribute are linked in. When linking a binary, all
SDK frameworks named in that binary's transitive dependency graph are
used.
*/
.add(attr("sdk_frameworks", STRING_LIST))
/*
Names of SDK frameworks to weakly link with. For instance,
"MediaAccessibility".
In difference to regularly linked SDK frameworks, symbols
from weakly linked frameworks do not cause an error if they
are not present at runtime.
*/
.add(attr("weak_sdk_frameworks", STRING_LIST))
/*
Names of SDK .dylib libraries to link with. For instance, "libz" or
"libarchive".
"libc++" is included automatically if the binary has any C++ or
Objective-C++ sources in its dependency tree. When linking a binary,
all libraries named in that binary's transitive dependency graph are
used.
*/
.add(attr("sdk_dylibs", STRING_LIST))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_sdk_frameworks_depender_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Iff a file matches this type, it is considered to use C++.
*/
static final FileType CPP_SOURCES = FileType.of(".cc", ".cpp", ".mm", ".cxx", ".C");
static final FileType NON_CPP_SOURCES = FileType.of(".m", ".c");
static final FileType ASSEMBLY_SOURCES = FileType.of(".s", ".S", ".asm");
static final FileType OBJECT_FILE_SOURCES = FileType.of(".o");
/**
* Header files, which are not compiled directly, but may be included/imported from source files.
*/
static final FileType HEADERS = FileType.of(".h", ".inc");
/**
* Files allowed in the srcs attribute. This includes private headers.
*/
static final FileTypeSet SRCS_TYPE =
FileTypeSet.of(
NON_CPP_SOURCES,
CPP_SOURCES,
ASSEMBLY_SOURCES,
OBJECT_FILE_SOURCES,
HEADERS);
/** Files that should actually be compiled. */
static final FileTypeSet COMPILABLE_SRCS_TYPE =
FileTypeSet.of(NON_CPP_SOURCES, CPP_SOURCES, ASSEMBLY_SOURCES);
/**
* Files that are already compiled.
*/
static final FileTypeSet PRECOMPILED_SRCS_TYPE = FileTypeSet.of(OBJECT_FILE_SOURCES);
static final FileTypeSet NON_ARC_SRCS_TYPE = FileTypeSet.of(FileType.of(".m", ".mm"));
static final FileTypeSet PLIST_TYPE = FileTypeSet.of(FileType.of(".plist"));
static final FileType STORYBOARD_TYPE = FileType.of(".storyboard");
static final FileType XIB_TYPE = FileType.of(".xib");
// TODO(bazel-team): Restrict this to actual header files only.
static final FileTypeSet HDRS_TYPE = FileTypeSet.ANY_FILE;
static final FileTypeSet ENTITLEMENTS_TYPE =
FileTypeSet.of(FileType.of(".entitlements", ".plist"));
static final FileTypeSet STRINGS_TYPE = FileTypeSet.of(FileType.of(".strings"));
/**
* Coverage note files which contain information to reconstruct the basic block graphs and assign
* source line numbers to blocks.
*/
static final FileType COVERAGE_NOTES = FileType.of(".gcno");
/**
* Common attributes for {@code objc_*} rules that allow the definition of resources such as
* storyboards. These resources are used during compilation of the declaring rule as well as when
* bundling a dependent bundle (application, extension, etc.).
*/
public static class ResourcesRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
Files which are plists of strings, often localizable.
These files are converted to binary plists (if they are not already)
and placed in the bundle root of the final package. If this file's
immediate containing directory is named *.lproj (e.g. en.lproj,
Base.lproj), it will be placed under a directory of that name in the
final bundle. This allows for localizable strings.
*/
.add(attr("strings", LABEL_LIST)
.allowedFileTypes(STRINGS_TYPE)
.direct_compile_time_input())
/*
Files which are .xib resources, possibly localizable.
These files are compiled to .nib files and placed the bundle root of
the final package. If this file's immediate containing directory is
named *.lproj (e.g. en.lproj, Base.lproj), it will be placed under a
directory of that name in the final bundle. This allows for
localizable UI.
*/
.add(attr("xibs", LABEL_LIST)
.direct_compile_time_input()
.allowedFileTypes(XIB_TYPE))
/*
Files which are .storyboard resources, possibly localizable.
These files are compiled to .storyboardc directories, which are
placed in the bundle root of the final package. If the storyboards's
immediate containing directory is named *.lproj (e.g. en.lproj,
Base.lproj), it will be placed under a directory of that name in the
final bundle. This allows for localizable UI.
*/
.add(attr("storyboards", LABEL_LIST)
.allowedFileTypes(STORYBOARD_TYPE))
/*
Files to include in the final application bundle.
They are not processed or compiled in any way besides the processing
done by the rules that actually generate them. These files are placed
in the root of the bundle (e.g. Payload/foo.app/...) in most cases.
However, if they appear to be localized (i.e. are contained in a
directory called *.lproj), they will be placed in a directory of the
same name in the app bundle.
*/
.add(attr("resources", LABEL_LIST).legacyAllowAnyFileType().direct_compile_time_input())
/*
Files to include in the final application bundle.
They are not processed or compiled in any way besides the processing
done by the rules that actually generate them. In differences to
Note that in the generated XCode project file, all files in the top directory of
the specified files will be included in the Xcode-generated app bundle. So
specifying
The paths are interpreted relative to the package directory, and the
genfiles and bin roots (e.g.
Unlike COPTS, these flags are added for this rule
and every rule that depends on it. (Note: not the rules it depends upon!) Be
very careful, since this may have far-reaching effects. When in doubt, add
"-iquote" flags to COPTS instead.
*/
.add(attr("includes", Type.STRING_LIST))
/*
List of A list of dynamic library targets to be linked against in this rule and included
in the final bundle. Libraries which are transitive dependencies of any such dylibs will
not be statically linked in this target (even if they are otherwise
transitively depended on via the Blaze will perform variable substitution on the plist file for the following values
(if they are strings in the top-level The key in Blaze will perform variable substitution on the plist file for the following values
(if they are strings in the top-level The key in By default this is set to
The following variables are substituted:
Bazel does not currently support adding entitlements to simulator builds. This
means that if you rely on behavior which must be specified in entitlements (like App
Groups) it will only work on a device. You can work around this by inlining the
entitlements into your binary. e.g.
If the application icon is not in an asset catalog, do not use this
attribute. Instead, add a CFBundleIcons entry to the Info.plist file.
*/
.add(attr(APP_ICON_ATTR, STRING))
/*
The name of the launch image.
The icon should be in one of the asset catalogs of this target or
a (transitive) dependency. In a new project, this is initialized
to "LaunchImage" by Xcode.
If the launch image is not in an asset catalog, do not use this
attribute. Instead, add an appropriately-named image resource to the
bundle.
*/
.add(attr(LAUNCH_IMAGE_ATTR, STRING))
/*
The location of the launch storyboard (.xib or .storyboard).
The provided storyboard will be compiled to the appropriate format
(.nib or .storyboardc respectively) and placed in the root of the
final package. If the storyboard's immediate containing directory is
named *.lproj (e.g. en.lproj, Base.lproj), it will be placed under a
directory of that name in the final bundle. This allows for
localizable UI.
The generated storyboard is registered in the final bundle's
*/
.add(
attr(LAUNCH_STORYBOARD_ATTR, LABEL)
.direct_compile_time_input()
.allowedFileTypes(FileTypeSet.of(XIB_TYPE, STORYBOARD_TYPE)))
/*
The bundle ID (reverse-DNS path followed by app name) of the binary.
If specified, it will override the bundle ID specified in the associated plist file. If
no bundle ID is specified on either this attribute or in the plist file, a junk value
will be used.
*/
.add(
attr(BUNDLE_ID_ATTR, STRING)
.value(
new Attribute.ComputedDefault() {
@Override
public Object getDefault(AttributeMap rule) {
// For tests and similar, we don't want to force people to explicitly
// specify throw-away data.
return "example." + rule.getName();
}
}))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_release_bundling_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(BundlingRule.class, ReleaseBundlingToolsRule.class)
.build();
}
}
/**
* Common attributes for rules that require tools to create a bundle meant for
* release (e.g. application or extension). Specifically, for rules which use the
* {@link ReleaseBundlingSupport} helper class.
*/
public static class ReleaseBundlingToolsRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
.add(
attr("$bundlemerge", LABEL)
.cfg(HOST)
.exec()
.value(env.getToolsLabel("//tools/objc:bundlemerge")))
.add(
attr("$environment_plist", LABEL)
.cfg(HOST)
.exec()
.value(env.getToolsLabel("//tools/objc:environment_plist")))
.add(
attr("$swiftstdlibtoolwrapper", LABEL)
.cfg(HOST)
.exec()
.value(env.getToolsLabel("//tools/objc:swiftstdlibtoolwrapper")))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$release_bundling_tools_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that create a signed IPA.
*/
public static class IpaRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
A tool that edits this target's IPA output after it is assembled but before it is
(optionally) signed.
The tool is invoked with a single positional argument which represents the path to a
directory containing the unzipped contents of the IPA. The only entry in this directory
will be the
The tool's execution must be hermetic given these inputs to ensure that its result can be
safely cached.
*/
.add(
attr("ipa_post_processor", LABEL)
.allowedRuleClasses(ANY_RULE)
.allowedFileTypes(FileTypeSet.ANY_FILE)
.exec())
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_ipa_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that use the iOS simulator.
*/
public static class SimulatorRule implements RuleDefinition {
static final String STD_REDIRECT_DYLIB_ATTR = "$std_redirect_dylib";
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
// Needed to run the binary in the simulator.
.add(attr(STD_REDIRECT_DYLIB_ATTR, LABEL).cfg(HOST).exec()
.value(env.getToolsLabel("//tools/objc:StdRedirect.dylib")))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_simulator_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that need to call xcrun.
*/
public static class XcrunRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
.add(attr("$xcrunwrapper", LABEL).cfg(HOST).exec()
.value(env.getToolsLabel("//tools/objc:xcrunwrapper")))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_xcrun_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(RequiresXcodeConfigRule.class)
.build();
}
}
/**
* Attributes for {@code apple_watch*} rules that creates a watch extension bundle.
*/
public static class WatchExtensionBundleRule implements RuleDefinition {
static final String WATCH_EXT_BUNDLE_ID_ATTR = "ext_bundle_id";
static final String WATCH_EXT_DEFAULT_PROVISIONING_PROFILE_ATTR =
":default_ext_provisioning_profile";
static final String WATCH_EXT_ENTITLEMENTS_ATTR = "ext_entitlements";
static final String WATCH_EXT_PROVISIONING_PROFILE_ATTR = "ext_provisioning_profile";
static final String WATCH_EXT_INFOPLISTS_ATTR = "ext_infoplists";
static final String WATCH_EXT_RESOURCES_ATTR = "ext_resources";
static final String WATCH_EXT_STRUCTURED_RESOURCES_ATTR = "ext_structured_resources";
static final String WATCH_EXT_STRINGS_ATTR = "ext_strings";
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
The bundle ID (reverse-DNS path followed by app name) of the watch extension binary.
If specified, it will override the bundle ID specified in the associated plist file. If
no bundle ID is specified on either this attribute or in the plist file, a junk value
will be used.
*/
.add(
attr(WATCH_EXT_BUNDLE_ID_ATTR, STRING)
.value(
new Attribute.ComputedDefault() {
@Override
public Object getDefault(AttributeMap rule) {
// For tests and similar, we don't want to force people to explicitly
// specify throw-away data.
return "example.ext." + rule.getName();
}
}))
/*
The entitlements file required for device builds of watch extension.
See
the apple documentation
for more information. If absent, the default entitlements from the
provisioning profile will be used.
The following variables are substituted as per
their definitions in Apple's documentation:
$(AppIdentifierPrefix) and $(CFBundleIdentifier).
*/
.add(attr(WATCH_EXT_ENTITLEMENTS_ATTR, LABEL).allowedFileTypes(ENTITLEMENTS_TYPE))
/*
Infoplist files to be merged. The merged output corresponds to appname-Info.plist
in Xcode projects. Duplicate keys between infoplist files will cause an error if
and only if the values conflict. If both Note that in the generated XCode project file, all files in the top directory of
the specified files will be included in the Xcode-generated app bundle. So
specifying
If the application icon is not in an asset catalog, do not use this
attribute. Instead, add a CFBundleIcons entry to the Info.plist file.
*/
.add(attr(WATCH_APP_ICON_ATTR, STRING))
/*
The entitlements file required for device builds of watch application.
See
the apple documentation
for more information. If absent, the default entitlements from the
provisioning profile will be used.
The following variables are substituted as per
their definitions in Apple's documentation:
$(AppIdentifierPrefix) and $(CFBundleIdentifier).
*/
.add(attr(WATCH_APP_ENTITLEMENTS_ATTR, LABEL).allowedFileTypes(ENTITLEMENTS_TYPE))
/*
Files that comprise the asset catalogs of the final linked binary.
Each file must have a containing directory named *.xcassets. This
containing directory becomes the root of one of the asset catalogs
linked with any binary that depends directly or indirectly on this
target.
*/
.add(
attr(WATCH_APP_ASSET_CATALOGS_ATTR, LABEL_LIST)
.legacyAllowAnyFileType()
.direct_compile_time_input())
/*
The bundle ID (reverse-DNS path followed by app name) of the watch application binary.
If specified, it will override the bundle ID specified in the associated plist file. If
no bundle ID is specified on either this attribute or in the plist file, a junk value
will be used.
*/
.add(
attr(WATCH_APP_BUNDLE_ID_ATTR, STRING)
.value(
new Attribute.ComputedDefault(WATCH_APP_NAME_ATTR) {
@Override
public Object getDefault(AttributeMap rule) {
// For tests and similar, we don't want to force people to explicitly
// specify throw-away data.
return "example.app." + rule.get(WATCH_APP_NAME_ATTR, STRING);
}
}))
/*
Infoplist files to be merged. The merged output corresponds to appname-Info.plist
in Xcode projects. Duplicate keys between infoplist files will cause an error if
and only if the values conflict. If both Note that in the generated XCode project file, all files in the top directory of
the specified files will be included in the Xcode-generated app bundle. So
specifying resources
these files are placed in the bundle root in
the same structure passed to this argument, so
["res/foo.png"]
will end up in
Payload/foo.app/res/foo.png
.
["res/foo.png"]
will lead to the inclusion of all files in
directory res
.
*/
.add(attr("structured_resources", LABEL_LIST)
.legacyAllowAnyFileType()
.direct_compile_time_input())
/*
Files that comprise the data models of the final linked binary.
Each file must have a containing directory named *.xcdatamodel, which
is usually contained by another *.xcdatamodeld (note the added d)
directory.
*/
.add(attr("datamodels", LABEL_LIST).legacyAllowAnyFileType()
.direct_compile_time_input())
/*
Files that comprise the asset catalogs of the final linked binary.
Each file must have a containing directory named *.xcassets. This
containing directory becomes the root of one of the asset catalogs
linked with any binary that depends directly or indirectly on this
target.
*/
.add(attr("asset_catalogs", LABEL_LIST).legacyAllowAnyFileType()
.direct_compile_time_input())
/*
The list of bundle targets that this target requires to be included
in the final bundle.
*/
.add(attr("bundles", LABEL_LIST)
.direct_compile_time_input()
.allowedRuleClasses("objc_bundle", "objc_bundle_library")
.allowedFileTypes())
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_resources_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(ResourceToolsRule.class, XcrunRule.class)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that process resources (by defining or consuming
* them).
*/
public static class ResourceToolsRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
.add(attr("$plmerge", LABEL).cfg(HOST).exec()
.value(env.getToolsLabel("//tools/objc:plmerge")))
.add(attr("$actoolwrapper", LABEL).cfg(HOST).exec()
.value(env.getToolsLabel("//tools/objc:actoolwrapper")))
.add(attr("$ibtoolwrapper", LABEL).cfg(HOST).exec()
.value(env.getToolsLabel("//tools/objc:ibtoolwrapper")))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_resource_tools_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that depend on a crosstool.
*/
public static class CrosstoolRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
.add(attr(CcToolchain.CC_TOOLCHAIN_DEFAULT_ATTRIBUTE_NAME, LABEL).value(APPLE_TOOLCHAIN))
.add(
attr(":lipo_context_collector", LABEL)
.value(NULL_LIPO_CONTEXT_COLLECTOR)
.skipPrereqValidatorCheck())
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_crosstool_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that can be input to compilation (i.e. can be
* dependencies of other compiling rules).
*/
public static class CompileDependencyRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
The list of C, C++, Objective-C, and Objective-C++ files that are
included as headers by source files in this rule or by users of this
library. These will be compiled separately from the source if modules
are enabled.
*/
.add(attr("hdrs", LABEL_LIST)
.direct_compile_time_input()
.allowedFileTypes(HDRS_TYPE))
/*
The list of C, C++, Objective-C, and Objective-C++ files that are
included as headers by source files in this rule or by users of this
library. Unlike hdrs, these will not be compiled separately from the
sources.
*/
.add(attr("textual_hdrs", LABEL_LIST)
.direct_compile_time_input()
.allowedFileTypes(HDRS_TYPE))
/*
List of #include/#import
search paths to add to this target
and all depending targets.
This is to support third party and open-sourced libraries that do not
specify the entire workspace path in their
#import/#include
statements.
blaze-genfiles/pkg/includedir
and blaze-out/pkg/includedir
) are included in addition to the
actual client root.
#include/#import
search paths to add to this target
and all depending targets, where each path is relative to
$(SDKROOT)/usr/include
.
*/
.add(attr("sdk_includes", Type.STRING_LIST))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_compile_dependency_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(ResourcesRule.class, SdkFrameworksDependerRule.class)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that contain compilable content.
*/
public static class CompilingRule implements RuleDefinition {
/**
* Rule class names for cc rules which are allowed as targets of the 'deps' attribute of this
* rule.
*/
static final ImmutableSet-D
flags to pass to the compiler. They should be in
the form KEY=VALUE
or simply KEY
and are
passed not only to the compiler for this target (as copts
are) but also to all objc_
dependers of this target.
Subject to "Make variable" substitution and
Bourne shell tokenization.
*/
.add(attr("defines", STRING_LIST))
/*
Enables clang module support (via -fmodules).
Setting this to 1 will allow you to @import system headers and other targets:
@import UIKit;
@import path_to_package_target;
*/
.add(attr("enable_modules", BOOLEAN))
/*
A custom Clang module map for this target. Use of a custom module map is discouraged. Most
users should use module maps generated by Bazel.
If specified, Bazel will not generate a module map for this target, but will pass the
provided module map to the compiler.
*/
.add(
attr("module_map", LABEL)
.allowedFileTypes(FileType.of(".modulemap")))
/* Provides the label for header_scanner tool that is used to scan inclusions for ObjC
sources and provide a list of required headers via a .header_list file.
Either points to a label for a binary which can be executed for header scanning or an
empty filegroup to indicate that the tool is unavailable. Due to the possibility of this
being an empty filegroup and executable prerequisites validating that they contain at
least one artifact this attribute cannot be #exec(). */
.add(
attr(HEADER_SCANNER_ATTRIBUTE, LABEL)
.cfg(HOST)
.value(
new LateBoundLabelsrcs
and non_arc_srcs
, even if some contain no
symbols referenced by the binary.
This is useful if your code isn't explicitly called by code in
the binary, e.g., if your code registers to receive some callback
provided by some service.
*/
.add(attr("alwayslink", BOOLEAN))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_alwayslink_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(CompileDependencyRule.class)
.build();
}
}
/**
* Protocol buffer related implicit attributes.
*/
static final String PROTO_COMPILER_ATTR = "$googlemac_proto_compiler";
static final String PROTO_COMPILER_SUPPORT_ATTR = "$googlemac_proto_compiler_support";
static final String PROTO_LIB_ATTR = "$lib_protobuf";
static final String PROTOBUF_WELL_KNOWN_TYPES = "$protobuf_well_known_types";
/**
* Template for the fat binary output (using Apple's "lipo" tool to combine binaries of multiple
* architectures).
*/
static final SafeImplicitOutputsFunction LIPOBIN_OUTPUT = fromTemplates("%{name}_lipobin");
/**
* Common attributes for {@code objc_*} rules that link sources and dependencies.
*/
public static class LinkingRule implements RuleDefinition {
private final ObjcProtoAspect objcProtoAspect;
public LinkingRule(ObjcProtoAspect objcProtoAspect) {
this.objcProtoAspect = objcProtoAspect;
}
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
.add(
attr("$j2objc_dead_code_pruner", LABEL)
.allowedFileTypes(FileType.of(".py"))
.cfg(HOST)
.exec()
.singleArtifact()
.value(env.getToolsLabel("//tools/objc:j2objc_dead_code_pruner")))
.add(attr("$dummy_lib", LABEL).value(env.getToolsLabel("//tools/objc:dummy_lib")))
.add(
attr(PROTO_COMPILER_ATTR, LABEL)
.allowedFileTypes(FileType.of(".sh"))
.cfg(HOST)
.singleArtifact()
.value(env.getToolsLabel("//tools/objc:protobuf_compiler_wrapper")))
.add(
attr(PROTO_COMPILER_SUPPORT_ATTR, LABEL)
.legacyAllowAnyFileType()
.cfg(HOST)
.value(env.getToolsLabel("//tools/objc:protobuf_compiler_support")))
.add(
ProtoSourceFileBlacklist.blacklistFilegroupAttribute(
PROTOBUF_WELL_KNOWN_TYPES,
ImmutableList.of(env.getToolsLabel("//tools/objc:protobuf_well_known_types"))))
.override(builder.copy("deps").aspect(objcProtoAspect))
/*
Extra flags to pass to the linker.
*/
.add(attr("linkopts", STRING_LIST))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$objc_linking_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(CompilingRule.class)
.build();
}
}
/**
* Common attributes for apple rules that produce outputs for a given platform type (such as ios
* or watchos).
*/
public static class PlatformRule implements RuleDefinition {
/**
* Attribute name for apple platform type (e.g. ios or watchos).
*/
static final String PLATFORM_TYPE_ATTR_NAME = "platform_type";
/**
* Attribute name for the minimum OS version (e.g. "7.3").
*/
static final String MINIMUM_OS_VERSION = "minimum_os_version";
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
The type of platform for which to create artifacts in this rule.
This dictates which Apple platform SDK is used for compilation/linking and which flag is
used to determine the architectures for which to build. For example, if ios
is selected, then the output binaries/libraries will be created combining all
architectures specified in --ios_multi_cpus
.
Options are:
*/
.add(
attr(PLATFORM_TYPE_ATTR_NAME, STRING)
.mandatory()
.nonconfigurable("Determines the configuration transition on deps"))
/*
The minimum OS version that this target and its dependencies should be built for.
This should be a dotted version string such as "7.3".
*/
.add(
attr(MINIMUM_OS_VERSION, STRING)
.nonconfigurable("Determines the configuration transition on deps"))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$apple_platform_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for apple rules that build multi-architecture outputs for a given platform
* type (such as ios or watchos).
*/
public static class MultiArchPlatformRule implements RuleDefinition {
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
MultiArchSplitTransitionProvider splitTransitionProvider =
new MultiArchSplitTransitionProvider();
return builder
// This is currently a hack to obtain all child configurations regardless of the attribute
// values of this rule -- this rule does not currently use the actual info provided by
// this attribute.
.add(
attr(CHILD_CONFIG_ATTR, LABEL)
.cfg(splitTransitionProvider)
.value(ObjcRuleClasses.APPLE_TOOLCHAIN))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$apple_multiarch_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(PlatformRule.class, CrosstoolRule.class)
.build();
}
}
/**
* Common attributes for apple rules that can depend on one or more dynamic libraries.
*/
public static class DylibDependingRule implements RuleDefinition {
private final ObjcProtoAspect objcProtoAspect;
public DylibDependingRule(ObjcProtoAspect objcProtoAspect) {
this.objcProtoAspect = objcProtoAspect;
}
/**
* Attribute name for dylib dependencies.
*/
static final String DYLIBS_ATTR_NAME = "dylibs";
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
ios
: architectures gathered from --ios_multi_cpus
.
macos
: architectures gathered from --macos_cpus
.
tvos
: architectures gathered from --tvos_cpus
.
watchos
: architectures gathered from --watchos_cpus
.
deps
attribute) to avoid duplicate symbols.
*/
.add(attr(DYLIBS_ATTR_NAME, LABEL_LIST)
.direct_compile_time_input()
.mandatoryProviders(ImmutableList.of(
SkylarkProviderIdentifier.forKey(
AppleDynamicFrameworkProvider.SKYLARK_CONSTRUCTOR.getKey())))
.allowedFileTypes()
.aspect(objcProtoAspect))
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$apple_dylib_depending_rule")
.type(RuleClassType.ABSTRACT)
.build();
}
}
/**
* Common attributes for {@code objc_*} rules that create a bundle. Specifically, for rules
* which use the {@link Bundling} helper class.
*/
public static class BundlingRule implements RuleDefinition {
static final String INFOPLIST_ATTR = "infoplist";
static final String FAMILIES_ATTR = "families";
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
The infoplist file. This corresponds to appname-Info.plist in Xcode projects.
dict
of the plist):
${EXECUTABLE_NAME}
: The name of the executable generated and included
in the bundle by blaze, which can be used as the value for
CFBundleExecutable
within the plist.
${BUNDLE_NAME}
: This target's name and bundle suffix (.bundle or .app)
in the formname
.suffix
.
${PRODUCT_NAME}
: This target's name.
${}
may be suffixed with :rfc1034identifier
(for
example ${PRODUCT_NAME::rfc1034identifier}
) in which case Blaze will
replicate Xcode's behavior and replace non-RFC1034-compliant characters with
-
.infoplist
and
infoplists
are specified, the files defined in both attributes will be used.
dict
of the plist):
${EXECUTABLE_NAME}
: The name of the executable generated and included
in the bundle by blaze, which can be used as the value for
CFBundleExecutable
within the plist.
${BUNDLE_NAME}
: This target's name and bundle suffix (.bundle or .app)
in the formname
.suffix
.
${PRODUCT_NAME}
: This target's name.
${}
may be suffixed with :rfc1034identifier
(for
example ${PRODUCT_NAME::rfc1034identifier}
) in which case Blaze will
replicate Xcode's behavior and replace non-RFC1034-compliant characters with
-
.TARGETED_DEVICE_FAMILY
build setting
in Xcode project files. It is a list of one or more of the strings
"iphone"
and "ipad"
.
"iphone"
, if explicitly specified may not be
empty.$(CFBundleIdentifier)
with the
bundle id and $(AppIdentifierPrefix)
with the value of the
ApplicationIdentifierPrefix
key from this target's provisioning profile (or
the default provisioning profile, if none is specified).
*/
.add(attr(ENTITLEMENTS_ATTR, LABEL).allowedFileTypes(ENTITLEMENTS_TYPE))
.add(
attr(EXTRA_ENTITLEMENTS_ATTR, LABEL)
.singleArtifact()
.cfg(HOST)
.value(
new LateBoundLabel
#if TARGET_OS_SIMULATOR
__asm(".section __TEXT,__entitlements");
__asm(".ascii \""
"<?xml version=\\\"1.0\\\" encoding=\\\"UTF-8\\\"?>\n"
"<!DOCTYPE plist PUBLIC \\\"-//Apple//DTD PLIST 1.0//EN\\\" "
"\\\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\\\">"
"<plist version=\\\"1.0\\\">"
"<dict>"
"<key>com.apple.security.application-groups</key>"
"<array>"
"<string>group.com.your.company</string>"
"</array>"
"</dict>"
"</plist>"
"\"
#endif
Info.plist
under the key
UILaunchStoryboardName
.
Payload
root directory of the IPA (for an ios_application) or
the PlugIns
root directory (for an ios_extension). Any changes made by the
tool must be made in this directory, whose contents will be (optionally) signed and then
zipped up as the final IPA after the tool terminates.
infoplist
and
infoplists
are specified, the files defined in both attributes will be used.
Blaze will perform variable substitution on the plist files for the following values:
*/
.add(attr(WATCH_EXT_INFOPLISTS_ATTR, BuildType.LABEL_LIST).allowedFileTypes(PLIST_TYPE))
/*
The provisioning profile (.mobileprovision file) to use when bundling
the watch extension.
This is only used for non-simulator builds.
*/
.add(
attr(WATCH_EXT_PROVISIONING_PROFILE_ATTR, LABEL)
.singleArtifact()
.allowedFileTypes(FileType.of(".mobileprovision")))
.add(
attr(WATCH_EXT_DEFAULT_PROVISIONING_PROFILE_ATTR, LABEL)
.singleArtifact()
.allowedFileTypes(FileType.of(".mobileprovision"))
.value(
new LateBoundLabel${EXECUTABLE_NAME}
: The name of the executable generated and included
in the bundle by blaze, which can be used as the value for
CFBundleExecutable
within the plist.
${BUNDLE_NAME}
: This target's name and bundle suffix (.bundle or .app)
in the formname
.suffix
.
${PRODUCT_NAME}
: This target's name.
resources
these files are placed in the bundle root in
the same structure passed to this argument, so
["res/foo.png"]
will end up in
Foo.app/res/foo.png
.
["res/foo.png"]
will lead to the inclusion of all files in
directory res
.
*/
.add(
attr(WATCH_EXT_STRUCTURED_RESOURCES_ATTR, LABEL_LIST)
.legacyAllowAnyFileType()
.direct_compile_time_input())
/*
Files which are plists of strings, often localizable to be added to watch extension.
These files are converted to binary plists (if they are not already)
and placed in the bundle root of the final package. If this file's
immediate containing directory is named *.lproj (e.g. en.lproj,
Base.lproj), it will be placed under a directory of that name in the
final bundle. This allows for localizable strings.
*/
.add(
attr(WATCH_EXT_STRINGS_ATTR, LABEL_LIST)
.allowedFileTypes(STRINGS_TYPE)
.direct_compile_time_input())
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$watch_extension_bundle_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(
AppleToolchain.RequiresXcodeConfigRule.class,
ResourceToolsRule.class,
ReleaseBundlingToolsRule.class,
XcrunRule.class)
.build();
}
}
/**
* Attributes for {@code apple_watch*} rules that creates a watch application bundle.
*/
public static class WatchApplicationBundleRule implements RuleDefinition {
static final String WATCH_APP_NAME_ATTR = "app_name";
static final String WATCH_APP_ICON_ATTR = "app_icon";
static final String WATCH_APP_BUNDLE_ID_ATTR = "app_bundle_id";
static final String WATCH_APP_DEFAULT_PROVISIONING_PROFILE_ATTR =
":default_app_provisioning_profile";
static final String WATCH_APP_ENTITLEMENTS_ATTR = "app_entitlements";
static final String WATCH_APP_PROVISIONING_PROFILE_ATTR = "app_provisioning_profile";
static final String WATCH_APP_ASSET_CATALOGS_ATTR = "app_asset_catalogs";
static final String WATCH_APP_INFOPLISTS_ATTR = "app_infoplists";
static final String WATCH_APP_STORYBOARDS_ATTR = "app_storyboards";
static final String WATCH_APP_RESOURCES_ATTR = "app_resources";
static final String WATCH_APP_STRUCTURED_RESOURCES_ATTR = "app_structured_resources";
static final String WATCH_APP_STRINGS_ATTR = "app_strings";
@Override
public RuleClass build(Builder builder, RuleDefinitionEnvironment env) {
return builder
/*
Name of the final watch application binary.
*/
.add(attr(WATCH_APP_NAME_ATTR, STRING).mandatory())
/*
The name of the watch application icon.
The icon should be in one of the asset catalogs of this target or
a (transitive) dependency. In a new project, this is initialized
to "AppIcon" by Xcode.
infoplist
and
infoplists
are specified, the files defined in both attributes will be used.
Blaze will perform variable substitution on the plist files for the following values:
*/
.add(attr(WATCH_APP_INFOPLISTS_ATTR, BuildType.LABEL_LIST).allowedFileTypes(PLIST_TYPE))
/*
The provisioning profile (.mobileprovision file) to use when bundling
the watch application.
This is only used for non-simulator builds.
*/
.add(
attr(WATCH_APP_PROVISIONING_PROFILE_ATTR, LABEL)
.singleArtifact()
.allowedFileTypes(FileType.of(".mobileprovision")))
.add(
attr(WATCH_APP_DEFAULT_PROVISIONING_PROFILE_ATTR, LABEL)
.singleArtifact()
.allowedFileTypes(FileType.of(".mobileprovision"))
.value(
new LateBoundLabel${EXECUTABLE_NAME}
: The name of the executable generated and included
in the bundle by blaze, which can be used as the value for
CFBundleExecutable
within the plist.
${BUNDLE_NAME}
: This target's name and bundle suffix (.bundle or .app)
in the formname
.suffix
.
${PRODUCT_NAME}
: This target's name.
resources
these files are placed in the bundle root in
the same structure passed to this argument, so
["res/foo.png"]
will end up in
Foo.app/res/foo.png
.
["res/foo.png"]
will lead to the inclusion of all files in
directory res
.
*/
.add(
attr(WATCH_APP_STRUCTURED_RESOURCES_ATTR, LABEL_LIST)
.legacyAllowAnyFileType()
.direct_compile_time_input())
/*
Files which are plists of strings, often localizable to be added to watch application.
These files are converted to binary plists (if they are not already)
and placed in the bundle root of the final package. If this file's
immediate containing directory is named *.lproj (e.g. en.lproj,
Base.lproj), it will be placed under a directory of that name in the
final bundle. This allows for localizable strings.
*/
.add(
attr(WATCH_APP_STRINGS_ATTR, LABEL_LIST)
.allowedFileTypes(STRINGS_TYPE)
.direct_compile_time_input())
.build();
}
@Override
public Metadata getMetadata() {
return RuleDefinition.Metadata.builder()
.name("$watch_application_bundle_rule")
.type(RuleClassType.ABSTRACT)
.ancestors(
AppleToolchain.RequiresXcodeConfigRule.class,
ResourceToolsRule.class,
ReleaseBundlingToolsRule.class,
XcrunRule.class)
.build();
}
}
}