aboutsummaryrefslogtreecommitdiff
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
parenta83f3866667bca073c4d4c5d80b4deb5ac05906c (diff)
Add rmdir builtin
-rw-r--r--build2/test/script/builtin.cxx135
-rw-r--r--tests/test/script/builtin/buildfile2
-rw-r--r--tests/test/script/builtin/rm.test71
-rw-r--r--tests/test/script/builtin/rmdir.test125
4 files changed, 292 insertions, 41 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_}
};
diff --git a/tests/test/script/builtin/buildfile b/tests/test/script/builtin/buildfile
index 8c4196d..fe25065 100644
--- a/tests/test/script/builtin/buildfile
+++ b/tests/test/script/builtin/buildfile
@@ -2,4 +2,4 @@
# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
# license : MIT; see accompanying LICENSE file
-./: test{cat echo mkdir rm touch} $b
+./: test{cat echo mkdir rm rmdir touch} $b
diff --git a/tests/test/script/builtin/rm.test b/tests/test/script/builtin/rm.test
index a6de003..937633b 100644
--- a/tests/test/script/builtin/rm.test
+++ b/tests/test/script/builtin/rm.test
@@ -37,21 +37,25 @@
: not-exists
:
- : Removing non-existing file fails.
- :
- $c <<EOI;
- rm a 2>>/~%EOE% == 1
- %rm: unable to remove '.+/test/rm/file/not-exists/test/1/a': .+%
- EOE
- EOI
- $b
+ {
+ : fail
+ :
+ : Removing non-existing file fails.
+ :
+ $c <<EOI;
+ rm a 2>>/~%EOE% == 1
+ %rm: unable to remove '.+/file/not-exists/fail/test/1/a': .+%
+ EOE
+ EOI
+ $b
- : not-exists-force
- :
- : Removing non-existing file succeeds with -f option.
- :
- $c <'rm -f a';
- $b
+ : force
+ :
+ : Removing non-existing file succeeds with -f option.
+ :
+ $c <'rm -f a';
+ $b
+ }
}
: dir
@@ -101,23 +105,26 @@
: outside-scope
:
- : Removing path outside the testscript working directory fails. Need to use
- : a path that unlikely exists (not to remove something useful).
- :
- :
- $c <<EOI;
- rm ../../a/b/c 2>>/~%EOE% == 1
- %rm: '.+/path/outside-scope/a/b/c' is out of working directory '.+/outside-scope/test'%
- EOE
- EOI
- $b
+ : Need to use a path that unlikely exists (not to remove something useful).
+ :
+ {
+ : fail
+ :
+ : Removing path outside the testscript working directory fails.
+ :
+ $c <<EOI;
+ rm ../../a/b/c 2>>/~%EOE% == 1
+ %rm: '.+/path/outside-scope/fail/a/b/c' is out of working directory '.+/path/outside-scope/fail/test'%
+ EOE
+ EOI
+ $b
- : outside-scope-force
- :
- : Removing path outside the testscript working directory succeeds with -f
- : option. Need to use a path that unlikely exists (not to remove something
- : useful).
- :
- $c <'rm -f ../../a/b/c';
- $b
+ : force
+ :
+ : Removing path outside the testscript working directory succeeds with -f
+ : option.
+ :
+ $c <'rm -f ../../a/b/c';
+ $b
+ }
}
diff --git a/tests/test/script/builtin/rmdir.test b/tests/test/script/builtin/rmdir.test
new file mode 100644
index 0000000..7621425
--- /dev/null
+++ b/tests/test/script/builtin/rmdir.test
@@ -0,0 +1,125 @@
+# file : tests/test/script/builtin/rmdir.test
+# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
+# license : MIT; see accompanying LICENSE file
+
+.include ../common.test
+
+: no-args
+:
+{
+ : fail
+ :
+ : Removing with no arguments fails.
+ :
+ $c <'rmdir 2>"rmdir: missing directory" == 1';
+ $b
+
+ : force
+ :
+ : Removing with no arguments succeeds with -f option.
+ :
+ $c <'rmdir -f';
+ $b
+}
+
+: dir
+:
+{
+ : empty-path
+ :
+ : Removing an empty path fails.
+ :
+ $c <<EOI;
+ rmdir '' 2>"rmdir: invalid path ''" == 1
+ EOI
+ $b
+
+ : test-scope
+ :
+ : Removing scope directory fails.
+ :
+ $c <<EOI;
+ rmdir ./ 2>"rmdir: '$~' contains test working directory '$~'" == 1
+ EOI
+ $b
+
+ : outside-scope
+ :
+ : Need to use a path that unlikely exists (not to remove something useful).
+ :
+ {
+ : fail
+ :
+ : Removing directory outside the testscript working directory fails.
+ :
+ $c <<EOI;
+ rmdir ../../a/b/c 2>>/~%EOE% == 1
+ %rmdir: '.+/dir/outside-scope/fail/a/b/c' is out of working directory '.+/dir/outside-scope/fail/test'%
+ EOE
+ EOI
+ $b
+
+ : force
+ :
+ : Removing path outside the testscript working directory succeeds with -f
+ : option.
+ :
+ $c <'rmdir -f ../../a/b/c';
+ $b
+ }
+
+ : exists
+ :
+ : Removing existing directory succeeds.
+ :
+ $c <<EOI;
+ mkdir a &!a;
+ rmdir a
+ EOI
+ $b
+
+ : not-exists
+ :
+ {
+ : fail
+ : Removing non-existing directory fails.
+ :
+ $c <<EOI;
+ rmdir a 2>>/~%EOE% == 1
+ %rmdir: unable to remove '.+/dir/not-exists/fail/test/1/a': .+%
+ EOE
+ EOI
+ $b
+
+ : force
+ :
+ : Removing non-existing directory succeeds with -f option.
+ :
+ $c <'rmdir -f a';
+ $b
+ }
+
+ : not-empty
+ :
+ : Removing non-empty directory fails.
+ :
+ $c <<EOI;
+ mkdir -p a/b;
+ rmdir a 2>>/~%EOE% == 1
+ %rmdir: unable to remove '.+/dir/not-empty/test/1/a': .+%
+ EOE
+ EOI
+ $b
+
+ : not-dir
+ :
+ : Removing not a directory path fails.
+ :
+ $c <<EOI;
+ touch a;
+ rmdir a 2>>/~%EOE% == 1
+ %rmdir: unable to remove '.+/dir/not-dir/test/1/a': .+%
+ EOE
+ EOI
+ $b
+}