@Override
  public SkyValue fetch(Rule rule, Path outputDirectory, Environment env)
      throws SkyFunctionException {
    AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
    PathFragment pathFragment = new PathFragment(mapper.get("path", Type.STRING));
    try {
      FileSystem fs = outputDirectory.getFileSystem();
      if (fs.supportsSymbolicLinksNatively()) {
        outputDirectory.createSymbolicLink(pathFragment);
      } else {
        FileSystemUtils.createDirectoryAndParents(outputDirectory);
        FileSystemUtils.copyTreesBelow(
            fs.getPath(getTargetPath(rule, getWorkspace())), outputDirectory);
      }
    } catch (IOException e) {
      throw new RepositoryFunctionException(
          new IOException(
              "Could not create symlink to repository " + pathFragment + ": " + e.getMessage()),
          Transience.TRANSIENT);
    }
    FileValue repositoryValue = getRepositoryDirectory(outputDirectory, env);
    if (repositoryValue == null) {
      // TODO(bazel-team): If this returns null, we unnecessarily recreate the symlink above on the
      // second execution.
      return null;
    }

    if (!repositoryValue.isDirectory()) {
      throw new RepositoryFunctionException(
          new IOException(rule + " must specify an existing directory"), Transience.TRANSIENT);
    }

    return RepositoryValue.create(outputDirectory);
  }