Skip to content

Commit

Permalink
create directory recursively when making dir for profiling files, pat…
Browse files Browse the repository at this point in the history
…ch for idaholab#16277
  • Loading branch information
YaqiWang committed Nov 24, 2020
1 parent ee00f87 commit 600d496
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 5 deletions.
20 changes: 20 additions & 0 deletions framework/include/utils/MooseUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,26 @@ std::string stripExtension(const std::string & s);
*/
std::pair<std::string, std::string> splitFileName(std::string full_file);

/**
* Recursively make directories
* @param dir_name A complete path
* @param throw_on_failure True to throw instead of error out when creating a directory is failed.
*
* The path can be relative like 'a/b/c' or absolute like '/a/b/c'.
* The path is allowed to contain '.' or '..'.
*/
void makedirs(const std::string & dir_name, bool throw_on_failure = false);

/**
* Recursively remove directories from inner-most when the directories are empty
* @param dir_name A complete path
* @param throw_on_failure True to throw instead of error out when deleting a directory is failed.
*
* The path can be relative like 'a/b/c' or absolute like '/a/b/c'.
* The path is allowed to contain '.' or '..'.
*/
void removedirs(const std::string & dir_name, bool throw_on_failure = false);

/**
* Function for converting a camel case name to a name containing underscores.
* @param camel_case_name A string containing camel casing
Expand Down
9 changes: 4 additions & 5 deletions framework/src/base/MooseApp.C
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
#include "libmesh/string_to_enum.h"
#include "libmesh/checkpoint_io.h"
#include "libmesh/mesh_base.h"
#include "libmesh/utility.h"

// System include for dynamic library methods
#ifdef LIBMESH_HAVE_DLOPEN
Expand Down Expand Up @@ -360,8 +359,8 @@ MooseApp::MooseApp(InputParameters parameters)
_cpu_profiling = true;
auto name = MooseUtils::splitFileName(profile_file);
if (!name.first.empty())
Utility::mkdir(name.first.c_str());
if (!name.first.empty() && processor_id() == 0)
MooseUtils::makedirs(name.first.c_str());
if (!ProfilerStart(profile_file.c_str()))
mooseError("CPU profiler is not started properly");
Expand All @@ -376,8 +375,8 @@ MooseApp::MooseApp(InputParameters parameters)
_heap_profiling = true;
auto name = MooseUtils::splitFileName(profile_file);
if (!name.first.empty())
Utility::mkdir(name.first.c_str());
if (!name.first.empty() && processor_id() == 0)
MooseUtils::makedirs(name.first.c_str());
HeapProfilerStart(profile_file.c_str());
if (!IsHeapProfilerRunning())
Expand Down
120 changes: 120 additions & 0 deletions framework/src/utils/MooseUtils.C
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "ExecFlagEnum.h"
#include "InfixIterator.h"

#include "libmesh/utility.h"
#include "libmesh/elem.h"

// External includes
Expand Down Expand Up @@ -337,6 +338,125 @@ splitFileName(std::string full_file)
return std::pair<std::string, std::string>(path, file);
}

void
makedirs(const std::string & dir_name, bool throw_on_failure)
{
// split path into directories with delimiter '/'
std::vector<std::string> split_dir_names;
MooseUtils::tokenize(dir_name, split_dir_names);

auto n = split_dir_names.size();

// remove '.' and '..' when possible
auto i = n;
i = 0;
while (i != n)
{
if (split_dir_names[i] == ".")
{
for (auto j = i + 1; j < n; ++j)
split_dir_names[j - 1] = split_dir_names[j];
--n;
}
else if (i > 0 && split_dir_names[i] == ".." && split_dir_names[i - 1] != "..")
{
for (auto j = i + 1; j < n; ++j)
split_dir_names[j - 2] = split_dir_names[j];
n -= 2;
--i;
}
else
++i;
}
if (n == 0)
return;

split_dir_names.resize(n);

// start creating directories recursively
std::string cur_dir = dir_name[0] == '/' ? "" : ".";
for (auto & dir : split_dir_names)
{
cur_dir += "/" + dir;

if (!pathExists(cur_dir))
{
auto code = Utility::mkdir(cur_dir.c_str());
if (code != 0)
{
std::string msg = "Failed creating directory " + dir_name;
if (throw_on_failure)
throw std::invalid_argument(msg);
else
mooseError(msg);
}
}
}
}

void
removedirs(const std::string & dir_name, bool throw_on_failure)
{
// split path into directories with delimiter '/'
std::vector<std::string> split_dir_names;
MooseUtils::tokenize(dir_name, split_dir_names);

auto n = split_dir_names.size();

// remove '.' and '..' when possible
auto i = n;
i = 0;
while (i != n)
{
if (split_dir_names[i] == ".")
{
for (auto j = i + 1; j < n; ++j)
split_dir_names[j - 1] = split_dir_names[j];
--n;
}
else if (i > 0 && split_dir_names[i] == ".." && split_dir_names[i - 1] != "..")
{
for (auto j = i + 1; j < n; ++j)
split_dir_names[j - 2] = split_dir_names[j];
n -= 2;
--i;
}
else
++i;
}
if (n == 0)
return;

split_dir_names.resize(n);

// start removing directories recursively
std::string base_dir = dir_name[0] == '/' ? "" : ".";
for (i = n; i > 0; --i)
{
std::string cur_dir = base_dir;
auto j = i;
for (j = 0; j < i; ++j)
cur_dir += "/" + split_dir_names[j];

// listDir should return at least '.' and '..'
if (pathExists(cur_dir) && listDir(cur_dir).size() == 2)
{
auto code = rmdir(cur_dir.c_str());
if (code != 0)
{
std::string msg = "Failed removing directory " + dir_name;
if (throw_on_failure)
throw std::invalid_argument(msg);
else
mooseError(msg);
}
}
else
// stop removing
break;
}
}

std::string
camelCaseToUnderscore(const std::string & camel_case_name)
{
Expand Down
48 changes: 48 additions & 0 deletions unit/src/MooseUtilsTest.C
Original file line number Diff line number Diff line change
Expand Up @@ -363,3 +363,51 @@ TEST(MooseUtils, realpath)
// ok as long mooseError is not triggered
MooseUtils::realpath("data/example_file");
}

TEST(MooseUtils, directory)
{
std::string path;

path = "a/b/c";
MooseUtils::makedirs(path);
EXPECT_TRUE(MooseUtils::pathExists(path));
MooseUtils::removedirs(path);
EXPECT_FALSE(MooseUtils::pathExists(path));

// mkdir for an existing directory
path = "a/b/c";
MooseUtils::makedirs(path);
MooseUtils::makedirs(path);
EXPECT_TRUE(MooseUtils::pathExists(path));
MooseUtils::removedirs(path);
EXPECT_FALSE(MooseUtils::pathExists(path));
MooseUtils::removedirs(path);
EXPECT_FALSE(MooseUtils::pathExists(path));

// test ..
path = "no_dir_name_like_this/../../b/c";
MooseUtils::makedirs(path);
EXPECT_TRUE(MooseUtils::pathExists("../b/c"));
MooseUtils::removedirs(path);
EXPECT_FALSE(MooseUtils::pathExists("../b/c"));
EXPECT_FALSE(MooseUtils::pathExists(path));

// test .
path = "./b/c";
MooseUtils::makedirs(path);
EXPECT_TRUE(MooseUtils::pathExists("b/c"));
MooseUtils::removedirs(path);
EXPECT_FALSE(MooseUtils::pathExists("b/c"));
EXPECT_FALSE(MooseUtils::pathExists(path));

// test absolute path
path = "a/b/c";
MooseUtils::makedirs(path);
std::string rpath = MooseUtils::realpath(path);
MooseUtils::removedirs(path);
MooseUtils::makedirs(rpath);
EXPECT_TRUE(MooseUtils::pathExists(path));
MooseUtils::removedirs(rpath);
EXPECT_FALSE(MooseUtils::pathExists(path));
EXPECT_THROW(MooseUtils::makedirs("/should_not_access", true), std::invalid_argument);
}

0 comments on commit 600d496

Please sign in to comment.