aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-03-18 00:33:28 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-03-19 16:13:40 +0300
commit5d6ce25142dd4624f785ebf2ffe4cc2afb8d49e0 (patch)
tree47769c7b671d0b93a0ae96c103944da3fcdb62b6
parent91e6f8b321fb470cfffa578dcd7f24669186604f (diff)
Add support for cp builtin -p option
-rw-r--r--build2/test/script/builtin.cxx65
-rw-r--r--doc/testscript.cli14
-rw-r--r--tests/test/script/builtin/cp-dir/cp-file0
-rw-r--r--tests/test/script/builtin/cp.test76
4 files changed, 136 insertions, 19 deletions
diff --git a/build2/test/script/builtin.cxx b/build2/test/script/builtin.cxx
index 80954a1..91d5bf2 100644
--- a/build2/test/script/builtin.cxx
+++ b/build2/test/script/builtin.cxx
@@ -258,6 +258,7 @@ namespace build2
cpfile (scope& sp,
const path& from, const path& to,
bool overwrite,
+ bool attrs,
bool cleanup,
const function<error_record()>& fail)
{
@@ -265,10 +266,15 @@ namespace build2
{
bool exists (file_exists (to));
- cpfile (from, to,
- overwrite
- ? cpflags::overwrite_permissions | cpflags::overwrite_content
- : cpflags::none);
+ cpflags f (
+ overwrite
+ ? cpflags::overwrite_permissions | cpflags::overwrite_content
+ : cpflags::none);
+
+ if (attrs)
+ f |= cpflags::overwrite_permissions | cpflags::copy_timestamps;
+
+ cpfile (from, to, f);
if (!exists && cleanup)
sp.clean ({cleanup_type::always, to}, true);
@@ -288,6 +294,7 @@ namespace build2
static void
cpdir (scope& sp,
const dir_path& from, const dir_path& to,
+ bool attrs,
bool cleanup,
const function<error_record()>& fail)
{
@@ -308,10 +315,20 @@ namespace build2
cpdir (sp,
path_cast<dir_path> (move (f)),
path_cast<dir_path> (move (t)),
+ attrs,
cleanup,
fail);
else
- cpfile (sp, f, t, false, cleanup, fail);
+ cpfile (sp, f, t, false /* overwrite */, attrs, cleanup, fail);
+ }
+
+ // Note that it is essential to copy timestamps and permissions after
+ // the directory content is copied.
+ //
+ if (attrs)
+ {
+ path_permissions (to, path_permissions (from));
+ dir_time (to, dir_time (from));
}
}
catch (const system_error& e)
@@ -321,10 +338,10 @@ namespace build2
}
}
- // cp [--no-cleanup] <src-file> <dst-file>
- // cp [--no-cleanup] -R|-r <src-dir> <dst-dir>
- // cp [--no-cleanup] <src-file>... <dst-dir>/
- // cp [--no-cleanup] -R|-r <src-path>... <dst-dir>/
+ // cp [-p] [--no-cleanup] <src-file> <dst-file>
+ // cp [-p] [--no-cleanup] -R|-r <src-dir> <dst-dir>
+ // cp [-p] [--no-cleanup] <src-file>... <dst-dir>/
+ // cp [-p] [--no-cleanup] -R|-r <src-path>... <dst-dir>/
//
// Note: can be executed synchronously.
//
@@ -353,6 +370,7 @@ namespace build2
// Process options.
//
bool recursive (false);
+ bool attrs (false);
bool cleanup (true);
for (; i != e; ++i)
{
@@ -360,6 +378,8 @@ namespace build2
if (o == "-R" || o == "-r")
recursive = true;
+ else if (o == "-p")
+ attrs = true;
else if (o == "--no-cleanup")
cleanup = false;
else
@@ -406,12 +426,19 @@ namespace build2
if (!recursive)
// Synopsis 1: make a file copy at the specified path.
//
- cpfile (sp, src, dst, true, cleanup, fail);
+ cpfile (sp,
+ src,
+ dst,
+ true /* overwrite */,
+ attrs,
+ cleanup,
+ fail);
else
// Synopsis 2: make a directory copy at the specified path.
//
cpdir (sp,
path_cast<dir_path> (src), path_cast<dir_path> (dst),
+ attrs,
cleanup,
fail);
}
@@ -429,13 +456,20 @@ namespace build2
cpdir (sp,
path_cast<dir_path> (src),
path_cast<dir_path> (dst / src.leaf ()),
+ attrs,
cleanup,
fail);
else
// Synopsis 3: copy a file into the specified directory. Also,
// here we cover synopsis 4 for the source path being a file.
//
- cpfile (sp, src, dst / src.leaf (), true, cleanup, fail);
+ cpfile (sp,
+ src,
+ dst / src.leaf (),
+ true /* overwrite */,
+ attrs,
+ cleanup,
+ fail);
}
}
@@ -612,10 +646,17 @@ namespace build2
if (dir)
cpdir (sp,
path_cast<dir_path> (target), path_cast<dir_path> (link),
+ false,
cleanup,
fail);
else
- cpfile (sp, target, link, false, cleanup, fail);
+ cpfile (sp,
+ target,
+ link,
+ false /* overwrite */,
+ true /* attrs */,
+ cleanup,
+ fail);
}
}
}
diff --git a/doc/testscript.cli b/doc/testscript.cli
index 666d553..353850a 100644
--- a/doc/testscript.cli
+++ b/doc/testscript.cli
@@ -2241,10 +2241,10 @@ Read files in order and write their contents to \c{stdout}. Read from
\h#builtins-cp|\c{cp}|
\
-cp [--no-cleanup] <src-file> <dst-file>
-cp [--no-cleanup] -R|-r <src-dir> <dst-dir>
-cp [--no-cleanup] <src-file>... <dst-dir>/
-cp [--no-cleanup] -R|-r <src-path>... <dst-dir>/
+cp [-p] [--no-cleanup] <src-file> <dst-file>
+cp [-p] [--no-cleanup] -R|-r <src-dir> <dst-dir>
+cp [-p] [--no-cleanup] <src-file>... <dst-dir>/
+cp [-p] [--no-cleanup] -R|-r <src-path>... <dst-dir>/
\
Copy files and/or directories. The first two forms make a copy of a single
@@ -2304,6 +2304,12 @@ files/directories does not exist or if the \i{dst-dir} filesystem entry does
not exist or is not a directory. For a \i{src-path} directory \c{cp} also fails
if the \i{dst-dir/src-name} filesystem entry already exists.
+\dl|
+
+\li|\n\c{-p}
+
+ Copy permissions as well as modification and access times.||
+
Unless the --no-cleanup option is specified, newly created files and
directories that are inside the script working directory are automatically
registered for cleanup.
diff --git a/tests/test/script/builtin/cp-dir/cp-file b/tests/test/script/builtin/cp-dir/cp-file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/test/script/builtin/cp-dir/cp-file
diff --git a/tests/test/script/builtin/cp.test b/tests/test/script/builtin/cp.test
index 4709459..6a26e35 100644
--- a/tests/test/script/builtin/cp.test
+++ b/tests/test/script/builtin/cp.test
@@ -103,9 +103,9 @@
{
: existing
:
- : Test that copy over an existing file doesn't register cleanup. If it does
- : then the file would be removed while leaving the embedded scope, and so
- : the cleanup registered by the outer touch would fail.
+ : Test that copy over an existing file does not register cleanup. If it
+ : does then the file would be removed while leaving the embedded scope,
+ : and so the cleanup registered by the outer touch would fail.
:
$c <<EOI && $b
+touch b
@@ -304,3 +304,73 @@
EOI
}
}
+
+: attrs
+:
+if ($cxx.target.class != 'windows')
+{
+ fs = 's/.+ (\S+\s+\S+\s+\S+)\s+cp-file/\1/p'
+ ds = 's/.+ (\S+\s+\S+\s+\S+)\s+cp-dir/\1/p'
+
+ : copy
+ :
+ {
+ : file
+ :
+ {
+ $c <<"EOI" && $b
+ ls -l $src_base/cp-dir | sed -n -e '$fs' | \
+ set t;
+
+ cp -p $src_base/cp-dir/cp-file ./;
+ ls -l | sed -n -e '$fs' >"\$t"
+ EOI
+ }
+
+ : dir
+ :
+ {
+ $c <<"EOI" && $b
+ ls -l $src_base | sed -n -e '$ds' | \
+ set t;
+
+ cp -p -r $src_base/cp-dir ./;
+ ls -l | sed -n -e '$ds' >"\$t"
+ EOI
+ }
+ }
+
+ : no-copy
+ :
+ {
+ : file
+ :
+ {
+ $c <<"EOI" && $b 2>>~%EOE% != 0
+ ls -l $src_base/cp-dir | sed -n -e '$fs' | \
+ set t;
+
+ cp $src_base/cp-dir/cp-file ./;
+ ls -l | sed -n -e '$fs' >"\$t"
+ EOI
+ %.+ error: sed stdout doesn't match expected%
+ %.+
+ EOE
+ }
+
+ : dir
+ :
+ {
+ $c <<"EOI" && $b 2>>~%EOE% != 0
+ ls -l $src_base | sed -n -e '$ds' | \
+ set t;
+
+ cp -r $src_base/cp-dir ./;
+ ls -l | sed -n -e '$ds' >"\$t"
+ EOI
+ %.+ error: sed stdout doesn't match expected%
+ %.+
+ EOE
+ }
+ }
+}