diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java new file mode 100644 index 0000000000..c48710ed16 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProvider.java @@ -0,0 +1,313 @@ +// Copyright 2014 Google Inc. 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.collect.nestedset.Order.LINK_ORDER; +import static com.google.devtools.build.lib.collect.nestedset.Order.STABLE_ORDER; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.TransitiveInfoProvider; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.collect.nestedset.Order; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.vfs.PathFragment; +import com.google.devtools.build.xcode.xcodegen.proto.XcodeGenProtos.TargetControl; + +import java.util.HashMap; +import java.util.Map; + +/** + * A provider that provides all compiling and linking information in the transitive closure of its + * deps that are needed for building Objective-C rules. + */ +@Immutable +public final class ObjcProvider implements TransitiveInfoProvider { + /** + * Represents one of the things this provider can provide transitively. Things are provided as + * {@link NestedSet}s of type E. + */ + public static class Key<E> { + private final Order order; + + private Key(Order order) { + this.order = Preconditions.checkNotNull(order); + } + } + + public static final Key<Artifact> LIBRARY = new Key<>(LINK_ORDER); + public static final Key<Artifact> IMPORTED_LIBRARY = new Key<>(LINK_ORDER); + + /** + * Single-architecture linked binaries to be combined for the final multi-architecture binary. + */ + public static final Key<Artifact> LINKED_BINARY = new Key<>(STABLE_ORDER); + + /** + * Indicates which libraries to load with {@code -force_load}. This is a subset of the union of + * the {@link #LIBRARY} and {@link #IMPORTED_LIBRARY} sets. + */ + public static final Key<Artifact> FORCE_LOAD_LIBRARY = new Key<>(LINK_ORDER); + + /** + * Libraries to pass with -force_load flags when setting the linkopts in Xcodegen. This is needed + * in addition to {@link #FORCE_LOAD_LIBRARY} because that one, contains a mixture of import + * archives (which are not built by Xcode) and built-from-source library archives (which are built + * by Xcode). Archives that are built by Xcode are placed directly under + * {@code BUILT_PRODUCTS_DIR} while those not built by Xcode appear somewhere in the Bazel + * workspace under {@code WORKSPACE_ROOT}. + */ + public static final Key<String> FORCE_LOAD_FOR_XCODEGEN = new Key<>(LINK_ORDER); + + public static final Key<Artifact> HEADER = new Key<>(STABLE_ORDER); + + /** + * Include search paths specified with {@code -I} on the command line. Also known as header search + * paths (and distinct from <em>user</em> header search paths). + */ + public static final Key<PathFragment> INCLUDE = new Key<>(LINK_ORDER); + + /** + * Key for values in {@code defines} attributes. These are passed as {@code -D} flags to all + * invocations of the compiler for this target and all depending targets. + */ + public static final Key<String> DEFINE = new Key<>(STABLE_ORDER); + + public static final Key<Artifact> ASSET_CATALOG = new Key<>(STABLE_ORDER); + + /** + * Added to {@link TargetControl#getGeneralResourceFileList()} when running Xcodegen. + */ + public static final Key<Artifact> GENERAL_RESOURCE_FILE = new Key<>(STABLE_ORDER); + + /** + * Exec paths of {@code .bundle} directories corresponding to imported bundles to link. + * These are passed to Xcodegen. + */ + public static final Key<PathFragment> BUNDLE_IMPORT_DIR = new Key<>(STABLE_ORDER); + + /** + * Files that are plopped into the final bundle at some arbitrary bundle path. Note that these are + * not passed to Xcodegen, and these don't include information about where the file originated + * from. + */ + public static final Key<BundleableFile> BUNDLE_FILE = new Key<>(STABLE_ORDER); + + public static final Key<PathFragment> XCASSETS_DIR = new Key<>(STABLE_ORDER); + public static final Key<String> SDK_DYLIB = new Key<>(STABLE_ORDER); + public static final Key<SdkFramework> SDK_FRAMEWORK = new Key<>(STABLE_ORDER); + public static final Key<SdkFramework> WEAK_SDK_FRAMEWORK = new Key<>(STABLE_ORDER); + public static final Key<Xcdatamodel> XCDATAMODEL = new Key<>(STABLE_ORDER); + public static final Key<Flag> FLAG = new Key<>(STABLE_ORDER); + + /** + * Merge zips to include in the bundle. The entries of these zip files are included in the final + * bundle with the same path. The entries in the merge zips should not include the bundle root + * path (e.g. {@code Foo.app}). + */ + public static final Key<Artifact> MERGE_ZIP = new Key<>(STABLE_ORDER); + + /** + * Exec paths of {@code .framework} directories corresponding to frameworks to link. These cause + * -F arguments (framework search paths) to be added to each compile action, and -framework (link + * framework) arguments to be added to each link action. + */ + public static final Key<PathFragment> FRAMEWORK_DIR = new Key<>(LINK_ORDER); + + /** + * Files in {@code .framework} directories that should be included as inputs when compiling and + * linking. + */ + public static final Key<Artifact> FRAMEWORK_FILE = new Key<>(STABLE_ORDER); + + /** + * Bundles which should be linked in as a nested bundle to the final application. + */ + public static final Key<Bundling> NESTED_BUNDLE = new Key<>(STABLE_ORDER); + + /** + * Artifact containing information on debug symbols + */ + public static final Key<Artifact> DEBUG_SYMBOLS = new Key<>(STABLE_ORDER); + + /** + * Flags that apply to a transitive build dependency tree. Each item in the enum corresponds to a + * flag. If the item is included in the key {@link #FLAG}, then the flag is considered set. + */ + public enum Flag { + /** + * Indicates that C++ (or Objective-C++) is used in any source file. This affects how the linker + * is invoked. + */ + USES_CPP; + } + + private final ImmutableMap<Key<?>, NestedSet<?>> items; + + // Items which should be passed to direct dependers, but not transitive dependers. + private final ImmutableMap<Key<?>, NestedSet<?>> nonPropagatedItems; + + private ObjcProvider( + ImmutableMap<Key<?>, NestedSet<?>> items, + ImmutableMap<Key<?>, NestedSet<?>> nonPropagatedItems) { + this.items = Preconditions.checkNotNull(items); + this.nonPropagatedItems = Preconditions.checkNotNull(nonPropagatedItems); + } + + /** + * All artifacts, bundleable files, etc. of the type specified by {@code key}. + */ + @SuppressWarnings("unchecked") + public <E> NestedSet<E> get(Key<E> key) { + Preconditions.checkNotNull(key); + NestedSetBuilder<E> builder = new NestedSetBuilder<>(key.order); + if (nonPropagatedItems.containsKey(key)) { + builder.addTransitive((NestedSet<E>) nonPropagatedItems.get(key)); + } + if (items.containsKey(key)) { + builder.addTransitive((NestedSet<E>) items.get(key)); + } + return builder.build(); + } + + /** + * Indicates whether {@code flag} is set on this provider. + */ + public boolean is(Flag flag) { + return Iterables.contains(get(FLAG), flag); + } + + /** + * Indicates whether this provider has any asset catalogs. This is true whenever some target in + * its transitive dependency tree specifies a non-empty {@code asset_catalogs} attribute. + */ + public boolean hasAssetCatalogs() { + return !get(XCASSETS_DIR).isEmpty(); + } + + /** + * A builder for this context with an API that is optimized for collecting information from + * several transitive dependencies. + */ + public static final class Builder { + private final Map<Key<?>, NestedSetBuilder<?>> items = new HashMap<>(); + private final Map<Key<?>, NestedSetBuilder<?>> nonPropagatedItems = new HashMap<>(); + + private static void maybeAddEmptyBuilder(Map<Key<?>, NestedSetBuilder<?>> set, Key<?> key) { + if (!set.containsKey(key)) { + set.put(key, new NestedSetBuilder<>(key.order)); + } + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void uncheckedAddAll(Key key, Iterable toAdd) { + maybeAddEmptyBuilder(items, key); + items.get(key).addAll(toAdd); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void uncheckedAddTransitive(Key key, NestedSet toAdd, boolean propagate) { + Map<Key<?>, NestedSetBuilder<?>> set = propagate ? items : nonPropagatedItems; + maybeAddEmptyBuilder(set, key); + set.get(key).addTransitive(toAdd); + } + + /** + * Adds elements in items, and propagate them to any (transitive) dependers on this + * ObjcProvider. + */ + public <E> Builder addTransitiveAndPropagate(Key<E> key, NestedSet<E> items) { + uncheckedAddTransitive(key, items, true); + return this; + } + + /** + * Add all elements from provider, and propagate them to any (transitive) dependers on this + * ObjcProvider. + */ + public Builder addTransitiveAndPropagate(ObjcProvider provider) { + for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.items.entrySet()) { + uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), true); + } + return this; + } + + /** + * Add all elements from a single key of the given provider, and propagate them to any + * (transitive) dependers on this ObjcProvider. + */ + public <E> Builder addTransitiveAndPropagate(Key<E> key, ObjcProvider provider) { + addTransitiveAndPropagate(key, provider.get(key)); + return this; + } + + /** + * Add all elements from providers, and propagate them to any (transitive) dependers on this + * ObjcProvider. + */ + public Builder addTransitiveAndPropagate(Iterable<ObjcProvider> providers) { + for (ObjcProvider provider : providers) { + addTransitiveAndPropagate(provider); + } + return this; + } + + /** + * Add elements from providers, but don't propagate them to any dependers on this ObjcProvider. + * These elements will be exposed to {@link #get(Key)} calls, but not to any ObjcProviders + * which add this provider to themself. + */ + public Builder addTransitiveWithoutPropagating(Iterable<ObjcProvider> providers) { + for (ObjcProvider provider : providers) { + for (Map.Entry<Key<?>, NestedSet<?>> typeEntry : provider.items.entrySet()) { + uncheckedAddTransitive(typeEntry.getKey(), typeEntry.getValue(), false); + } + } + return this; + } + + /** + * Add element, and propagate it to any (transitive) dependers on this ObjcProvider. + */ + public <E> Builder add(Key<E> key, E toAdd) { + uncheckedAddAll(key, ImmutableList.of(toAdd)); + return this; + } + + /** + * Add elements in toAdd, and propagate them to any (transitive) dependers on this ObjcProvider. + */ + public <E> Builder addAll(Key<E> key, Iterable<? extends E> toAdd) { + uncheckedAddAll(key, toAdd); + return this; + } + + public ObjcProvider build() { + ImmutableMap.Builder<Key<?>, NestedSet<?>> propagated = new ImmutableMap.Builder<>(); + for (Map.Entry<Key<?>, NestedSetBuilder<?>> typeEntry : items.entrySet()) { + propagated.put(typeEntry.getKey(), typeEntry.getValue().build()); + } + ImmutableMap.Builder<Key<?>, NestedSet<?>> nonPropagated = new ImmutableMap.Builder<>(); + for (Map.Entry<Key<?>, NestedSetBuilder<?>> typeEntry : nonPropagatedItems.entrySet()) { + nonPropagated.put(typeEntry.getKey(), typeEntry.getValue().build()); + } + return new ObjcProvider(propagated.build(), nonPropagated.build()); + } + } +} |