| #!/usr/bin/env python |
| # |
| # Copyright 2019 The Cobalt Authors. All Rights Reserved. |
| # |
| # 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 |
| # |
| # http://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. |
| |
| |
| """A portable interface for symlinking.""" |
| |
| |
| import argparse |
| import logging |
| import os |
| import shutil |
| import sys |
| |
| import _env # pylint: disable=relative-import,unused-import |
| |
| from starboard.tools import util |
| |
| |
| def IsWindows(): |
| return sys.platform in ['win32', 'cygwin'] |
| |
| |
| def ToLongPath(path): |
| """Converts to a path that supports long filenames.""" |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| return win_symlink.ToDevicePath(path) |
| else: |
| return path |
| |
| def IsSymLink(path): |
| """Platform neutral version os os.path.islink()""" |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| return win_symlink.IsReparsePoint(path) |
| else: |
| return os.path.islink(path) |
| |
| |
| def MakeSymLink(from_folder, link_folder): |
| """Makes a symlink. |
| |
| Args: |
| from_folder: Path to the actual folder |
| link_folder: Path to the link |
| |
| Returns: |
| None |
| """ |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| win_symlink.CreateReparsePoint(from_folder, link_folder) |
| else: |
| util.MakeDirs(os.path.dirname(link_folder)) |
| os.symlink(from_folder, link_folder) |
| |
| |
| def ReadSymLink(link_path): |
| """Returns the path (abs. or rel.) to the folder referred to by link_path.""" |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| path = win_symlink.ReadReparsePoint(link_path) |
| else: |
| try: |
| path = os.readlink(link_path) |
| except OSError: |
| path = None |
| return path |
| |
| |
| def DelSymLink(link_path): |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| win_symlink.UnlinkReparsePoint(link_path) |
| else: |
| os.unlink(link_path) |
| |
| |
| def Rmtree(path): |
| """See Rmtree() for documentation of this function.""" |
| if not os.path.exists(path): |
| return |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| win_symlink.RmtreeShallow(path) |
| else: |
| if os.path.islink(path): |
| os.unlink(path) |
| else: |
| shutil.rmtree(path) |
| |
| |
| def OsWalk(root_dir, topdown=True, onerror=None, followlinks=False): |
| if IsWindows(): |
| # pylint: disable=g-import-not-at-top |
| from starboard.tools import win_symlink |
| return win_symlink.OsWalk(root_dir, topdown, onerror, followlinks) |
| else: |
| return os.walk(root_dir, topdown, onerror, followlinks) |
| |
| |
| def _CreateArgumentParser(): |
| """Creates an argument parser for port_symlink.""" |
| |
| class MyParser(argparse.ArgumentParser): |
| |
| def error(self, message): |
| sys.stderr.write('error: %s\n' % message) |
| self.print_help() |
| sys.exit(2) |
| help_msg = ( |
| 'Example 1:\n' |
| ' python port_link.py --link "actual_folder_path" "link_path"\n\n' |
| 'Example 2:\n' |
| ' python port_link.py --link "../actual_folder_path" "link_path"\n\n') |
| # Enables new lines in the description and epilog. |
| formatter_class = argparse.RawDescriptionHelpFormatter |
| parser = MyParser(epilog=help_msg, formatter_class=formatter_class) |
| parser.add_argument( |
| '-a', |
| '--use_absolute_symlinks', |
| action='store_true', |
| help='Generated symlinks are stored as absolute paths.') |
| parser.add_argument( |
| '-f', |
| '--force', |
| action='store_true', |
| help='Force the symbolic link to be created, removing existing files and ' |
| 'directories if needed.') |
| group = parser.add_mutually_exclusive_group(required=True) |
| group.add_argument('--link', |
| help='Issues an scp command to upload src to remote_dst', |
| metavar='"path"', |
| nargs=2) |
| return parser |
| |
| |
| def main(): |
| util.SetupDefaultLoggingConfig() |
| parser = _CreateArgumentParser() |
| args = parser.parse_args() |
| |
| folder_path, link_path = args.link |
| if args.use_absolute_symlinks: |
| folder_path = os.path.abspath(folder_path) |
| link_path = os.path.abspath(link_path) |
| if '.' in folder_path: |
| d1 = os.path.abspath(folder_path) |
| else: |
| d1 = os.path.abspath(os.path.join(link_path, folder_path)) |
| if not os.path.isdir(d1): |
| logging.warning('%s is not a directory.', d1) |
| if args.force: |
| Rmtree(link_path) |
| MakeSymLink(from_folder=folder_path, link_folder=link_path) |
| |
| |
| if __name__ == '__main__': |
| main() |