1# Copyright 2020 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 Lite Support Bazel dependencies.""" 16 17_SINGLE_URL_WHITELIST = [] 18 19def _is_windows(ctx): 20 return ctx.os.name.lower().find("windows") != -1 21 22def _wrap_bash_cmd(ctx, cmd): 23 if _is_windows(ctx): 24 bazel_sh = _get_env_var(ctx, "BAZEL_SH") 25 if not bazel_sh: 26 fail("BAZEL_SH environment variable is not set") 27 cmd = [bazel_sh, "-l", "-c", " ".join(["\"%s\"" % s for s in cmd])] 28 return cmd 29 30def _get_env_var(ctx, name): 31 if name in ctx.os.environ: 32 return ctx.os.environ[name] 33 else: 34 return None 35 36# Checks if we should use the system lib instead of the bundled one 37def _use_system_lib(ctx, name): 38 syslibenv = _get_env_var(ctx, "TF_SYSTEM_LIBS") 39 if syslibenv: 40 for n in syslibenv.strip().split(","): 41 if n.strip() == name: 42 return True 43 return False 44 45# Executes specified command with arguments and calls 'fail' if it exited with 46# non-zero code 47def _execute_and_check_ret_code(repo_ctx, cmd_and_args): 48 result = repo_ctx.execute(cmd_and_args, timeout = 60) 49 if result.return_code != 0: 50 fail(("Non-zero return code({1}) when executing '{0}':\n" + "Stdout: {2}\n" + 51 "Stderr: {3}").format( 52 " ".join([str(x) for x in cmd_and_args]), 53 result.return_code, 54 result.stdout, 55 result.stderr, 56 )) 57 58# Apply a patch_file to the repository root directory 59# Runs 'patch -p1' on both Windows and Unix. 60def _apply_patch(ctx, patch_file): 61 patch_command = ["patch", "-p1", "-d", ctx.path("."), "-i", ctx.path(patch_file)] 62 cmd = _wrap_bash_cmd(ctx, patch_command) 63 _execute_and_check_ret_code(ctx, cmd) 64 65def _apply_delete(ctx, paths): 66 for path in paths: 67 if path.startswith("/"): 68 fail("refusing to rm -rf path starting with '/': " + path) 69 if ".." in path: 70 fail("refusing to rm -rf path containing '..': " + path) 71 cmd = _wrap_bash_cmd(ctx, ["rm", "-rf"] + [ctx.path(path) for path in paths]) 72 _execute_and_check_ret_code(ctx, cmd) 73 74def _third_party_http_archive(ctx): 75 """Downloads and creates Bazel repos for dependencies. 76 77 This is a swappable replacement for both http_archive() and 78 new_http_archive() that offers some additional features. It also helps 79 ensure best practices are followed. 80 """ 81 if ("mirror.tensorflow.org" not in ctx.attr.urls[0] and 82 (len(ctx.attr.urls) < 2 and 83 ctx.attr.name not in _SINGLE_URL_WHITELIST.to_list())): 84 fail("third_party_http_archive(urls) must have redundant URLs. The " + 85 "mirror.tensorflow.org URL must be present and it must come first. " + 86 "Even if you don't have permission to mirror the file, please " + 87 "put the correctly formatted mirror URL there anyway, because " + 88 "someone will come along shortly thereafter and mirror the file.") 89 90 use_syslib = _use_system_lib(ctx, ctx.attr.name) 91 92 # Use "BUILD.bazel" to avoid conflict with third party projects that contain a 93 # file or directory called "BUILD" 94 buildfile_path = ctx.path("BUILD.bazel") 95 96 if use_syslib: 97 if ctx.attr.system_build_file == None: 98 fail("Bazel was configured with TF_SYSTEM_LIBS to use a system " + 99 "library for %s, but no system build file for %s was configured. " + 100 "Please add a system_build_file attribute to the repository rule" + 101 "for %s." % (ctx.attr.name, ctx.attr.name, ctx.attr.name)) 102 ctx.symlink(Label(ctx.attr.system_build_file), buildfile_path) 103 104 else: 105 ctx.download_and_extract( 106 ctx.attr.urls, 107 "", 108 ctx.attr.sha256, 109 ctx.attr.type, 110 ctx.attr.strip_prefix, 111 ) 112 if ctx.attr.delete: 113 _apply_delete(ctx, ctx.attr.delete) 114 if ctx.attr.patch_file != None: 115 _apply_patch(ctx, ctx.attr.patch_file) 116 ctx.symlink(Label(ctx.attr.build_file), buildfile_path) 117 118 link_dict = {} 119 if use_syslib: 120 link_dict.update(ctx.attr.system_link_files) 121 122 for internal_src, external_dest in ctx.attr.link_files.items(): 123 # if syslib and link exists in both, use the system one 124 if external_dest not in link_dict.values(): 125 link_dict[internal_src] = external_dest 126 127 for internal_src, external_dest in link_dict.items(): 128 ctx.symlink(Label(internal_src), ctx.path(external_dest)) 129 130# For link_files, specify each dict entry as: 131# "//path/to/source:file": "localfile" 132third_party_http_archive = repository_rule( 133 attrs = { 134 "sha256": attr.string(mandatory = True), 135 "urls": attr.string_list( 136 mandatory = True, 137 allow_empty = False, 138 ), 139 "strip_prefix": attr.string(), 140 "type": attr.string(), 141 "delete": attr.string_list(), 142 "build_file": attr.string(mandatory = True), 143 "system_build_file": attr.string(mandatory = False), 144 "patch_file": attr.label(), 145 "link_files": attr.string_dict(), 146 "system_link_files": attr.string_dict(), 147 }, 148 environ = [ 149 "TF_SYSTEM_LIBS", 150 ], 151 implementation = _third_party_http_archive, 152) 153