| # Copyright 2019 The Bazel Authors. All rights reserved. | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #    http://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | """Configure local Resource Compilers for every available target platform. | 
 |  | 
 | Platform support: | 
 |  | 
 | - Linux/macOS/non-Windows: this repository rule is almost a no-op. | 
 |   It generates an empty BUILD file, and does not actually discover any | 
 |   toolchains. The register_local_rc_exe_toolchains() method is a no-op. | 
 |  | 
 | - Windows: this repository rule discovers the Windows SDK path, the installed | 
 |   rc.exe compilers. The register_local_rc_exe_toolchains() registers a toolchain | 
 |   for each rc.exe compiler. | 
 |  | 
 | Usage: | 
 |  | 
 |     load("//src/main/res:winsdk_configure.bzl", "winsdk_configure") | 
 |  | 
 |     winsdk_configure(name = "local_config_winsdk") | 
 |  | 
 |     load("@local_config_winsdk//:toolchains.bzl", "register_local_rc_exe_toolchains") | 
 |  | 
 |     register_local_rc_exe_toolchains() | 
 | """ | 
 |  | 
 | load("@bazel_tools//tools/cpp:cc_configure.bzl", "MSVC_ENVVARS") | 
 | load("@bazel_tools//tools/cpp:windows_cc_configure.bzl", "find_vc_path", "setup_vc_env_vars") | 
 |  | 
 | # Keys: target architecture, as in <Windows-SDK-path>/<target-architecture>/bin/rc.exe | 
 | # Values: corresponding Bazel CPU value under @platforms//cpu:* | 
 | _TARGET_ARCH = { | 
 |     "arm": "arm", | 
 |     "arm64": "aarch64", | 
 |     "x64": "x86_64", | 
 |     "x86": "x86_32", | 
 | } | 
 |  | 
 | def _find_rc_exes(root): | 
 |     result = {} | 
 |     for a in _TARGET_ARCH: | 
 |         exe = root.get_child(a).get_child("rc.exe") | 
 |         if exe.exists: | 
 |             result[a] = str(exe) | 
 |     return result | 
 |  | 
 | def _find_all_rc_exe(repository_ctx): | 
 |     if not repository_ctx.os.name.startswith("windows"): | 
 |         return {} | 
 |  | 
 |     vc = find_vc_path(repository_ctx) | 
 |     if vc: | 
 |         env = setup_vc_env_vars( | 
 |             repository_ctx, | 
 |             vc, | 
 |             envvars = [ | 
 |                 "WindowsSdkDir", | 
 |                 "WindowsSdkVerBinPath", | 
 |             ], | 
 |             allow_empty = True, | 
 |             escape = False, | 
 |         ) | 
 |  | 
 |         # Try the versioned directory. | 
 |         sdk = env.get("WindowsSdkVerBinPath") | 
 |         if sdk: | 
 |             archs = _find_rc_exes(repository_ctx.path(sdk)) | 
 |             if archs: | 
 |                 return archs | 
 |  | 
 |         # Try the unversioned directory (typically Windows 8.1 SDK). | 
 |         sdk = env.get("WindowsSdkDir") | 
 |         if sdk: | 
 |             archs = _find_rc_exes(repository_ctx.path(sdk).get_child("bin")) | 
 |             if archs: | 
 |                 return archs | 
 |     return {} | 
 |  | 
 | def _toolchain_defs(repository_ctx, rc_exes): | 
 |     if not rc_exes: | 
 |         return "" | 
 |  | 
 |     result = ["""# Auto-generated by winsdk_configure.bzl | 
 | load( | 
 |     "@io_bazel//src/main/res:winsdk_toolchain.bzl", | 
 |     "WINDOWS_RESOURCE_COMPILER_TOOLCHAIN_TYPE", | 
 |     "windows_resource_compiler_toolchain", | 
 | )"""] | 
 |  | 
 |     for arch, rc_path in rc_exes.items(): | 
 |         wrapper = "rc_%s.bat" % arch | 
 |         repository_ctx.file( | 
 |             wrapper, | 
 |             content = "@\"%s\" %%*" % rc_path, | 
 |             executable = True, | 
 |         ) | 
 |  | 
 |         result.append( | 
 |             """ | 
 | windows_resource_compiler_toolchain( | 
 |     name = "local_{arch}_tc", | 
 |     rc_exe = "{wrapper}", | 
 | ) | 
 |  | 
 | toolchain( | 
 |     name = "local_{arch}", | 
 |     exec_compatible_with = [ | 
 |         "@platforms//os:windows", | 
 |         "@platforms//cpu:x86_64", | 
 |     ], | 
 |     target_compatible_with = [ | 
 |         "@platforms//os:windows", | 
 |         "@platforms//cpu:{cpu}", | 
 |     ], | 
 |     toolchain = ":local_{arch}_tc", | 
 |     toolchain_type = WINDOWS_RESOURCE_COMPILER_TOOLCHAIN_TYPE, | 
 |     visibility = ["//visibility:public"], | 
 | ) | 
 | """.format( | 
 |                 arch = arch, | 
 |                 wrapper = wrapper, | 
 |                 cpu = _TARGET_ARCH[arch], | 
 |             ), | 
 |         ) | 
 |  | 
 |     return "\n".join(result) | 
 |  | 
 | def _toolchain_labels(repository_ctx, rc_exes): | 
 |     tc_labels = [ | 
 |         "\"@{repo}//:local_{arch}\"".format(repo = repository_ctx.name, arch = arch) | 
 |         for arch in rc_exes | 
 |     ] | 
 |     if rc_exes: | 
 |         body = "native.register_toolchains(%s)" % ", ".join(tc_labels) | 
 |     else: | 
 |         body = "pass" | 
 |  | 
 |     return """# Auto-generated by winsdk_configure.bzl | 
 |  | 
 | def register_local_rc_exe_toolchains(): | 
 |     {body} | 
 | """.format(body = body) | 
 |  | 
 | def _impl(repository_ctx): | 
 |     rc_exes = _find_all_rc_exe(repository_ctx) | 
 |     repository_ctx.file( | 
 |         "BUILD", | 
 |         content = _toolchain_defs(repository_ctx, rc_exes), | 
 |         executable = False, | 
 |     ) | 
 |  | 
 |     repository_ctx.file( | 
 |         "toolchains.bzl", | 
 |         content = _toolchain_labels(repository_ctx, rc_exes), | 
 |         executable = False, | 
 |     ) | 
 |  | 
 | winsdk_configure = repository_rule( | 
 |     implementation = _impl, | 
 |     local = True, | 
 |     environ = list(MSVC_ENVVARS), | 
 | ) |