1# Copyright 2024 The Bazel 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"""Skylib module containing path operations on directories.""" 16 17load("//lib:paths.bzl", "paths") 18 19_NOT_FOUND = """{directory} does not contain an entry named {name}. 20Instead, it contains the following entries: 21{children} 22 23""" 24_WRONG_TYPE = "Expected {dir}/{name} to have type {want}, but got {got}" 25 26# These correspond to an "enum". 27FILE = "file" 28DIRECTORY = "directory" 29 30def _check_path_relative(path): 31 if paths.is_absolute(path): 32 fail("Path must be relative. Got {path}".format(path = path)) 33 34def _get_direct_child(directory, name, require_type = None): 35 """Gets the direct child of a directory. 36 37 Args: 38 directory: (DirectoryInfo) The directory to look within. 39 name: (string) The name of the directory/file to look for. 40 require_type: (Optional[DIRECTORY|FILE]) If provided, must return 41 either a the corresponding type. 42 43 Returns: 44 (File|DirectoryInfo) The content contained within. 45 """ 46 entry = directory.entries.get(name, None) 47 if entry == None: 48 fail(_NOT_FOUND.format( 49 directory = directory.human_readable, 50 name = repr(name), 51 children = "\n".join(directory.entries.keys()), 52 )) 53 if require_type == DIRECTORY and type(entry) == "File": 54 fail(_WRONG_TYPE.format( 55 dir = directory.human_readable, 56 name = name, 57 want = "Directory", 58 got = "File", 59 )) 60 61 if require_type == FILE and type(entry) != "File": 62 fail(_WRONG_TYPE.format( 63 dir = directory.human_readable, 64 name = name, 65 want = "File", 66 got = "Directory", 67 )) 68 return entry 69 70def get_path(directory, path, require_type = None): 71 """Gets a subdirectory or file contained within a directory. 72 73 Example: `get_path(directory, "a/b", require_type=FILE)` 74 -> the file corresponding to `directory.path + "/a/b"` 75 76 Args: 77 directory: (DirectoryInfo) The directory to look within. 78 path: (string) The path of the directory to look for within it. 79 require_type: (Optional[DIRECTORY|FILE]) If provided, must return 80 either a the corresponding type. 81 82 Returns: 83 (File|DirectoryInfo) The directory contained within. 84 """ 85 _check_path_relative(path) 86 87 chunks = path.split("/") 88 for dirname in chunks[:-1]: 89 directory = _get_direct_child(directory, dirname, require_type = DIRECTORY) 90 return _get_direct_child( 91 directory, 92 chunks[-1], 93 require_type = require_type, 94 ) 95