def migrate_folder(source, target): try: create_link = False if not os.path.isdir(source): logger.error(f"[{source}] does not exist or is not a directory.") return source_backup = f"{source}.sav" # seems Python does not support dictory junction well (os.path.islink is always False) # use isdir AND !exists to guess dictory junction condition if os.path.exists(source): # not a junction, rename source first before copying logger.info(f"Renaming {source} to {source_backup}...") os.rename(source, source_backup) create_link = True # copy folder content only if target not created yet if not os.path.exists(target): logger.info(f"Copying content from {source_backup} to {target}...") shutil.copytree(source_backup, target, copy_function=shutil.copy) if create_link: logger.info(f"Creating link from {source} to {source_backup}...") #win32api.CreateSymbolicLink(source, target) _winapi.CreateJunction(target, source) except Exception: logger.error("Failed to migrate from {source} to {target}.", exc_info=True)
def SymlinkAction(target, source, env): target = target if is_List(target) else [target] source = source if is_List(source) else [source] if len(target) != 1 or len(source) != 1: raise UserError("Symlink only takes a single target and source") abs_src = os.path.abspath(str(source[0])) abs_trg = os.path.abspath(str(target[0])) if not os.path.isdir(abs_src): raise UserError( "Only folder symlink are allowed due to Windows limitation") try: os.unlink(abs_trg) except Exception: pass if env["HOST_OS"] == "win32": try: import _winapi _winapi.CreateJunction(abs_src, abs_trg) except Exception as e: raise UserError( f"Can't do a NTFS junction as symlink fallback ({abs_src} -> {abs_trg})" ) from e else: try: os.symlink(abs_src, abs_trg) except Exception as e: raise UserError( f"Can't create symlink ({abs_src} -> {abs_trg})") from e
def make_directory_symlink(new_symlink_file_path: Path, existing_dir: Path) -> Path: gflogging.log( f"symlink: from {str(new_symlink_file_path)} to {str(existing_dir)}") check(existing_dir.is_dir(), AssertionError(f"Not a directory: {existing_dir}")) file_mkdirs_parent(new_symlink_file_path) # symlink_to takes a path relative to the location of the new file (or an absolute path, but we avoid this). symlink_contents = os.path.relpath(str(existing_dir), start=str(new_symlink_file_path.parent)) try: new_symlink_file_path.symlink_to(symlink_contents, target_is_directory=True) except OSError: if get_platform() != "Windows": raise # Retry using junctions under Windows. try: # noinspection PyUnresolvedReferences import _winapi # pylint: disable=import-error,import-outside-toplevel; # Unlike symlink_to, CreateJunction takes a path relative to the current directory. _winapi.CreateJunction(str(existing_dir), str(new_symlink_file_path)) return new_symlink_file_path except ModuleNotFoundError: pass raise return new_symlink_file_path
def symlink(target, link): """os.symlink() but use a junction point on Windows.""" if islink(link): unlink(link) if platform.system() == "Windows": _winapi.CreateJunction(target, link) else: os.symlink(target, link) # pylint: disable=no-member
def _symlink_compat(conanfile, src, dst): """On Windows, symlinks require admin privileges, so we use a directory junction instead""" if conanfile.settings.os == "Windows": import _winapi _winapi.CreateJunction(str(src), str(dst)) else: os.symlink(src, dst)
def make_link(point_from, point_to): if os.path.exists(point_from): print('Already linked:', point_from, '<--->', point_to) return if platform.system() == 'Windows': import _winapi _winapi.CreateJunction(point_to, point_from) else: os.symlink(point_to, point_from, True)
def create_link_in_user_addon_directory(directory, link_path): if os.path.exists(link_path): os.remove(link_path) if sys.platform == "win32": import _winapi _winapi.CreateJunction(str(directory), str(link_path)) else: os.symlink(str(directory), str(link_path), target_is_directory=True)
def __init__(self, *args: str, **kwargs: str) -> None: # https://stackoverflow.com/a/19102520 super().__init__(*args, **kwargs) self.windows = platform.system() == "Windows" if self.windows: self.exeext = ".exe" else: self.exeext = "" self.repository_root_dir = os.path.dirname( os.path.dirname(os.path.realpath(__file__))) self.user_scripts_dir = tempfile.mkdtemp() os.mkdir(os.path.join(self.user_scripts_dir, "addons")) addon_dir = os.path.join(self.user_scripts_dir, "addons", "io_scene_vrm_saturday06") if self.windows: import _winapi _winapi.CreateJunction(self.repository_root_dir, addon_dir) else: os.symlink(self.repository_root_dir, addon_dir) test_vrm_dir = os.environ.get( "BLENDER_VRM_TEST_VRM_DIR", os.path.join(self.repository_root_dir, "tests", "vrm"), ) command = [self.find_blender_command(), "--version"] completed_process = subprocess.run( command, check=False, capture_output=True, ) stdout_str = self.process_output_to_str(completed_process.stdout) stderr_str = self.process_output_to_str(completed_process.stderr) output = ("\n ".join(command) + "\n===== stdout =====\n" + stdout_str + "===== stderr =====\n" + stderr_str + "==================") if completed_process.returncode != 0: raise Exception("Failed to execute command:\n" + output) major_minor = ".".join( stdout_str.splitlines()[0].split(" ")[1].split(".")[:2]) self.test_temp_vrm_dir = os.path.join(test_vrm_dir, major_minor, "temp") self.test_in_vrm_dir = os.path.join(test_vrm_dir, "in") self.test_out_vrm_dir = os.path.join(test_vrm_dir, major_minor, "out") self.test_out2_vrm_dir = os.path.join(test_vrm_dir, major_minor, "out2") self.test_out3_vrm_dir = os.path.join(test_vrm_dir, major_minor, "out3") os.makedirs(self.test_temp_vrm_dir, exist_ok=True) os.makedirs(self.test_out_vrm_dir, exist_ok=True) os.makedirs(self.test_out2_vrm_dir, exist_ok=True) os.makedirs(self.test_out3_vrm_dir, exist_ok=True)
def CreateJunctionFromPackage(source, destination): # Before creating a junction, clean the destionation path first if not PBTools.PurgeDestionation(destination): LogError( "Can't clean existing files in destionation junction point: " + destination) # Create junction from package contents to destination try: _winapi.CreateJunction(source, destination) except: LogError("Can't create junction point from " + source + " to " + destination)
def create_junction_from_package(source, destination): # Before creating a junction, clean the destionation path first if not PBTools.purge_destination(destination): log_error( "Can't clean existing files in destionation junction point: " + destination) # Create junction from package contents to destination try: _winapi.CreateJunction(source, destination) except: log_error("Can't create junction point from " + source + " to " + destination)
def link(self): if self.config.create_dirs: if not os.path.exists(self.config.source_dir): os.makedirs(self.config.source_dir) if not os.path.exists(self.config.target_dir): os.makedirs(self.config.target_dir) self.game = self._get_game() if not self.game: sys.exit("no game to link") self.fix_paths() link_msg = "unlink" if self.config.reverse else "link" if not ask_yes_no(f'Are you sure you want to {link_msg} "{self.game}"', default="n"): sys.exit("Exiting...") source_exists = os.path.exists(self.source_path) target_exists = os.path.exists(self.target_path) if not source_exists and not target_exists: sys.exit("Game folder does not exist in either location") if self.config.reverse: if source_exists and target_exists: # this remove directory will fail if not a link, unless the directory is empty # if the dir is empty, then it's not a big deal if the directory is accidentally deleted os.rmdir(self.source_path) CopyProgress.move(self.target_path, self.source_path) print( f"Junction removed: {self.source_path} <== {self.target_path}" ) elif not target_exists: sys.exit("Target does not exist") else: CopyProgress.move(self.target_path, self.source_path) print( f"Junction removed: {self.source_path} <== {self.target_path}" ) else: if source_exists and target_exists: sys.exit("Game folder exists in both locations") elif source_exists: if not os.path.isdir(self.source_path): sys.exit(f"{self.source_path} is not a directory") CopyProgress.move(self.source_path, self.target_path) if not os.path.isdir(self.target_path): sys.exit(f"{self.target_path} is not a directory") _winapi.CreateJunction(self.target_path, self.source_path) print( f"Junction created: {self.source_path} ==> {self.target_path}")
def symlink(target, link): """os.symlink() but use a junction point on Windows. """ if islink(link): unlink(link) if platform.system() == "Windows": if sys.version_info[:2] < (3, 5): with open(os.devnull, "w") as nul: subprocess.check_call(["mklink", "/J", link, target], shell=True, stdout=nul) else: _winapi.CreateJunction(target, link) else: os.symlink(target, link) # pylint: disable=no-member
def create_link(src, link, is_dir): src_abs = os.path.abspath(src) link_abs = os.path.abspath(link) if os.path.exists(src_abs): if not os.path.exists(link_abs): if is_dir: if is_windows: _winapi.CreateJunction(src_abs, link_abs) else: os.symlink(src_abs, link_abs, True) else: os.link(src_abs, link_abs) log("Link created: {0} => {1}".format(src_abs, link_abs)) else: log("Link already exists".format(link_abs)) else: log("Invalid link source: {0}".format(src_abs))
def symlink(src, dest, *args, **kwargs): if os.name == "nt": # Try to use junctions first. try: import _winapi _winapi.CreateJunction(src, dest) return except KeyboardInterrupt as e: raise e except Exception: # Ok, probably linking a file and not a directory # trying a regular symlink. try: os.symlink(src, dest, *args, **kwargs) except OSError as e: raise_error_if( "symbolic link privilege not held" in str(e), "Permission denied while attempting to create a symlink: {}\n" "Please ensure the 'Create symbolic links' right is granted " "to your user in the 'Local Security Policy'.", dest) raise e else: os.symlink(src, dest, *args, **kwargs)
def __init__(self, *args: str, **kwargs: str) -> None: # https://stackoverflow.com/a/19102520 super().__init__(*args, **kwargs) self.windows = platform.system() == "Windows" if self.windows: self.exeext = ".exe" else: self.exeext = "" self.repository_root_dir = os.path.dirname( os.path.dirname(os.path.realpath(__file__))) self.user_scripts_dir = tempfile.mkdtemp() os.mkdir(os.path.join(self.user_scripts_dir, "addons")) addon_dir = os.path.join(self.user_scripts_dir, "addons", "io_scene_vrm_saturday06") if self.windows: import _winapi _winapi.CreateJunction(self.repository_root_dir, addon_dir) else: os.symlink(self.repository_root_dir, addon_dir) test_vrm_dir = os.environ.get( "BLENDER_VRM_TEST_VRM_DIR", os.path.join(self.repository_root_dir, "tests", "vrm"), ) self.test_temp_vrm_dir = os.path.join(test_vrm_dir, "temp") self.test_in_vrm_dir = os.path.join(test_vrm_dir, "in") self.test_out_vrm_dir = os.path.join(test_vrm_dir, "out") self.test_out2_vrm_dir = os.path.join(test_vrm_dir, "out2") self.test_out3_vrm_dir = os.path.join(test_vrm_dir, "out3") os.makedirs(self.test_temp_vrm_dir, exist_ok=True) os.makedirs(self.test_out_vrm_dir, exist_ok=True) os.makedirs(self.test_out2_vrm_dir, exist_ok=True) os.makedirs(self.test_out3_vrm_dir, exist_ok=True)
def extract(self, path: Optional[Any] = None, targets: Optional[List[str]] = None, return_dict: bool = False) -> Optional[Dict[str, IO[Any]]]: target_junction = [] # type: List[pathlib.Path] target_sym = [] # type: List[pathlib.Path] target_files = [] # type: List[Tuple[pathlib.Path, Dict[str, Any]]] target_dirs = [] # type: List[pathlib.Path] if path is not None: if isinstance(path, str): path = pathlib.Path(path) try: if not path.exists(): path.mkdir(parents=True) else: pass except OSError as e: if e.errno == errno.EEXIST and path.is_dir(): pass else: raise e fnames = [ ] # type: List[str] # check duplicated filename in one archive? for f in self.files: # TODO: sanity check # check whether f.filename with invalid characters: '../' if f.filename.startswith('../'): raise Bad7zFile # When archive has a multiple files which have same name. # To guarantee order of archive, multi-thread decompression becomes off. # Currently always overwrite by latter archives. # TODO: provide option to select overwrite or skip. if f.filename not in fnames: outname = f.filename else: i = 0 while True: outname = f.filename + '_%d' % i if outname not in fnames: break fnames.append(outname) if path is not None: outfilename = path.joinpath(outname) else: outfilename = pathlib.Path(outname) if targets is not None and f.filename not in targets: self.worker.register_filelike(f.id, None) continue if f.is_directory: if not outfilename.exists(): target_dirs.append(outfilename) target_files.append((outfilename, f.file_properties())) else: pass elif f.is_socket: pass elif f.is_symlink: target_sym.append(outfilename) self.worker.register_filelike(f.id, outfilename) elif f.is_junction: target_junction.append(outfilename) self.worker.register_filelike(f.id, outfilename) else: self.worker.register_filelike(f.id, outfilename) target_files.append((outfilename, f.file_properties())) for target_dir in sorted(target_dirs): try: target_dir.mkdir() except FileExistsError: if target_dir.is_dir(): # skip rare case pass elif target_dir.is_file(): raise Exception( "Directory name is existed as a normal file.") else: raise Exception( "Directory making fails on unknown condition.") self.worker.extract(self.fp, parallel=(not self.password_protected and not self._filePassed), return_dict=return_dict) if return_dict: return self.worker._dict # create symbolic links on target path as a working directory. # if path is None, work on current working directory. for t in target_sym: sym_dst = t.resolve() with sym_dst.open('rb') as b: sym_src = b.read().decode( encoding='utf-8') # symlink target name stored in utf-8 sym_dst.unlink() # unlink after close(). sym_dst.symlink_to(pathlib.Path(sym_src)) # create junction point only on windows platform if sys.platform.startswith('win'): for t in target_junction: junction_dst = t.resolve() with junction_dst.open('rb') as b: junction_target = pathlib.Path( b.read().decode(encoding='utf-8')) junction_dst.unlink() _winapi.CreateJunction( junction_target, str(junction_dst)) # type: ignore # noqa for o, p in target_files: self._set_file_property(o, p) return None
# Load Addon ######################################## addon_directory = bpy.utils.user_resource('SCRIPTS', "addons") addon_folder_name = os.path.basename(external_addon_directory) symlink_path = os.path.join(addon_directory, addon_folder_name) if not os.path.exists(addon_directory): os.makedirs(addon_directory) if os.path.exists(symlink_path): os.remove(symlink_path) if sys.platform == "win32": import _winapi _winapi.CreateJunction(external_addon_directory, symlink_path) else: os.symlink(external_addon_directory, symlink_path, target_is_directory=True) bpy.ops.wm.addon_enable(module=addon_folder_name) # Operators ######################################## class UpdateAddonOperator(bpy.types.Operator): bl_idname = "dev.update_addon" bl_label = "Update Addon"
import os, sys, platform from os.path import join, abspath import pathlib _, assetsDir, binAssetsDir = sys.argv try: binDir = abspath(join(binAssetsDir, os.pardir)) pathlib.Path(assetsDir).mkdir(parents=True, exist_ok=True) pathlib.Path(binDir).mkdir(parents=True, exist_ok=True) if platform.system() == 'Windows': import _winapi _winapi.CreateJunction(assetsDir, binAssetsDir) else: os.symlink(assetsDir, binAssetsDir) print(f"Created Symlink {binAssetsDir} -> {assetsDir}") except FileExistsError: print(f"Symlink already exists: {binAssetsDir}") except Exception as e: print(f"{sys.argv}\n{e}")
def extractall(self, path: Optional[Any] = None) -> None: """Extract all members from the archive to the current working directory and set owner, modification time and permissions on directories afterwards. `path' specifies a different directory to extract to. """ target_junction = [] # type: List[Tuple[BinaryIO, str]] target_sym = [] # type: List[Tuple[BinaryIO, str]] target_files = [] # type: List[Tuple[pathlib.Path, Dict[str, Any]]] target_dirs = [] # type: List[pathlib.Path] self.reset() if path is not None: if isinstance(path, str): path = pathlib.Path(path) try: if not path.exists(): path.mkdir(parents=True) else: pass except OSError as e: if e.errno == errno.EEXIST and path.is_dir(): pass else: raise e multi_thread = self.header.main_streams is not None and self.header.main_streams.unpackinfo.numfolders > 1 and \ self.header.main_streams.packinfo.numstreams == self.header.main_streams.unpackinfo.numfolders fnames = [] # type: List[str] for f in self.files: # TODO: sanity check # check whether f.filename with invalid characters: '../' if f.filename.startswith('../'): raise Bad7zFile # When archive has a multiple files which have same name. # To guarantee order of archive, multi-thread decompression becomes off. # Currently always overwrite by latter archives. # TODO: provide option to select overwrite or skip. if f.filename in fnames: multi_thread = False fnames.append(f.filename) if path is not None: outfilename = path.joinpath(f.filename) else: outfilename = pathlib.Path(f.filename) if f.is_directory: if not outfilename.exists(): target_dirs.append(outfilename) target_files.append((outfilename, f.file_properties())) else: pass elif f.is_socket: pass elif f.is_symlink: buf = io.BytesIO() pair = (buf, f.filename) target_sym.append(pair) self.worker.register_filelike(f.id, buf) elif f.is_junction: buf = io.BytesIO() pair = (buf, f.filename) target_junction.append(pair) self.worker.register_filelike(f.id, buf) else: self.worker.register_filelike(f.id, outfilename) target_files.append((outfilename, f.file_properties())) for target_dir in sorted(target_dirs): try: target_dir.mkdir() except FileExistsError: if target_dir.is_dir(): # skip rare case pass elif target_dir.is_file(): raise Exception( "Directory name is existed as a normal file.") else: raise Exception( "Directory making fails on unknown condition.") self.worker.extract(self.fp, multithread=multi_thread) for b, t in target_sym: b.seek(0) sym_src_org = b.read().decode( encoding='utf-8') # symlink target name stored in utf-8 dirname = os.path.dirname(t) if path: sym_src = path.joinpath(dirname, sym_src_org) sym_dst = path.joinpath(t) else: sym_src = pathlib.Path(dirname).joinpath(sym_src_org) sym_dst = pathlib.Path(t) sym_dst.symlink_to(sym_src) # create junction point only on windows platform if sys.platform.startswith('win'): for b, t in target_junction: b.seek(0) junction_target = pathlib.Path( b.read().decode(encoding='utf-8')) dirname = os.path.dirname(t) if path: junction_point = path.joinpath(t) else: junction_point = pathlib.Path(dirname).joinpath(t) _winapi.CreateJunction(junction_target, junction_point) # type: ignore # noqa for o, p in target_files: self._set_file_property(o, p)
if platform.system() == "Windows": import _winapi exeext = ".exe" else: exeext = "" blender_command = sys.argv[1] if len(sys.argv) > 1 else "blender" + exeext repository_root_dir = os.path.dirname( os.path.dirname(os.path.realpath(__file__))) user_scripts_dir = tempfile.mkdtemp() os.mkdir(os.path.join(user_scripts_dir, "addons")) addon_dir = os.path.join(user_scripts_dir, "addons", "io_scene_vrm_saturday06") if platform.system() == "Windows": _winapi.CreateJunction(repository_root_dir, addon_dir) else: os.symlink(repository_root_dir, addon_dir) env = os.environ.copy() env["BLENDER_USER_SCRIPTS"] = user_scripts_dir subprocess.run( [ blender_command, "-noaudio", # sound system to None (less output on stdout) "--factory-startup", # factory settings "--addons", "io_scene_vrm_saturday06", # enable the addon "--python-exit-code", "1", "--background",
def _extract( self, path: Optional[Any] = None, targets: Optional[List[str]] = None, return_dict: bool = False, callback: Optional[ExtractCallback] = None ) -> Optional[Dict[str, IO[Any]]]: if callback is not None and not isinstance(callback, ExtractCallback): raise ValueError( 'Callback specified is not a subclass of py7zr.callbacks.ExtractCallback class' ) elif callback is not None: self.reporterd = threading.Thread(target=self.reporter, args=(callback, ), daemon=True) self.reporterd.start() target_junction = [] # type: List[pathlib.Path] target_sym = [] # type: List[pathlib.Path] target_files = [] # type: List[Tuple[pathlib.Path, Dict[str, Any]]] target_dirs = [] # type: List[pathlib.Path] if path is not None: if isinstance(path, str): path = pathlib.Path(path) try: if not path.exists(): path.mkdir(parents=True) else: pass except OSError as e: if e.errno == errno.EEXIST and path.is_dir(): pass else: raise e fnames = [ ] # type: List[str] # check duplicated filename in one archive? self.q.put(('pre', None, None)) for f in self.files: # TODO: sanity check # check whether f.filename with invalid characters: '../' if f.filename.startswith('../'): raise Bad7zFile # When archive has a multiple files which have same name # To guarantee order of archive, multi-thread decompression becomes off. # Currently always overwrite by latter archives. # TODO: provide option to select overwrite or skip. if f.filename not in fnames: outname = f.filename else: i = 0 while True: outname = f.filename + '_%d' % i if outname not in fnames: break fnames.append(outname) if path is not None: outfilename = path.joinpath(outname) else: outfilename = pathlib.Path(outname) if os.name == 'nt': if outfilename.is_absolute(): # hack for microsoft windows path length limit < 255 outfilename = pathlib.WindowsPath('\\\\?\\' + str(outfilename)) if targets is not None and f.filename not in targets: self.worker.register_filelike(f.id, None) continue if f.is_directory: if not outfilename.exists(): target_dirs.append(outfilename) target_files.append((outfilename, f.file_properties())) else: pass elif f.is_socket: pass elif return_dict: fname = outfilename.as_posix() _buf = io.BytesIO() self._dict[fname] = _buf self.worker.register_filelike(f.id, MemIO(_buf)) elif f.is_symlink: target_sym.append(outfilename) try: if outfilename.exists(): outfilename.unlink() except OSError as ose: if ose.errno not in [errno.ENOENT]: raise self.worker.register_filelike(f.id, outfilename) elif f.is_junction: target_junction.append(outfilename) self.worker.register_filelike(f.id, outfilename) else: self.worker.register_filelike(f.id, outfilename) target_files.append((outfilename, f.file_properties())) for target_dir in sorted(target_dirs): try: target_dir.mkdir() except FileExistsError: if target_dir.is_dir(): # skip rare case pass elif target_dir.is_file(): raise Exception( "Directory name is existed as a normal file.") else: raise Exception( "Directory making fails on unknown condition.") if callback is not None: self.worker.extract(self.fp, parallel=(not self.password_protected and not self._filePassed), q=self.q) else: self.worker.extract(self.fp, parallel=(not self.password_protected and not self._filePassed)) self.q.put(('post', None, None)) if return_dict: return self._dict else: # create symbolic links on target path as a working directory. # if path is None, work on current working directory. for t in target_sym: sym_dst = t.resolve() with sym_dst.open('rb') as b: sym_src = b.read().decode( encoding='utf-8' ) # symlink target name stored in utf-8 sym_dst.unlink() # unlink after close(). sym_dst.symlink_to(pathlib.Path(sym_src)) # create junction point only on windows platform if sys.platform.startswith('win'): for t in target_junction: junction_dst = t.resolve() with junction_dst.open('rb') as b: junction_target = pathlib.Path( b.read().decode(encoding='utf-8')) junction_dst.unlink() _winapi.CreateJunction( junction_target, str(junction_dst)) # type: ignore # noqa # set file properties for o, p in target_files: self._set_file_property(o, p) return None