Support optional file extension in TmpFile
TmpFile can now be supplied an optional file extension. This change
was motivated by validation work using the XCode SDK Metal compiler,
which expects a source file to end with the ".metal" extension.
See
https://developer.apple.com/documentation/metal/libraries/understanding_the_metal_shading_language_filename_extension
Bug: tint:535
Change-Id: I5d44baa20ba350530ace46569e238c4627135e51
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/45720
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Ben Clayton <bclayton@google.com>
Commit-Queue: Arman Uguray <armansito@chromium.org>
diff --git a/src/utils/tmpfile.h b/src/utils/tmpfile.h
index fbb66ff..b24fa51 100644
--- a/src/utils/tmpfile.h
+++ b/src/utils/tmpfile.h
@@ -28,7 +28,9 @@
/// Constructor.
/// Creates a new temporary file which can be written to.
/// The temporary file will be automatically deleted on destruction.
- TmpFile();
+ /// @param extension optional file extension to use with the file. The file
+ /// have no extension by default.
+ explicit TmpFile(std::string extension = "");
/// Destructor.
/// Deletes the temporary file.
diff --git a/src/utils/tmpfile_other.cc b/src/utils/tmpfile_other.cc
index 8a90238..8ee60e6 100644
--- a/src/utils/tmpfile_other.cc
+++ b/src/utils/tmpfile_other.cc
@@ -17,7 +17,7 @@
namespace tint {
namespace utils {
-TmpFile::TmpFile() = default;
+TmpFile::TmpFile(std::string) = default;
TmpFile::~TmpFile() = default;
diff --git a/src/utils/tmpfile_posix.cc b/src/utils/tmpfile_posix.cc
index 5bf7804..fac1847 100644
--- a/src/utils/tmpfile_posix.cc
+++ b/src/utils/tmpfile_posix.cc
@@ -15,15 +15,25 @@
#include "src/utils/tmpfile.h"
#include <unistd.h>
+#include <limits>
+
+#include "src/debug.h"
namespace tint {
namespace utils {
namespace {
-std::string TmpFilePath() {
- char name[] = "tint_XXXXXX";
- int file = mkstemp(name);
+std::string TmpFilePath(std::string ext) {
+ // mkstemps requires an `int` for the file extension name but STL represents
+ // size_t. Pre-C++20 there the behavior for unsigned-to-signed conversion
+ // (when the source value exceeds the representable range) is implementation
+ // defined. While such a large file extension is unlikely in practice, we
+ // enforce this here at runtime.
+ TINT_ASSERT(ext.length() <=
+ static_cast<size_t>(std::numeric_limits<int>::max()));
+ std::string name = "tint_XXXXXX" + ext;
+ int file = mkstemps(&name[0], static_cast<int>(ext.length()));
if (file != -1) {
close(file);
return name;
@@ -33,7 +43,8 @@
} // namespace
-TmpFile::TmpFile() : path_(TmpFilePath()) {}
+TmpFile::TmpFile(std::string extension)
+ : path_(TmpFilePath(std::move(extension))) {}
TmpFile::~TmpFile() {
if (!path_.empty()) {
diff --git a/src/utils/tmpfile_test.cc b/src/utils/tmpfile_test.cc
index 44b2f72..5416da5 100644
--- a/src/utils/tmpfile_test.cc
+++ b/src/utils/tmpfile_test.cc
@@ -66,6 +66,25 @@
ASSERT_FALSE(file);
}
+TEST(TmpFileTest, FileExtension) {
+ const std::string kExt = ".foo";
+ std::string path;
+ {
+ TmpFile tmp(kExt);
+ if (!tmp) {
+ GTEST_SKIP() << "Unable create a temporary file";
+ }
+ path = tmp.Path();
+ }
+
+ ASSERT_GT(path.length(), kExt.length());
+ EXPECT_EQ(kExt, path.substr(path.length() - kExt.length()));
+
+ // Check the file has been deleted when it fell out of scope
+ std::ifstream file(path);
+ ASSERT_FALSE(file);
+}
+
} // namespace
} // namespace utils
} // namespace tint
diff --git a/src/utils/tmpfile_windows.cc b/src/utils/tmpfile_windows.cc
index 23fb4b3..764f2ad 100644
--- a/src/utils/tmpfile_windows.cc
+++ b/src/utils/tmpfile_windows.cc
@@ -32,7 +32,7 @@
} // namespace
-TmpFile::TmpFile() : path_(TmpFilePath()) {}
+TmpFile::TmpFile(std::string ext) : path_(TmpFilePath() + ext) {}
TmpFile::~TmpFile() {
if (!path_.empty()) {