From fa5bc957c1e01fa84875651844654bdc32397168 Mon Sep 17 00:00:00 2001
From: "Darryl L. Miles" <darryl.miles@darrylmiles.org>
Date: Sat, 18 Feb 2023 08:51:23 +0000
Subject: [PATCH] -f clean: QoL improvement on Windows concerning file/dir
 removal locking

When using the -f argument be more forgiving with the expectation of a clean
workspace and the expectation of the new sby run being responsible for
directory creation.

This is a usability and quality of life improvement for Windows users where
the OS can implement file and directory locking implicitly.  In the EDA world
it is common to have multiple tools in use at any one time and it can become
tortious to have to close files / exit 3rd party applications to release
locking so sby is happy to rerun.

This change will prevent sby claiming a terminal error has occurred when it
fails to create a directory that already exists.  It also now considers the
environment to be 'clean' (as per -f) if all the non-directory elements of
the file tree have been deleted, leaving potentially an empty a skeleton of
directories.
---
 sbysrc/sby.py      | 15 +++++++++++++--
 sbysrc/sby_core.py |  5 +++--
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/sbysrc/sby.py b/sbysrc/sby.py
index 5052128..20ae345 100644
--- a/sbysrc/sby.py
+++ b/sbysrc/sby.py
@@ -412,6 +412,17 @@ if (workdir is not None) and (len(tasknames) != 1):
     print("ERROR: Exactly one task is required when workdir is specified. Specify the task or use --prefix instead of -d.", file=sys.stderr)
     sys.exit(1)
 
+# Check there are no files in this dir or any of its subdirs
+def check_dirtree_empty_of_files(dir):
+    list = os.listdir(dir)
+    if list:
+        for fn in list:
+            child_dir = os.path.join(dir, fn)
+            if os.path.isdir(child_dir) and check_dirtree_empty_of_files(child_dir):
+                continue
+            return False
+    return True
+
 def start_task(taskloop, taskname):
     sbyconfig, _, _, _ = read_sbyconfig(sbydata, taskname)
 
@@ -446,10 +457,10 @@ def start_task(taskloop, taskname):
 
         if reusedir:
             pass
-        elif os.path.isdir(my_workdir):
+        elif os.path.isdir(my_workdir) and not check_dirtree_empty_of_files(my_workdir):
             print(f"ERROR: Directory '{my_workdir}' already exists, use -f to overwrite the existing directory.")
             sys.exit(1)
-        else:
+        elif not os.path.isdir(my_workdir):
             os.makedirs(my_workdir)
 
     else:
diff --git a/sbysrc/sby_core.py b/sbysrc/sby_core.py
index ae28b6a..4426f4d 100644
--- a/sbysrc/sby_core.py
+++ b/sbysrc/sby_core.py
@@ -920,10 +920,11 @@ class SbyTask(SbyConfig):
     def makedirs(self, path):
         if self.reusedir and os.path.isdir(path):
             rmtree(path, ignore_errors=True)
-        os.makedirs(path)
+        if not os.path.isdir(path):
+            os.makedirs(path)
 
     def copy_src(self):
-        os.makedirs(self.workdir + "/src")
+        self.makedirs(self.workdir + "/src")
 
         for dstfile, lines in self.verbatim_files.items():
             dstfile = self.workdir + "/src/" + dstfile