[engine] Add ctx.io.tempdir()
Change-Id: I87d46e15e3887569c65767899ea9c60cbb0fc8c4
Reviewed-on: https://fuchsia-review.googlesource.com/c/shac-project/shac/+/862002
Commit-Queue: Auto-Submit <auto-submit@fuchsia-infra.iam.gserviceaccount.com>
Fuchsia-Auto-Submit: Marc-Antoine Ruel <maruel@google.com>
Reviewed-by: Oliver Newman <olivernewman@google.com>
diff --git a/doc/stdlib.md b/doc/stdlib.md
index e03d92f..c955fac 100644
--- a/doc/stdlib.md
+++ b/doc/stdlib.md
@@ -133,6 +133,7 @@
Fields:
- read_file
+- tempdir
## ctx.io.read_file
@@ -160,6 +161,11 @@
Content of the file as bytes.
+## ctx.io.tempdir
+
+Returns a new temporary directory.
+
+
## ctx.os
ctx.os is the object that exposes the API to interact with the operating
diff --git a/doc/stdlib.star b/doc/stdlib.star
index 2fa837f..8f7c821 100644
--- a/doc/stdlib.star
+++ b/doc/stdlib.star
@@ -153,6 +153,11 @@
pass
+def _ctx_io_tempdir():
+ """Returns a new temporary directory."""
+ pass
+
+
def _ctx_os_exec(cmd, cwd = None, env = None, allow_network = False, raise_on_failure = True):
"""Runs a command as a subprocess.
@@ -329,6 +334,7 @@
# ctx.io is the object that exposes the API to interact with the file system.
io = struct(
read_file = _ctx_io_read_file,
+ tempdir = _ctx_io_tempdir,
),
# ctx.os is the object that exposes the API to interact with the operating
# system.
diff --git a/internal/engine/run.go b/internal/engine/run.go
index 2110bd0..c2922fd 100644
--- a/internal/engine/run.go
+++ b/internal/engine/run.go
@@ -434,6 +434,9 @@
if err != nil {
return "", err
}
+ if i >= 1000000 {
+ return "", errors.New("too many temporary directories requested")
+ }
p := filepath.Join(s.tmpdir, strconv.Itoa(i))
if err = os.Mkdir(p, 0o700); err != nil {
return "", err
diff --git a/internal/engine/run_test.go b/internal/engine/run_test.go
index 3104ece..7aba9a0 100644
--- a/internal/engine/run_test.go
+++ b/internal/engine/run_test.go
@@ -1212,16 +1212,22 @@
want string
}{
{
- name: "ctx-io-read_file-size.star",
- want: "[//ctx-io-read_file-size.star:16] {\n \"key\":\n",
+ "ctx-io-read_file-size.star",
+ "[//ctx-io-read_file-size.star:16] {\n \"key\":\n",
},
{
- name: "ctx-io-read_file.star",
- want: "[//ctx-io-read_file.star:17] {\"key\": \"value\"}\n",
+ "ctx-io-read_file.star",
+ "[//ctx-io-read_file.star:17] {\"key\": \"value\"}\n",
},
{
- name: "ctx-os-exec-abspath.star",
- want: func() string {
+ "ctx-io-tempdir.star",
+ "[//ctx-io-tempdir.star:16] /0/0\n" +
+ "[//ctx-io-tempdir.star:17] /0/1\n" +
+ "[//ctx-io-tempdir.star:18] /0/2\n",
+ },
+ {
+ "ctx-os-exec-abspath.star",
+ func() string {
// TODO(maruel): Decide if we want to do CRLF translation automatically.
if runtime.GOOS == "windows" {
return "[//ctx-os-exec-abspath.star:17] Hello, world\r\n\n"
@@ -1230,8 +1236,8 @@
}(),
},
{
- name: "ctx-os-exec-env.star",
- want: func() string {
+ "ctx-os-exec-env.star",
+ func() string {
// TODO(maruel): Decide if we want to do CRLF translation automatically.
if runtime.GOOS == "windows" {
return "[//ctx-os-exec-env.star:24] FOO=foo-value\r\nBAR=bar-value\n"
@@ -1240,54 +1246,54 @@
}(),
},
{
- name: "ctx-os-exec-success.star",
- want: "[//ctx-os-exec-success.star:21] retcode: 0\n" +
+ "ctx-os-exec-success.star",
+ "[//ctx-os-exec-success.star:21] retcode: 0\n" +
"[//ctx-os-exec-success.star:22] stdout: hello from stdout\n" +
"[//ctx-os-exec-success.star:23] stderr: hello from stderr\n",
},
{
- name: "ctx-re-allmatches.star",
- want: "[//ctx-re-allmatches.star:17] ()\n" +
+ "ctx-re-allmatches.star",
+ "[//ctx-re-allmatches.star:17] ()\n" +
"[//ctx-re-allmatches.star:19] (match(groups = (\"TODO(foo)\",), offset = 4), match(groups = (\"TODO(bar)\",), offset = 14))\n" +
"[//ctx-re-allmatches.star:21] (match(groups = (\"anc\", \"n\", \"c\"), offset = 0),)\n",
},
{
- name: "ctx-re-match.star",
- want: "[//ctx-re-match.star:17] None\n" +
+ "ctx-re-match.star",
+ "[//ctx-re-match.star:17] None\n" +
"[//ctx-re-match.star:19] match(groups = (\"TODO(foo)\",), offset = 4)\n" +
"[//ctx-re-match.star:21] match(groups = (\"anc\", \"n\", \"c\"), offset = 0)\n" +
"[//ctx-re-match.star:23] match(groups = (\"a\", None), offset = 0)\n",
},
{
- name: "dir-ctx.star",
- want: "[//dir-ctx.star:16] [\"emit\", \"io\", \"os\", \"re\", \"scm\"]\n",
+ "dir-ctx.star",
+ "[//dir-ctx.star:16] [\"emit\", \"io\", \"os\", \"re\", \"scm\"]\n",
},
{
- name: "dir-shac.star",
- want: "[//dir-shac.star:15] [\"check\", \"commit_hash\", \"register_check\", \"version\"]\n",
+ "dir-shac.star",
+ "[//dir-shac.star:15] [\"check\", \"commit_hash\", \"register_check\", \"version\"]\n",
},
{
- name: "print-shac-version.star",
- want: "[//print-shac-version.star:15] " + v + "\n",
+ "print-shac-version.star",
+ "[//print-shac-version.star:15] " + v + "\n",
},
{
- name: "shac-check.star",
- want: "[//shac-check.star:19] str(check): <check hello_world>\n" +
+ "shac-check.star",
+ "[//shac-check.star:19] str(check): <check hello_world>\n" +
"[//shac-check.star:20] type(check): shac.check\n" +
"[//shac-check.star:21] bool(check): True\n" +
"[//shac-check.star:26] hashed: set([<check hello_world>])\n",
},
{
- name: "shac-register_check-object.star",
- want: "[//shac-register_check-object.star:16] running from a check object\n",
+ "shac-register_check-object.star",
+ "[//shac-register_check-object.star:16] running from a check object\n",
},
{
- name: "shac-register_check.star",
- want: "[//shac-register_check.star:16] running\n",
+ "shac-register_check.star",
+ "[//shac-register_check.star:16] running\n",
},
{
- name: "true.star",
- want: "[//true.star:15] True\n",
+ "true.star",
+ "[//true.star:15] True\n",
},
}
want := make([]string, len(data))
diff --git a/internal/engine/runtime_ctx.go b/internal/engine/runtime_ctx.go
index 19a30e0..8a55905 100644
--- a/internal/engine/runtime_ctx.go
+++ b/internal/engine/runtime_ctx.go
@@ -32,6 +32,7 @@
}),
"io": toValue("ctx.io", starlark.StringDict{
"read_file": newBuiltin("ctx.io.read_file", ctxIoReadFile),
+ "tempdir": newBuiltin("ctx.io.tempdir", ctxIoTempdir),
}),
"os": toValue("ctx.os", starlark.StringDict{
"exec": newBuiltin("ctx.os.exec", ctxOsExec),
diff --git a/internal/engine/runtime_ctx_io.go b/internal/engine/runtime_ctx_io.go
index faa4cb1..94001d5 100644
--- a/internal/engine/runtime_ctx_io.go
+++ b/internal/engine/runtime_ctx_io.go
@@ -63,6 +63,17 @@
return starlark.Bytes(b), nil
}
+// ctxIoTempdir implements native function ctx.io.tempdir().
+//
+// Make sure to update //doc/stdlib.star whenever this function is modified.
+func ctxIoTempdir(ctx context.Context, s *shacState, name string, args starlark.Tuple, kwargs []starlark.Tuple) (starlark.Value, error) {
+ if err := starlark.UnpackArgs(name, args, kwargs); err != nil {
+ return nil, err
+ }
+ t, err := s.newTempDir()
+ return starlark.String(t), err
+}
+
// Support functions.
// readFileImpl is similar to os.ReadFile() albeit it limits the amount of data
diff --git a/internal/engine/testdata/print/ctx-io-tempdir.star b/internal/engine/testdata/print/ctx-io-tempdir.star
new file mode 100644
index 0000000..6041047
--- /dev/null
+++ b/internal/engine/testdata/print/ctx-io-tempdir.star
@@ -0,0 +1,20 @@
+# Copyright 2023 The Shac Authors
+#
+# 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.
+
+def cb(ctx):
+ print(ctx.io.tempdir()[-4:])
+ print(ctx.io.tempdir()[-4:])
+ print(ctx.io.tempdir()[-4:])
+
+shac.register_check(cb)