pw_toolchain: Fix cp command for Windows

The `cp` executable is not always present on the Windows machine.

This commit adds the fallback mechanism based on the python script
to simulate the desired behaviour of `cp -af` command. The original
behaviour has been kept untouched.

Change-Id: I423542abfdf960ab29ba719f889bf143b462411b
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/29780
Reviewed-by: Michael Spang <spang@google.com>
Commit-Queue: Michael Spang <spang@google.com>
diff --git a/pw_toolchain/py/BUILD.gn b/pw_toolchain/py/BUILD.gn
new file mode 100644
index 0000000..7a27fb7
--- /dev/null
+++ b/pw_toolchain/py/BUILD.gn
@@ -0,0 +1,26 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+import("$dir_pw_build/python.gni")
+
+pw_python_package("py") {
+  setup = [ "setup.py" ]
+  sources = [
+    "pw_toolchain/__init__.py",
+    "pw_toolchain/copy_with_metadata.py",
+  ]
+  pylintrc = "$dir_pigweed/.pylintrc"
+}
diff --git a/pw_toolchain/py/pw_toolchain/__init__.py b/pw_toolchain/py/pw_toolchain/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pw_toolchain/py/pw_toolchain/__init__.py
diff --git a/pw_toolchain/py/pw_toolchain/copy_with_metadata.py b/pw_toolchain/py/pw_toolchain/copy_with_metadata.py
new file mode 100644
index 0000000..59fde19
--- /dev/null
+++ b/pw_toolchain/py/pw_toolchain/copy_with_metadata.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""Emulation of `cp -af src dest`."""
+
+import logging
+import os
+import shutil
+import stat
+import sys
+
+_LOG = logging.getLogger(__name__)
+
+
+def copy_with_metadata(src, dest):
+    """Emulation of `cp -af in out` command."""
+    if not os.path.exists(src):
+        _LOG.error('No such file or directory.')
+        return -1
+
+    try:
+        if os.path.isdir(src):
+            shutil.copytree(src, dest, symlinks=True)
+        else:
+            shutil.copy2(src, dest, follow_symlinks=False)
+    except:
+        _LOG.error('Error during copying procedure.')
+        return -1
+
+    return 0
+
+
+def main():
+    # Require exactly two arguments, source and destination.
+    if (len(sys.argv) - 1) != 2:
+        _LOG.error('Incorrect parameters provided.')
+        return -1
+
+    return copy_with_metadata(sys.argv[1], sys.argv[2])
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/pw_toolchain/py/pw_toolchain/py.typed b/pw_toolchain/py/pw_toolchain/py.typed
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pw_toolchain/py/pw_toolchain/py.typed
diff --git a/pw_toolchain/py/setup.py b/pw_toolchain/py/setup.py
new file mode 100644
index 0000000..088fe50
--- /dev/null
+++ b/pw_toolchain/py/setup.py
@@ -0,0 +1,25 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+"""pw_toolchain"""
+
+import setuptools  # type: ignore
+
+setuptools.setup(name='pw_toolchain',
+                 version='0.0.1',
+                 author='Pigweed Authors',
+                 author_email='pigweed-developers@googlegroups.com',
+                 description='Pigweed toolchain wrapper',
+                 packages=setuptools.find_packages(),
+                 package_data={'pw_toolchain': ['py.typed']},
+                 zip_safe=False)
diff --git a/pw_toolchain/universal_tools.gni b/pw_toolchain/universal_tools.gni
index 8fcc062..820276d 100644
--- a/pw_toolchain/universal_tools.gni
+++ b/pw_toolchain/universal_tools.gni
@@ -12,9 +12,18 @@
 # License for the specific language governing permissions and limitations under
 # the License.
 
+import("//build_overrides/pigweed.gni")
+
 pw_universal_copy = {
   if (host_os == "win") {
-    command = "cp -af {{source}} {{output}}"
+    cp_command = "cp -af {{source}} {{output}}"
+
+    # Use python script in absence of cp command.
+    copy_tool_path =
+        rebase_path(dir_pw_toolchain) + "/py/pw_toolchain/copy_with_metadata.py"
+    fallback_command = "python $copy_tool_path {{source}} {{output}}"
+
+    command = "cmd /c \"($cp_command > NUL 2>&1) || ($fallback_command)\""
   } else {
     # Use a hard link if possible as this is faster. Also, Mac doesn't
     # preserve timestamps properly with cp -af.