aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/analysis/WorkspaceStatusAction.java
blob: fa156d97e6e07296e6f55f77d3359b7068f7a145 (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// 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.analysis;

import com.google.common.base.Splitter;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.AbstractAction;
import com.google.devtools.build.lib.actions.ActionOwner;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.ArtifactFactory;
import com.google.devtools.build.lib.actions.ArtifactOwner;
import com.google.devtools.build.lib.actions.Executor.ActionContext;
import com.google.devtools.build.lib.util.OptionsUtils;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.lib.vfs.Path;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.common.options.Option;
import com.google.devtools.common.options.OptionsBase;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * An action writing the workspace status files.
 *
 * <p>These files represent information about the environment the build was run in. They are used
 * by language-specific build info factories to make the data in them available for individual
 * languages (e.g. by turning them into .h files for C++)
 *
 * <p>The format of these files a list of key-value pairs, one for each line. The key and the value
 * are separated by a space.
 *
 * <p>There are two of these files: volatile and stable. Changes in the volatile file do not
 * cause rebuilds if no other file is changed. This is useful for frequently-changing information
 * that does not significantly affect the build, e.g. the current time.
 */
public abstract class WorkspaceStatusAction extends AbstractAction {

  /**
   * Options controlling the workspace status command.
   */
  public static class Options extends OptionsBase {
    @Option(name = "embed_label",
        defaultValue = "",
        category = "misc",
        help = "Embed source control revision or release label in binary")
    public String embedLabel;

    @Option(name = "workspace_status_command",
        defaultValue = "",
        category = "misc",
        converter = OptionsUtils.PathFragmentConverter.class,
        help = "A command invoked at the beginning of the build to provide status "
            + "information about the workspace in the form of key/value pairs.  "
            + "See the User's Manual for the full specification.")
    public PathFragment workspaceStatusCommand;
  }

  /**
   * The type of a workspace status action key.
   */
  public enum KeyType {
    INTEGER,
    STRING,
    VERBATIM,
  }

  /**
   * Language for keys that should be present in the build info for every language.
   */
  // TODO(bazel-team): Once this is released, migrate the only place in the depot to use
  // the BUILD_USERNAME, BUILD_HOSTNAME and BUILD_DIRECTORY keys instead of BUILD_INFO. Then
  // language-specific build info keys can be removed.
  public static final String ALL_LANGUAGES = "*";

  /**
   * Action context required by the actions that write language-specific workspace status artifacts.
   */
  public static interface Context extends ActionContext {
    ImmutableMap<String, Key> getStableKeys();
    ImmutableMap<String, Key> getVolatileKeys();
  }

  /**
   * A key in the workspace status info file.
   */
  public static class Key {
    private final KeyType type;

    /**
     * Should be set to ALL_LANGUAGES if the key should be present in the build info of every
     * language.
     */
    private final String language;
    private final String defaultValue;
    private final String redactedValue;

    private Key(KeyType type, String language, String defaultValue, String redactedValue) {
      this.type = type;
      this.language = language;
      this.defaultValue = defaultValue;
      this.redactedValue = redactedValue;
    }

    public KeyType getType() {
      return type;
    }

    public boolean isInLanguage(String language) {
      return this.language.equals(ALL_LANGUAGES) || this.language.equals(language);
    }

    public String getDefaultValue() {
      return defaultValue;
    }

    public String getRedactedValue() {
      return redactedValue;
    }

    public static Key forLanguage(
        String language, KeyType type, String defaultValue, String redactedValue) {
      return new Key(type, language, defaultValue, redactedValue);
    }

    public static Key of(KeyType type, String defaultValue, String redactedValue) {
      return new Key(type, ALL_LANGUAGES, defaultValue, redactedValue);
    }
  }

  /**
   * Parses the output of the workspace status action.
   *
   * <p>The output is a text file with each line representing a workspace status info key.
   * The key is the part of the line before the first space and should consist of the characters
   * [A-Z_] (although this is not checked). Everything after the first space is the value.
   */
  public static Map<String, String> parseValues(Path file) throws IOException {
    HashMap<String, String> result = new HashMap<>();
    Splitter lineSplitter = Splitter.on(' ').limit(2);
    for (String line :
        Splitter.on('\n').split(new String(FileSystemUtils.readContentAsLatin1(file)))) {
      List<String> items = lineSplitter.splitToList(line);
      if (items.size() != 2) {
        continue;
      }

      result.put(items.get(0), items.get(1));
    }

    return ImmutableMap.copyOf(result);
  }

  /**
   * Factory for {@link WorkspaceStatusAction}.
   */
  public interface Factory {
    /**
     * Creates the workspace status action.
     *
     * <p>If the objects returned for two builds are equals, the workspace status action can be
     * be reused between them. Note that this only applies to the action object itself (the action
     * will be unconditionally re-executed on every build)
     */
    WorkspaceStatusAction createWorkspaceStatusAction(
        ArtifactFactory artifactFactory, ArtifactOwner artifactOwner, Supplier<UUID> buildId);

    /**
     * Creates a dummy workspace status map. Used in cases where the build failed, so that part of
     * the workspace status is nevertheless available.
     */
    Map<String, String> createDummyWorkspaceStatus();
  }

  protected WorkspaceStatusAction(ActionOwner owner,
      Iterable<Artifact> inputs,
      Iterable<Artifact> outputs) {
    super(owner, inputs, outputs);
  }

  /**
   * The volatile status artifact containing items that may change even if nothing changed
   * between the two builds, e.g. current time.
   */
  public abstract Artifact getVolatileStatus();

  /**
   * The stable status artifact containing items that change only if information relevant to the
   * build changes, e.g. the name of the user running the build or the hostname.
   */
  public abstract Artifact getStableStatus();
}