# # Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. # Apache-2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import os import glob import re MARK_ID = "###->" MLNX_KFG_MARKER = "mellanox" SDK_MARKER = "mellanox_sdk" HW_MGMT_MARKER = "mellanox_hw_mgmt" SLK_PATCH_LOC = "src/sonic-linux-kernel/patch/" SLK_KCONFIG = SLK_PATCH_LOC + "kconfig-inclusions" SLK_KCONFIG_EXCLUDE = SLK_PATCH_LOC + "kconfig-exclusions" SLK_SERIES = SLK_PATCH_LOC + "series" NON_UP_PATCH_DIR = "platform/mellanox/non-upstream-patches/" NON_UP_PATCH_LOC = NON_UP_PATCH_DIR + "patches" NON_UP_PATCH_DIFF = NON_UP_PATCH_DIR + "series.patch" KCFG_HDR_RE = "\[(.*)\]" KERNEL_BACKPORTS = "kernel_backports" # kconfig_inclusion headers to consider HDRS = ["common", "amd64"] class FileHandler: @staticmethod def write_lines(path, lines, raw=False): # Create the dir if it doesn't exist already os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'w') as f: for line in lines: if raw: f.write(f"{line}") else: f.write(f"{line}\n") @staticmethod def read_raw(path): # Read the data line by line into a list data = [] with open(path) as im: data = im.readlines() return data @staticmethod def read_strip(path, as_string=False): # Read the data line by line into a list and strip whitelines data = FileHandler.read_raw(path) data = [d.strip() for d in data] if as_string: return "\n".join(data) return data @staticmethod def read_strip_minimal(path, as_string=False, ignore_start_with="#"): # Read the data line by line into a list, strip spaces and ignore comments data = FileHandler.read_raw(path) filtered_data = [] for l in data: l = l.strip() if l and not l.startswith(ignore_start_with): filtered_data.append(l) if as_string: return "\n".join(filtered_data) return filtered_data @staticmethod def read_dir(path, ext="*") -> list: return [os.path.basename(f) for f in glob.glob(os.path.join(path, ext))] @staticmethod def find_marker_indices(lines: list, marker=None) -> tuple: i_start = -1 i_end = len(lines) # print("TEST", marker, lines) if marker: for index, line in enumerate(lines): # assumes one unique marker per file # if multiple marker sections are present, reads the first one if line.strip().startswith(MARK_ID): if marker+"-start" in line: i_start = index elif marker+"-end" in line: i_end = index # print(i_start, i_end) return (i_start, i_end) @staticmethod def read_kconfig_inclusion(path, marker=MLNX_KFG_MARKER): lines = FileHandler.read_strip(path) if not marker: return lines i_start, i_end = FileHandler.find_marker_indices(lines, marker) if i_start < 0 or i_end >= len(lines): print("-> WARNING No Marker Found") return [] return lines[i_start+1:i_end] @staticmethod def write_lines_marker(path, writable_opts: list, marker=None): # if marker is none, just write the opts into the file, # otherwise write the data only b/w the marker curr_data = FileHandler.read_raw(path) i_start, i_end = FileHandler.find_marker_indices(curr_data, marker) newline_writ_opts = [opt + "\n" for opt in writable_opts] if i_start < 0 or i_end >= len(curr_data): print("-> WARNING No Marker Found, writing data at the end of file") curr_data.extend(["\n"]) curr_data.extend(newline_writ_opts) else: curr_data = curr_data[0:i_start+1] + newline_writ_opts + curr_data[i_end:] print("-> INFO Written the following opts: \n{}".format("".join(FileHandler.read_raw(path)))) FileHandler.write_lines(path, curr_data, True) @staticmethod def read_kconfig_parser(path) -> dict: # kconfig_inclusion output formatted to {"no_parent", "common":[,], "amd64": [,], "arm64": [,]} lines = FileHandler.read_strip_minimal(path) ret = dict({"no_parent":[]}) curr_hdr = "" for line in lines: match = re.search(KCFG_HDR_RE, line) if match: curr_hdr = match.group(1) ret[curr_hdr] = [] elif curr_hdr in ret: ret[curr_hdr].append(line) else: ret["no_parent"].append(line) return ret class KCFG: @staticmethod def parse_opt_str(opt: str) -> tuple: if not opt.startswith("CONFIG"): print("-> DEBUG: Malformed kconfig opt, {}".format(opt)) return () tmp = opt.split("=") if len(tmp) != 2: print("-> DEBUG: Malformed kconfig opt, {}".format(opt)) return () return (tmp[0], tmp[1]) @staticmethod def parse_opts_strs(kcfg_sec: list) -> list(tuple()): opts = [] # list of tuples (CONFIG_*, "m|y|n") for kcfg in kcfg_sec: tmp = KCFG.parse_opt_str(kcfg) if tmp: opts.append(tmp) return opts @staticmethod def get_writable_opts(opts): lines = [] for opt in opts: lines.append("{}={}".format(opt[0], opt[1])) return lines class Action(): def __init__(self, args): self.args = args def perform(self): pass def write_user_out(self): pass def build_commit_description(changes): if not changes: return "" content = "\n" content = content + " ## Patch List\n" for key, value in changes.items(): content = content + f"* {key} : {value}\n" return content def parse_id(id_): if id_ and id_ != "N/A": id_ = "https://github.com/torvalds/linux/commit/" + id_ if id_ == "N/A": id_ = "" return id_