aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoProvider.java
blob: 9316720f1c6674b2a157d3be1481c6c6059a0890 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2016 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 com.google.common.base.Preconditions;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.packages.NativeInfo;
import com.google.devtools.build.lib.packages.NativeProvider;
import com.google.devtools.build.lib.vfs.PathFragment;

/**
 * A provider that provides all protos and portable proto filters information in the transitive
 * closure of its dependencies that are needed for generating and compiling only one version of
 * proto files.
 *
 * <p>This provider also propagates the headers and search path for the protobuf runtime library.
 * This solves the issue that the proto bundling behavior (gather all the protos in the top target
 * and generate, compile and link only one version in the final binary) needs this data at the
 * linking target but the dependency on the runtime library is defined on the objc_proto_library.
 *
 * <p>Ideally we should make objc_binary (and other linking targets such as ios_extension_binary)
 * depend on the runtime library's ObjcProvider. Unfortunately this runs into a bug where Xcode
 * project generation cannot handle the dependency if it points to a label in an external workspace
 * (such as {@code @bazel_tools}). To avoid breaking Xcode project generation for all binary targets
 * all the time (whether protos are used or not), the dependency is specified on objc_proto_library
 * instead.
 */
public class ObjcProtoProvider extends NativeInfo {

  /** Skylark name for the ObjcProtoProvider. */
  public static final String SKYLARK_NAME = "ObjcProto";

  /** Skylark constructor and identifier for AppleExecutableBinaryInfo. */
  public static final NativeProvider<ObjcProtoProvider> SKYLARK_CONSTRUCTOR =
      new NativeProvider<ObjcProtoProvider>(ObjcProtoProvider.class, SKYLARK_NAME) {};

  private final NestedSet<NestedSet<Artifact>> protoGroups;
  private final NestedSet<Artifact> protobufHeaders;
  private final NestedSet<PathFragment> protobufHeaderSearchPaths;
  private final NestedSet<Artifact> portableProtoFilters;

  private ObjcProtoProvider(
      NestedSet<NestedSet<Artifact>> protoGroups,
      NestedSet<Artifact> portableProtoFilters,
      NestedSet<Artifact> protobufHeaders,
      NestedSet<PathFragment> protobufHeaderSearchPaths) {
    super(SKYLARK_CONSTRUCTOR);
    this.protoGroups = Preconditions.checkNotNull(protoGroups);
    this.portableProtoFilters = Preconditions.checkNotNull(portableProtoFilters);
    this.protobufHeaders = Preconditions.checkNotNull(protobufHeaders);
    this.protobufHeaderSearchPaths = Preconditions.checkNotNull(protobufHeaderSearchPaths);
  }

  /** Returns the set of all proto groups that the dependencies of this provider has seen. */
  public NestedSet<NestedSet<Artifact>> getProtoGroups() {
    return protoGroups;
  }

  /** Returns the header artifacts provided by the Protobuf library. */
  public NestedSet<Artifact> getProtobufHeaders() {
    return protobufHeaders;
  }

  /** Returns the header search paths provided by the Protobuf library. */
  public NestedSet<PathFragment> getProtobufHeaderSearchPaths() {
    return protobufHeaderSearchPaths;
  }

  /**
   * Returns the set of all the associated filters to the collected protos.
   */
  public NestedSet<Artifact> getPortableProtoFilters() {
    return portableProtoFilters;
  }

  /**
   * 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 NestedSetBuilder<NestedSet<Artifact>> protoGroups =
        NestedSetBuilder.stableOrder();
    private final NestedSetBuilder<Artifact> portableProtoFilters = NestedSetBuilder.stableOrder();
    private final NestedSetBuilder<Artifact> protobufHeaders = NestedSetBuilder.stableOrder();
    private final NestedSetBuilder<PathFragment> protobufHeaderSearchPaths =
        NestedSetBuilder.linkOrder();

    /**
     * Adds a proto group to be propagated. Each group represents a proto_library target and
     * contains protos to be built along with their transitive dependencies. We propagate protos as
     * groups because the grouping provides relationship information between the protos, which can
     * be used to limit the number of inputs to each proto generation action.
     */
    public Builder addProtoGroup(NestedSet<Artifact> protoGroup) {
      this.protoGroups.add(protoGroup);
      return this;
    }

    /** Adds the header artifacts provided by the Protobuf library. */
    public Builder addProtobufHeaders(NestedSet<Artifact> protobufHeaders) {
      this.protobufHeaders.addTransitive(protobufHeaders);
      return this;
    }

    /** Adds the header search paths provided by the Protobuf library. */
    public Builder addProtobufHeaderSearchPaths(NestedSet<PathFragment> protobufHeaderSearchPaths) {
      this.protobufHeaderSearchPaths.addTransitive(protobufHeaderSearchPaths);
      return this;
    }

    /**
     * Adds all the proto filters to the set of dependencies.
     */
    public Builder addPortableProtoFilters(NestedSet<Artifact> protoFilters) {
      this.portableProtoFilters.addTransitive(protoFilters);
      return this;
    }

    /**
     * Add all protos and filters from providers, and propagate them to any (transitive) dependers
     * on this ObjcProtoProvider.
     */
    public Builder addTransitive(Iterable<ObjcProtoProvider> providers) {
      for (ObjcProtoProvider provider : providers) {
        this.protoGroups.addTransitive(provider.getProtoGroups());
        this.portableProtoFilters.addTransitive(provider.getPortableProtoFilters());
        this.protobufHeaders.addTransitive(provider.getProtobufHeaders());
        this.protobufHeaderSearchPaths.addTransitive(provider.getProtobufHeaderSearchPaths());
      }
      return this;
    }

    /**
     * Whether this provider has any protos or filters.
     */
    public boolean isEmpty() {
      return protoGroups.isEmpty() && portableProtoFilters.isEmpty();
    }

    public ObjcProtoProvider build() {
      return new ObjcProtoProvider(
          protoGroups.build(),
          portableProtoFilters.build(),
          protobufHeaders.build(),
          protobufHeaderSearchPaths.build());
    }
  }
}