blob: f91d02969e460832f1ca3407a7b97b4a6b9905e5 [file] [log] [blame]
// Copyright 2017 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.cpp;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Joiner;
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.RuleContext;
import com.google.devtools.build.lib.analysis.config.CompilationMode;
import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException;
import com.google.devtools.build.lib.analysis.platform.ToolchainInfo;
import com.google.devtools.build.lib.analysis.skylark.SkylarkRuleContext;
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.events.Location;
import com.google.devtools.build.lib.packages.NativeInfo;
import com.google.devtools.build.lib.packages.NativeProvider;
import com.google.devtools.build.lib.packages.Provider;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import com.google.devtools.build.lib.packages.SkylarkInfo;
import com.google.devtools.build.lib.rules.cpp.CcCompilationHelper.CompilationInfo;
import com.google.devtools.build.lib.rules.cpp.CcLinkingHelper.LinkingInfo;
import com.google.devtools.build.lib.rules.cpp.CcModule.CcSkylarkInfo;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ActionConfig;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.ArtifactNamePattern;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.EnvEntry;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.EnvSet;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Feature;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FeatureConfiguration;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Flag;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FlagGroup;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.FlagSet;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.VariableWithValue;
import com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.WithFeatureSet;
import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.Expandable;
import com.google.devtools.build.lib.rules.cpp.CcToolchainVariables.StringValueParser;
import com.google.devtools.build.lib.rules.cpp.LinkerInputs.LibraryToLink;
import com.google.devtools.build.lib.skyframe.serialization.ObjectCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec;
import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec.VisibleForSerialization;
import com.google.devtools.build.lib.skylarkbuildapi.SkylarkRuleContextApi;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcModuleApi;
import com.google.devtools.build.lib.skylarkbuildapi.cpp.CcSkylarkInfoApi;
import com.google.devtools.build.lib.skylarkinterface.Param;
import com.google.devtools.build.lib.skylarkinterface.ParamType;
import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable;
import com.google.devtools.build.lib.syntax.EvalException;
import com.google.devtools.build.lib.syntax.EvalUtils;
import com.google.devtools.build.lib.syntax.Runtime.NoneType;
import com.google.devtools.build.lib.syntax.SkylarkDict;
import com.google.devtools.build.lib.syntax.SkylarkList;
import com.google.devtools.build.lib.syntax.SkylarkNestedSet;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.StringUtil;
import com.google.devtools.build.lib.vfs.PathFragment;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
/** A module that contains Skylark utilities for C++ support. */
public class CcModule
implements CcModuleApi<
CcToolchainProvider,
FeatureConfiguration,
CcToolchainVariables,
LibraryToLink,
CcLinkParams,
CcSkylarkInfo> {
private enum RegisterActions {
ALWAYS,
NEVER,
CONDITIONALLY;
private final String skylarkName;
RegisterActions() {
this.skylarkName = toString().toLowerCase();
}
public String getSkylarkName() {
return skylarkName;
}
public static RegisterActions fromString(
String skylarkName, Location location, String fieldForError) throws EvalException {
for (RegisterActions registerActions : values()) {
if (registerActions.getSkylarkName().equals(skylarkName)) {
return registerActions;
}
}
throw new EvalException(
location,
String.format(
"Possibles values for %s: %s",
fieldForError,
Joiner.on(", ")
.join(
Arrays.stream(values())
.map(RegisterActions::getSkylarkName)
.collect(ImmutableList.toImmutableList()))));
}
}
/**
* C++ Skylark rules should have this provider so that native rules can depend on them. This will
* eventually go away once b/73921130 is fixed.
*/
@Immutable
@AutoCodec
public static final class CcSkylarkInfo extends NativeInfo implements CcSkylarkInfoApi {
public static final ObjectCodec<CcSkylarkInfo> CODEC = new CcModule_CcSkylarkInfo_AutoCodec();
public static final NativeProvider<CcSkylarkInfo> PROVIDER =
new NativeProvider<CcSkylarkInfo>(CcSkylarkInfo.class, "CcSkylarkInfo") {};
@AutoCodec.Instantiator
@VisibleForSerialization
CcSkylarkInfo() {
super(PROVIDER);
}
}
@Override
public Provider getCcToolchainProvider() {
return ToolchainInfo.PROVIDER;
}
@Override
public FeatureConfiguration configureFeatures(
CcToolchainProvider toolchain,
SkylarkList<String> requestedFeatures,
SkylarkList<String> unsupportedFeatures)
throws EvalException {
return CcCommon.configureFeaturesOrThrowEvalException(
ImmutableSet.copyOf(requestedFeatures),
ImmutableSet.copyOf(unsupportedFeatures),
toolchain);
}
@Override
public String getToolForAction(FeatureConfiguration featureConfiguration, String actionName) {
return featureConfiguration.getToolPathForAction(actionName);
}
@Override
public boolean isEnabled(FeatureConfiguration featureConfiguration, String featureName) {
return featureConfiguration.isEnabled(featureName);
}
@Override
public SkylarkList<String> getCommandLine(
FeatureConfiguration featureConfiguration,
String actionName,
CcToolchainVariables variables) {
return SkylarkList.createImmutable(featureConfiguration.getCommandLine(actionName, variables));
}
@Override
public SkylarkDict<String, String> getEnvironmentVariable(
FeatureConfiguration featureConfiguration,
String actionName,
CcToolchainVariables variables) {
return SkylarkDict.copyOf(
null, featureConfiguration.getEnvironmentVariables(actionName, variables));
}
@Override
public CcToolchainVariables getCompileBuildVariables(
CcToolchainProvider ccToolchainProvider,
FeatureConfiguration featureConfiguration,
Object sourceFile,
Object outputFile,
Object userCompileFlags,
Object includeDirs,
Object quoteIncludeDirs,
Object systemIncludeDirs,
Object defines,
boolean usePic,
boolean addLegacyCxxOptions)
throws EvalException {
return CompileBuildVariables.setupVariablesOrThrowEvalException(
featureConfiguration,
ccToolchainProvider,
convertFromNoneable(sourceFile, /* defaultValue= */ null),
convertFromNoneable(outputFile, /* defaultValue= */ null),
/* gcnoFile= */ null,
/* dwoFile= */ null,
/* ltoIndexingFile= */ null,
/* includes= */ ImmutableList.of(),
userFlagsToIterable(ccToolchainProvider.getCppConfiguration(), userCompileFlags),
/* cppModuleMap= */ null,
usePic,
/* fakeOutputFile= */ null,
/* fdoStamp= */ null,
/* dotdFileExecPath= */ null,
/* variablesExtensions= */ ImmutableList.of(),
/* additionalBuildVariables= */ ImmutableMap.of(),
/* directModuleMaps= */ ImmutableList.of(),
asStringNestedSet(includeDirs),
asStringNestedSet(quoteIncludeDirs),
asStringNestedSet(systemIncludeDirs),
asStringNestedSet(defines),
addLegacyCxxOptions);
}
@Override
public CcToolchainVariables getLinkBuildVariables(
CcToolchainProvider ccToolchainProvider,
FeatureConfiguration featureConfiguration,
Object librarySearchDirectories,
Object runtimeLibrarySearchDirectories,
Object userLinkFlags,
Object outputFile,
Object paramFile,
Object defFile,
boolean isUsingLinkerNotArchiver,
boolean isCreatingSharedLibrary,
boolean mustKeepDebug,
boolean useTestOnlyFlags,
boolean isStaticLinkingMode)
throws EvalException {
return LinkBuildVariables.setupVariables(
isUsingLinkerNotArchiver,
/* binDirectoryPath= */ null,
convertFromNoneable(outputFile, /* defaultValue= */ null),
isCreatingSharedLibrary,
convertFromNoneable(paramFile, /* defaultValue= */ null),
/* thinltoParamFile= */ null,
/* thinltoMergedObjectFile= */ null,
mustKeepDebug,
/* symbolCounts= */ null,
ccToolchainProvider,
featureConfiguration,
useTestOnlyFlags,
/* isLtoIndexing= */ false,
userFlagsToIterable(ccToolchainProvider.getCppConfiguration(), userLinkFlags),
/* interfaceLibraryBuilder= */ null,
/* interfaceLibraryOutput= */ null,
/* ltoOutputRootPrefix= */ null,
convertFromNoneable(defFile, /* defaultValue= */ null),
/* fdoProvider= */ null,
asStringNestedSet(runtimeLibrarySearchDirectories),
/* librariesToLink= */ null,
asStringNestedSet(librarySearchDirectories),
/* isLegacyFullyStaticLinkingMode= */ false,
isStaticLinkingMode,
/* addIfsoRelatedVariables= */ false);
}
@Override
public CcToolchainVariables getVariables() {
return CcToolchainVariables.EMPTY;
}
/**
* Converts an object that can be the NoneType to the actual object if it is not or returns the
* default value if none.
*/
@SuppressWarnings("unchecked")
protected static <T> T convertFromNoneable(Object obj, @Nullable T defaultValue) {
if (EvalUtils.isNullOrNone(obj)) {
return defaultValue;
}
return (T) obj;
}
/** Converts an object that can be ether SkylarkNestedSet or None into NestedSet. */
protected NestedSet<String> asStringNestedSet(Object o) {
SkylarkNestedSet skylarkNestedSet = convertFromNoneable(o, /* defaultValue= */ null);
if (skylarkNestedSet != null) {
return skylarkNestedSet.getSet(String.class);
} else {
return NestedSetBuilder.emptySet(Order.STABLE_ORDER);
}
}
/** Converts an object that can be either SkylarkList, or None into ImmutableList. */
protected ImmutableList<String> asStringImmutableList(Object o) {
SkylarkList skylarkList = convertFromNoneable(o, /* defaultValue= */ null);
if (skylarkList != null) {
return skylarkList.getImmutableList();
} else {
return ImmutableList.of();
}
}
/**
* Converts an object that represents user flags and can be either SkylarkNestedSet , SkylarkList,
* or None into Iterable.
*/
protected Iterable<String> userFlagsToIterable(CppConfiguration cppConfiguration, Object o)
throws EvalException {
if (o instanceof SkylarkNestedSet) {
if (cppConfiguration.disableDepsetInUserFlags()) {
throw new EvalException(
Location.BUILTIN,
"Passing depset into user flags is deprecated (see "
+ "--incompatible_disable_depset_in_cc_user_flags), use list instead.");
}
return asStringNestedSet(o);
} else if (o instanceof SkylarkList) {
return asStringImmutableList(o);
} else if (o instanceof NoneType) {
return ImmutableList.of();
} else {
throw new EvalException(Location.BUILTIN, "Only depset and list is allowed.");
}
}
@Override
public LibraryToLink createLibraryLinkerInput(
SkylarkRuleContext skylarkRuleContext, Artifact library, String skylarkArtifactCategory)
throws EvalException {
CcCommon.checkRuleWhitelisted(skylarkRuleContext);
ArtifactCategory artifactCategory =
ArtifactCategory.fromString(
skylarkArtifactCategory,
skylarkRuleContext.getRuleContext().getRule().getLocation(),
"artifact_category");
return LinkerInputs.opaqueLibraryToLink(
library, artifactCategory, CcLinkingOutputs.libraryIdentifierOf(library));
}
@Override
public LibraryToLink createSymlinkLibraryLinkerInput(
SkylarkRuleContext skylarkRuleContext, CcToolchainProvider ccToolchain, Artifact library) {
Artifact dynamicLibrarySymlink =
SolibSymlinkAction.getDynamicLibrarySymlink(
skylarkRuleContext.getRuleContext(),
ccToolchain.getSolibDirectory(),
library,
/* preserveName= */ true,
/* prefixConsumer= */ true,
skylarkRuleContext.getRuleContext().getConfiguration());
return LinkerInputs.solibLibraryToLink(
dynamicLibrarySymlink, library, CcLinkingOutputs.libraryIdentifierOf(library));
}
@Override
public CcLinkParams createCcLinkParams(
SkylarkRuleContext skylarkRuleContext,
Object skylarkLibrariesToLink,
Object skylarkDynamicLibrariesForRuntime,
Object skylarkUserLinkFlags)
throws EvalException {
CcCommon.checkRuleWhitelisted(skylarkRuleContext);
SkylarkNestedSet librariesToLink = convertFromNoneable(skylarkLibrariesToLink, null);
SkylarkNestedSet dynamicLibrariesForRuntime =
convertFromNoneable(skylarkDynamicLibrariesForRuntime, null);
SkylarkNestedSet userLinkFlags = convertFromNoneable(skylarkUserLinkFlags, null);
CcLinkParams.Builder builder = CcLinkParams.builder();
if (librariesToLink != null) {
builder.addLibraries(librariesToLink.toCollection(LibraryToLink.class));
}
if (dynamicLibrariesForRuntime != null) {
builder.addDynamicLibrariesForRuntime(
dynamicLibrariesForRuntime.toCollection(Artifact.class));
}
if (userLinkFlags != null) {
builder.addLinkOpts(userLinkFlags.toCollection(String.class));
}
return builder.build();
}
@Override
public CcSkylarkInfo createCcSkylarkInfo(Object skylarkRuleContextObject) throws EvalException {
SkylarkRuleContext skylarkRuleContext =
convertFromNoneable(skylarkRuleContextObject, /* defaultValue= */ null);
if (skylarkRuleContext != null) {
CcCommon.checkRuleWhitelisted(skylarkRuleContext);
}
return new CcSkylarkInfo();
}
@SkylarkCallable(
name = "merge_cc_linking_infos",
documented = false,
parameters = {
@Param(
name = "cc_linking_infos",
doc = "cc_linking_infos to be merged.",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class)
})
public CcLinkingInfo mergeCcLinkingInfos(SkylarkList<CcLinkingInfo> ccLinkingInfos) {
return CcLinkingInfo.merge(ccLinkingInfos);
}
@SkylarkCallable(
name = "merge_cc_compilation_infos",
documented = false,
parameters = {
@Param(
name = "cc_compilation_infos",
doc = "cc_compilation_infos to be merged.",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class)
})
public CcCompilationInfo mergeCcCompilationInfos(
SkylarkList<CcCompilationInfo> ccCompilationInfos) {
return CcCompilationInfo.merge(ccCompilationInfos);
}
protected static CompilationInfo compile(
CppSemantics cppSemantics,
SkylarkRuleContext skylarkRuleContext,
Object skylarkFeatureConfiguration,
Object skylarkCcToolchainProvider,
SkylarkList<Artifact> sources,
SkylarkList<Artifact> headers,
Object skylarkIncludes,
Object skylarkCopts,
String generateNoPicOutputs,
String generatePicOutputs,
Object skylarkAdditionalCompilationInputs,
Object skylarkAdditionalIncludeScanningRoots,
SkylarkList<CcCompilationInfo> ccCompilationInfos,
Object purpose)
throws EvalException {
CcCommon.checkRuleWhitelisted(skylarkRuleContext);
RuleContext ruleContext = skylarkRuleContext.getRuleContext();
CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null);
FeatureConfiguration featureConfiguration =
convertFromNoneable(skylarkFeatureConfiguration, null);
Pair<List<Artifact>, List<Artifact>> separatedHeadersAndSources =
separateSourcesFromHeaders(sources);
FdoProvider fdoProvider = ccToolchainProvider.getFdoProvider();
// TODO(plf): Need to flatten the nested set to convert the Strings to PathFragment. This could
// be avoided if path fragments are ever added to Skylark or in the C++ code we take Strings
// instead of PathFragments.
List<String> includeDirs = convertSkylarkListOrNestedSetToList(skylarkIncludes, String.class);
CcCompilationHelper helper =
new CcCompilationHelper(
ruleContext,
cppSemantics,
featureConfiguration,
CcCompilationHelper.SourceCategory.CC,
ccToolchainProvider,
fdoProvider)
.addPublicHeaders(headers)
.addIncludeDirs(
includeDirs
.stream()
.map(PathFragment::create)
.collect(ImmutableList.toImmutableList()))
.addPrivateHeaders(separatedHeadersAndSources.first)
.addSources(separatedHeadersAndSources.second)
.addCcCompilationInfos(ccCompilationInfos)
.setPurpose(convertFromNoneable(purpose, null));
SkylarkNestedSet additionalCompilationInputs =
convertFromNoneable(skylarkAdditionalCompilationInputs, null);
if (additionalCompilationInputs != null) {
helper.addAdditionalCompilationInputs(
additionalCompilationInputs.toCollection(Artifact.class));
}
SkylarkNestedSet additionalIncludeScanningRoots =
convertFromNoneable(skylarkAdditionalIncludeScanningRoots, null);
if (additionalIncludeScanningRoots != null) {
helper.addAditionalIncludeScanningRoots(
additionalIncludeScanningRoots.toCollection(Artifact.class));
}
SkylarkNestedSet copts = convertFromNoneable(skylarkCopts, null);
if (copts != null) {
helper.setCopts(copts.getSet(String.class));
}
Location location = ruleContext.getRule().getLocation();
RegisterActions generateNoPicOption =
RegisterActions.fromString(generateNoPicOutputs, location, "generate_no_pic_outputs");
if (!generateNoPicOption.equals(RegisterActions.CONDITIONALLY)) {
helper.setGenerateNoPicAction(generateNoPicOption == RegisterActions.ALWAYS);
}
RegisterActions generatePicOption =
RegisterActions.fromString(generatePicOutputs, location, "generate_pic_outputs");
if (!generatePicOption.equals(RegisterActions.CONDITIONALLY)) {
helper.setGeneratePicAction(generatePicOption == RegisterActions.ALWAYS);
}
try {
return helper.compile();
} catch (RuleErrorException e) {
throw new EvalException(ruleContext.getRule().getLocation(), e);
}
}
protected static LinkingInfo link(
CppSemantics cppSemantics,
SkylarkRuleContext skylarkRuleContext,
Object skylarkFeatureConfiguration,
Object skylarkCcToolchainProvider,
CcCompilationOutputs ccCompilationOutputs,
Object skylarkLinkopts,
boolean shouldCreateStaticLibraries,
Object dynamicLibrary,
SkylarkList<CcLinkingInfo> skylarkCcLinkingInfos,
boolean neverLink)
throws InterruptedException, EvalException {
CcCommon.checkRuleWhitelisted(skylarkRuleContext);
RuleContext ruleContext = skylarkRuleContext.getRuleContext();
CcToolchainProvider ccToolchainProvider = convertFromNoneable(skylarkCcToolchainProvider, null);
FeatureConfiguration featureConfiguration =
convertFromNoneable(skylarkFeatureConfiguration, null);
FdoProvider fdoProvider = ccToolchainProvider.getFdoProvider();
NestedSet<String> linkopts =
convertSkylarkListOrNestedSetToNestedSet(skylarkLinkopts, String.class);
CcLinkingHelper helper =
new CcLinkingHelper(
ruleContext,
cppSemantics,
featureConfiguration,
ccToolchainProvider,
fdoProvider,
ruleContext.getConfiguration())
.addLinkopts(linkopts)
.setShouldCreateStaticLibraries(shouldCreateStaticLibraries)
.setDynamicLibrary(convertFromNoneable(dynamicLibrary, null))
.addCcLinkingInfos(skylarkCcLinkingInfos)
.setNeverLink(neverLink);
try {
return helper.link(ccCompilationOutputs, CcCompilationContext.EMPTY);
} catch (RuleErrorException e) {
throw new EvalException(ruleContext.getRule().getLocation(), e);
}
}
/**
* TODO(plf): This method exists only temporarily. Once the existing C++ rules have been migrated,
* they should pass sources and headers separately.
*/
private static Pair<List<Artifact>, List<Artifact>> separateSourcesFromHeaders(
Iterable<Artifact> artifacts) {
List<Artifact> headers = new ArrayList<>();
List<Artifact> sources = new ArrayList<>();
for (Artifact artifact : artifacts) {
if (CppFileTypes.CPP_HEADER.matches(artifact.getExecPath())) {
headers.add(artifact);
} else {
sources.add(artifact);
}
}
return Pair.of(headers, sources);
}
/** Converts an object that can be the either SkylarkNestedSet or None into NestedSet. */
@SuppressWarnings("unchecked")
protected Object skylarkListToSkylarkNestedSet(Object o) throws EvalException {
if (o instanceof SkylarkList) {
SkylarkList<String> list = (SkylarkList<String>) o;
SkylarkNestedSet.Builder builder =
SkylarkNestedSet.builder(Order.STABLE_ORDER, Location.BUILTIN);
for (Object entry : list) {
builder.addDirect(entry);
}
return builder.build();
}
return o;
}
@SuppressWarnings("unchecked")
private static <T> List<T> convertSkylarkListOrNestedSetToList(Object o, Class<T> type) {
return o instanceof SkylarkNestedSet
? ((SkylarkNestedSet) o).getSet(type).toList()
: ((SkylarkList) o).getImmutableList();
}
@SuppressWarnings("unchecked")
private static <T> NestedSet<T> convertSkylarkListOrNestedSetToNestedSet(
Object o, Class<T> type) {
return o instanceof SkylarkNestedSet
? ((SkylarkNestedSet) o).getSet(type)
: NestedSetBuilder.wrap(Order.COMPILE_ORDER, (SkylarkList<T>) o);
}
@SkylarkCallable(
name = "create_cc_toolchain_config_info",
documented = false,
parameters = {
@Param(
name = "ctx",
positional = false,
named = true,
type = SkylarkRuleContextApi.class,
doc = "The rule context."),
@Param(
name = "features",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "action_configs",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "artifact_name_patterns",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "cxx_builtin_include_directories",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "toolchain_identifier",
positional = false,
type = String.class,
named = true),
@Param(name = "host_system_name", positional = false, type = String.class, named = true),
@Param(name = "target_system_name", positional = false, type = String.class, named = true),
@Param(name = "target_cpu", positional = false, type = String.class, named = true),
@Param(name = "target_libc", positional = false, type = String.class, named = true),
@Param(name = "compiler", positional = false, type = String.class, named = true),
@Param(name = "abi_version", positional = false, type = String.class, named = true),
@Param(name = "abi_libc_version", positional = false, type = String.class, named = true),
@Param(
name = "supports_gold_linker",
positional = false,
defaultValue = "False",
type = Boolean.class,
named = true),
@Param(
name = "supports_start_end_lib",
positional = false,
type = Boolean.class,
defaultValue = "False",
named = true),
@Param(
name = "supports_interface_shared_objects",
positional = false,
type = Boolean.class,
defaultValue = "False",
named = true),
@Param(
name = "supports_embedded_filegroup",
positional = false,
type = Boolean.class,
defaultValue = "False",
named = true),
@Param(
name = "static_runtime_filegroup",
positional = false,
noneable = true,
defaultValue = "None",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = NoneType.class)},
named = true),
@Param(
name = "dynamic_runtime_filegroup",
positional = false,
noneable = true,
defaultValue = "None",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = NoneType.class)},
named = true),
@Param(
name = "supports_fission",
positional = false,
type = Boolean.class,
defaultValue = "False",
named = true),
@Param(
name = "supports_dsym",
positional = false,
type = Boolean.class,
defaultValue = "False",
named = true),
@Param(
name = "needs_pic",
positional = false,
type = Boolean.class,
defaultValue = "False",
named = true),
@Param(
name = "tool_paths",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "compiler_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "cxx_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "unfiltered_cxx_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "linker_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "dynamic_library_linker_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "test_only_linker_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "objcopy_embed_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "ld_embed_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "compilation_mode_compiler_flags",
positional = false,
named = true,
defaultValue = "{}",
type = SkylarkDict.class),
@Param(
name = "compilation_mode_cxx_flags",
positional = false,
named = true,
defaultValue = "{}",
type = SkylarkDict.class),
@Param(
name = "compilation_mode_linker_flags",
positional = false,
named = true,
defaultValue = "{}",
type = SkylarkDict.class),
@Param(
name = "mostly_static_linking_mode_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "dynamic_linking_mode_flags",
positional = false,
named = true,
defaultValue = "None",
noneable = true,
allowedTypes = {
@ParamType(type = SkylarkList.class),
@ParamType(type = NoneType.class)
}),
@Param(
name = "fully_static_linking_mode_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "mostly_static_libraries_linking_mode_flags",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "make_variables",
positional = false,
named = true,
defaultValue = "[]",
type = SkylarkList.class),
@Param(
name = "builtin_sysroot",
positional = false,
noneable = true,
defaultValue = "None",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = NoneType.class)},
named = true),
@Param(
name = "default_libc_top",
positional = false,
noneable = true,
defaultValue = "None",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = NoneType.class)},
named = true),
@Param(
name = "cc_target_os",
positional = false,
noneable = true,
defaultValue = "None",
allowedTypes = {@ParamType(type = String.class), @ParamType(type = NoneType.class)},
named = true),
})
public CcToolchainConfigInfo ccToolchainConfigInfoFromSkylark(
SkylarkRuleContext skylarkRuleContext,
SkylarkList<Object> features,
SkylarkList<Object> actionConfigs,
SkylarkList<Object> artifactNamePatterns,
SkylarkList<String> cxxBuiltInIncludeDirectories,
String toolchainIdentifier,
String hostSystemName,
String targetSystemName,
String targetCpu,
String targetLibc,
String compiler,
String abiVersion,
String abiLibcVersion,
Boolean supportsGoldLinker,
Boolean supportsStartEndLib,
Boolean supportsInterfaceSharedObjects,
Boolean supportsEmbeddedRuntimes,
Object staticRuntimesFilegroup,
Object dynamicRuntimesFilegroup,
Boolean supportsFission,
Boolean supportsDsym,
Boolean needsPic,
SkylarkList<Object> toolPaths,
SkylarkList<String> compilerFlags,
SkylarkList<String> cxxFlags,
SkylarkList<String> unfilteredCxxFlags,
SkylarkList<String> linkerFlags,
SkylarkList<String> dynamicLibraryLinkerFlags,
SkylarkList<String> testOnlyLinkerFlags,
SkylarkList<String> objcopyEmbedFlags,
SkylarkList<String> ldEmbedFlags,
Object compilationModeCompilerFlagsUnchecked,
Object compilationModeCxxFlagsUnchecked,
Object compilationModeLinkerFlagsUnchecked,
SkylarkList<String> mostlyStaticLinkingModeFlags,
Object dynamicLinkingModeFlags,
SkylarkList<String> fullyStaticLinkingModeFlags,
SkylarkList<String> mostlyStaticLibrariesLinkingModeFlags,
SkylarkList<Object> makeVariables,
Object builtinSysroot,
Object defaultLibcTop,
Object ccTargetOs)
throws InvalidConfigurationException, EvalException {
CppConfiguration config =
skylarkRuleContext.getConfiguration().getFragment(CppConfiguration.class);
if (!config.enableCcToolchainConfigInfoFromSkylark()) {
throw new InvalidConfigurationException("Creating a CcToolchainConfigInfo is not enabled.");
}
if (!skylarkRuleContext.getConfiguration().disableLateBoundOptionDefaults()
|| !config.disableMakeVariables()) {
throw new InvalidConfigurationException(
"--incompatible_disable_late_bound_option_defaults and "
+ "--incompatible_disable_cc_configuration_make_variables must be set to true in "
+ "order to configure the C++ toolchain from Starlark.");
}
ImmutableList.Builder<Feature> featureBuilder = ImmutableList.builder();
for (Object feature : features) {
featureBuilder.add(featureFromSkylark((SkylarkInfo) feature));
}
ImmutableList.Builder<ActionConfig> actionConfigBuilder = ImmutableList.builder();
for (Object actionConfig : actionConfigs) {
actionConfigBuilder.add(actionConfigFromSkylark((SkylarkInfo) actionConfig));
}
ImmutableList.Builder<ArtifactNamePattern> artifactNamePatternBuilder = ImmutableList.builder();
for (Object artifactNamePattern : artifactNamePatterns) {
artifactNamePatternBuilder.add(
artifactNamePatternFromSkylark((SkylarkInfo) artifactNamePattern));
}
ImmutableList.Builder<Pair<String, String>> toolPathPairs = ImmutableList.builder();
for (Object toolPath : toolPaths) {
toolPathPairs.add(toolPathFromSkylark((SkylarkInfo) toolPath));
}
ImmutableList.Builder<Pair<String, String>> makeVariablePairs = ImmutableList.builder();
for (Object makeVariable : makeVariables) {
makeVariablePairs.add(makeVariableFromSkylark((SkylarkInfo) makeVariable));
}
SkylarkList<String> dynamicModeFlags =
convertFromNoneable(dynamicLinkingModeFlags, /* defaultValue= */ null);
boolean hasDynamicLinkingModeFlags = dynamicModeFlags != null;
return new CcToolchainConfigInfo(
actionConfigBuilder.build(),
featureBuilder.build(),
artifactNamePatternBuilder.build(),
ImmutableList.copyOf(cxxBuiltInIncludeDirectories),
toolchainIdentifier,
hostSystemName,
targetSystemName,
targetCpu,
targetLibc,
compiler,
abiVersion,
abiLibcVersion,
supportsGoldLinker,
supportsStartEndLib,
supportsInterfaceSharedObjects,
supportsEmbeddedRuntimes,
convertFromNoneable(staticRuntimesFilegroup, /* defaultValue= */ ""),
convertFromNoneable(dynamicRuntimesFilegroup, /* defaultValue= */ ""),
supportsFission,
supportsDsym,
needsPic,
toolPathPairs.build(),
ImmutableList.copyOf(compilerFlags),
ImmutableList.copyOf(cxxFlags),
ImmutableList.copyOf(unfilteredCxxFlags),
ImmutableList.copyOf(linkerFlags),
ImmutableList.copyOf(dynamicLibraryLinkerFlags),
ImmutableList.copyOf(testOnlyLinkerFlags),
ImmutableList.copyOf(objcopyEmbedFlags),
ImmutableList.copyOf(ldEmbedFlags),
getCompilationModeFlagsFromSkylark(
compilationModeCompilerFlagsUnchecked, "compilation_mode_compiler_flags"),
getCompilationModeFlagsFromSkylark(
compilationModeCxxFlagsUnchecked, "compilation_mode_cxx_flags"),
getCompilationModeFlagsFromSkylark(
compilationModeLinkerFlagsUnchecked, "compilation_mode_linker_flags"),
ImmutableList.copyOf(mostlyStaticLinkingModeFlags),
hasDynamicLinkingModeFlags ? ImmutableList.copyOf(dynamicModeFlags) : ImmutableList.of(),
ImmutableList.copyOf(fullyStaticLinkingModeFlags),
ImmutableList.copyOf(mostlyStaticLibrariesLinkingModeFlags),
makeVariablePairs.build(),
convertFromNoneable(builtinSysroot, /* defaultValue= */ ""),
convertFromNoneable(defaultLibcTop, /* defaultValue= */ ""),
convertFromNoneable(ccTargetOs, /* defaultValue= */ ""),
hasDynamicLinkingModeFlags);
}
/** Checks whether the {@link SkylarkInfo} is of the required type. */
private static void checkRightProviderType(SkylarkInfo provider, String type)
throws EvalException {
String providerType = (String) provider.getValueOrNull("type_name");
if (providerType == null) {
providerType = provider.getProvider().getPrintableName();
}
if (!provider.hasField("type_name") || !provider.getValue("type_name").equals(type)) {
throw new EvalException(
provider.getCreationLoc(),
String.format("Expected object of type '%s', received '%s'.", type, providerType));
}
}
/** Creates a {@link Feature} from a {@link SkylarkInfo}. */
@VisibleForTesting
static Feature featureFromSkylark(SkylarkInfo featureStruct)
throws InvalidConfigurationException, EvalException {
checkRightProviderType(featureStruct, "feature");
String name = getFieldFromSkylarkProvider(featureStruct, "name", String.class);
Boolean enabled = getFieldFromSkylarkProvider(featureStruct, "enabled", Boolean.class);
if (name == null || (name.isEmpty() && !enabled)) {
throw new EvalException(
featureStruct.getCreationLoc(),
"A feature must either have a nonempty 'name' field or be enabled.");
}
if (!name.matches("^[_a-z]*$")) {
throw new EvalException(
featureStruct.getCreationLoc(),
String.format(
"A feature's name must consist solely of lowercase letters and '_', got '%s'", name));
}
ImmutableList.Builder<FlagSet> flagSetBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> flagSets =
getSkylarkProviderListFromSkylarkField(featureStruct, "flag_sets");
for (SkylarkInfo flagSet : flagSets) {
flagSetBuilder.add(flagSetFromSkylark(flagSet));
}
ImmutableList.Builder<EnvSet> envSetBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> envSets =
getSkylarkProviderListFromSkylarkField(featureStruct, "env_sets");
for (SkylarkInfo envSet : envSets) {
envSetBuilder.add(envSetFromSkylark(envSet));
}
ImmutableList.Builder<ImmutableSet<String>> requiresBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> requires =
getSkylarkProviderListFromSkylarkField(featureStruct, "requires");
for (SkylarkInfo featureSetStruct : requires) {
if (!featureSetStruct.hasField("type_name")
|| !featureSetStruct.getValue("type_name").equals("feature_set")) {
throw new EvalException(
featureStruct.getCreationLoc(), "expected object of type 'feature_set'.");
}
ImmutableSet<String> featureSet =
getStringSetFromSkylarkProviderField(featureSetStruct, "features");
requiresBuilder.add(featureSet);
}
ImmutableList<String> implies = getStringListFromSkylarkProviderField(featureStruct, "implies");
ImmutableList<String> provides =
getStringListFromSkylarkProviderField(featureStruct, "provides");
return new Feature(
name,
flagSetBuilder.build(),
envSetBuilder.build(),
enabled,
requiresBuilder.build(),
implies,
provides);
}
/**
* Creates a Pair(name, value) that represents a {@link
* com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.MakeVariable} from a {@link
* SkylarkInfo}.
*/
@VisibleForTesting
static Pair<String, String> makeVariableFromSkylark(SkylarkInfo makeVariableStruct)
throws EvalException {
checkRightProviderType(makeVariableStruct, "make_variable");
String name = getFieldFromSkylarkProvider(makeVariableStruct, "name", String.class);
String value = getFieldFromSkylarkProvider(makeVariableStruct, "value", String.class);
if (name == null || name.isEmpty()) {
throw new EvalException(
makeVariableStruct.getCreationLoc(),
"'name' parameter of make_variable must be a nonempty string.");
}
if (value == null || value.isEmpty()) {
throw new EvalException(
makeVariableStruct.getCreationLoc(),
"'value' parameter of make_variable must be a nonempty string.");
}
return Pair.of(name, value);
}
/**
* Creates a Pair(name, path) that represents a {@link
* com.google.devtools.build.lib.view.config.crosstool.CrosstoolConfig.ToolPath} from a {@link
* SkylarkInfo}.
*/
@VisibleForTesting
static Pair<String, String> toolPathFromSkylark(SkylarkInfo toolPathStruct) throws EvalException {
checkRightProviderType(toolPathStruct, "tool_path");
String name = getFieldFromSkylarkProvider(toolPathStruct, "name", String.class);
String path = getFieldFromSkylarkProvider(toolPathStruct, "path", String.class);
if (name == null || name.isEmpty()) {
throw new EvalException(
toolPathStruct.getCreationLoc(),
"'name' parameter of tool_path must be a nonempty string.");
}
if (path == null || path.isEmpty()) {
throw new EvalException(
toolPathStruct.getCreationLoc(),
"'path' parameter of tool_path must be a nonempty string.");
}
return Pair.of(name, path);
}
/** Creates a {@link VariableWithValue} from a {@link SkylarkInfo}. */
@VisibleForTesting
static VariableWithValue variableWithValueFromSkylark(SkylarkInfo variableWithValueStruct)
throws EvalException {
checkRightProviderType(variableWithValueStruct, "variable_with_value");
String name = getFieldFromSkylarkProvider(variableWithValueStruct, "name", String.class);
String value = getFieldFromSkylarkProvider(variableWithValueStruct, "value", String.class);
if (name == null || name.isEmpty()) {
throw new EvalException(
variableWithValueStruct.getCreationLoc(),
"'name' parameter of variable_with_value must be a nonempty string.");
}
if (value == null || value.isEmpty()) {
throw new EvalException(
variableWithValueStruct.getCreationLoc(),
"'value' parameter of variable_with_value must be a nonempty string.");
}
return new VariableWithValue(name, value);
}
/** Creates an {@link EnvEntry} from a {@link SkylarkInfo}. */
@VisibleForTesting
static EnvEntry envEntryFromSkylark(SkylarkInfo envEntryStruct)
throws InvalidConfigurationException, EvalException {
checkRightProviderType(envEntryStruct, "env_entry");
String key = getFieldFromSkylarkProvider(envEntryStruct, "key", String.class);
String value = getFieldFromSkylarkProvider(envEntryStruct, "value", String.class);
if (key == null || key.isEmpty()) {
throw new EvalException(
envEntryStruct.getCreationLoc(),
"'key' parameter of env_entry must be a nonempty string.");
}
if (value == null || value.isEmpty()) {
throw new EvalException(
envEntryStruct.getCreationLoc(),
"'value' parameter of env_entry must be a nonempty string.");
}
StringValueParser parser = new StringValueParser(value);
return new EnvEntry(key, parser.getChunks());
}
/** Creates a {@link WithFeatureSet} from a {@link SkylarkInfo}. */
@VisibleForTesting
static WithFeatureSet withFeatureSetFromSkylark(SkylarkInfo withFeatureSetStruct)
throws EvalException {
checkRightProviderType(withFeatureSetStruct, "with_feature_set");
ImmutableSet<String> features =
getStringSetFromSkylarkProviderField(withFeatureSetStruct, "features");
ImmutableSet<String> notFeatures =
getStringSetFromSkylarkProviderField(withFeatureSetStruct, "not_features");
return new WithFeatureSet(features, notFeatures);
}
/** Creates an {@link EnvSet} from a {@link SkylarkInfo}. */
@VisibleForTesting
static EnvSet envSetFromSkylark(SkylarkInfo envSetStruct)
throws InvalidConfigurationException, EvalException {
checkRightProviderType(envSetStruct, "env_set");
ImmutableSet<String> actions = getStringSetFromSkylarkProviderField(envSetStruct, "actions");
if (actions.isEmpty()) {
throw new EvalException(
envSetStruct.getCreationLoc(), "actions parameter of env_set must be a nonempty list.");
}
ImmutableList.Builder<EnvEntry> envEntryBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> envEntryStructs =
getSkylarkProviderListFromSkylarkField(envSetStruct, "env_entries");
for (SkylarkInfo envEntryStruct : envEntryStructs) {
envEntryBuilder.add(envEntryFromSkylark(envEntryStruct));
}
ImmutableSet.Builder<WithFeatureSet> withFeatureSetBuilder = ImmutableSet.builder();
ImmutableList<SkylarkInfo> withFeatureSetStructs =
getSkylarkProviderListFromSkylarkField(envSetStruct, "with_features");
for (SkylarkInfo withFeatureSetStruct : withFeatureSetStructs) {
withFeatureSetBuilder.add(withFeatureSetFromSkylark(withFeatureSetStruct));
}
return new EnvSet(actions, envEntryBuilder.build(), withFeatureSetBuilder.build());
}
/** Creates a {@link FlagGroup} from a {@link SkylarkInfo}. */
@VisibleForTesting
static FlagGroup flagGroupFromSkylark(SkylarkInfo flagGroupStruct)
throws InvalidConfigurationException, EvalException {
checkRightProviderType(flagGroupStruct, "flag_group");
ImmutableList.Builder<Expandable> expandableBuilder = ImmutableList.builder();
ImmutableList<String> flags = getStringListFromSkylarkProviderField(flagGroupStruct, "flags");
for (String flag : flags) {
StringValueParser parser = new StringValueParser(flag);
expandableBuilder.add(new Flag(parser.getChunks()));
}
ImmutableList<SkylarkInfo> flagGroups =
getSkylarkProviderListFromSkylarkField(flagGroupStruct, "flag_groups");
for (SkylarkInfo flagGroup : flagGroups) {
expandableBuilder.add(flagGroupFromSkylark(flagGroup));
}
if (flagGroups.size() > 0 && flags.size() > 0) {
throw new EvalException(
flagGroupStruct.getCreationLoc(),
"flag_group must contain either a list of flags or a list of flag_groups.");
}
if (flagGroups.size() == 0 && flags.size() == 0) {
throw new EvalException(
flagGroupStruct.getCreationLoc(), "Both 'flags' and 'flag_groups' are empty.");
}
String iterateOver = getFieldFromSkylarkProvider(flagGroupStruct, "iterate_over", String.class);
String expandIfAvailable =
getFieldFromSkylarkProvider(flagGroupStruct, "expand_if_available", String.class);
String expandIfNotAvailable =
getFieldFromSkylarkProvider(flagGroupStruct, "expand_if_not_available", String.class);
String expandIfTrue =
getFieldFromSkylarkProvider(flagGroupStruct, "expand_if_true", String.class);
String expandIfFalse =
getFieldFromSkylarkProvider(flagGroupStruct, "expand_if_false", String.class);
SkylarkInfo expandIfEqualStruct =
getFieldFromSkylarkProvider(flagGroupStruct, "expand_if_equal", SkylarkInfo.class);
VariableWithValue expandIfEqual =
expandIfEqualStruct == null ? null : variableWithValueFromSkylark(expandIfEqualStruct);
return new FlagGroup(
expandableBuilder.build(),
iterateOver,
expandIfAvailable == null ? ImmutableSet.of() : ImmutableSet.of(expandIfAvailable),
expandIfNotAvailable == null ? ImmutableSet.of() : ImmutableSet.of(expandIfNotAvailable),
expandIfTrue,
expandIfFalse,
expandIfEqual);
}
/** Creates a {@link FlagSet} from a {@link SkylarkInfo}. */
@VisibleForTesting
static FlagSet flagSetFromSkylark(SkylarkInfo flagSetStruct)
throws InvalidConfigurationException, EvalException {
checkRightProviderType(flagSetStruct, "flag_set");
ImmutableSet<String> actions = getStringSetFromSkylarkProviderField(flagSetStruct, "actions");
if (actions.isEmpty()) {
throw new EvalException(
flagSetStruct.getCreationLoc(), "'actions' field of flag_set must be a nonempty list.");
}
ImmutableList.Builder<FlagGroup> flagGroupsBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> flagGroups =
getSkylarkProviderListFromSkylarkField(flagSetStruct, "flag_groups");
for (SkylarkInfo flagGroup : flagGroups) {
flagGroupsBuilder.add(flagGroupFromSkylark(flagGroup));
}
ImmutableSet.Builder<WithFeatureSet> withFeatureSetBuilder = ImmutableSet.builder();
ImmutableList<SkylarkInfo> withFeatureSetStructs =
getSkylarkProviderListFromSkylarkField(flagSetStruct, "with_features");
for (SkylarkInfo withFeatureSetStruct : withFeatureSetStructs) {
withFeatureSetBuilder.add(withFeatureSetFromSkylark(withFeatureSetStruct));
}
return new FlagSet(
actions, ImmutableSet.of(), withFeatureSetBuilder.build(), flagGroupsBuilder.build());
}
/**
* Creates a {@link com.google.devtools.build.lib.rules.cpp.CcToolchainFeatures.Tool} from a
* {@link SkylarkInfo}.
*/
@VisibleForTesting
static CcToolchainFeatures.Tool toolFromSkylark(SkylarkInfo toolStruct) throws EvalException {
checkRightProviderType(toolStruct, "tool");
String toolPathString = getFieldFromSkylarkProvider(toolStruct, "path", String.class);
PathFragment toolPath = toolPathString == null ? null : PathFragment.create(toolPathString);
if (toolPath != null && toolPath.isEmpty()) {
throw new EvalException(
toolStruct.getCreationLoc(), "The 'path' field of tool must be a nonempty string.");
}
ImmutableSet.Builder<WithFeatureSet> withFeatureSetBuilder = ImmutableSet.builder();
ImmutableList<SkylarkInfo> withFeatureSetStructs =
getSkylarkProviderListFromSkylarkField(toolStruct, "with_features");
for (SkylarkInfo withFeatureSetStruct : withFeatureSetStructs) {
withFeatureSetBuilder.add(withFeatureSetFromSkylark(withFeatureSetStruct));
}
ImmutableSet<String> executionRequirements =
getStringSetFromSkylarkProviderField(toolStruct, "execution_requirements");
return new CcToolchainFeatures.Tool(
toolPath, executionRequirements, withFeatureSetBuilder.build());
}
/** Creates an {@link ActionConfig} from a {@link SkylarkInfo}. */
@VisibleForTesting
static ActionConfig actionConfigFromSkylark(SkylarkInfo actionConfigStruct)
throws InvalidConfigurationException, EvalException {
checkRightProviderType(actionConfigStruct, "action_config");
String actionName =
getFieldFromSkylarkProvider(actionConfigStruct, "action_name", String.class);
if (actionName == null || actionName.isEmpty()) {
throw new EvalException(
actionConfigStruct.getCreationLoc(),
"The 'action_name' field of action_config must be a nonempty string.");
}
if (!actionName.matches("^[_a-z]*$")) {
throw new EvalException(
actionConfigStruct.getCreationLoc(),
String.format(
"An action_config's name must consist solely of lowercase letters and '_', got '%s'",
actionName));
}
Boolean enabled = getFieldFromSkylarkProvider(actionConfigStruct, "enabled", Boolean.class);
ImmutableList.Builder<CcToolchainFeatures.Tool> toolBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> toolStructs =
getSkylarkProviderListFromSkylarkField(actionConfigStruct, "tools");
for (SkylarkInfo toolStruct : toolStructs) {
toolBuilder.add(toolFromSkylark(toolStruct));
}
ImmutableList.Builder<FlagSet> flagSetBuilder = ImmutableList.builder();
ImmutableList<SkylarkInfo> flagSets =
getSkylarkProviderListFromSkylarkField(actionConfigStruct, "flag_sets");
for (SkylarkInfo flagSet : flagSets) {
flagSetBuilder.add(flagSetFromSkylark(flagSet));
}
ImmutableList<String> implies =
getStringListFromSkylarkProviderField(actionConfigStruct, "implies");
return new ActionConfig(
actionName, actionName, toolBuilder.build(), flagSetBuilder.build(), enabled, implies);
}
/** Creates an {@link ArtifactNamePattern} from a {@link SkylarkInfo}. */
@VisibleForTesting
static ArtifactNamePattern artifactNamePatternFromSkylark(SkylarkInfo artifactNamePatternStruct)
throws EvalException {
checkRightProviderType(artifactNamePatternStruct, "artifact_name_pattern");
String categoryName =
getFieldFromSkylarkProvider(artifactNamePatternStruct, "category_name", String.class);
if (categoryName == null || categoryName.isEmpty()) {
throw new EvalException(
artifactNamePatternStruct.getCreationLoc(),
"The 'category_name' field of artifact_name_pattern must be a nonempty string.");
}
ArtifactCategory foundCategory = null;
for (ArtifactCategory artifactCategory : ArtifactCategory.values()) {
if (categoryName.equals(artifactCategory.getCategoryName())) {
foundCategory = artifactCategory;
}
}
if (foundCategory == null) {
throw new EvalException(
artifactNamePatternStruct.getCreationLoc(),
String.format("Artifact category %s not recognized.", categoryName));
}
String extension =
getFieldFromSkylarkProvider(artifactNamePatternStruct, "extension", String.class);
if (extension == null || extension.isEmpty()) {
throw new EvalException(
artifactNamePatternStruct.getCreationLoc(),
"The 'extension' field of artifact_name_pattern must be a nonempty string.");
}
if (!foundCategory.getAllowedExtensions().contains(extension)) {
throw new EvalException(
artifactNamePatternStruct.getCreationLoc(),
String.format(
"Unrecognized file extension '%s', allowed extensions are %s,"
+ " please check artifact_name_pattern configuration for %s in your rule.",
extension,
StringUtil.joinEnglishList(foundCategory.getAllowedExtensions(), "or", "'"),
foundCategory.getCategoryName()));
}
String prefix = getFieldFromSkylarkProvider(artifactNamePatternStruct, "prefix", String.class);
if (prefix == null || prefix.isEmpty()) {
throw new EvalException(
artifactNamePatternStruct.getCreationLoc(),
"The 'prefix' field of artifact_name_pattern must be a nonempty string.");
}
return new ArtifactNamePattern(foundCategory, prefix, extension);
}
private static <T> T getFieldFromSkylarkProvider(
SkylarkInfo provider, String fieldName, Class<T> clazz) throws EvalException {
Object obj = provider.getValueOrNull(fieldName);
if (obj == null) {
throw new EvalException(
provider.getCreationLoc(), String.format("Missing mandatory field '%s'", fieldName));
}
if (clazz.isInstance(obj)) {
return clazz.cast(obj);
}
if (NoneType.class.isInstance(obj)) {
return null;
}
throw new EvalException(
provider.getCreationLoc(),
String.format("Field '%s' is not of '%s' type.", fieldName, clazz.getName()));
}
/** Returns a list of strings from a field of a {@link SkylarkInfo}. */
private static ImmutableList<String> getStringListFromSkylarkProviderField(
SkylarkInfo provider, String fieldName) throws EvalException {
return SkylarkList.castSkylarkListOrNoneToList(
provider.getValueOrNull(fieldName), String.class, fieldName)
.stream()
.collect(ImmutableList.toImmutableList());
}
/** Returns a set of strings from a field of a {@link SkylarkInfo}. */
private static ImmutableSet<String> getStringSetFromSkylarkProviderField(
SkylarkInfo provider, String fieldName) throws EvalException {
return SkylarkList.castSkylarkListOrNoneToList(
provider.getValueOrNull(fieldName), String.class, fieldName)
.stream()
.collect(ImmutableSet.toImmutableSet());
}
/** Returns a list of SkylarkInfo providers from a field of a {@link SkylarkInfo}. */
private static ImmutableList<SkylarkInfo> getSkylarkProviderListFromSkylarkField(
SkylarkInfo provider, String fieldName) throws EvalException {
return SkylarkList.castSkylarkListOrNoneToList(
provider.getValueOrNull(fieldName), SkylarkInfo.class, fieldName)
.stream()
.collect(ImmutableList.toImmutableList());
}
private static ImmutableMap<CompilationMode, ImmutableList<String>>
getCompilationModeFlagsFromSkylark(Object compilationModeFlags, String field)
throws EvalException {
Map<String, SkylarkList> compilationModeLinkerFlagsMap =
SkylarkDict.castSkylarkDictOrNoneToDict(
compilationModeFlags, String.class, SkylarkList.class, field);
ImmutableMap.Builder<CompilationMode, ImmutableList<String>> compilationModeFlagsBuilder =
ImmutableMap.builder();
for (Entry<String, SkylarkList> entry : compilationModeLinkerFlagsMap.entrySet()) {
compilationModeFlagsBuilder.put(
CompilationMode.valueOf(entry.getKey()),
ImmutableList.copyOf(
convertSkylarkListOrNestedSetToList(entry.getValue(), String.class)));
}
return compilationModeFlagsBuilder.build();
}
}