Skip to content

Commit

Permalink
Add an experimental Starlark API to create extra link time libraries.
Browse files Browse the repository at this point in the history
Restricted to builtins for internal use and unlikely to ever be made public.

PiperOrigin-RevId: 493287412
Change-Id: Ied7ce6fe59e05ccdf71f72808b3ebf4e5841e991
  • Loading branch information
c-mita authored and copybara-github committed Dec 6, 2022
1 parent bc03c76 commit ef37fdb
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import com.google.devtools.build.lib.rules.cpp.Link.LinkTargetType;
import com.google.devtools.build.lib.rules.cpp.Link.LinkingMode;
import com.google.devtools.build.lib.starlarkbuildapi.cpp.CcModuleApi;
import com.google.devtools.build.lib.starlarkbuildapi.cpp.ExtraLinkTimeLibraryApi;
import com.google.devtools.build.lib.util.FileTypeSet;
import com.google.devtools.build.lib.util.Pair;
import com.google.devtools.build.lib.util.StringUtil;
Expand All @@ -89,6 +90,8 @@
import net.starlark.java.eval.NoneType;
import net.starlark.java.eval.Sequence;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkCallable;
import net.starlark.java.eval.StarlarkFunction;
import net.starlark.java.eval.StarlarkInt;
import net.starlark.java.eval.StarlarkList;
import net.starlark.java.eval.StarlarkThread;
Expand Down Expand Up @@ -2609,6 +2612,40 @@ public Sequence<Artifact> getBuildInfo(StarlarkRuleContext ruleContext, Starlark
ruleContext.getRuleContext().getBuildInfo(CppBuildInfo.KEY));
}

@StarlarkMethod(
name = "create_extra_link_time_library",
documented = false,
doc =
"Creates a custom ExtraLinkTimeLibrary object. Extra keyword arguments are passed to the"
+ " provided build function when build_libraries is called. Arguments that are"
+ " depsets will be added transitively when these are combined via"
+ " cc_common.merge_cc_infos. For arguments that are not depsets, only one copy will"
+ " be maintained.",
parameters = {
@Param(name = "build_library_func", positional = false, named = true),
},
extraKeywords = @Param(name = "data"),
useStarlarkThread = true)
public ExtraLinkTimeLibraryApi createExtraLinkTimeLibrary(
StarlarkCallable buildLibraryFunc, Dict<String, Object> dataSetsMap, StarlarkThread thread)
throws EvalException {
if (!isBuiltIn(thread)) {
throw Starlark.errorf(
"Cannot use experimental ExtraLinkTimeLibrary creation API outside of builtins");
}
boolean nonGlobalFunc = false;
if (buildLibraryFunc instanceof StarlarkFunction) {
StarlarkFunction fn = (StarlarkFunction) buildLibraryFunc;
if (fn.getModule().getGlobal(fn.getName()) != fn) {
nonGlobalFunc = true;
}
}
if (nonGlobalFunc) {
throw Starlark.errorf("Passed function must be top-level functions.");
}
return new StarlarkDefinedLinkTimeLibrary(buildLibraryFunc, ImmutableMap.copyOf(dataSetsMap));
}

private ImmutableList<Pair<Artifact, Label>> convertSequenceTupleToPair(Sequence<?> sequenceTuple)
throws EvalException {
return Sequence.cast(sequenceTuple, Tuple.class, "files").stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,7 @@ public static final Builder builder() {
* Builder for {@link ExtraLinkTimeLibraries}.
*/
public static final class Builder {
private Map<Class<? extends ExtraLinkTimeLibrary>, ExtraLinkTimeLibrary.Builder> libraries =
new LinkedHashMap<>();
private final Map<Object, ExtraLinkTimeLibrary.Builder> libraries = new LinkedHashMap<>();

private Builder() {
// Nothing to do.
Expand All @@ -98,19 +97,17 @@ public ExtraLinkTimeLibraries build() {
@CanIgnoreReturnValue
public final Builder addTransitive(ExtraLinkTimeLibraries dep) {
for (ExtraLinkTimeLibrary depLibrary : dep.getExtraLibraries()) {
Class<? extends ExtraLinkTimeLibrary> c = depLibrary.getClass();
libraries.computeIfAbsent(c, k -> depLibrary.getBuilder());
libraries.get(c).addTransitive(depLibrary);
add(depLibrary);
}
return this;
}

/** Add a single library to build. */
@CanIgnoreReturnValue
public final Builder add(ExtraLinkTimeLibrary b) {
Class<? extends ExtraLinkTimeLibrary> c = b.getClass();
libraries.computeIfAbsent(c, k -> b.getBuilder());
libraries.get(c).addTransitive(b);
public final Builder add(ExtraLinkTimeLibrary depLibrary) {
Object key = depLibrary.getKey();
libraries.computeIfAbsent(key, k -> depLibrary.getBuilder());
libraries.get(key).addTransitive(depLibrary);
return this;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,14 @@ BuildLibraryOutput buildLibraries(
Builder getBuilder();

/**
* The Builder interface builds an ExtraLinkTimeLibrary.
* Used to identify the "class" of this Library. The Java class is usually sufficient unless
* behaviour is controlled dynamically.
*/
default Object getKey() {
return this.getClass();
}

/** The Builder interface builds an ExtraLinkTimeLibrary. */
public interface Builder {
/**
* Add the inputs associated with another instance of the same
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
// Copyright 2022 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.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.starlark.StarlarkRuleContext;
import com.google.devtools.build.lib.collect.nestedset.Depset;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException;
import java.util.HashMap;
import java.util.Objects;
import javax.annotation.Nullable;
import net.starlark.java.eval.EvalException;
import net.starlark.java.eval.Mutability;
import net.starlark.java.eval.Starlark;
import net.starlark.java.eval.StarlarkCallable;
import net.starlark.java.eval.StarlarkList;
import net.starlark.java.eval.StarlarkSemantics;
import net.starlark.java.eval.StarlarkThread;
import net.starlark.java.eval.StarlarkValue;
import net.starlark.java.eval.Structure;
import net.starlark.java.eval.Tuple;

/**
* An implementation of ExtraLinkTimeLibrary that uses functions and data passed in from Starlark.
*/
public class StarlarkDefinedLinkTimeLibrary implements ExtraLinkTimeLibrary, Structure {

StarlarkDefinedLinkTimeLibrary(
StarlarkCallable buildLibraryFunction, ImmutableMap<String, Object> objectMap) {
this.buildLibraryFunction = buildLibraryFunction;
this.objectMap = objectMap;
this.key = Key.createKey(buildLibraryFunction, objectMap);
}

// Starlark function to create the output library.
private final StarlarkCallable buildLibraryFunction;

// Map of parameter names to values. Depsets should be combined when merging libraries.
private final ImmutableMap<String, Object> objectMap;

// Key object used to determine the "class" of the library implementation.
// The equals method is used to determine equality.
private final Key key;

@Override
public Key getKey() {
return key;
}

@Override
public ExtraLinkTimeLibrary.Builder getBuilder() {
return new Builder();
}

@Override
public BuildLibraryOutput buildLibraries(
RuleContext ruleContext, boolean staticMode, boolean forDynamicLibrary)
throws RuleErrorException, InterruptedException {
ruleContext.initStarlarkRuleContext();
StarlarkRuleContext starlarkContext = ruleContext.getStarlarkRuleContext();
StarlarkSemantics semantics = starlarkContext.getStarlarkSemantics();

Object response = null;
try (Mutability mu = Mutability.create("extra_link_time_library_build_libraries_function")) {
StarlarkThread thread = new StarlarkThread(mu, semantics);
response =
Starlark.call(
thread,
buildLibraryFunction,
ImmutableList.of(starlarkContext, staticMode, forDynamicLibrary),
objectMap);
} catch (EvalException e) {
throw new RuleErrorException(e);
}
String errorMsg =
buildLibraryFunction.getName()
+ " in "
+ buildLibraryFunction.getLocation()
+ " should return (depset[CcLinkingContext], depset[File])";
if (!(response instanceof Tuple)) {
throw new RuleErrorException(errorMsg);
}
Tuple responseTuple = (Tuple) response;
if (responseTuple.size() != 2) {
throw new RuleErrorException(errorMsg);
}
if (!(responseTuple.get(0) instanceof Depset) || !(responseTuple.get(1) instanceof Depset)) {
throw new RuleErrorException(errorMsg);
}
try {
return new BuildLibraryOutput(
((Depset) responseTuple.get(0)).getSet(CcLinkingContext.LinkerInput.class),
((Depset) responseTuple.get(1)).getSet(Artifact.class));
} catch (Depset.TypeException e) {
throw new RuleErrorException(e);
}
}

@Nullable
@Override
public StarlarkValue getValue(String key) throws EvalException {
return (StarlarkValue) objectMap.get(key);
}

@Override
public ImmutableCollection<String> getFieldNames() {
return objectMap.keySet();
}

@Override
public void setField(String field, Object value) throws EvalException {
throw Starlark.errorf("ExtraLinkLibrary does not support field assignment");
}

@Override
public String getErrorMessageForUnknownField(String field) {
return String.format("No argument '%s' was passed to this ExtraLinkLibrary", field);
}

/**
* Class to identify the "class" of a StarlarkDefinedLinkTimeLibrary. Uses the build function and
* the split between depset and non-depset parameters to determine equality.
*/
private static class Key {

private final Object builderFunction;
private final ImmutableList<String> constantFields;
private final ImmutableList<String> depsetFields;

private Key(
Object builderFunction,
ImmutableList<String> constantFields,
ImmutableList<String> depsetFields) {
this.builderFunction = builderFunction;
this.constantFields = constantFields;
this.depsetFields = depsetFields;
}

public static Key createKey(Object builderFunction, ImmutableMap<String, Object> objectMap) {
ImmutableList.Builder<String> depsetFields = ImmutableList.builder();
ImmutableList.Builder<String> constantFields = ImmutableList.builder();
for (String key : objectMap.keySet()) {
if (objectMap.get(key) instanceof Depset) {
depsetFields.add(key);
} else {
constantFields.add(key);
}
}
return new Key(builderFunction, constantFields.build(), depsetFields.build());
}

@Override
public boolean equals(Object other) {
if (!(other instanceof Key)) {
return false;
}
Key key = (Key) other;
return builderFunction.equals(key.builderFunction)
&& constantFields.equals(key.constantFields)
&& depsetFields.equals(key.depsetFields);
}

@Override
public int hashCode() {
return Objects.hash(builderFunction, constantFields, depsetFields);
}
}

private static class Builder implements ExtraLinkTimeLibrary.Builder {

private StarlarkCallable buildLibraryFunction;

private final HashMap<String, ImmutableList.Builder<Depset>> depsetMapBuilder = new HashMap<>();

private final HashMap<String, Object> constantsMap = new HashMap<>();

private Builder() {}

@Override
public ExtraLinkTimeLibrary build() {
ImmutableMap.Builder<String, Object> builder = new ImmutableMap.Builder<>();
for (String key : depsetMapBuilder.keySet()) {
try {
builder.put(
key,
Depset.depset(
Order.LINK_ORDER.getStarlarkName(),
StarlarkList.immutableOf(),
StarlarkList.immutableCopyOf(depsetMapBuilder.get(key).build()),
StarlarkSemantics.DEFAULT));
} catch (EvalException e) {
// should never happen; exception comes from bad order argument.
throw new IllegalStateException(e);
}
}
builder.putAll(constantsMap);
return new StarlarkDefinedLinkTimeLibrary(buildLibraryFunction, builder.buildOrThrow());
}

@Override
public void addTransitive(ExtraLinkTimeLibrary dep) {
StarlarkDefinedLinkTimeLibrary library = (StarlarkDefinedLinkTimeLibrary) dep;
if (buildLibraryFunction == null) {
buildLibraryFunction = library.buildLibraryFunction;
}
for (String key : library.objectMap.keySet()) {
Object value = library.objectMap.get(key);
if (value instanceof Depset) {
depsetMapBuilder.computeIfAbsent(key, k -> ImmutableList.builder()).add((Depset) value);
} else {
constantsMap.put(key, value);
}
}
}
}
}

0 comments on commit ef37fdb

Please sign in to comment.