// 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 sibling
is not specified, file name is relative to "
+ "package directory, otherwise the file is in the same directory as "
+ "sibling
. "
+ "You must create an action that generates the file.
"
+ "Files that are specified in rule's outputs do not need to be declared and are "
+ "available through ctx.outputs.",
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());
}
}
@SkylarkCallable(
name = "declare_directory",
doc =
"Declares that rule or aspect create a directory with the given name, in the "
+ "current package. You must create an action that generates the file.
"
+ "Files that are specified in rule's outputs do not need to be declared and are "
+ "available through ctx.outputs.",
parameters = {
@Param(
name = "filename",
type = String.class,
doc =
"If no 'sibling' provided, path of the new directory, relative "
+ "to the current package. Otherwise a base name for a file "
+ "('sibling' defines a directory)."
),
@Param(
name = "sibling",
doc = "A file that lives in the same directory as the newly declared directory.",
type = Artifact.class,
noneable = true,
positional = false,
named = true,
defaultValue = "None"
)
}
)
public Artifact declareDirectory(String filename, Object sibling) throws EvalException {
context.checkMutable("actions.declare_directory");
if (Runtime.NONE.equals(sibling)) {
return ruleContext.getPackageRelativeTreeArtifact(
PathFragment.create(filename), newFileRoot());
} else {
PathFragment original = ((Artifact) sibling).getRootRelativePath();
PathFragment fragment = original.replaceName(filename);
return ruleContext.getTreeArtifact(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;
}
}