From aa731afb731ffb2db0f33127dbe8ba649bf2888d Mon Sep 17 00:00:00 2001 From: Damien Martin-Guillerez Date: Tue, 23 Feb 2016 15:43:12 +0000 Subject: Implements repository_ctx.file repository_ctx.file enable writing random file in the remote repository tree. Issue #893: Step 4 of http://goo.gl/OZV3o0. See http://goo.gl/fD4ZsY. -- MOS_MIGRATED_REVID=115338910 --- .../skylark/SkylarkRepositoryContext.java | 43 +++++++++++++++++++ .../skylark/SkylarkRepositoryContextTest.java | 48 ++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java index 04a868b9d0..c48a964d82 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContext.java @@ -34,6 +34,8 @@ import com.google.devtools.build.skyframe.SkyFunctionException.Transience; import java.io.File; import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Map; @@ -124,6 +126,7 @@ public class SkylarkRepositoryContext { ) public void symlink(SkylarkPath from, SkylarkPath to) throws RepositoryFunctionException { try { + checkInOutputDirectory(to); to.path.createSymbolicLink(from.path); } catch (IOException e) { throw new RepositoryFunctionException( @@ -133,6 +136,46 @@ public class SkylarkRepositoryContext { } } + private void checkInOutputDirectory(SkylarkPath path) throws RepositoryFunctionException { + if (!path.path.getPathString().startsWith(outputDirectory.getPathString())) { + throw new RepositoryFunctionException( + new IOException("Cannot write outside of the output directory for path " + path), + Transience.TRANSIENT); + } + } + + @SkylarkCallable(name = "file", documented = false) + public void createFile(SkylarkPath path) throws RepositoryFunctionException { + createFile(path, ""); + } + + @SkylarkCallable( + name = "file", + doc = "Generate a file in the output directory with the provided content" + ) + public void createFile(SkylarkPath path, String content) throws RepositoryFunctionException { + try { + checkInOutputDirectory(path); + makeDirectories(path.path); + try (OutputStream stream = path.path.getOutputStream()) { + stream.write(content.getBytes(StandardCharsets.UTF_8)); + } + } catch (IOException e) { + throw new RepositoryFunctionException(e, Transience.TRANSIENT); + } + } + + // Create parent directories for the given path + private void makeDirectories(Path path) throws IOException { + if (!path.isRootDirectory()) { + Path parent = path.getParentDirectory(); + if (!parent.exists()) { + makeDirectories(path.getParentDirectory()); + parent.createDirectory(); + } + } + } + @SkylarkCallable( name = "os", structField = true, diff --git a/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java b/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java index e2db60ace4..22b185e603 100644 --- a/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java +++ b/src/test/java/com/google/devtools/build/lib/bazel/repository/skylark/SkylarkRepositoryContextTest.java @@ -15,15 +15,18 @@ package com.google.devtools.build.lib.bazel.repository.skylark; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.io.CharStreams; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.packages.Attribute; import com.google.devtools.build.lib.packages.Package; import com.google.devtools.build.lib.packages.Rule; import com.google.devtools.build.lib.packages.RuleClass; import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; +import com.google.devtools.build.lib.rules.repository.RepositoryFunction.RepositoryFunctionException; import com.google.devtools.build.lib.syntax.Argument.Passed; import com.google.devtools.build.lib.syntax.BuiltinFunction; import com.google.devtools.build.lib.syntax.FuncallExpression; @@ -38,6 +41,9 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; import java.util.Map; /** @@ -115,4 +121,46 @@ public class SkylarkRepositoryContextTest { assertThat(context.which("true").toString()).isEqualTo("/bin/true"); assertThat(context.which("false").toString()).isEqualTo("/path/sbin/false"); } + + @Test + public void testFile() throws Exception { + setUpContexForRule("test"); + context.createFile(context.path("foobar")); + context.createFile(context.path("foo/bar"), "foobar"); + context.createFile(context.path("bar/foo/bar")); + + testOutputFile(outputDirectory.getChild("foobar"), ""); + testOutputFile(outputDirectory.getRelative("foo/bar"), "foobar"); + testOutputFile(outputDirectory.getRelative("bar/foo/bar"), ""); + + try { + context.createFile(context.path("/absolute")); + fail("Expected error on creating path outside of the output directory"); + } catch (RepositoryFunctionException ex) { + assertThat(ex.getCause().getMessage()) + .isEqualTo("Cannot write outside of the output directory for path /absolute"); + } + try { + context.createFile(context.path("../somepath")); + fail("Expected error on creating path outside of the output directory"); + } catch (RepositoryFunctionException ex) { + assertThat(ex.getCause().getMessage()) + .isEqualTo("Cannot write outside of the output directory for path /somepath"); + } + try { + context.createFile(context.path("foo/../../somepath")); + fail("Expected error on creating path outside of the output directory"); + } catch (RepositoryFunctionException ex) { + assertThat(ex.getCause().getMessage()) + .isEqualTo("Cannot write outside of the output directory for path /somepath"); + } + } + + private void testOutputFile(Path path, String content) throws IOException { + assertThat(path.exists()).isTrue(); + assertThat( + CharStreams.toString( + new InputStreamReader(path.getInputStream(), StandardCharsets.UTF_8))) + .isEqualTo(content); + } } -- cgit v1.2.3