예제 #1
0
def expand_symlinks(indir, relfile):
  """Follows symlinks in |relfile|, but treating symlinks that point outside the
  build tree as if they were ordinary directories/files. Returns the final
  symlink-free target and a list of paths to symlinks encountered in the
  process.

  The rule about symlinks outside the build tree is for the benefit of the
  Chromium OS ebuild, which symlinks the output directory to an unrelated path
  in the chroot.

  Fails when a directory loop is detected, although in theory we could support
  that case.
  """
  is_directory = relfile.endswith(os.path.sep)
  done = indir
  todo = relfile.strip(os.path.sep)
  symlinks = []

  while todo:
    pre_symlink, symlink, post_symlink = file_path.split_at_symlink(done, todo)
    if not symlink:
      todo = file_path.fix_native_path_case(done, todo)
      done = os.path.join(done, todo)
      break
    symlink_path = os.path.join(done, pre_symlink, symlink)
    post_symlink = post_symlink.lstrip(os.path.sep)
    # readlink doesn't exist on Windows.
    # pylint: disable=E1101
    target = os.path.normpath(os.path.join(done, pre_symlink))
    symlink_target = os.readlink(symlink_path)
    if os.path.isabs(symlink_target):
      # Absolute path are considered a normal directories. The use case is
      # generally someone who puts the output directory on a separate drive.
      target = symlink_target
    else:
      # The symlink itself could be using the wrong path case.
      target = file_path.fix_native_path_case(target, symlink_target)

    if not os.path.exists(target):
      raise MappingError(
          'Symlink target doesn\'t exist: %s -> %s' % (symlink_path, target))
    target = file_path.get_native_path_case(target)
    if not file_path.path_starts_with(indir, target):
      done = symlink_path
      todo = post_symlink
      continue
    if file_path.path_starts_with(target, symlink_path):
      raise MappingError(
          'Can\'t map recursive symlink reference %s -> %s' %
          (symlink_path, target))
    logging.info('Found symlink: %s -> %s', symlink_path, target)
    symlinks.append(os.path.relpath(symlink_path, indir))
    # Treat the common prefix of the old and new paths as done, and start
    # scanning again.
    target = target.split(os.path.sep)
    symlink_path = symlink_path.split(os.path.sep)
    prefix_length = 0
    for target_piece, symlink_path_piece in zip(target, symlink_path):
      if target_piece == symlink_path_piece:
        prefix_length += 1
      else:
        break
    done = os.path.sep.join(target[:prefix_length])
    todo = os.path.join(
        os.path.sep.join(target[prefix_length:]), post_symlink)

  relfile = os.path.relpath(done, indir)
  relfile = relfile.rstrip(os.path.sep) + is_directory * os.path.sep
  return relfile, symlinks
예제 #2
0
    def load_isolate(
        self, cwd, isolate_file, path_variables, config_variables, extra_variables, blacklist, ignore_broken_items
    ):
        """Updates self.isolated and self.saved_state with information loaded from a
    .isolate file.

    Processes the loaded data, deduce root_dir, relative_cwd.
    """
        # Make sure to not depend on os.getcwd().
        assert os.path.isabs(isolate_file), isolate_file
        isolate_file = file_path.get_native_path_case(isolate_file)
        logging.info(
            "CompleteState.load_isolate(%s, %s, %s, %s, %s, %s)",
            cwd,
            isolate_file,
            path_variables,
            config_variables,
            extra_variables,
            ignore_broken_items,
        )

        # Config variables are not affected by the paths and must be used to
        # retrieve the paths, so update them first.
        self.saved_state.update_config(config_variables)

        with open(isolate_file, "r") as f:
            # At that point, variables are not replaced yet in command and infiles.
            # infiles may contain directory entries and is in posix style.
            command, infiles, read_only, isolate_cmd_dir = isolate_format.load_isolate_for_config(
                os.path.dirname(isolate_file), f.read(), self.saved_state.config_variables
            )

        # Processes the variables with the new found relative root. Note that 'cwd'
        # is used when path variables are used.
        path_variables = normalize_path_variables(cwd, path_variables, isolate_cmd_dir)
        # Update the rest of the saved state.
        self.saved_state.update(isolate_file, path_variables, extra_variables)

        total_variables = self.saved_state.path_variables.copy()
        total_variables.update(self.saved_state.config_variables)
        total_variables.update(self.saved_state.extra_variables)
        command = [isolate_format.eval_variables(i, total_variables) for i in command]

        total_variables = self.saved_state.path_variables.copy()
        total_variables.update(self.saved_state.extra_variables)
        infiles = [isolate_format.eval_variables(f, total_variables) for f in infiles]
        # root_dir is automatically determined by the deepest root accessed with the
        # form '../../foo/bar'. Note that path variables must be taken in account
        # too, add them as if they were input files.
        self.saved_state.root_dir = isolate_format.determine_root_dir(
            isolate_cmd_dir, infiles + self.saved_state.path_variables.values()
        )
        # The relative directory is automatically determined by the relative path
        # between root_dir and the directory containing the .isolate file,
        # isolate_base_dir.
        relative_cwd = os.path.relpath(isolate_cmd_dir, self.saved_state.root_dir)
        # Now that we know where the root is, check that the path_variables point
        # inside it.
        for k, v in self.saved_state.path_variables.iteritems():
            dest = os.path.join(isolate_cmd_dir, relative_cwd, v)
            if not file_path.path_starts_with(self.saved_state.root_dir, dest):
                raise isolated_format.MappingError(
                    "Path variable %s=%r points outside the inferred root directory "
                    "%s; %s" % (k, v, self.saved_state.root_dir, dest)
                )
        # Normalize the files based to self.saved_state.root_dir. It is important to
        # keep the trailing os.path.sep at that step.
        infiles = [
            file_path.relpath(file_path.normpath(os.path.join(isolate_cmd_dir, f)), self.saved_state.root_dir)
            for f in infiles
        ]
        follow_symlinks = sys.platform != "win32"
        # Expand the directories by listing each file inside. Up to now, trailing
        # os.path.sep must be kept.
        infiles = isolated_format.expand_directories_and_symlinks(
            self.saved_state.root_dir, infiles, tools.gen_blacklist(blacklist), follow_symlinks, ignore_broken_items
        )

        # Finally, update the new data to be able to generate the foo.isolated file,
        # the file that is used by run_isolated.py.
        self.saved_state.update_isolated(command, infiles, read_only, relative_cwd)
        logging.debug(self)
예제 #3
0
def expand_symlinks(indir, relfile):
  """Follows symlinks in |relfile|, but treating symlinks that point outside the
  build tree as if they were ordinary directories/files. Returns the final
  symlink-free target and a list of paths to symlinks encountered in the
  process.

  The rule about symlinks outside the build tree is for the benefit of the
  Chromium OS ebuild, which symlinks the output directory to an unrelated path
  in the chroot.

  Fails when a directory loop is detected, although in theory we could support
  that case.
  """
  is_directory = relfile.endswith(os.path.sep)
  done = indir
  todo = relfile.strip(os.path.sep)
  symlinks = []

  while todo:
    pre_symlink, symlink, post_symlink = file_path.split_at_symlink(done, todo)
    if not symlink:
      todo = file_path.fix_native_path_case(done, todo)
      done = os.path.join(done, todo)
      break
    symlink_path = os.path.join(done, pre_symlink, symlink)
    post_symlink = post_symlink.lstrip(os.path.sep)
    # readlink doesn't exist on Windows.
    # pylint: disable=E1101
    target = os.path.normpath(os.path.join(done, pre_symlink))
    symlink_target = os.readlink(symlink_path)
    if os.path.isabs(symlink_target):
      # Absolute path are considered a normal directories. The use case is
      # generally someone who puts the output directory on a separate drive.
      target = symlink_target
    else:
      # The symlink itself could be using the wrong path case.
      target = file_path.fix_native_path_case(target, symlink_target)

    if not os.path.exists(target):
      raise MappingError(
          'Symlink target doesn\'t exist: %s -> %s' % (symlink_path, target))
    target = file_path.get_native_path_case(target)
    if not file_path.path_starts_with(indir, target):
      done = symlink_path
      todo = post_symlink
      continue
    if file_path.path_starts_with(target, symlink_path):
      raise MappingError(
          'Can\'t map recursive symlink reference %s -> %s' %
          (symlink_path, target))
    logging.info('Found symlink: %s -> %s', symlink_path, target)
    symlinks.append(os.path.relpath(symlink_path, indir))
    # Treat the common prefix of the old and new paths as done, and start
    # scanning again.
    target = target.split(os.path.sep)
    symlink_path = symlink_path.split(os.path.sep)
    prefix_length = 0
    for target_piece, symlink_path_piece in zip(target, symlink_path):
      if target_piece == symlink_path_piece:
        prefix_length += 1
      else:
        break
    done = os.path.sep.join(target[:prefix_length])
    todo = os.path.join(
        os.path.sep.join(target[prefix_length:]), post_symlink)

  relfile = os.path.relpath(done, indir)
  relfile = relfile.rstrip(os.path.sep) + is_directory * os.path.sep
  return relfile, symlinks
예제 #4
0
    def load_isolate(self, cwd, isolate_file, path_variables, config_variables,
                     extra_variables, blacklist, ignore_broken_items,
                     collapse_symlinks):
        """Updates self.isolated and self.saved_state with information loaded from a
    .isolate file.

    Processes the loaded data, deduce root_dir, relative_cwd.
    """
        # Make sure to not depend on os.getcwd().
        assert os.path.isabs(isolate_file), isolate_file
        isolate_file = file_path.get_native_path_case(isolate_file)
        logging.info('CompleteState.load_isolate(%s, %s, %s, %s, %s, %s, %s)',
                     cwd, isolate_file, path_variables, config_variables,
                     extra_variables, ignore_broken_items, collapse_symlinks)

        # Config variables are not affected by the paths and must be used to
        # retrieve the paths, so update them first.
        self.saved_state.update_config(config_variables)

        with fs.open(isolate_file, 'r') as f:
            # At that point, variables are not replaced yet in command and infiles.
            # infiles may contain directory entries and is in posix style.
            command, infiles, read_only, isolate_cmd_dir = (
                isolate_format.load_isolate_for_config(
                    os.path.dirname(isolate_file), f.read(),
                    self.saved_state.config_variables))

        # Processes the variables with the new found relative root. Note that 'cwd'
        # is used when path variables are used.
        path_variables = normalize_path_variables(cwd, path_variables,
                                                  isolate_cmd_dir)
        # Update the rest of the saved state.
        self.saved_state.update(isolate_file, path_variables, extra_variables)

        total_variables = self.saved_state.path_variables.copy()
        total_variables.update(self.saved_state.config_variables)
        total_variables.update(self.saved_state.extra_variables)
        command = [
            isolate_format.eval_variables(i, total_variables) for i in command
        ]

        total_variables = self.saved_state.path_variables.copy()
        total_variables.update(self.saved_state.extra_variables)
        infiles = [
            isolate_format.eval_variables(f, total_variables) for f in infiles
        ]
        # root_dir is automatically determined by the deepest root accessed with the
        # form '../../foo/bar'. Note that path variables must be taken in account
        # too, add them as if they were input files.
        self.saved_state.root_dir = isolate_format.determine_root_dir(
            isolate_cmd_dir,
            infiles + self.saved_state.path_variables.values())
        # The relative directory is automatically determined by the relative path
        # between root_dir and the directory containing the .isolate file,
        # isolate_base_dir.
        relative_cwd = os.path.relpath(isolate_cmd_dir,
                                       self.saved_state.root_dir)
        # Now that we know where the root is, check that the path_variables point
        # inside it.
        for k, v in self.saved_state.path_variables.iteritems():
            dest = os.path.join(isolate_cmd_dir, relative_cwd, v)
            if not file_path.path_starts_with(self.saved_state.root_dir, dest):
                raise isolated_format.MappingError(
                    'Path variable %s=%r points outside the inferred root directory '
                    '%s; %s' % (k, v, self.saved_state.root_dir, dest))
        # Normalize the files based to self.saved_state.root_dir. It is important to
        # keep the trailing os.path.sep at that step.
        infiles = [
            file_path.relpath(
                file_path.normpath(os.path.join(isolate_cmd_dir, f)),
                self.saved_state.root_dir) for f in infiles
        ]
        follow_symlinks = False
        if not collapse_symlinks:
            follow_symlinks = sys.platform != 'win32'
        # Expand the directories by listing each file inside. Up to now, trailing
        # os.path.sep must be kept.
        infiles = isolated_format.expand_directories_and_symlinks(
            self.saved_state.root_dir, infiles, tools.gen_blacklist(blacklist),
            follow_symlinks, ignore_broken_items)

        # Finally, update the new data to be able to generate the foo.isolated file,
        # the file that is used by run_isolated.py.
        self.saved_state.update_isolated(command, infiles, read_only,
                                         relative_cwd)
        logging.debug(self)