diff options
author | 2017-06-27 14:38:45 +0200 | |
---|---|---|
committer | 2017-06-27 15:06:32 +0200 | |
commit | 2e84e7ccba814253bf1402242c2ba6dab9293c6e (patch) | |
tree | 09594e73f3dcf1218194cdebba86f3114be7fd09 /src/main/java/com/google/devtools | |
parent | c5ddd422180b85e44a39bb7f7b6119be6a44918b (diff) |
Add 'ctx.actions' and implement 'ctx.action.declare_file'.
RELNOTES: None.
PiperOrigin-RevId: 160264501
Diffstat (limited to 'src/main/java/com/google/devtools')
3 files changed, 126 insertions, 3 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java index 74c7682f09..4a956db6aa 100644 --- a/src/main/java/com/google/devtools/build/lib/actions/Artifact.java +++ b/src/main/java/com/google/devtools/build/lib/actions/Artifact.java @@ -99,8 +99,8 @@ import javax.annotation.Nullable; + "Files. If you have a Skylark rule that needs to create a new File, you might need to " + "add the label to the attrs (if it's an input) or the outputs (if it's an output). Then " + "you can access the File through the rule's <a href='ctx.html'>context</a>. You can " - + "also use <a href='ctx.html#new_file'>ctx.new_file</a> to create a new file in the rule " - + "implementation.</p>") + + "also use <a href='actions.html#declare_file'>ctx.actions.declare_file</a> to " + + "declare a new file in the rule implementation.</p>") public class Artifact implements FileType.HasFilename, ActionInput, SkylarkValue, Comparable<Object> { diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java new file mode 100644 index 0000000000..6157392a68 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkActionFactory.java @@ -0,0 +1,110 @@ +// 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; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.Root; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.skylarkinterface.Param; +import com.google.devtools.build.lib.skylarkinterface.SkylarkCallable; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModule; +import com.google.devtools.build.lib.skylarkinterface.SkylarkModuleCategory; +import com.google.devtools.build.lib.skylarkinterface.SkylarkValue; +import com.google.devtools.build.lib.syntax.EvalException; +import com.google.devtools.build.lib.syntax.Printer; +import com.google.devtools.build.lib.syntax.Runtime; +import com.google.devtools.build.lib.vfs.PathFragment; + +/** + * Provides a Skylark interface for all action creation needs. + */ +@SkylarkModule( + name = "actions", + category = SkylarkModuleCategory.BUILTIN, + doc = "Module providing functions to create actions." +) + +public class SkylarkActionFactory implements SkylarkValue { + private final SkylarkRuleContext context; + private RuleContext ruleContext; + + + public SkylarkActionFactory(SkylarkRuleContext context, RuleContext ruleContext) { + this.context = context; + this.ruleContext = ruleContext; + } + + Root newFileRoot() throws EvalException { + return context.isForAspect() + ? ruleContext.getConfiguration().getBinDirectory(ruleContext.getRule().getRepository()) + : ruleContext.getBinOrGenfilesDirectory(); + } + + + @SkylarkCallable( + name = "declare_file", + doc = + "Declares that rule or aspect creates a file with the given filename. " + + "If <code>sibling</code> is not specified, file name is relative to " + + "package directory, otherwise the file is in the same directory as " + + "<code>sibling</code>. " + + "You must create an action that generates the file. <br>" + + "Files that are specified in rule's outputs do not need to be declared and are " + + "available through <a href=\"ctx.html#outputs\">ctx.outputs</a>.", + parameters = { + @Param( + name = "filename", + type = String.class, + doc = + "If no 'sibling' provided, path of the new file, relative " + + "to the current package. Otherwise a base name for a file " + + "('sibling' determines a directory)." + ), + @Param( + name = "sibling", + doc = "A file that lives in the same directory as the newly created file.", + type = Artifact.class, + noneable = true, + positional = false, + named = true, + defaultValue = "None" + ) + } + ) + public Artifact declareFile(String filename, Object sibling) throws EvalException { + context.checkMutable("actions.declareFile"); + if (Runtime.NONE.equals(sibling)) { + return ruleContext.getPackageRelativeArtifact(filename, newFileRoot()); + } else { + PathFragment original = ((Artifact) sibling).getRootRelativePath(); + PathFragment fragment = original.replaceName(filename); + return ruleContext.getDerivedArtifact(fragment, newFileRoot()); + } + } + + @Override + public boolean isImmutable() { + return context.isImmutable(); + } + + @Override + public void write(Appendable buffer, char quotationMark) { + Printer.append(buffer, "actions for"); + context.write(buffer, quotationMark); + } + + void nullify() { + ruleContext = null; + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java index d85a2ecf16..8fee2dda42 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java +++ b/src/main/java/com/google/devtools/build/lib/rules/SkylarkRuleContext.java @@ -169,6 +169,8 @@ public final class SkylarkRuleContext implements SkylarkValue { // after this object has been nullified. private final String ruleLabelCanonicalName; + private final SkylarkActionFactory actionFactory; + // The fields below intended to be final except that they can be cleared by calling `nullify()` // when the object becomes featureless. private RuleContext ruleContext; @@ -195,6 +197,7 @@ public final class SkylarkRuleContext implements SkylarkValue { public SkylarkRuleContext(RuleContext ruleContext, @Nullable AspectDescriptor aspectDescriptor) throws EvalException, InterruptedException { + this.actionFactory = new SkylarkActionFactory(this, ruleContext); this.ruleContext = Preconditions.checkNotNull(ruleContext); this.ruleLabelCanonicalName = ruleContext.getLabel().getCanonicalForm(); this.fragments = new FragmentCollection(ruleContext, ConfigurationTransition.NONE); @@ -293,6 +296,7 @@ public final class SkylarkRuleContext implements SkylarkValue { * rule implementation function has exited). */ public void nullify() { + actionFactory.nullify(); ruleContext = null; fragments = null; hostFragments = null; @@ -611,6 +615,15 @@ public final class SkylarkRuleContext implements SkylarkValue { return DefaultProvider.SKYLARK_CONSTRUCTOR; } + @SkylarkCallable( + name = "actions", + structField = true, + doc = "Functions to declare files and create actions." + ) + public SkylarkActionFactory actions() { + return actionFactory; + } + @SkylarkCallable(name = "created_actions", doc = "For rules with <a href=\"globals.html#rule._skylark_testable\">_skylark_testable" + "</a> set to <code>True</code>, this returns an " @@ -867,7 +880,7 @@ public final class SkylarkRuleContext implements SkylarkValue { } } - private boolean isForAspect() { + boolean isForAspect() { return ruleAttributesCollection != null; } |