Add temporary directory helpers and a createDirectories write option - #3045
Conversation
Add three file system methods to FilePath: - getSystemTemporaryDirectory() returns the platform temporary directory, queried with GetTempPath on Windows and the TMPDIR, TMP, TEMP and TEMPDIR environment variables on POSIX, falling back to /tmp. - createTemporaryDirectory() creates a uniquely-named directory under a given parent, defaulting to the system temporary directory, and throws if it cannot be created. POSIX platforms use mkdtemp(), which names and creates the directory atomically and restricts access to the owner. Windows has no equivalent, so candidate names are passed to CreateDirectory, which fails rather than succeeding on an existing path, and only a collision is retried. - removeDirectory() removes a directory, optionally recursively, classifying entries with lstat or the reparse point attribute so that symbolic links are removed without deleting their targets' contents. Add an XmlWriteOptions::createDirectories flag, which causes writeToXmlFile() to create the parent directory hierarchy of the target file if it does not already exist. Tests cover the default and explicit parent directories, the uniqueness and owner-only permissions of each created directory, the reporting of a creation failure, directory removal with and without recursion, and the round-tripping of a document written to a newly created hierarchy.
|
Is there a reason why these aren't just implemented in terms of std::filesystem wherever possible? Are those inadequate in a way I have not previously appreciated? |
|
@lgritz I was considering exactly that while putting this together - but MaterialX is used in a whole bunch of different places javascript bindings via emscripten, and I wasn't 100% sure that std::filesystem is supported in all the places. Also hoping to land this fairly easily, as a precursor to some other more important work - so didn't want to rock the boat too much. Given you also flagged what I was thinking I'll file an issue and we can come back to std::filesystem conversion as a separate piece of work. In my very cursory investigation with Claude, it did notice that std::filesystem calls throw exceptions that are more specific that std::exception, but the MaterialX pattern for exceptions is to inherit from that. So I wonder if moving to std::filesystem might invalidate some client code where its catching the |
|
Don't let me derail you. In all honestly, I was thinking "gosh, I just minimally wrap std::filesystem for some of these... are there shortcomings I'm unaware of?" I assume these are holdovers from the pre-C++17 days. |
(This work is extracted and refined from #2739)
This adds a small set of file system utilities for creating and removing temporary directories, along with an option for
writeToXmlFile()to create the directory hierarchy of its target file. Together these let tools and tests stagegenerated documents in scratch locations without pre-creating the directory structure themselves, which currently requires each caller to reimplement platform-specific temporary directory logic.
The following specific changes are included:
FilePath::getSystemTemporaryDirectory(), returning the platform temporary directory. This is queried withGetTempPathon Windows, and with theTMPDIR,TMP,TEMPandTEMPDIRenvironment variables on POSIX,falling back to
/tmp. The directory is not created, and its existence is not guaranteed.FilePath::createTemporaryDirectory(), creating a uniquely named directory under a given parent and defaulting to the system temporary directory, and throwing anExceptionif the directory cannot be created. POSIX platforms usemkdtemp(), which generates the name, creates the directory atomically, and restricts access to the owner. Windows has no equivalent, so candidate names are passed toCreateDirectory, which fails rather than succeeding when the path already exists; only a name collision is retried, and any other error is reported.FilePath::removeDirectory(), removing a directory and optionally its contents, and returning whether the removal succeeded. A recursive removal classifies entries withlstator the reparse point attribute, so that symbolic links within the directory are removed without deleting the contents of their targets.XmlWriteOptions::createDirectoriesflag, causingwriteToXmlFile()to create the parent directory hierarchy of the target file if it does not already exist. The flag defaults to false, so the behavior of existing code is unchanged.New cases in
MaterialXTestcover the default and explicit parent directories, the uniqueness and owner-only permissions of each created directory, the reporting of a creation failure under a read-only parent, directory removal with and without recursion, the preservation of a symbolic link's target during a recursive removal, and the round-tripping of a document written into a newly created hierarchy. The permission, read-only parent and symbolic link cases are guarded to POSIX. I have run the full test suite on macOS; the Windows code paths are exercised only by CI.