Add pkg_install.destdir_flag. (#990)

Add pkg_install.destdir_flag.

This allows `pkg_install(destdir)` to be set from a string_flag.
The use case is to push destdir to a flag you can set in .bazelrc instead of pushing an environment variable through the build.
diff --git a/pkg/install.bzl b/pkg/install.bzl
index fec35c8..ee3d195 100644
--- a/pkg/install.bzl
+++ b/pkg/install.bzl
@@ -17,6 +17,7 @@
 run`-able installation script.
 """
 
+load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
 load("@rules_python//python:defs.bzl", "py_binary")
 load("//pkg:providers.bzl", "PackageDirsInfo", "PackageFilegroupInfo", "PackageFilesInfo", "PackageSymlinkInfo")
 load("//pkg/private:pkg_files.bzl", "create_mapping_context_from_ctx", "process_src", "write_manifest")
@@ -34,6 +35,11 @@
 
     manifest_file = ctx.actions.declare_file(ctx.attr.name + "-install-manifest.json")
 
+    destdir = ctx.attr.destdir
+    if ctx.attr.destdir_flag:
+        if BuildSettingInfo in ctx.attr.destdir_flag:
+            destdir = ctx.attr.destdir_flag[BuildSettingInfo].value
+
     # Write out the manifest in terms of "short" paths, which are those expected
     # when you make `bazel run`nable binaries).
     #
@@ -62,7 +68,7 @@
             "{WORKSPACE_NAME}": ctx.workspace_name,
             # Used to annotate --help with "bazel run //path/to/your:installer"
             "{TARGET_LABEL}": label_str,
-            "{DEFAULT_DESTDIR}": ctx.attr.destdir,
+            "{DEFAULT_DESTDIR}": destdir,
         },
         is_executable = True,
     )
@@ -100,6 +106,7 @@
             doc = "Source mapping/grouping targets",
         ),
         "destdir": attr.string(),
+        "destdir_flag": attr.label(doc = "string flag to obtain destdir from"),
         # This is private for now -- one could perhaps imagine making this
         # public, but that would require more documentation of the underlying
         # scripts and expected interfaces.
@@ -111,7 +118,7 @@
     executable = True,
 )
 
-def pkg_install(name, srcs, destdir = None, **kwargs):
+def pkg_install(name, srcs, destdir = None, destdir_flag = None, **kwargs):
     """Create an installer script from pkg_filegroups and friends.
 
     This macro allows users to create `bazel run`nable installation scripts
@@ -170,14 +177,18 @@
 
             If this is an absolute path, it is used as-is. If this is a relative
             path, it is interpreted against `BUILD_WORKSPACE_DIRECTORY`.
+        destdir_flag: A string_flag target used to obtain the value of destdir.
         **kwargs: common rule attributes
 
     """
+    if destdir and destdir_flag:
+        fail("You may only set on of destdir or destdir_flag")
 
     _pkg_install_script(
         name = name + "_install_script",
         srcs = srcs,
         destdir = destdir,
+        destdir_flag = destdir_flag,
         **kwargs
     )
 
diff --git a/tests/install/BUILD b/tests/install/BUILD
index 7d952d4..459b392 100644
--- a/tests/install/BUILD
+++ b/tests/install/BUILD
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+load("@bazel_skylib//rules:common_settings.bzl", "string_flag")
 load("@rules_python//python:defs.bzl", "py_test")
 load("//pkg:install.bzl", "pkg_install")
 load("//pkg:mappings.bzl", "pkg_attributes", "pkg_files", "pkg_mkdirs", "pkg_mklink")
@@ -25,6 +26,7 @@
     args = ["-v"],
     data = [
         ":test_installer",
+        ":test_installer_flag",
     ],
     imports = ["../.."],
     main = "test.py",
@@ -115,3 +117,16 @@
     link_name = "/lib/fake.so.1",
     target = "fake.so.1.2.3",
 )
+
+string_flag(
+    name = "install_dir",
+    build_setting_default = "FromFlag",
+)
+
+pkg_install(
+    name = "test_installer_flag",
+    srcs = [
+        ":artifact-in-owned-dir",
+    ],
+    destdir_flag = ":install_dir",
+)
diff --git a/tests/install/test.py b/tests/install/test.py
index 270e835..927d48c 100644
--- a/tests/install/test.py
+++ b/tests/install/test.py
@@ -211,6 +211,25 @@
         self.assertEqual(num_missing, 0)
 
 
+class DestdirFlagTest(unittest.TestCase):
+
+    @classmethod
+    def setUpClass(cls):
+        super().setUpClass()
+        r = runfiles.Create()
+        cls.script_path = r.Rlocation(
+            f"rules_pkg/tests/install/test_installer_flag_install_script.py"
+        )
+
+    def test_installer_build_from_flag(self):
+        # This is about as good as we can do without a lot of scaffolding.
+        # To test the flag, we would have to invoke bazel from bazel, which is messy.
+        with open(self.script_path) as installer:
+            script = installer.read()
+            # Default value from BUILD file.
+            self.assertIn("FromFlag", script)
+
+
 class WipeTest(PkgInstallTestBase):
     def test_wipe(self):
         self.installdir.mkdir(exist_ok=True)