1# Copyright 2017 The TensorFlow Authors. All rights reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Utilities for defining TensorFlow Bazel dependencies.""" 16 17_SINGLE_URL_WHITELIST = depset([ 18 "arm_compiler", 19]) 20 21def _is_windows(ctx): 22 return ctx.os.name.lower().find("windows") != -1 23 24def _wrap_bash_cmd(ctx, cmd): 25 if _is_windows(ctx): 26 bazel_sh = _get_env_var(ctx, "BAZEL_SH") 27 if not bazel_sh: 28 fail("BAZEL_SH environment variable is not set") 29 cmd = [bazel_sh, "-l", "-c", " ".join(["\"%s\"" % s for s in cmd])] 30 return cmd 31 32def _get_env_var(ctx, name): 33 if name in ctx.os.environ: 34 return ctx.os.environ[name] 35 else: 36 return None 37 38# Checks if we should use the system lib instead of the bundled one 39def _use_system_lib(ctx, name): 40 syslibenv = _get_env_var(ctx, "TF_SYSTEM_LIBS") 41 if syslibenv: 42 for n in syslibenv.strip().split(","): 43 if n.strip() == name: 44 return True 45 return False 46 47# Executes specified command with arguments and calls 'fail' if it exited with 48# non-zero code 49def _execute_and_check_ret_code(repo_ctx, cmd_and_args): 50 result = repo_ctx.execute(cmd_and_args, timeout = 60) 51 if result.return_code != 0: 52 fail(("Non-zero return code({1}) when executing '{0}':\n" + "Stdout: {2}\n" + 53 "Stderr: {3}").format( 54 " ".join([str(x) for x in cmd_and_args]), 55 result.return_code, 56 result.stdout, 57 result.stderr, 58 )) 59 60def _repos_are_siblings(): 61 return Label("@foo//bar").workspace_root.startswith("../") 62 63# Apply a patch_file to the repository root directory 64# Runs 'patch -p1' on both Windows and Unix. 65def _apply_patch(ctx, patch_file): 66 patch_command = ["patch", "-p1", "-d", ctx.path("."), "-i", ctx.path(patch_file)] 67 cmd = _wrap_bash_cmd(ctx, patch_command) 68 _execute_and_check_ret_code(ctx, cmd) 69 70def _apply_delete(ctx, paths): 71 for path in paths: 72 if path.startswith("/"): 73 fail("refusing to rm -rf path starting with '/': " + path) 74 if ".." in path: 75 fail("refusing to rm -rf path containing '..': " + path) 76 cmd = _wrap_bash_cmd(ctx, ["rm", "-rf"] + [ctx.path(path) for path in paths]) 77 _execute_and_check_ret_code(ctx, cmd) 78 79def _tf_http_archive(ctx): 80 if ("mirror.tensorflow.org" not in ctx.attr.urls[0] and 81 (len(ctx.attr.urls) < 2 and 82 ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())): 83 fail("tf_http_archive(urls) must have redundant URLs. The " + 84 "mirror.tensorflow.org URL must be present and it must come first. " + 85 "Even if you don't have permission to mirror the file, please " + 86 "put the correctly formatted mirror URL there anyway, because " + 87 "someone will come along shortly thereafter and mirror the file.") 88 89 use_syslib = _use_system_lib(ctx, ctx.attr.name) 90 91 # Work around the bazel bug that redownloads the whole library. 92 # Remove this after https://github.com/bazelbuild/bazel/issues/10515 is fixed. 93 if ctx.attr.additional_build_files: 94 for internal_src in ctx.attr.additional_build_files: 95 _ = ctx.path(Label(internal_src)) 96 97 # End of workaround. 98 99 if not use_syslib: 100 ctx.download_and_extract( 101 ctx.attr.urls, 102 "", 103 ctx.attr.sha256, 104 ctx.attr.type, 105 ctx.attr.strip_prefix, 106 ) 107 if ctx.attr.delete: 108 _apply_delete(ctx, ctx.attr.delete) 109 if ctx.attr.patch_file != None: 110 _apply_patch(ctx, ctx.attr.patch_file) 111 112 if use_syslib and ctx.attr.system_build_file != None: 113 # Use BUILD.bazel to avoid conflict with third party projects with 114 # BUILD or build (directory) underneath. 115 ctx.template("BUILD.bazel", ctx.attr.system_build_file, { 116 "%prefix%": ".." if _repos_are_siblings() else "external", 117 }, False) 118 119 elif ctx.attr.build_file != None: 120 # Use BUILD.bazel to avoid conflict with third party projects with 121 # BUILD or build (directory) underneath. 122 ctx.template("BUILD.bazel", ctx.attr.build_file, { 123 "%prefix%": ".." if _repos_are_siblings() else "external", 124 }, False) 125 126 if use_syslib: 127 for internal_src, external_dest in ctx.attr.system_link_files.items(): 128 ctx.symlink(Label(internal_src), ctx.path(external_dest)) 129 130 if ctx.attr.additional_build_files: 131 for internal_src, external_dest in ctx.attr.additional_build_files.items(): 132 ctx.symlink(Label(internal_src), ctx.path(external_dest)) 133 134tf_http_archive = repository_rule( 135 attrs = { 136 "sha256": attr.string(mandatory = True), 137 "urls": attr.string_list( 138 mandatory = True, 139 allow_empty = False, 140 ), 141 "strip_prefix": attr.string(), 142 "type": attr.string(), 143 "delete": attr.string_list(), 144 "patch_file": attr.label(), 145 "build_file": attr.label(), 146 "system_build_file": attr.label(), 147 "system_link_files": attr.string_dict(), 148 "additional_build_files": attr.string_dict(), 149 }, 150 environ = [ 151 "TF_SYSTEM_LIBS", 152 ], 153 implementation = _tf_http_archive, 154) 155 156"""Downloads and creates Bazel repos for dependencies. 157 158This is a swappable replacement for both http_archive() and 159new_http_archive() that offers some additional features. It also helps 160ensure best practices are followed. 161""" 162 163def _third_party_http_archive(ctx): 164 if ("mirror.tensorflow.org" not in ctx.attr.urls[0] and 165 (len(ctx.attr.urls) < 2 and 166 ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())): 167 fail("tf_http_archive(urls) must have redundant URLs. The " + 168 "mirror.tensorflow.org URL must be present and it must come first. " + 169 "Even if you don't have permission to mirror the file, please " + 170 "put the correctly formatted mirror URL there anyway, because " + 171 "someone will come along shortly thereafter and mirror the file.") 172 173 use_syslib = _use_system_lib(ctx, ctx.attr.name) 174 175 # Use "BUILD.bazel" to avoid conflict with third party projects that contain a 176 # file or directory called "BUILD" 177 buildfile_path = ctx.path("BUILD.bazel") 178 179 if use_syslib: 180 if ctx.attr.system_build_file == None: 181 fail("Bazel was configured with TF_SYSTEM_LIBS to use a system " + 182 "library for %s, but no system build file for %s was configured. " + 183 "Please add a system_build_file attribute to the repository rule" + 184 "for %s." % (ctx.attr.name, ctx.attr.name, ctx.attr.name)) 185 ctx.symlink(Label(ctx.attr.system_build_file), buildfile_path) 186 187 else: 188 ctx.download_and_extract( 189 ctx.attr.urls, 190 "", 191 ctx.attr.sha256, 192 ctx.attr.type, 193 ctx.attr.strip_prefix, 194 ) 195 if ctx.attr.delete: 196 _apply_delete(ctx, ctx.attr.delete) 197 if ctx.attr.patch_file != None: 198 _apply_patch(ctx, ctx.attr.patch_file) 199 ctx.symlink(Label(ctx.attr.build_file), buildfile_path) 200 201 link_dict = {} 202 if use_syslib: 203 link_dict.update(ctx.attr.system_link_files) 204 205 for internal_src, external_dest in ctx.attr.link_files.items(): 206 # if syslib and link exists in both, use the system one 207 if external_dest not in link_dict.values(): 208 link_dict[internal_src] = external_dest 209 210 for internal_src, external_dest in link_dict.items(): 211 ctx.symlink(Label(internal_src), ctx.path(external_dest)) 212 213# Downloads and creates Bazel repos for dependencies. 214# 215# This is an upgrade for tf_http_archive that works with go/tfbr-thirdparty. 216# 217# For link_files, specify each dict entry as: 218# "//path/to/source:file": "localfile" 219third_party_http_archive = repository_rule( 220 attrs = { 221 "sha256": attr.string(mandatory = True), 222 "urls": attr.string_list( 223 mandatory = True, 224 allow_empty = False, 225 ), 226 "strip_prefix": attr.string(), 227 "type": attr.string(), 228 "delete": attr.string_list(), 229 "build_file": attr.string(mandatory = True), 230 "system_build_file": attr.string(mandatory = False), 231 "patch_file": attr.label(), 232 "link_files": attr.string_dict(), 233 "system_link_files": attr.string_dict(), 234 }, 235 environ = [ 236 "TF_SYSTEM_LIBS", 237 ], 238 implementation = _third_party_http_archive, 239) 240