1"""Functions common across configure rules.""" 2 3BAZEL_SH = "BAZEL_SH" 4PYTHON_BIN_PATH = "PYTHON_BIN_PATH" 5PYTHON_LIB_PATH = "PYTHON_LIB_PATH" 6TF_PYTHON_CONFIG_REPO = "TF_PYTHON_CONFIG_REPO" 7 8def auto_config_fail(msg): 9 """Output failure message when auto configuration fails.""" 10 red = "\033[0;31m" 11 no_color = "\033[0m" 12 fail("%sConfiguration Error:%s %s\n" % (red, no_color, msg)) 13 14def which(repository_ctx, program_name): 15 """Returns the full path to a program on the execution platform. 16 17 Args: 18 repository_ctx: the repository_ctx 19 program_name: name of the program on the PATH 20 21 Returns: 22 The full path to a program on the execution platform. 23 """ 24 if is_windows(repository_ctx): 25 if not program_name.endswith(".exe"): 26 program_name = program_name + ".exe" 27 return execute( 28 repository_ctx, 29 ["C:\\Windows\\System32\\where.exe", program_name], 30 ).stdout.replace("\\", "\\\\").rstrip() 31 32 return execute(repository_ctx, ["which", program_name]).stdout.rstrip() 33 34def get_python_bin(repository_ctx): 35 """Gets the python bin path. 36 37 Args: 38 repository_ctx: the repository_ctx 39 40 Returns: 41 The python bin path. 42 """ 43 python_bin = get_host_environ(repository_ctx, PYTHON_BIN_PATH) 44 if python_bin != None: 45 return python_bin 46 47 # First check for an explicit "python3" 48 python_bin = which(repository_ctx, "python3") 49 if python_bin != None: 50 return python_bin 51 52 # Some systems just call pythone3 "python" 53 python_bin = which(repository_ctx, "python") 54 if python_bin != None: 55 return python_bin 56 57 auto_config_fail("Cannot find python in PATH, please make sure " + 58 "python is installed and add its directory in PATH, or --define " + 59 "%s='/something/else'.\nPATH=%s" % ( 60 PYTHON_BIN_PATH, 61 get_environ("PATH", ""), 62 )) 63 return python_bin # unreachable 64 65def get_bash_bin(repository_ctx): 66 """Gets the bash bin path. 67 68 Args: 69 repository_ctx: the repository_ctx 70 71 Returns: 72 The bash bin path. 73 """ 74 bash_bin = get_host_environ(repository_ctx, BAZEL_SH) 75 if bash_bin != None: 76 return bash_bin 77 bash_bin_path = which(repository_ctx, "bash") 78 if bash_bin_path == None: 79 auto_config_fail("Cannot find bash in PATH, please make sure " + 80 "bash is installed and add its directory in PATH, or --define " + 81 "%s='/path/to/bash'.\nPATH=%s" % ( 82 BAZEL_SH, 83 get_environ("PATH", ""), 84 )) 85 return bash_bin_path 86 87def read_dir(repository_ctx, src_dir): 88 """Returns a sorted list with all files in a directory. 89 90 Finds all files inside a directory, traversing subfolders and following 91 symlinks. 92 93 Args: 94 repository_ctx: the repository_ctx 95 src_dir: the directory to traverse 96 97 Returns: 98 A sorted list with all files in a directory. 99 """ 100 if is_windows(repository_ctx): 101 src_dir = src_dir.replace("/", "\\") 102 find_result = execute( 103 repository_ctx, 104 ["C:\\Windows\\System32\\cmd.exe", "/c", "dir", src_dir, "/b", "/s", "/a-d"], 105 empty_stdout_fine = True, 106 ) 107 108 # src_files will be used in genrule.outs where the paths must 109 # use forward slashes. 110 result = find_result.stdout.replace("\\", "/") 111 else: 112 find_result = execute( 113 repository_ctx, 114 ["find", src_dir, "-follow", "-type", "f"], 115 empty_stdout_fine = True, 116 ) 117 result = find_result.stdout 118 return sorted(result.splitlines()) 119 120def get_environ(repository_ctx, name, default_value = None): 121 """Returns the value of an environment variable on the execution platform. 122 123 Args: 124 repository_ctx: the repository_ctx 125 name: the name of environment variable 126 default_value: the value to return if not set 127 128 Returns: 129 The value of the environment variable 'name' on the execution platform 130 or 'default_value' if it's not set. 131 """ 132 if is_windows(repository_ctx): 133 result = execute( 134 repository_ctx, 135 ["C:\\Windows\\System32\\cmd.exe", "/c", "echo", "%" + name + "%"], 136 empty_stdout_fine = True, 137 ) 138 else: 139 cmd = "echo -n \"$%s\"" % name 140 result = execute( 141 repository_ctx, 142 [get_bash_bin(repository_ctx), "-c", cmd], 143 empty_stdout_fine = True, 144 ) 145 if len(result.stdout) == 0: 146 return default_value 147 return result.stdout 148 149def get_host_environ(repository_ctx, name, default_value = None): 150 """Returns the value of an environment variable on the host platform. 151 152 The host platform is the machine that Bazel runs on. 153 154 Args: 155 repository_ctx: the repository_ctx 156 name: the name of environment variable 157 158 Returns: 159 The value of the environment variable 'name' on the host platform. 160 """ 161 if name in repository_ctx.os.environ: 162 return repository_ctx.os.environ.get(name).strip() 163 164 if hasattr(repository_ctx.attr, "environ") and name in repository_ctx.attr.environ: 165 return repository_ctx.attr.environ.get(name).strip() 166 167 return default_value 168 169def is_windows(repository_ctx): 170 """Returns true if the execution platform is Windows. 171 172 Args: 173 repository_ctx: the repository_ctx 174 175 Returns: 176 If the execution platform is Windows. 177 """ 178 os_name = "" 179 if hasattr(repository_ctx.attr, "exec_properties") and "OSFamily" in repository_ctx.attr.exec_properties: 180 os_name = repository_ctx.attr.exec_properties["OSFamily"] 181 else: 182 os_name = repository_ctx.os.name 183 184 return os_name.lower().find("windows") != -1 185 186def get_cpu_value(repository_ctx): 187 """Returns the name of the host operating system. 188 189 Args: 190 repository_ctx: The repository context. 191 Returns: 192 A string containing the name of the host operating system. 193 """ 194 if is_windows(repository_ctx): 195 return "Windows" 196 result = raw_exec(repository_ctx, ["uname", "-s"]) 197 return result.stdout.strip() 198 199def execute( 200 repository_ctx, 201 cmdline, 202 error_msg = None, 203 error_details = None, 204 empty_stdout_fine = False): 205 """Executes an arbitrary shell command. 206 207 Args: 208 repository_ctx: the repository_ctx object 209 cmdline: list of strings, the command to execute 210 error_msg: string, a summary of the error if the command fails 211 error_details: string, details about the error or steps to fix it 212 empty_stdout_fine: bool, if True, an empty stdout result is fine, 213 otherwise it's an error 214 Returns: 215 The result of repository_ctx.execute(cmdline) 216 """ 217 result = raw_exec(repository_ctx, cmdline) 218 if result.stderr or not (empty_stdout_fine or result.stdout): 219 fail( 220 "\n".join([ 221 error_msg.strip() if error_msg else "Repository command failed", 222 result.stderr.strip(), 223 error_details if error_details else "", 224 ]), 225 ) 226 return result 227 228def raw_exec(repository_ctx, cmdline): 229 """Executes a command via repository_ctx.execute() and returns the result. 230 231 This method is useful for debugging purposes. For example, to print all 232 commands executed as well as their return code. 233 234 Args: 235 repository_ctx: the repository_ctx 236 cmdline: the list of args 237 238 Returns: 239 The 'exec_result' of repository_ctx.execute(). 240 """ 241 return repository_ctx.execute(cmdline) 242 243def files_exist(repository_ctx, paths, bash_bin = None): 244 """Checks which files in paths exists. 245 246 Args: 247 repository_ctx: the repository_ctx 248 paths: a list of paths 249 bash_bin: path to the bash interpreter 250 251 Returns: 252 Returns a list of Bool. True means that the path at the 253 same position in the paths list exists. 254 """ 255 if bash_bin == None: 256 bash_bin = get_bash_bin(repository_ctx) 257 258 cmd_tpl = "[ -e \"%s\" ] && echo True || echo False" 259 cmds = [cmd_tpl % path for path in paths] 260 cmd = " ; ".join(cmds) 261 262 stdout = execute(repository_ctx, [bash_bin, "-c", cmd]).stdout.strip() 263 return [val == "True" for val in stdout.splitlines()] 264 265def realpath(repository_ctx, path, bash_bin = None): 266 """Returns the result of "realpath path". 267 268 Args: 269 repository_ctx: the repository_ctx 270 path: a path on the file system 271 bash_bin: path to the bash interpreter 272 273 Returns: 274 Returns the result of "realpath path" 275 """ 276 if bash_bin == None: 277 bash_bin = get_bash_bin(repository_ctx) 278 279 return execute(repository_ctx, [bash_bin, "-c", "realpath \"%s\"" % path]).stdout.strip() 280 281def err_out(result): 282 """Returns stderr if set, else stdout. 283 284 This function is a workaround for a bug in RBE where stderr is returned as stdout. Instead 285 of using result.stderr use err_out(result) instead. 286 287 Args: 288 result: the exec_result. 289 290 Returns: 291 The stderr if set, else stdout 292 """ 293 if len(result.stderr) == 0: 294 return result.stdout 295 return result.stderr 296 297def config_repo_label(config_repo, target): 298 """Construct a label from config_repo and target. 299 300 This function exists to ease the migration from preconfig to remote config. In preconfig 301 the TF_*_CONFIG_REPO environ variables are set to packages in the main repo while in 302 remote config they will point to remote repositories. 303 304 Args: 305 config_repo: a remote repository or package. 306 target: a target 307 Returns: 308 A label constructed from config_repo and target. 309 """ 310 if config_repo.startswith("@") and not config_repo.find("//") > 0: 311 # remote config is being used. 312 return Label(config_repo + "//" + target) 313 elif target.startswith(":"): 314 return Label(config_repo + target) 315 else: 316 return Label(config_repo + "/" + target) 317