From eab70fae3b9a42bd9dbbaf3072274e2f9d9dcca7 Mon Sep 17 00:00:00 2001 From: Valerii Koval Date: Thu, 23 Sep 2021 23:26:42 +0300 Subject: [PATCH] Properly handle "--keep-build-dir" option in platformio ci command (#4061) This fixes #4011 and possible "FileExists" errors when the "platformio ci" command by safely copying sources to the build folder --- HISTORY.rst | 1 + platformio/commands/ci.py | 11 +++---- tests/commands/test_ci.py | 63 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 5 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 3d31d53e..90089cb4 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -12,6 +12,7 @@ PlatformIO Core 5 ~~~~~~~~~~~~~~~~~~ - Fixed a "KeyError: Invalid board option 'build.cpu'" when using a precompiled library with a board that does not have a CPU field in the manifest (`issue #4056 `_) +- Fixed a "FileExist" error when the `platformio ci `__ command is used in pair with the ``--keep-build-dir`` option (`issue #4011 `_) 5.2.0 (2021-09-13) ~~~~~~~~~~~~~~~~~~ diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 5bc5b38b..58d8fef7 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -122,7 +122,7 @@ def cli( # pylint: disable=too-many-arguments, too-many-branches fs.rmtree(build_dir) -def _copy_contents(dst_dir, contents): +def _copy_contents(dst_dir, contents): # pylint: disable=too-many-branches items = {"dirs": set(), "files": set()} for path in contents: @@ -134,14 +134,15 @@ def _copy_contents(dst_dir, contents): dst_dir_name = os.path.basename(dst_dir) if dst_dir_name == "src" and len(items["dirs"]) == 1: - shutil.copytree(list(items["dirs"]).pop(), dst_dir, symlinks=True) + if not os.path.isdir(dst_dir): + shutil.copytree(list(items["dirs"]).pop(), dst_dir, symlinks=True) else: if not os.path.isdir(dst_dir): os.makedirs(dst_dir) for d in items["dirs"]: - shutil.copytree( - d, os.path.join(dst_dir, os.path.basename(d)), symlinks=True - ) + src_dst_dir = os.path.join(dst_dir, os.path.basename(d)) + if not os.path.isdir(src_dst_dir): + shutil.copytree(d, src_dst_dir, symlinks=True) if not items["files"]: return diff --git a/tests/commands/test_ci.py b/tests/commands/test_ci.py index 0ea22dd6..95b31a8f 100644 --- a/tests/commands/test_ci.py +++ b/tests/commands/test_ci.py @@ -88,6 +88,69 @@ def test_ci_keep_build_dir(clirunner, tmpdir_factory, validate_cliresult): assert "board: metro" in result.output +def test_ci_keep_build_dir_single_src_dir( + clirunner, tmpdir_factory, validate_cliresult +): + build_dir = str(tmpdir_factory.mktemp("ci_build_dir")) + + # Run two times to detect possible "AlreadyExists" errors + for _ in range(2): + result = clirunner.invoke( + cmd_ci, + [ + join("examples", "wiring-blink", "src"), + "-b", + "uno", + "--build-dir", + build_dir, + "--keep-build-dir", + ], + ) + validate_cliresult(result) + + +def test_ci_keep_build_dir_nested_src_dirs( + clirunner, tmpdir_factory, validate_cliresult +): + + build_dir = str(tmpdir_factory.mktemp("ci_build_dir")) + + # Split default Arduino project in two parts + src_dir1 = tmpdir_factory.mktemp("src_1") + src_dir1.join("src1.cpp").write( + """ +void setup() {} +""" + ) + + src_dir2 = tmpdir_factory.mktemp("src_2") + src_dir2.join("src2.cpp").write( + """ +void loop() {} +""" + ) + + src_dir1 = str(src_dir1) + src_dir2 = str(src_dir2) + + # Run two times to detect possible "AlreadyExists" errors + for _ in range(2): + result = clirunner.invoke( + cmd_ci, + [ + src_dir1, + src_dir2, + "-b", + "teensy40", + "--build-dir", + build_dir, + "--keep-build-dir", + ], + ) + + validate_cliresult(result) + + def test_ci_project_conf(clirunner, validate_cliresult): project_dir = join("examples", "wiring-blink") result = clirunner.invoke(