@@ -115,6 +115,8 @@ class InstallOptions:
115115 # Command line arguments
116116 non_interactive : bool = False
117117 refresh_all : bool = False
118+ force_rebuild : bool = False
119+ remove_build_dir : bool = True
118120
119121
120122# Constant for option descriptions
@@ -160,7 +162,9 @@ class InstallOptions:
160162 "jetbrains_run_config_dir" : "Directory where JetBrains run configurations will be stored." ,
161163 "boost_src_dir" : "Directory where the source files of the Boost libraries are located." ,
162164 "non_interactive" : "Whether to use all default options without interactive prompts." ,
163- "refresh_all" : "Call the command to refresh dependencies for all configurations"
165+ "refresh_all" : "Call the command to refresh dependencies for all configurations" ,
166+ "force_rebuild" : "Whether to force a rebuild of all dependencies, even if they are already built." ,
167+ "remove_build_dir" : "Whether to remove the build directory of dependencies after installation." ,
164168}
165169
166170
@@ -466,11 +470,28 @@ def is_macos(self):
466470 """
467471 return os .name == "posix" and sys .platform .startswith ("darwin" )
468472
469- def cmake_workflow (self , src_dir , build_type , build_dir , install_dir , extra_args = None , cc_flags = None , cxx_flags = None ):
473+ def cmake_workflow (self , src_dir , build_type , build_dir , install_dir , extra_args = None , cc_flags = None ,
474+ cxx_flags = None , force_rebuild = False , remove_build_dir = True ):
470475 """
471476 Configures and builds a CMake project.
472477 """
473478
479+ # Check if we can skip the build
480+ if self .is_non_empty_dir (install_dir ):
481+ if force_rebuild or self .prompt_option ("force_rebuild" ):
482+ print (f"Force rebuild requested. Removing existing install directory { install_dir } ." )
483+ shutil .rmtree (install_dir , ignore_errors = True )
484+ if self .is_non_empty_dir (build_dir ):
485+ print (f"Removing existing build directory { build_dir } ." )
486+ shutil .rmtree (build_dir , ignore_errors = True )
487+ else :
488+ print (f"Install directory { install_dir } already exists and is not empty. Skipping build." )
489+ return
490+ if self .is_non_empty_dir (build_dir ):
491+ shutil .rmtree (build_dir , ignore_errors = True )
492+ if self .is_non_empty_dir (install_dir ):
493+ shutil .rmtree (install_dir , ignore_errors = True )
494+
474495 # Adjust any potential CMake flags from extra_args
475496 if cc_flags is None :
476497 cc_flags = ""
@@ -593,6 +614,8 @@ def cmake_workflow(self, src_dir, build_type, build_dir, install_dir, extra_args
593614 if cmake_build_type :
594615 install_args .extend (["--config" , cmake_build_type ])
595616 self .run_cmd (install_args )
617+ if remove_build_dir and self .prompt_option ('remove_build_dir' ):
618+ shutil .rmtree (build_dir , ignore_errors = True )
596619
597620 def is_executable (self , path ):
598621 if not os .path .exists (path ):
@@ -606,6 +629,14 @@ def is_executable(self, path):
606629 else :
607630 return os .access (path , os .X_OK )
608631
632+ def is_non_empty_dir (self , path ):
633+ """
634+ Checks if the given path is a non-empty directory.
635+ :param path: The path to check.
636+ :return: bool: True if the path is a non-empty directory, False otherwise.
637+ """
638+ return os .path .exists (path ) and os .path .isdir (path ) and len (os .listdir (path )) > 0
639+
609640 @lru_cache (maxsize = 1 )
610641 def get_vs_install_locations (self ):
611642 p = os .environ .get ('ProgramFiles(x86)' , r"C:\Program Files (x86)" )
@@ -1087,8 +1118,10 @@ def install_llvm(self):
10871118 self .prompt_option ("llvm_commit" )
10881119 os .makedirs (self .options .llvm_src_dir , exist_ok = True )
10891120 self .run_cmd ([self .options .git_path , "init" ], self .options .llvm_src_dir )
1090- self .run_cmd ([self .options .git_path , "remote" , "add" , "origin" , self .options .llvm_repo ], self .options .llvm_src_dir )
1091- self .run_cmd ([self .options .git_path , "fetch" , "--depth" , "1" , "origin" , self .options .llvm_commit ], self .options .llvm_src_dir )
1121+ self .run_cmd ([self .options .git_path , "remote" , "add" , "origin" , self .options .llvm_repo ],
1122+ self .options .llvm_src_dir )
1123+ self .run_cmd ([self .options .git_path , "fetch" , "--depth" , "1" , "origin" , self .options .llvm_commit ],
1124+ self .options .llvm_src_dir )
10921125 self .run_cmd ([self .options .git_path , "checkout" , "FETCH_HEAD" ], self .options .llvm_src_dir )
10931126
10941127 llvm_subproject_dir = os .path .join (self .options .llvm_src_dir , "llvm" )
@@ -1283,7 +1316,6 @@ def create_cmake_presets(self):
12831316 new_preset ["cacheVariables" ]["GIT_EXECUTABLE" ] = self .options .git_path
12841317 new_preset ["cacheVariables" ]["GIT_ROOT" ] = os .path .dirname (self .options .git_path )
12851318
1286-
12871319 # Update cache variables path prefixes with their relative equivalents
12881320 mrdocs_src_dir_parent = os .path .dirname (self .options .mrdocs_src_dir )
12891321 if mrdocs_src_dir_parent == self .options .mrdocs_src_dir :
@@ -1365,7 +1397,7 @@ def install_mrdocs(self):
13651397 f"-D{ arg } =-fsanitize={ flag_name } -fno-sanitize-recover={ flag_name } -fno-omit-frame-pointer" )
13661398
13671399 self .cmake_workflow (self .options .mrdocs_src_dir , self .options .mrdocs_build_type , self .options .mrdocs_build_dir ,
1368- self .options .mrdocs_install_dir , extra_args )
1400+ self .options .mrdocs_install_dir , extra_args , force_rebuild = True , remove_build_dir = False )
13691401
13701402 if self .options .mrdocs_build_dir and self .prompt_option ("mrdocs_run_tests" ):
13711403 # Look for ctest path relative to the cmake path
@@ -2120,6 +2152,7 @@ def refresh_all(self):
21202152 print (f"Running bootstrap refresh with arguments: { args } " )
21212153 subprocess .run (args , check = True )
21222154
2155+
21232156def get_command_line_args ():
21242157 """
21252158 Parses command line arguments and returns them as a dictionary.
0 commit comments