-
Notifications
You must be signed in to change notification settings - Fork 4.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add an experimental Starlark API to create extra link time libraries.
Restricted to builtins for internal use and unlikely to ever be made public. PiperOrigin-RevId: 493287412 Change-Id: Ied7ce6fe59e05ccdf71f72808b3ebf4e5841e991
- Loading branch information
1 parent
bc03c76
commit ef37fdb
Showing
4 changed files
with
283 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
233 changes: 233 additions & 0 deletions
233
src/main/java/com/google/devtools/build/lib/rules/cpp/StarlarkDefinedLinkTimeLibrary.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} | ||
} | ||
} | ||
} |