def expand_directory_and_symlink(indir, relfile, blacklist, follow_symlinks):
  """Expands a single input. It can result in multiple outputs.

  This function is recursive when relfile is a directory.

  Note: this code doesn't properly handle recursive symlink like one created
  with:
    ln -s .. foo
  """
  if os.path.isabs(relfile):
    raise MappingError('Can\'t map absolute path %s' % relfile)

  infile = file_path.normpath(os.path.join(indir, relfile))
  if not infile.startswith(indir):
    raise MappingError('Can\'t map file %s outside %s' % (infile, indir))

  filepath = os.path.join(indir, relfile)
  native_filepath = file_path.get_native_path_case(filepath)
  if filepath != native_filepath:
    # Special case './'.
    if filepath != native_filepath + '.' + os.path.sep:
      # While it'd be nice to enforce path casing on Windows, it's impractical.
      # Also give up enforcing strict path case on OSX. Really, it's that sad.
      # The case where it happens is very specific and hard to reproduce:
      # get_native_path_case(
      #    u'Foo.framework/Versions/A/Resources/Something.nib') will return
      # u'Foo.framework/Versions/A/resources/Something.nib', e.g. lowercase 'r'.
      #
      # Note that this is really something deep in OSX because running
      # ls Foo.framework/Versions/A
      # will print out 'Resources', while file_path.get_native_path_case()
      # returns a lower case 'r'.
      #
      # So *something* is happening under the hood resulting in the command 'ls'
      # and Carbon.File.FSPathMakeRef('path').FSRefMakePath() to disagree.  We
      # have no idea why.
      if sys.platform not in ('darwin', 'win32'):
        raise MappingError(
            'File path doesn\'t equal native file path\n%s != %s' %
            (filepath, native_filepath))

  symlinks = []
  if follow_symlinks:
    try:
      relfile, symlinks = expand_symlinks(indir, relfile)
    except OSError:
      # The file doesn't exist, it will throw below.
      pass

  if relfile.endswith(os.path.sep):
    if not os.path.isdir(infile):
      raise MappingError(
          '%s is not a directory but ends with "%s"' % (infile, os.path.sep))

    # Special case './'.
    if relfile.startswith('.' + os.path.sep):
      relfile = relfile[2:]
    outfiles = symlinks
    try:
      for filename in fs.listdir(infile):
        inner_relfile = os.path.join(relfile, filename)
        if blacklist and blacklist(inner_relfile):
          continue
        if os.path.isdir(os.path.join(indir, inner_relfile)):
          inner_relfile += os.path.sep
        outfiles.extend(
            expand_directory_and_symlink(indir, inner_relfile, blacklist,
                                         follow_symlinks))
      return outfiles
    except OSError as e:
      raise MappingError(
          'Unable to iterate over directory %s.\n%s' % (infile, e))
  else:
    # Always add individual files even if they were blacklisted.
    if os.path.isdir(infile):
      raise MappingError(
          'Input directory %s must have a trailing slash' % infile)

    if not os.path.isfile(infile):
      raise MappingError('Input file %s doesn\'t exist' % infile)

    return symlinks + [relfile]
Example #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)
Example #3
0
def expand_directory_and_symlink(indir, relfile, blacklist, follow_symlinks):
  """Expands a single input. It can result in multiple outputs.

  This function is recursive when relfile is a directory.

  Note: this code doesn't properly handle recursive symlink like one created
  with:
    ln -s .. foo
  """
  if os.path.isabs(relfile):
    raise MappingError('Can\'t map absolute path %s' % relfile)

  infile = file_path.normpath(os.path.join(indir, relfile))
  if not infile.startswith(indir):
    raise MappingError('Can\'t map file %s outside %s' % (infile, indir))

  filepath = os.path.join(indir, relfile)
  native_filepath = file_path.get_native_path_case(filepath)
  if filepath != native_filepath:
    # Special case './'.
    if filepath != native_filepath + '.' + os.path.sep:
      # While it'd be nice to enforce path casing on Windows, it's impractical.
      # Also give up enforcing strict path case on OSX. Really, it's that sad.
      # The case where it happens is very specific and hard to reproduce:
      # get_native_path_case(
      #    u'Foo.framework/Versions/A/Resources/Something.nib') will return
      # u'Foo.framework/Versions/A/resources/Something.nib', e.g. lowercase 'r'.
      #
      # Note that this is really something deep in OSX because running
      # ls Foo.framework/Versions/A
      # will print out 'Resources', while file_path.get_native_path_case()
      # returns a lower case 'r'.
      #
      # So *something* is happening under the hood resulting in the command 'ls'
      # and Carbon.File.FSPathMakeRef('path').FSRefMakePath() to disagree.  We
      # have no idea why.
      if sys.platform not in ('darwin', 'win32'):
        raise MappingError(
            'File path doesn\'t equal native file path\n%s != %s' %
            (filepath, native_filepath))

  symlinks = []
  if follow_symlinks:
    try:
      relfile, symlinks = expand_symlinks(indir, relfile)
    except OSError:
      # The file doesn't exist, it will throw below.
      pass

  if relfile.endswith(os.path.sep):
    if not os.path.isdir(infile):
      raise MappingError(
          '%s is not a directory but ends with "%s"' % (infile, os.path.sep))

    # Special case './'.
    if relfile.startswith('.' + os.path.sep):
      relfile = relfile[2:]
    outfiles = symlinks
    try:
      for filename in fs.listdir(infile):
        inner_relfile = os.path.join(relfile, filename)
        if blacklist and blacklist(inner_relfile):
          continue
        if os.path.isdir(os.path.join(indir, inner_relfile)):
          inner_relfile += os.path.sep
        outfiles.extend(
            expand_directory_and_symlink(indir, inner_relfile, blacklist,
                                         follow_symlinks))
      return outfiles
    except OSError as e:
      raise MappingError(
          'Unable to iterate over directory %s.\n%s' % (infile, e))
  else:
    # Always add individual files even if they were blacklisted.
    if os.path.isdir(infile):
      raise MappingError(
          'Input directory %s must have a trailing slash' % infile)

    if not os.path.isfile(infile):
      raise MappingError('Input file %s doesn\'t exist' % infile)

    return symlinks + [relfile]
Example #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)