1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2021 The Android Open Source Project 3*6dbdd20aSAndroid Build Coastguard Worker# 4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*6dbdd20aSAndroid Build Coastguard Worker# 8*6dbdd20aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*6dbdd20aSAndroid Build Coastguard Worker# 10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License. 15*6dbdd20aSAndroid Build Coastguard Worker"""Generates TypeScript files that import all subdirectories and 16*6dbdd20aSAndroid Build Coastguard Workerregisters them with plugin registry. If you have three modules: 17*6dbdd20aSAndroid Build Coastguard Worker- core/ 18*6dbdd20aSAndroid Build Coastguard Worker- plugins/foo_plugin/ 19*6dbdd20aSAndroid Build Coastguard Worker- plugins/bar_plugin/ 20*6dbdd20aSAndroid Build Coastguard WorkerIn general you would like the dependency to only go one way: 21*6dbdd20aSAndroid Build Coastguard Worker- plugins/foo_plugin/ -> core/ 22*6dbdd20aSAndroid Build Coastguard WorkerWe want to avoid manually editing core/ for every plugin. 23*6dbdd20aSAndroid Build Coastguard Worker 24*6dbdd20aSAndroid Build Coastguard WorkerThis generates code like: 25*6dbdd20aSAndroid Build Coastguard Worker 26*6dbdd20aSAndroid Build Coastguard Workerimport {plugin as fooPlugin} from '../plugins/foo_plugin'; 27*6dbdd20aSAndroid Build Coastguard Workerimport {plugin as barPlugin} from '../plugins/bar_plugin'; 28*6dbdd20aSAndroid Build Coastguard Worker 29*6dbdd20aSAndroid Build Coastguard Workerexport default [ 30*6dbdd20aSAndroid Build Coastguard Worker fooPlugin, 31*6dbdd20aSAndroid Build Coastguard Worker barPlugin, 32*6dbdd20aSAndroid Build Coastguard Worker]; 33*6dbdd20aSAndroid Build Coastguard Worker""" 34*6dbdd20aSAndroid Build Coastguard Worker 35*6dbdd20aSAndroid Build Coastguard Workerfrom __future__ import print_function 36*6dbdd20aSAndroid Build Coastguard Worker 37*6dbdd20aSAndroid Build Coastguard Workerimport os 38*6dbdd20aSAndroid Build Coastguard Workerimport argparse 39*6dbdd20aSAndroid Build Coastguard Workerimport re 40*6dbdd20aSAndroid Build Coastguard Worker 41*6dbdd20aSAndroid Build Coastguard WorkerROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 42*6dbdd20aSAndroid Build Coastguard WorkerUI_SRC_DIR = os.path.join(ROOT_DIR, 'ui', 'src') 43*6dbdd20aSAndroid Build Coastguard WorkerPLUGINS_PATH = os.path.join(UI_SRC_DIR, 'common', 'plugins') 44*6dbdd20aSAndroid Build Coastguard Worker 45*6dbdd20aSAndroid Build Coastguard Worker 46*6dbdd20aSAndroid Build Coastguard Workerdef to_camel_case(s): 47*6dbdd20aSAndroid Build Coastguard Worker # Split string on periods and underscores 48*6dbdd20aSAndroid Build Coastguard Worker first, *rest = re.split(r'\.|\_', s) 49*6dbdd20aSAndroid Build Coastguard Worker return first + ''.join(x.title() for x in rest) 50*6dbdd20aSAndroid Build Coastguard Worker 51*6dbdd20aSAndroid Build Coastguard Worker 52*6dbdd20aSAndroid Build Coastguard Workerdef is_plugin_dir(dir): 53*6dbdd20aSAndroid Build Coastguard Worker # Ensure plugins contain a file called index.ts. This avoids the issue empty 54*6dbdd20aSAndroid Build Coastguard Worker # dirs are detected as plugins. 55*6dbdd20aSAndroid Build Coastguard Worker return os.path.isdir(dir) and os.path.exists(os.path.join(dir, 'index.ts')) 56*6dbdd20aSAndroid Build Coastguard Worker 57*6dbdd20aSAndroid Build Coastguard Worker 58*6dbdd20aSAndroid Build Coastguard Workerdef plugin_has_css(dir): 59*6dbdd20aSAndroid Build Coastguard Worker return os.path.isdir(dir) and os.path.exists(os.path.join(dir, 'styles.scss')) 60*6dbdd20aSAndroid Build Coastguard Worker 61*6dbdd20aSAndroid Build Coastguard Worker 62*6dbdd20aSAndroid Build Coastguard Workerdef gen_imports(input_dir, output_path): 63*6dbdd20aSAndroid Build Coastguard Worker paths = [os.path.join(input_dir, p) for p in os.listdir(input_dir)] 64*6dbdd20aSAndroid Build Coastguard Worker paths = [p for p in paths if is_plugin_dir(p)] 65*6dbdd20aSAndroid Build Coastguard Worker paths.sort() 66*6dbdd20aSAndroid Build Coastguard Worker 67*6dbdd20aSAndroid Build Coastguard Worker output_dir = os.path.dirname(output_path) 68*6dbdd20aSAndroid Build Coastguard Worker 69*6dbdd20aSAndroid Build Coastguard Worker imports = [] 70*6dbdd20aSAndroid Build Coastguard Worker registrations = [] 71*6dbdd20aSAndroid Build Coastguard Worker for path in paths: 72*6dbdd20aSAndroid Build Coastguard Worker rel_path = os.path.relpath(path, output_dir) 73*6dbdd20aSAndroid Build Coastguard Worker snake_name = os.path.basename(path) 74*6dbdd20aSAndroid Build Coastguard Worker camel_name = to_camel_case(snake_name) 75*6dbdd20aSAndroid Build Coastguard Worker imports.append(f"import {camel_name} from '{rel_path}';") 76*6dbdd20aSAndroid Build Coastguard Worker registrations.append(camel_name) 77*6dbdd20aSAndroid Build Coastguard Worker 78*6dbdd20aSAndroid Build Coastguard Worker import_text = '\n'.join(imports) 79*6dbdd20aSAndroid Build Coastguard Worker registration_text = 'export default [\n' 80*6dbdd20aSAndroid Build Coastguard Worker for camel_name in registrations: 81*6dbdd20aSAndroid Build Coastguard Worker registration_text += f" {camel_name},\n" 82*6dbdd20aSAndroid Build Coastguard Worker registration_text += '];\n' 83*6dbdd20aSAndroid Build Coastguard Worker 84*6dbdd20aSAndroid Build Coastguard Worker expected = f"{import_text}\n\n{registration_text}" 85*6dbdd20aSAndroid Build Coastguard Worker 86*6dbdd20aSAndroid Build Coastguard Worker with open(f"{output_path}.ts", 'w') as f: 87*6dbdd20aSAndroid Build Coastguard Worker f.write(expected) 88*6dbdd20aSAndroid Build Coastguard Worker return True 89*6dbdd20aSAndroid Build Coastguard Worker 90*6dbdd20aSAndroid Build Coastguard Worker 91*6dbdd20aSAndroid Build Coastguard Workerdef gen_css_import(input_dir, output_path): 92*6dbdd20aSAndroid Build Coastguard Worker paths = [os.path.join(input_dir, p) for p in os.listdir(input_dir)] 93*6dbdd20aSAndroid Build Coastguard Worker paths = [p for p in paths if plugin_has_css(p)] 94*6dbdd20aSAndroid Build Coastguard Worker paths.sort() 95*6dbdd20aSAndroid Build Coastguard Worker 96*6dbdd20aSAndroid Build Coastguard Worker output_dir = os.path.dirname(output_path) 97*6dbdd20aSAndroid Build Coastguard Worker 98*6dbdd20aSAndroid Build Coastguard Worker imports = [] 99*6dbdd20aSAndroid Build Coastguard Worker for path in paths: 100*6dbdd20aSAndroid Build Coastguard Worker rel_path = os.path.relpath(path, output_dir) 101*6dbdd20aSAndroid Build Coastguard Worker imports.append(f"@import '{rel_path}/styles';") 102*6dbdd20aSAndroid Build Coastguard Worker 103*6dbdd20aSAndroid Build Coastguard Worker import_text = '\n'.join(imports) 104*6dbdd20aSAndroid Build Coastguard Worker 105*6dbdd20aSAndroid Build Coastguard Worker with open(f"{output_path}.scss", 'w') as f: 106*6dbdd20aSAndroid Build Coastguard Worker f.write(import_text) 107*6dbdd20aSAndroid Build Coastguard Worker return True 108*6dbdd20aSAndroid Build Coastguard Worker 109*6dbdd20aSAndroid Build Coastguard Worker 110*6dbdd20aSAndroid Build Coastguard Workerdef main(): 111*6dbdd20aSAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 112*6dbdd20aSAndroid Build Coastguard Worker description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 113*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('INPUT') 114*6dbdd20aSAndroid Build Coastguard Worker parser.add_argument('--out', required=True) 115*6dbdd20aSAndroid Build Coastguard Worker args = parser.parse_args() 116*6dbdd20aSAndroid Build Coastguard Worker input_dir = args.INPUT 117*6dbdd20aSAndroid Build Coastguard Worker output_path = args.out 118*6dbdd20aSAndroid Build Coastguard Worker 119*6dbdd20aSAndroid Build Coastguard Worker if not os.path.isdir(input_dir): 120*6dbdd20aSAndroid Build Coastguard Worker print(f'INPUT argument {input_dir} must be a directory') 121*6dbdd20aSAndroid Build Coastguard Worker exit(1) 122*6dbdd20aSAndroid Build Coastguard Worker 123*6dbdd20aSAndroid Build Coastguard Worker output_dir = os.path.dirname(output_path) 124*6dbdd20aSAndroid Build Coastguard Worker if output_dir and not os.path.isdir(output_dir): 125*6dbdd20aSAndroid Build Coastguard Worker print(f'--out ({output_path}) parent directory ({output_dir}) must exist') 126*6dbdd20aSAndroid Build Coastguard Worker exit(1) 127*6dbdd20aSAndroid Build Coastguard Worker if os.path.isdir(output_path): 128*6dbdd20aSAndroid Build Coastguard Worker print(f'--out ({output_path}) should not be a directory') 129*6dbdd20aSAndroid Build Coastguard Worker exit(1) 130*6dbdd20aSAndroid Build Coastguard Worker 131*6dbdd20aSAndroid Build Coastguard Worker success = gen_imports(input_dir, output_path) 132*6dbdd20aSAndroid Build Coastguard Worker success = success | gen_css_import(input_dir, output_path) 133*6dbdd20aSAndroid Build Coastguard Worker return 0 if success else 1 134*6dbdd20aSAndroid Build Coastguard Worker 135*6dbdd20aSAndroid Build Coastguard Worker 136*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__': 137*6dbdd20aSAndroid Build Coastguard Worker exit(main()) 138