aboutsummaryrefslogtreecommitdiff
path: root/build2/test
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2017-01-14 21:06:53 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2017-01-19 17:56:07 +0300
commit78854e832f84d58236ca1009338a62640cd5a543 (patch)
tree77bc1341ecb77a2f28a0979c5678ef4abacdfe2a /build2/test
parenta83f3866667bca073c4d4c5d80b4deb5ac05906c (diff)
Add rmdir builtin
Diffstat (limited to 'build2/test')
-rw-r--r--build2/test/script/builtin.cxx135
1 files changed, 127 insertions, 8 deletions
diff --git a/build2/test/script/builtin.cxx b/build2/test/script/builtin.cxx
index d00a8a1..f911b60 100644
--- a/build2/test/script/builtin.cxx
+++ b/build2/test/script/builtin.cxx
@@ -374,8 +374,8 @@ namespace build2
//
// Remove a file or directory. A path must not be the test working
// directory or its parent directory. It also must not be outside the
- // testscript working directory unless -f option is specified. Note that
- // directories are not removed by default.
+ // testscript working directory unless the -f option is specified. Note
+ // that directories are not removed by default.
//
// -r
// Remove directories recursively. Must be specified to remove even an
@@ -437,14 +437,16 @@ namespace build2
throw failed ();
}
+ const dir_path& wd (sp.wd_path);
+ const dir_path& rwd (sp.root->wd_path);
+
for (; i != args.end (); ++i)
{
- path p (parse_path (*i, sp.wd_path));
+ path p (parse_path (*i, wd));
- const dir_path& wd (sp.root->wd_path);
- if (!p.sub (wd) && !force)
+ if (!p.sub (rwd) && !force)
{
- cerr << "rm: '" << p << "' is out of working directory '" << wd
+ cerr << "rm: '" << p << "' is out of working directory '" << rwd
<< "'" << endl;
throw failed ();
}
@@ -461,10 +463,10 @@ namespace build2
throw failed ();
}
- if (sp.wd_path.sub (d))
+ if (wd.sub (d))
{
cerr << "rm: '" << p << "' contains test working directory '"
- << sp.wd_path << "'" << endl;
+ << wd << "'" << endl;
throw failed ();
}
@@ -510,6 +512,122 @@ namespace build2
return 1;
}
+ // rmdir [-f] <path>...
+ //
+ // Remove a directory. The directory must be empty and not be the test
+ // working directory or its parent directory. It also must not be outside
+ // the testscript working directory unless the -f option is specified.
+ //
+ // -f
+ // Do not fail if no directory is specified, the directory does not
+ // exist, or is outside the script working directory.
+ //
+ // Note: can be executed synchronously.
+ //
+ static uint8_t
+ rmdir (scope& sp,
+ const strings& args,
+ auto_fd in, auto_fd out, auto_fd err) noexcept
+ try
+ {
+ uint8_t r (1);
+ ofdstream cerr (move (err));
+
+ try
+ {
+ in.close ();
+ out.close ();
+
+ auto i (args.begin ());
+
+ // Process options.
+ //
+ bool force (false);
+ for (; i != args.end (); ++i)
+ {
+ if (*i == "-f")
+ force = true;
+ else
+ {
+ if (*i == "--")
+ ++i;
+
+ break;
+ }
+ }
+
+ // Remove directories.
+ //
+ if (i == args.end () && !force)
+ {
+ cerr << "rmdir: missing directory" << endl;
+ throw failed ();
+ }
+
+ const dir_path& wd (sp.wd_path);
+ const dir_path& rwd (sp.root->wd_path);
+
+ for (; i != args.end (); ++i)
+ {
+ dir_path p (path_cast<dir_path> (parse_path (*i, wd)));
+
+ if (wd.sub (p))
+ {
+ cerr << "rmdir: '" << p << "' contains test working directory '"
+ << wd << "'" << endl;
+ throw failed ();
+ }
+
+ if (!p.sub (rwd) && !force)
+ {
+ cerr << "rmdir: '" << p << "' is out of working directory '"
+ << rwd << "'" << endl;
+ throw failed ();
+ }
+
+ try
+ {
+ rmdir_status s (try_rmdir (p));
+
+ if (s == rmdir_status::not_empty)
+ throw system_error (ENOTEMPTY, system_category ());
+ else if (s == rmdir_status::not_exist && !force)
+ throw system_error (ENOENT, system_category ());
+ }
+ catch (const system_error& e)
+ {
+ cerr << "rmdir: unable to remove '" << p << "': " << e << endl;
+ throw failed ();
+ }
+ }
+
+ r = 0;
+ }
+ catch (const invalid_path& e)
+ {
+ cerr << "rmdir: invalid path '" << e.path << "'" << endl;
+ }
+ // Can be thrown while closing in, out or writing to cerr (that's why
+ // need to check its state before writing).
+ //
+ catch (const io_error& e)
+ {
+ if (cerr.good ())
+ cerr << "rmdir: " << e << endl;
+ }
+ catch (const failed&)
+ {
+ // Diagnostics has already been issued.
+ }
+
+ cerr.close ();
+ return r;
+ }
+ catch (const std::exception&)
+ {
+ return 1;
+ }
+
// touch <file>...
//
// Change file access and modification times to the current time. Create
@@ -689,6 +807,7 @@ namespace build2
{"false", &false_},
{"mkdir", &sync_impl<&mkdir>},
{"rm", &sync_impl<&rm>},
+ {"rmdir", &sync_impl<&rmdir>},
{"touch", &sync_impl<&touch>},
{"true", &true_}
};