// 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.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.annotations.VisibleForTesting; 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.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; 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.actions.ExecutionRequirements; 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.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.rules.apple.AppleConfiguration; 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.apple.Platform; import com.google.devtools.build.lib.rules.cpp.CppConfiguration; 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 { static final String CLANG = "clang"; static final String CLANG_PLUSPLUS = "clang++"; static final String SWIFT = "swift"; static final String LIBTOOL = "libtool"; 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=*/ ""); } /** * 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. return new IntermediateArtifacts( ruleContext, /*archiveFileNameSuffix=*/"_j2objc"); } /** * Returns a {@link J2ObjcMappingFileProvider} containing J2ObjC mapping files from rules * that can be reached transitively through the "deps" attribute. * * @param ruleContext the rule context of the current rule * @return a {@link J2ObjcMappingFileProvider} containing J2ObjC mapping files information from * the transitive closure. */ public static J2ObjcMappingFileProvider j2ObjcMappingFileProvider(RuleContext ruleContext) { J2ObjcMappingFileProvider.Builder builder = new J2ObjcMappingFileProvider.Builder(); Iterable providers = ruleContext.getPrerequisites("deps", Mode.TARGET, J2ObjcMappingFileProvider.class); for (J2ObjcMappingFileProvider provider : providers) { builder.addTransitive(provider); } return builder.build(); } 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()); } @VisibleForTesting static final Iterable AUTOMATIC_SDK_FRAMEWORKS = ImmutableList.of( new SdkFramework("Foundation"), new SdkFramework("UIKit")); /** * 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 APPLE_TOOLCHAIN = new LateBoundLabel(CROSSTOOL_LABEL, CppConfiguration.class) { @Override public Label resolve( Rule rule, AttributeMap attributes, BuildConfiguration configuration) { return configuration.getFragment(CppConfiguration.class).getCcToolchainRuleLabel(); } }; /** * A null value for the lipo context collector. Objc builds do not use a lipo context collector. */ // TODO(b/28084560): Allow :lipo_context_collector not to be set instead of having a null // instance. public static final LateBoundLabel NULL_LIPO_CONTEXT_COLLECTOR = new LateBoundLabel() { @Override public Label resolve( Rule rule, AttributeMap attributes, BuildConfiguration configuration) { return null; } }; /** * Creates a new spawn action builder with apple environment variables set that are typically * needed by the apple toolchain. This should be used to start to build spawn actions that, in * order to run, require both a darwin architecture and a collection of environment variables * which contain information about the target and host architectures. This implicitly * assumes that this action is targeting ios platforms, and that * {@link AppleConfiguration#getIosCpu()} is the source of truth for their target architecture. * * @deprecated use {@link #spawnAppleEnvActionBuilder(RuleContext, Platform)} instead */ // TODO(cparsons): Refactor callers to use the alternate method. Callers should be aware // of their effective Platform. @Deprecated static SpawnAction.Builder spawnAppleEnvActionBuilder(RuleContext ruleContext) { AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); return spawnAppleEnvActionBuilder(ruleContext, Platform.forIosArch(appleConfiguration.getIosCpu())); } /** * Creates a new spawn action builder with apple environment variables set that are typically * needed by the apple toolchain. This should be used to start to build spawn actions that, in * order to run, require both a darwin architecture and a collection of environment variables * which contain information about the target and host architectures. */ // TODO(cparsons): Refactor this method to take the configuration fragment instead of // retrieving it from the rule context. static SpawnAction.Builder spawnAppleEnvActionBuilder(RuleContext ruleContext, Platform targetPlatform) { AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class); ImmutableMap.Builder envBuilder = ImmutableMap.builder() .putAll(appleConfiguration.getTargetAppleEnvironment(targetPlatform)) .putAll(appleConfiguration.getAppleHostSystemEnv()); return spawnOnDarwinActionBuilder() .setEnvironment(envBuilder.build()); } /** * Creates a new spawn action builder that requires a darwin architecture to run. */ static SpawnAction.Builder spawnOnDarwinActionBuilder() { return new SpawnAction.Builder() .setExecutionInfo(ImmutableMap.of(ExecutionRequirements.REQUIRES_DARWIN, "")); } /** * Creates a new spawn action builder that requires a darwin architecture to run and calls bash * to execute cmd. * Once we have a fix for b/21874752 we should be able to call setShellCommand(cmd) * directly, but right now we don't have a buildhelpers package on Macs so we must specify * the path to /bin/bash explicitly. */ static SpawnAction.Builder spawnBashOnDarwinActionBuilder(String cmd) { return spawnOnDarwinActionBuilder() .setShellCommand(ImmutableList.of("/bin/bash", "-c", cmd)); } /** * Creates a new configured target builder with the given {@code filesToBuild}, which are also * used as runfiles. * * @param ruleContext the current rule context * @param filesToBuild files to build for this target. These also become the data runfiles */ static RuleConfiguredTargetBuilder ruleConfiguredTarget(RuleContext ruleContext, NestedSet filesToBuild) { RunfilesProvider runfilesProvider = RunfilesProvider.withData( new Runfiles.Builder( ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) .addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES).build(), new Runfiles.Builder( ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) .addTransitiveArtifacts(filesToBuild).build()); return new RuleConfiguredTargetBuilder(ruleContext) .setFilesToBuild(filesToBuild) .add(RunfilesProvider.class, runfilesProvider); } /** * Attributes for {@code objc_*} rules that have compiler options. */ public static class CoptsRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment environment) { return builder /* Extra flags to pass to the compiler. Subject to "Make variable" substitution and Bourne shell tokenization. These flags will only apply to this target, and not those upon which it depends, or those which depend on it.

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"); static final FileType SWIFT_SOURCES = FileType.of(".swift"); /** * 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, SWIFT_SOURCES, HEADERS); /** * Files that should actually be compiled. */ static final FileTypeSet COMPILABLE_SRCS_TYPE = FileTypeSet.of(NON_CPP_SOURCES, CPP_SOURCES, ASSEMBLY_SOURCES, SWIFT_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 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.

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 ["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 export an xcode project. */ public static class XcodegenRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder .add(attr("$xcodegen", LABEL).cfg(HOST).exec() .value(env.getToolsLabel("//tools/objc:xcodegen"))) .add(attr("$dummy_source", LABEL) .value(env.getToolsLabel("//tools/objc:objc_dummy.mm"))) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$objc_xcodegen_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(":cc_toolchain", 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)) /* A header defining the Objective-C interfaces to be exposed in Swift. */ .add(attr("bridging_header", BuildType.LABEL) .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.

The paths are interpreted relative to the package directory, and the genfiles and bin roots (e.g. blaze-genfiles/pkg/includedir and blaze-out/pkg/includedir) are included in addition to the actual client root.

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 "-I" flags to COPTS instead. */ .add(attr("includes", Type.STRING_LIST)) /* List of #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 { private static final Iterable ALLOWED_DEPS_RULE_CLASSES = ImmutableSet.of( "objc_library", "objc_import", "objc_framework", "objc_proto_library", "j2objc_library", "cc_library", "cc_inc_library", "ios_framework", "swift_library", "experimental_objc_library"); @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder /* The list of C, C++, Objective-C, and Objective-C++ source and header files that are processed to create the library target. These are your checked-in files, plus any generated files. Source files are compiled into .o files with Clang. Header files may be included/imported by any source or header in the srcs attribute of this target, but not by headers in hdrs or any targets that depend on this rule. Additionally, precompiled .o files may be given as srcs. Be careful to ensure consistency in the architecture of provided .o files and that of the build to avoid missing symbol linker errors. */ .add(attr("srcs", LABEL_LIST).direct_compile_time_input().allowedFileTypes(SRCS_TYPE)) /* The list of Objective-C files that are processed to create the library target that DO NOT use ARC. The files in this attribute are treated very similar to those in the srcs attribute, but are compiled without ARC enabled. */ .add( attr("non_arc_srcs", LABEL_LIST) .direct_compile_time_input() .allowedFileTypes(NON_ARC_SRCS_TYPE)) /* Header file to prepend to every source file being compiled (both arc and non-arc). Use of pch files is actively discouraged in BUILD files, and this should be considered deprecated. Since pch files are not actually precompiled this is not a build-speed enhancement, and instead is just a global dependency. From a build efficiency point of view you are actually better including what you need directly in your sources where you need it. */ .add(attr("pch", LABEL).direct_compile_time_input().allowedFileTypes(FileType.of(".pch"))) /* The list of targets that are linked together to form the final bundle. */ .override( attr("deps", LABEL_LIST) .direct_compile_time_input() .allowedRuleClasses(ALLOWED_DEPS_RULE_CLASSES) .allowedFileTypes()) /* The list of targets that are required in order to build this target, but which are not included in the final bundle. This attribute should only rarely be used, and probably only for proto dependencies. */ .add( attr("non_propagated_deps", LABEL_LIST) .direct_compile_time_input() .allowedRuleClasses(ALLOWED_DEPS_RULE_CLASSES) .allowedFileTypes()) /* Extra -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)) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$objc_compiling_rule") .type(RuleClassType.ABSTRACT) .ancestors( BaseRuleClasses.RuleBase.class, CompileDependencyRule.class, CoptsRule.class, XcrunRule.class) .build(); } } /** * Common attributes for {@code objc_*} rules that can optionally be set to {@code alwayslink}. */ public static class AlwaysLinkRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder /* If 1, any bundle or binary that depends (directly or indirectly) on this library will link in all the object files for the files listed in srcs 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(); } } /** * Common attributes for {@code objc_*} rules that link sources and dependencies. */ public static class LinkingRule implements RuleDefinition { @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder .add( attr("$dumpsyms", LABEL) .cfg(HOST) .singleArtifact() .value(env.getToolsLabel("//tools/osx/crosstool:dump_syms"))) .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"))) /* 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 {@code objc_*} rules that create a bundle. */ 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. Blaze will perform variable substitution on the plist file for the following values:

*/ .add(attr(INFOPLIST_ATTR, LABEL).allowedFileTypes(PLIST_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 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("infoplists", BuildType.LABEL_LIST).allowedFileTypes(PLIST_TYPE)) /* The device families to which this bundle or binary is targeted. This is known as the TARGETED_DEVICE_FAMILY build setting in Xcode project files. It is a list of one or more of the strings "iphone" and "ipad".

By default this is set to "iphone", if explicitly specified may not be empty.

*/ .add( attr(FAMILIES_ATTR, STRING_LIST) .value(ImmutableList.of(TargetDeviceFamily.IPHONE.getNameInRule()))) .add( attr("$momcwrapper", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/objc:momcwrapper"))) .add( attr("$swiftstdlibtoolwrapper", LABEL) .cfg(HOST) .exec() .value(env.getToolsLabel("//tools/objc:swiftstdlibtoolwrapper"))) .build(); } @Override public Metadata getMetadata() { return RuleDefinition.Metadata.builder() .name("$objc_bundling_rule") .type(RuleClassType.ABSTRACT) .ancestors( AppleToolchain.RequiresXcodeConfigRule.class, ResourcesRule.class, ResourceToolsRule.class, XcrunRule.class) .build(); } } /** * Common attributes for {@code objc_*} rules that create a bundle meant for release (e.g. * application or extension). */ public static class ReleaseBundlingRule implements RuleDefinition { static final String APP_ICON_ATTR = "app_icon"; static final String BUNDLE_ID_ATTR = "bundle_id"; static final String DEFAULT_PROVISIONING_PROFILE_ATTR = ":default_provisioning_profile"; static final String ENTITLEMENTS_ATTR = "entitlements"; static final String EXTRA_ENTITLEMENTS_ATTR = ":extra_entitlements"; static final String LAUNCH_IMAGE_ATTR = "launch_image"; static final String LAUNCH_STORYBOARD_ATTR = "launch_storyboard"; static final String PROVISIONING_PROFILE_ATTR = "provisioning_profile"; @Override public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { return builder /* The entitlements file required for device builds of this 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(ENTITLEMENTS_ATTR, LABEL) .allowedFileTypes(ENTITLEMENTS_TYPE)) .add( attr(EXTRA_ENTITLEMENTS_ATTR, LABEL) .singleArtifact() .value( new LateBoundLabel(ObjcConfiguration.class) { @Override public Label resolve( Rule rule, AttributeMap attributes, BuildConfiguration configuration) { return configuration .getFragment(ObjcConfiguration.class) .getExtraEntitlements(); } }) .allowedFileTypes(ENTITLEMENTS_TYPE)) /* The provisioning profile (.mobileprovision file) to use when bundling the application. This is only used for non-simulator builds. */ .add( attr(PROVISIONING_PROFILE_ATTR, LABEL) .singleArtifact() .allowedFileTypes(FileType.of(".mobileprovision"))) // Will be used if provisioning_profile is null. .add( attr(DEFAULT_PROVISIONING_PROFILE_ATTR, LABEL) .singleArtifact() .allowedFileTypes(FileType.of(".mobileprovision")) .value( new LateBoundLabel(ObjcConfiguration.class) { @Override public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) { AppleConfiguration appleConfiguration = configuration.getFragment(AppleConfiguration.class); if (appleConfiguration.getBundlingPlatform() != Platform.IOS_DEVICE) { return null; } if (rule.isAttributeValueExplicitlySpecified(PROVISIONING_PROFILE_ATTR)) { return null; } return appleConfiguration.getDefaultProvisioningProfileLabel(); } })) /* The name of the 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.

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 Info.plist under the key UILaunchStoryboardName.

*/ .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). */ 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"))) .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 Payload root directory of the IPA. 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.

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 IOSSIM_ATTR = "$iossim"; 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(IOSSIM_ATTR, LABEL).cfg(HOST).exec() .value(env.getToolsLabel("//third_party/iossim:iossim"))) .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"; static final String WATCH_EXT_FAMILIES_ATTR = "ext_families"; @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)) /* The device families to which the watch extension is targeted. This is known as the TARGETED_DEVICE_FAMILY build setting in Xcode project files. It is a list of one or more of the strings "iphone" and "ipad".

By default this is set to "iphone", if explicitly specified may not be empty.

The watch application is always built for "watch" for device builds and "iphone, watch" for simulator builds. */ .add( attr(WATCH_EXT_FAMILIES_ATTR, STRING_LIST) .value(ImmutableList.of(TargetDeviceFamily.IPHONE.getNameInRule()))) /* 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 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(ObjcConfiguration.class) { @Override public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) { AppleConfiguration appleConfiguration = configuration.getFragment(AppleConfiguration.class); if (appleConfiguration.getBundlingPlatform() != Platform.IOS_DEVICE) { return null; } if (rule.isAttributeValueExplicitlySpecified( WATCH_EXT_PROVISIONING_PROFILE_ATTR)) { return null; } return appleConfiguration.getDefaultProvisioningProfileLabel(); } })) /* Files to include in the final watch extension 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. 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(WATCH_EXT_RESOURCES_ATTR, LABEL_LIST).legacyAllowAnyFileType() .direct_compile_time_input()) /* Files to include in the final watch extension bundle. They are not processed or compiled in any way besides the processing done by the rules that actually generate them. In differences to 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.

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 ["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.

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 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:

  • ${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.
*/ .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(ObjcConfiguration.class) { @Override public Label resolve(Rule rule, AttributeMap attributes, BuildConfiguration configuration) { AppleConfiguration appleConfiguration = configuration.getFragment(AppleConfiguration.class); if (appleConfiguration.getBundlingPlatform() != Platform.IOS_DEVICE) { return null; } if (rule.isAttributeValueExplicitlySpecified( WATCH_APP_PROVISIONING_PROFILE_ATTR)) { return null; } return appleConfiguration.getDefaultProvisioningProfileLabel(); } })) /* Files which are .storyboard resources for the watch application, possibly localizable. These files are compiled and placed in the bundle root of the final package. */ .add(attr(WATCH_APP_STORYBOARDS_ATTR, LABEL_LIST) .allowedFileTypes(STORYBOARD_TYPE)) /* Files to include in the final watch 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. 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(WATCH_APP_RESOURCES_ATTR, LABEL_LIST).legacyAllowAnyFileType() .direct_compile_time_input()) /* Files to include in the final watch 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 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.

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 ["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(); } } }