blob: 966de1f7653a81e652a9d4156ebad3131c9e6042 [file] [log] [blame]
// Copyright 2018 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.repository;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.FileValue;
import com.google.devtools.build.lib.cmdline.LabelConstants;
import com.google.devtools.build.lib.packages.BazelLibrary;
import com.google.devtools.build.lib.packages.BuildFileContainsErrorsException;
import com.google.devtools.build.lib.packages.NoSuchThingException;
import com.google.devtools.build.lib.rules.repository.ResolvedFileValue.ResolvedFileKey;
import com.google.devtools.build.lib.skyframe.PrecomputedValue;
import com.google.devtools.build.lib.syntax.BuildFileAST;
import com.google.devtools.build.lib.syntax.Mutability;
import com.google.devtools.build.lib.syntax.ParserInputSource;
import com.google.devtools.build.lib.syntax.StarlarkSemantics;
import com.google.devtools.build.lib.vfs.FileSystemUtils;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/** Function computing the Starlark value of 'resolved' in a file. */
public class ResolvedFileFunction implements SkyFunction {
@Override
@Nullable
public SkyValue compute(SkyKey skyKey, Environment env)
throws InterruptedException, SkyFunctionException {
ResolvedFileKey key = (ResolvedFileKey) skyKey;
StarlarkSemantics starlarkSemantics = PrecomputedValue.STARLARK_SEMANTICS.get(env);
if (starlarkSemantics == null) {
return null;
}
FileValue fileValue = (FileValue) env.getValue(FileValue.key(key.getPath()));
if (fileValue == null) {
return null;
}
try {
if (!fileValue.exists()) {
throw new ResolvedFileFunctionException(
new NoSuchThingException("Specified resolved file '" + key.getPath() + "' not found."));
} else {
byte[] bytes =
FileSystemUtils.readWithKnownFileSize(
key.getPath().asPath(), key.getPath().asPath().getFileSize());
BuildFileAST ast =
BuildFileAST.parseSkylarkFile(
ParserInputSource.create(bytes, key.getPath().asPath().asFragment()),
env.getListener());
if (ast.containsErrors()) {
throw new ResolvedFileFunctionException(
new BuildFileContainsErrorsException(
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
"Failed to parse file resolved file " + key.getPath()));
}
com.google.devtools.build.lib.syntax.Environment resolvedEnvironment;
try (Mutability mutability = Mutability.create("resolved file %s", key.getPath())) {
resolvedEnvironment =
com.google.devtools.build.lib.syntax.Environment.builder(mutability)
.setSemantics(starlarkSemantics)
.setGlobals(BazelLibrary.GLOBALS)
.build();
if (!ast.exec(resolvedEnvironment, env.getListener())) {
throw new ResolvedFileFunctionException(
new BuildFileContainsErrorsException(
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
"Failed to evaluate resolved file " + key.getPath()));
}
}
Object resolved = resolvedEnvironment.moduleLookup("resolved");
if (resolved == null) {
throw new ResolvedFileFunctionException(
new BuildFileContainsErrorsException(
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
"Symbol 'resolved' not exported in resolved file " + key.getPath()));
}
if (!(resolved instanceof List)) {
throw new ResolvedFileFunctionException(
new BuildFileContainsErrorsException(
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
"Symbol 'resolved' in resolved file " + key.getPath() + " not a list"));
}
ImmutableList.Builder<Map<String, Object>> result
= new ImmutableList.Builder<Map<String, Object>>();
for (Object entry : (List) resolved) {
if (!(entry instanceof Map)) {
throw new ResolvedFileFunctionException(
new BuildFileContainsErrorsException(
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
"Symbol 'resolved' in resolved file "
+ key.getPath()
+ " contains a non-map entry"));
}
ImmutableMap.Builder<String, Object> entryBuilder
= new ImmutableMap.Builder<String, Object>();
for (Map.Entry<Object, Object> keyValue : ((Map<Object, Object>) entry).entrySet()) {
Object attribute = keyValue.getKey();
if (!(attribute instanceof String)) {
throw new ResolvedFileFunctionException(
new BuildFileContainsErrorsException(
LabelConstants.EXTERNAL_PACKAGE_IDENTIFIER,
"Symbol 'resolved' in resolved file "
+ key.getPath()
+ " contains a non-string key in one of its entries"));
}
entryBuilder.put((String) attribute, keyValue.getValue());
}
result.add(entryBuilder.build());
}
return new ResolvedFileValue(result.build());
}
} catch (IOException e) {
throw new ResolvedFileFunctionException(e);
}
}
@Override
@Nullable
public String extractTag(SkyKey skyKey) {
return null;
}
private static final class ResolvedFileFunctionException extends SkyFunctionException {
ResolvedFileFunctionException(IOException e) {
super(e, SkyFunctionException.Transience.PERSISTENT);
}
ResolvedFileFunctionException(NoSuchThingException e) {
super(e, SkyFunctionException.Transience.PERSISTENT);
}
}
}