Skip to content

Commit c4f01e7

Browse files
authored
feat(oiiotool): Added create-dir cmd arg to create dir if it doesn't exist (#4762)
Fixes #4598. It was noted in the issue that running `oiiotool` with output filename inside a folder that doesn't exist will cause OIIO to consume time and resources, but fail eventually due to the folder not existing. Take for example the command: ``` oiiotool --create-dir -i input.png -o ./output_folder/output.png -o ./output_folder_2/output.png ``` During the output action, it creates the directories if needed (`output_folder` and `output_folder_2`), and outputs the images into the folder. Added unit tests. Tested it locally, and it creates both the above folders and outputs the image. --------- Signed-off-by: DharshanV <[email protected]>
1 parent 73be2d8 commit c4f01e7

File tree

7 files changed

+62
-0
lines changed

7 files changed

+62
-0
lines changed

src/doc/oiiotool.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -932,6 +932,11 @@ output each one to a different file, with names `sub0001.tif`,
932932
Sets "no clobber" mode, in which existing images on disk will never be
933933
overridden, even if the `-o` command specifies that file.
934934

935+
.. option:: --create-dir
936+
937+
Create output directories if it doesn't exists already
938+
during the `-o` output action.
939+
935940
.. option:: --threads <n>
936941

937942
Use *n* execution threads if it helps to speed up image operations. The

src/include/OpenImageIO/filesystem.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ inline bool create_directory (string_view path) {
165165
return create_directory (path, err);
166166
}
167167

168+
/// Create directory for every element in `path` that does not already exist.
169+
/// Return true for success, false for failure and place an error message in err.
170+
OIIO_UTIL_API bool create_directories(string_view path, std::string& err) noexcept;
171+
168172
/// Copy a file, directory, or link. It is an error if 'to' already exists.
169173
/// The file names are all UTF-8 encoded. Return true upon success, false upon
170174
/// failure and place an error message in err.

src/libutil/filesystem.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,17 @@ Filesystem::create_directory(string_view path, std::string& err)
382382
return ok;
383383
}
384384

385+
bool
386+
Filesystem::create_directories(string_view path, std::string& err) noexcept
387+
{
388+
error_code ec;
389+
bool ok = filesystem::create_directories(u8path(path), ec);
390+
if (ok)
391+
err.clear();
392+
else
393+
err = ec.message();
394+
return ok;
395+
}
385396

386397
bool
387398
Filesystem::copy(string_view from, string_view to, std::string& err)

src/oiiotool/oiiotool.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5514,6 +5514,26 @@ output_file(Oiiotool& ot, cspan<const char*> argv)
55145514
return;
55155515
}
55165516

5517+
const std::string outputdirpath = Filesystem::parent_path(filename);
5518+
if (!outputdirpath.empty() && !Filesystem::exists(outputdirpath)) {
5519+
if (ot.create_dir) {
5520+
std::string err;
5521+
const bool ok = Filesystem::create_directories(outputdirpath, err);
5522+
if (!ok) {
5523+
ot.errorfmt(command,
5524+
"Failed to create output directory: {}\n\tError: {}",
5525+
outputdirpath, err);
5526+
return;
5527+
}
5528+
} else {
5529+
ot.errorfmt(command,
5530+
"Non-existent output directory: {}\n"
5531+
"\t--create-dir to create missing output directories",
5532+
outputdirpath);
5533+
return;
5534+
}
5535+
}
5536+
55175537
if (ot.noclobber && Filesystem::exists(filename)) {
55185538
ot.warningfmt(command, "{} already exists, not overwriting.", filename);
55195539
return;
@@ -6458,6 +6478,8 @@ Oiiotool::getargs(int argc, char* argv[])
64586478
.help("Do not overwrite existing files");
64596479
ap.arg("--noclobber", &ot.noclobber)
64606480
.hidden(); // synonym
6481+
ap.arg("--create-dir", &ot.create_dir)
6482+
.help("Create output directories if it doesn't exists");
64616483
ap.arg("--threads %d:N")
64626484
.help("Number of threads (default 0 == #cores)")
64636485
.OTACTION(set_threads);

src/oiiotool/oiiotool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ class Oiiotool {
8282
bool skip_bad_frames = false; // Just skip a bad frame, don't exit
8383
bool nostderr = false; // If true, use stdout for errors
8484
bool noerrexit = false; // Don't exit on error
85+
bool create_dir = false;
8586
std::string dumpdata_C_name;
8687
std::string full_command_line;
8788
std::string printinfo_metamatch;

testsuite/oiiotool/ref/out.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ half data[2][2][3] =
5858
{ /* (0, 1): */ { 0.000000000, 0.000000000, 0.000000000 },
5959
/* (1, 1): */ { 1.000000000, 1.000000000, 0.000000000 } },
6060
};
61+
oiiotool ERROR: -o : Non-existent output directory: folder1/folder2
62+
--create-dir to create missing output directories
63+
Full command line was:
64+
> oiiotool --create 2x2 1 -o folder1/folder2/out.tif
65+
folder1/folder2/out.tif : 2 x 2, 1 channel, float tiff
6166
Comparing "filled.tif" and "ref/filled.tif"
6267
PASS
6368
Comparing "autotrim.tif" and "ref/autotrim.tif"

testsuite/oiiotool/run.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#!/usr/bin/env python
2+
import os
3+
import shutil
24

35
# Copyright Contributors to the OpenImageIO project.
46
# SPDX-License-Identifier: Apache-2.0
@@ -239,6 +241,18 @@
239241
command += oiiotool ("-echo dumpdata: --dumpdata dump.exr")
240242
command += oiiotool ("-echo dumpdata:C --dumpdata:C=data dump.exr")
241243

244+
# Test --create-dir
245+
# Remove `folder1/` if it already exists in order to subsequent test run to pass
246+
root_folder = "folder1"
247+
if os.path.exists(root_folder) and os.path.isdir(root_folder):
248+
shutil.rmtree(root_folder)
249+
250+
# Validate -o failed due to missing directory `folder1/folder2/`
251+
command += oiiotool (f"--create 2x2 1 -o {root_folder}/folder2/out.tif")
252+
# Validate -o sucessed with flag `--create-dir` and `out.tif` is valid and inside directory `folder1/folder2/`
253+
command += oiiotool (f"--create-dir --create 2x2 1 -o {root_folder}/folder2/out.tif")
254+
command += oiiotool (f"--info {root_folder}/folder2/out.tif")
255+
242256
# To add more tests, just append more lines like the above and also add
243257
# the new 'feature.tif' (or whatever you call it) to the outputs list,
244258
# below.

0 commit comments

Comments
 (0)