1*bcb5dc79SHONG Yifan# Copyright 2024 The Bazel Authors. All rights reserved. 2*bcb5dc79SHONG Yifan# 3*bcb5dc79SHONG Yifan# Licensed under the Apache License, Version 2.0 (the "License"); 4*bcb5dc79SHONG Yifan# you may not use this file except in compliance with the License. 5*bcb5dc79SHONG Yifan# You may obtain a copy of the License at 6*bcb5dc79SHONG Yifan# 7*bcb5dc79SHONG Yifan# http://www.apache.org/licenses/LICENSE-2.0 8*bcb5dc79SHONG Yifan# 9*bcb5dc79SHONG Yifan# Unless required by applicable law or agreed to in writing, software 10*bcb5dc79SHONG Yifan# distributed under the License is distributed on an "AS IS" BASIS, 11*bcb5dc79SHONG Yifan# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*bcb5dc79SHONG Yifan# See the License for the specific language governing permissions and 13*bcb5dc79SHONG Yifan# limitations under the License. 14*bcb5dc79SHONG Yifan 15*bcb5dc79SHONG Yifan"""Skylib module containing path operations on directories.""" 16*bcb5dc79SHONG Yifan 17*bcb5dc79SHONG Yifanload("//lib:paths.bzl", "paths") 18*bcb5dc79SHONG Yifan 19*bcb5dc79SHONG Yifan_NOT_FOUND = """{directory} does not contain an entry named {name}. 20*bcb5dc79SHONG YifanInstead, it contains the following entries: 21*bcb5dc79SHONG Yifan{children} 22*bcb5dc79SHONG Yifan 23*bcb5dc79SHONG Yifan""" 24*bcb5dc79SHONG Yifan_WRONG_TYPE = "Expected {dir}/{name} to have type {want}, but got {got}" 25*bcb5dc79SHONG Yifan 26*bcb5dc79SHONG Yifan# These correspond to an "enum". 27*bcb5dc79SHONG YifanFILE = "file" 28*bcb5dc79SHONG YifanDIRECTORY = "directory" 29*bcb5dc79SHONG Yifan 30*bcb5dc79SHONG Yifandef _check_path_relative(path): 31*bcb5dc79SHONG Yifan if paths.is_absolute(path): 32*bcb5dc79SHONG Yifan fail("Path must be relative. Got {path}".format(path = path)) 33*bcb5dc79SHONG Yifan 34*bcb5dc79SHONG Yifandef _get_direct_child(directory, name, require_type = None): 35*bcb5dc79SHONG Yifan """Gets the direct child of a directory. 36*bcb5dc79SHONG Yifan 37*bcb5dc79SHONG Yifan Args: 38*bcb5dc79SHONG Yifan directory: (DirectoryInfo) The directory to look within. 39*bcb5dc79SHONG Yifan name: (string) The name of the directory/file to look for. 40*bcb5dc79SHONG Yifan require_type: (Optional[DIRECTORY|FILE]) If provided, must return 41*bcb5dc79SHONG Yifan either a the corresponding type. 42*bcb5dc79SHONG Yifan 43*bcb5dc79SHONG Yifan Returns: 44*bcb5dc79SHONG Yifan (File|DirectoryInfo) The content contained within. 45*bcb5dc79SHONG Yifan """ 46*bcb5dc79SHONG Yifan entry = directory.entries.get(name, None) 47*bcb5dc79SHONG Yifan if entry == None: 48*bcb5dc79SHONG Yifan fail(_NOT_FOUND.format( 49*bcb5dc79SHONG Yifan directory = directory.human_readable, 50*bcb5dc79SHONG Yifan name = repr(name), 51*bcb5dc79SHONG Yifan children = "\n".join(directory.entries.keys()), 52*bcb5dc79SHONG Yifan )) 53*bcb5dc79SHONG Yifan if require_type == DIRECTORY and type(entry) == "File": 54*bcb5dc79SHONG Yifan fail(_WRONG_TYPE.format( 55*bcb5dc79SHONG Yifan dir = directory.human_readable, 56*bcb5dc79SHONG Yifan name = name, 57*bcb5dc79SHONG Yifan want = "Directory", 58*bcb5dc79SHONG Yifan got = "File", 59*bcb5dc79SHONG Yifan )) 60*bcb5dc79SHONG Yifan 61*bcb5dc79SHONG Yifan if require_type == FILE and type(entry) != "File": 62*bcb5dc79SHONG Yifan fail(_WRONG_TYPE.format( 63*bcb5dc79SHONG Yifan dir = directory.human_readable, 64*bcb5dc79SHONG Yifan name = name, 65*bcb5dc79SHONG Yifan want = "File", 66*bcb5dc79SHONG Yifan got = "Directory", 67*bcb5dc79SHONG Yifan )) 68*bcb5dc79SHONG Yifan return entry 69*bcb5dc79SHONG Yifan 70*bcb5dc79SHONG Yifandef get_path(directory, path, require_type = None): 71*bcb5dc79SHONG Yifan """Gets a subdirectory or file contained within a directory. 72*bcb5dc79SHONG Yifan 73*bcb5dc79SHONG Yifan Example: `get_path(directory, "a/b", require_type=FILE)` 74*bcb5dc79SHONG Yifan -> the file corresponding to `directory.path + "/a/b"` 75*bcb5dc79SHONG Yifan 76*bcb5dc79SHONG Yifan Args: 77*bcb5dc79SHONG Yifan directory: (DirectoryInfo) The directory to look within. 78*bcb5dc79SHONG Yifan path: (string) The path of the directory to look for within it. 79*bcb5dc79SHONG Yifan require_type: (Optional[DIRECTORY|FILE]) If provided, must return 80*bcb5dc79SHONG Yifan either a the corresponding type. 81*bcb5dc79SHONG Yifan 82*bcb5dc79SHONG Yifan Returns: 83*bcb5dc79SHONG Yifan (File|DirectoryInfo) The directory contained within. 84*bcb5dc79SHONG Yifan """ 85*bcb5dc79SHONG Yifan _check_path_relative(path) 86*bcb5dc79SHONG Yifan 87*bcb5dc79SHONG Yifan chunks = path.split("/") 88*bcb5dc79SHONG Yifan for dirname in chunks[:-1]: 89*bcb5dc79SHONG Yifan directory = _get_direct_child(directory, dirname, require_type = DIRECTORY) 90*bcb5dc79SHONG Yifan return _get_direct_child( 91*bcb5dc79SHONG Yifan directory, 92*bcb5dc79SHONG Yifan chunks[-1], 93*bcb5dc79SHONG Yifan require_type = require_type, 94*bcb5dc79SHONG Yifan ) 95