|  | # Copyright (c) 2022 Project CHIP Authors | 
|  | # | 
|  | # 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 fnmatch | 
|  | import logging | 
|  | import os | 
|  | from dataclasses import dataclass, field | 
|  | from typing import Iterator, List, Optional | 
|  |  | 
|  | from .types import IdlFileType, InputIdlFile | 
|  | from .using_codegen import (CodegenBridgePregenerator, CodegenCppAppPregenerator, CodegenJavaClassPregenerator, | 
|  | CodegenJavaJNIPregenerator) | 
|  | from .using_zap import ZapApplicationPregenerator | 
|  |  | 
|  |  | 
|  | def _IdlsInDirectory(top_directory_name: str, truncate_length: int): | 
|  | for root, dirs, files in os.walk(top_directory_name): | 
|  | for file in files: | 
|  | if file.endswith('.zap'): | 
|  | yield InputIdlFile(file_type=IdlFileType.ZAP, | 
|  | full_path=os.path.join(root, file), | 
|  | relative_path=os.path.join(root[truncate_length:], file)) | 
|  | if file.endswith('.matter'): | 
|  | yield InputIdlFile(file_type=IdlFileType.MATTER, | 
|  | full_path=os.path.join(root, file), | 
|  | relative_path=os.path.join(root[truncate_length:], file)) | 
|  |  | 
|  |  | 
|  | def _FindAllIdls(sdk_root: str, external_roots: Optional[List[str]]) -> Iterator[InputIdlFile]: | 
|  | relevant_subdirs = [ | 
|  | 'examples',  # all example apps | 
|  | 'src',      # realistically only controller/data_model | 
|  | ] | 
|  |  | 
|  | while sdk_root.endswith('/'): | 
|  | sdk_root = sdk_root[:-1] | 
|  | sdk_root_length = len(sdk_root) | 
|  |  | 
|  | # first go over SDK items | 
|  | for subdir_name in relevant_subdirs: | 
|  | top_directory_name = os.path.join(sdk_root, subdir_name) | 
|  | logging.debug(f"Searching {top_directory_name}") | 
|  | for idl in _IdlsInDirectory(top_directory_name, sdk_root_length+1): | 
|  | yield idl | 
|  |  | 
|  | # next external roots | 
|  | if external_roots: | 
|  | for root in external_roots: | 
|  | root = os.path.normpath(root) | 
|  | for idl in _IdlsInDirectory(root, len(root) + 1): | 
|  | yield idl | 
|  |  | 
|  |  | 
|  | @dataclass | 
|  | class TargetFilter: | 
|  | # If set, only the specified files are accepted for codegen | 
|  | file_type: Optional[IdlFileType] = None | 
|  |  | 
|  | # If non-empty only the given paths will be code-generated | 
|  | path_glob: List[str] = field(default_factory=list) | 
|  |  | 
|  |  | 
|  | # TODO: the build GlobMatcher is more complete by supporting `{}` grouping | 
|  | #       For now this limited glob seems sufficient. | 
|  | class GlobMatcher: | 
|  | def __init__(self, pattern: str): | 
|  | self.pattern = pattern | 
|  |  | 
|  | def matches(self, s: str): | 
|  | return fnmatch.fnmatch(s, self.pattern) | 
|  |  | 
|  |  | 
|  | def FindPregenerationTargets(sdk_root: str, external_roots: Optional[List[str]], filter: TargetFilter, runner): | 
|  | """Finds all relevand pre-generation targets in the given | 
|  | SDK root. | 
|  |  | 
|  | Pre-generation targets are based on zap and matter files with options | 
|  | on what rules to pregenerate and how. | 
|  | """ | 
|  |  | 
|  | generators = [ | 
|  | # Jinja-based codegen | 
|  | CodegenBridgePregenerator(sdk_root), | 
|  | CodegenJavaJNIPregenerator(sdk_root), | 
|  | CodegenJavaClassPregenerator(sdk_root), | 
|  | CodegenCppAppPregenerator(sdk_root), | 
|  |  | 
|  | # ZAP codegen | 
|  | ZapApplicationPregenerator(sdk_root), | 
|  | ] | 
|  |  | 
|  | path_matchers = [GlobMatcher(pattern) for pattern in filter.path_glob] | 
|  |  | 
|  | for idl in _FindAllIdls(sdk_root, external_roots): | 
|  | if filter.file_type is not None: | 
|  | if idl.file_type != filter.file_type: | 
|  | logging.debug(f"Will not process file of type {idl.file_type}: {idl.relative_path}") | 
|  | continue | 
|  |  | 
|  | if path_matchers: | 
|  | if all([not matcher.matches(idl.relative_path) for matcher in path_matchers]): | 
|  | logging.debug(f"Glob not matched for {idl.relative_path}") | 
|  | continue | 
|  |  | 
|  | for generator in generators: | 
|  | if generator.Accept(idl): | 
|  | yield generator.CreateTarget(idl, runner=runner) |