load(
    "@bazel_tools//tools/build_defs/repo:utils.bzl",
    "patch",
    "workspace_and_buildfile",
)

FALLBACK_SETUP = """
fallback="_bazel_fallback"
original=`readlink _bazel_original`

test -z "$debug" || {
  set -x
}

test -r "$original" || {
  echo "$repository $original not available - using $tarball" 1>&2
  ln -sfrt . "$fallback"/*
  exit $?
}
echo "$repository using $original from your local system" 1>&2
ln -sfrt . "$original"/*
"""

def _local_with_fallback(ctx):
    ctx.extract(ctx.attr.fallback, output = "_bazel_fallback")
    ctx.symlink(ctx.attr.path, "_bazel_original")

    result = ctx.execute(
        ["/bin/sh", "-c", FALLBACK_SETUP],
        environment = {
            "debug": ["", "1"][int(ctx.attr.debug)],
            "repository": ctx.name,
            "tarball": str(ctx.attr.fallback),
        },
        quiet = False,
    )

    patch(ctx)
    workspace_and_buildfile(ctx)

local_with_fallback = repository_rule(
    doc = """Uses a local repository, or falls back to a tarball in your repo.

This respository rule will either use a directory on your system as
a repository, similar to new_local_repository, or fallback to unpacking
a .tar.gz checked into your repository.

Using is very simple:

  load("@enkit//bazel/utils:repository.bzl, "local_with_fallback")

  local_with_fallback(
      name = "ProprietaryHorribleness",
      path = "/opt/proprietary/horribleness",
      fallback = "//bazel:horribleness-replacement.tar.gz",

      build_file = "//bazel:horribleness.BUILD.bazel",
  )

To specify a BUILD file and WORKSPACE, you can use `build_file`,
`build_file_content`, `workspace_file`, exactly like with `http_archive`
(the same underlying library is used).
Patch options are also supported.
""",
    implementation = _local_with_fallback,
    local = True,
    attrs = {
        "path": attr.string(doc = "Path of the local repository on your system", mandatory = True),
        "fallback": attr.label(doc = "Label of a tarball to extract if the local repository cannot be found", allow_single_file = True, mandatory = True),
        "debug": attr.bool(doc = "Set to True to turn on debugging"),

        # All the options below are used directly by workspace_and_buildfile and patch, from
        # the bazel upstream utilities - @bazel_tools//tools/build_defs/repo:utils.bzl.
        # Unfortunately, they are not exported in any constant / define that we can easily
        # reuse, so cut and paste it is.
        "patches": attr.label_list(
            default = [],
            doc =
                "A list of files that are to be applied as patches after " +
                "extracting the archive. By default, it uses the Bazel-native patch implementation " +
                "which doesn't support fuzz match and binary patch, but Bazel will fall back to use " +
                "patch command line tool if `patch_tool` attribute is specified or there are " +
                "arguments other than `-p` in `patch_args` attribute.",
        ),
        "patch_tool": attr.string(
            default = "",
            doc = "The patch(1) utility to use. If this is specified, Bazel will use the specifed " +
                  "patch tool instead of the Bazel-native patch implementation.",
        ),
        "patch_args": attr.string_list(
            default = ["-p0"],
            doc =
                "The arguments given to the patch tool. Defaults to -p0, " +
                "however -p1 will usually be needed for patches generated by " +
                "git. If multiple -p arguments are specified, the last one will take effect." +
                "If arguments other than -p are specified, Bazel will fall back to use patch " +
                "command line tool instead of the Bazel-native patch implementation. When falling " +
                "back to patch command line tool and patch_tool attribute is not specified, " +
                "`patch` will be used.",
        ),
        "patch_cmds": attr.string_list(
            default = [],
            doc = "Sequence of Bash commands to be applied on Linux/Macos after patches are applied.",
        ),
        "patch_cmds_win": attr.string_list(
            default = [],
            doc = "Sequence of Powershell commands to be applied on Windows after patches are " +
                  "applied. If this attribute is not set, patch_cmds will be executed on Windows, " +
                  "which requires Bash binary to exist.",
        ),
        "build_file": attr.label(
            allow_single_file = True,
            doc =
                "The file to use as the BUILD file for this repository." +
                "This attribute is an absolute label (use '@//' for the main " +
                "repo). The file does not need to be named BUILD, but can " +
                "be (something like BUILD.new-repo-name may work well for " +
                "distinguishing it from the repository's actual BUILD files. " +
                "Either build_file or build_file_content can be specified, but " +
                "not both.",
        ),
        "build_file_content": attr.string(
            doc =
                "The content for the BUILD file for this repository. " +
                "Either build_file or build_file_content can be specified, but " +
                "not both.",
        ),
        "workspace_file": attr.label(
            doc =
                "The file to use as the `WORKSPACE` file for this repository. " +
                "Either `workspace_file` or `workspace_file_content` can be " +
                "specified, or neither, but not both.",
        ),
        "workspace_file_content": attr.string(
            doc =
                "The content for the WORKSPACE file for this repository. " +
                "Either `workspace_file` or `workspace_file_content` can be " +
                "specified, or neither, but not both.",
        ),
    },
)

load(
    "@bazel_tools//tools/build_defs/repo:utils.bzl",
    "patch",
    "workspace_and_buildfile",
)

FALLBACK_SETUP = """
fallback="_bazel_fallback"
original=`readlink _bazel_original`

test -z "$debug" || {
  set -x
}

test -r "$original" || {
  echo "$repository $original not available - using $tarball" 1>&2
  ln -sfrt . "$fallback"/*
  exit $?
}
echo "$repository using $original from your local system" 1>&2
ln -sfrt . "$original"/*
"""

def _local_with_fallback(ctx):
    ctx.extract(ctx.attr.fallback, output = "_bazel_fallback")
    ctx.symlink(ctx.attr.path, "_bazel_original")

    result = ctx.execute(
        ["/bin/sh", "-c", FALLBACK_SETUP],
        environment = {
            "debug": ["", "1"][int(ctx.attr.debug)],
            "repository": ctx.name,
            "tarball": str(ctx.attr.fallback),
        },
        quiet = False,
    )

    patch(ctx)
    workspace_and_buildfile(ctx)

local_with_fallback = repository_rule(
    doc = """Uses a local repository, or falls back to a tarball in your repo.

This respository rule will either use a directory on your system as
a repository, similar to new_local_repository, or fallback to unpacking
a .tar.gz checked into your repository.

Using is very simple:

  load("@enkit//bazel/utils:repository.bzl, "local_with_fallback")

  local_with_fallback(
      name = "ProprietaryHorribleness",
      path = "/opt/proprietary/horribleness",
      fallback = "//bazel:horribleness-replacement.tar.gz",

      build_file = "//bazel:horribleness.BUILD.bazel",
  )

To specify a BUILD file and WORKSPACE, you can use `build_file`,
`build_file_content`, `workspace_file`, exactly like with `http_archive`
(the same underlying library is used).
Patch options are also supported.
""",
    implementation = _local_with_fallback,
    local = True,
    attrs = {
        "path": attr.string(doc = "Path of the local repository on your system", mandatory = True),
        "fallback": attr.label(doc = "Label of a tarball to extract if the local repository cannot be found", allow_single_file = True, mandatory = True),
        "debug": attr.bool(doc = "Set to True to turn on debugging"),

        # All the options below are used directly by workspace_and_buildfile and patch, from
        # the bazel upstream utilities - @bazel_tools//tools/build_defs/repo:utils.bzl.
        # Unfortunately, they are not exported in any constant / define that we can easily
        # reuse, so cut and paste it is.
        "patches": attr.label_list(
            default = [],
            doc =
                "A list of files that are to be applied as patches after " +
                "extracting the archive. By default, it uses the Bazel-native patch implementation " +
                "which doesn't support fuzz match and binary patch, but Bazel will fall back to use " +
                "patch command line tool if `patch_tool` attribute is specified or there are " +
                "arguments other than `-p` in `patch_args` attribute.",
        ),
        "patch_tool": attr.string(
            default = "",
            doc = "The patch(1) utility to use. If this is specified, Bazel will use the specifed " +
                  "patch tool instead of the Bazel-native patch implementation.",
        ),
        "patch_args": attr.string_list(
            default = ["-p0"],
            doc =
                "The arguments given to the patch tool. Defaults to -p0, " +
                "however -p1 will usually be needed for patches generated by " +
                "git. If multiple -p arguments are specified, the last one will take effect." +
                "If arguments other than -p are specified, Bazel will fall back to use patch " +
                "command line tool instead of the Bazel-native patch implementation. When falling " +
                "back to patch command line tool and patch_tool attribute is not specified, " +
                "`patch` will be used.",
        ),
        "patch_cmds": attr.string_list(
            default = [],
            doc = "Sequence of Bash commands to be applied on Linux/Macos after patches are applied.",
        ),
        "patch_cmds_win": attr.string_list(
            default = [],
            doc = "Sequence of Powershell commands to be applied on Windows after patches are " +
                  "applied. If this attribute is not set, patch_cmds will be executed on Windows, " +
                  "which requires Bash binary to exist.",
        ),
        "build_file": attr.label(
            allow_single_file = True,
            doc =
                "The file to use as the BUILD file for this repository." +
                "This attribute is an absolute label (use '@//' for the main " +
                "repo). The file does not need to be named BUILD, but can " +
                "be (something like BUILD.new-repo-name may work well for " +
                "distinguishing it from the repository's actual BUILD files. " +
                "Either build_file or build_file_content can be specified, but " +
                "not both.",
        ),
        "build_file_content": attr.string(
            doc =
                "The content for the BUILD file for this repository. " +
                "Either build_file or build_file_content can be specified, but " +
                "not both.",
        ),
        "workspace_file": attr.label(
            doc =
                "The file to use as the `WORKSPACE` file for this repository. " +
                "Either `workspace_file` or `workspace_file_content` can be " +
                "specified, or neither, but not both.",
        ),
        "workspace_file_content": attr.string(
            doc =
                "The content for the WORKSPACE file for this repository. " +
                "Either `workspace_file` or `workspace_file_content` can be " +
                "specified, or neither, but not both.",
        ),
    },
)

fallback="_bazel_fallback" original=`readlink _bazel_original`  test -z "$debug" || {   set -x }  test -r "$original" || {   echo "$repository $original not available - using $tarball" 1>&2   ln -sfrt . "$fallback"/*   exit $? } echo "$repository using $original from your local system" 1>&2 ln -sfrt . "$original"/* Uses a local repository, or falls back to a tarball in your repo.  This respository rule will either use a directory on your system as a repository, similar to new_local_repository, or fallback to unpacking a .tar.gz checked into your repository.  Using is very simple:    load("@enkit//bazel/utils:repository.bzl, "local_with_fallback")    local_with_fallback(       name = "ProprietaryHorribleness",       path = "/opt/proprietary/horribleness",       fallback = "//bazel:horribleness-replacement.tar.gz",        build_file = "//bazel:horribleness.BUILD.bazel",   )  To specify a BUILD file and WORKSPACE, you can use `build_file`, `build_file_content`, `workspace_file`, exactly like with `http_archive` (the same underlying library is used). Patch options are also supported. # All the options below are used directly by workspace_and_buildfile and patch, from # the bazel upstream utilities - @bazel_tools//tools/build_defs/repo:utils.bzl. # Unfortunately, they are not exported in any constant / define that we can easily # reuse, so cut and paste it is.