From 189200e17d2e087867e5126e5bf4d8774ea9c8f1 Mon Sep 17 00:00:00 2001 From: Zhixiong Chi Date: Fri, 16 Jul 2021 18:13:09 +0800 Subject: [PATCH] stx-tool: config: Add the support of config module Implement the stx config module so that the developer could define the customized configuration before the starlingx building. Now support the action: show|add|get|unset The usage of stx config module is like the git command. Please refer to the more help information with the command 'stx config --help' Story: 2008862 Task: 42515 Signed-off-by: Zhixiong Chi Change-Id: I0310d1b591415f111e30efdf5bd76b555f725416 --- pylint.rc | 2 +- stx-init-env | 2 + stx.conf | 14 +++ stx/lib/stx/helper.py | 32 ++++++ stx/lib/stx/stx_configparser.py | 184 ++++++++++++++++++++++++++++++++ stx/lib/stx/stx_main.py | 61 +++++++++++ stx/lib/stx/utils.py | 61 +++++++++++ 7 files changed, 355 insertions(+), 1 deletion(-) create mode 100644 stx-init-env create mode 100644 stx.conf create mode 100644 stx/lib/stx/helper.py create mode 100644 stx/lib/stx/stx_configparser.py create mode 100644 stx/lib/stx/utils.py diff --git a/pylint.rc b/pylint.rc index 34a963a7..10351b79 100755 --- a/pylint.rc +++ b/pylint.rc @@ -43,7 +43,7 @@ extension-pkg-whitelist=lxml.etree,greenlet # See "Messages Control" section of # https://pylint.readthedocs.io/en/latest/user_guide # We are disabling (C)onvention -disable=C, +disable=C [REPORTS] # Set the output format. Available formats are text, parseable, colorized, msvs diff --git a/stx-init-env b/stx-init-env new file mode 100644 index 00000000..52c04bf1 --- /dev/null +++ b/stx-init-env @@ -0,0 +1,2 @@ +export PRJDIR=$(pwd) +export PATH=$PRJDIR/stx/bin:$PATH diff --git a/stx.conf b/stx.conf new file mode 100644 index 00000000..f1d2746f --- /dev/null +++ b/stx.conf @@ -0,0 +1,14 @@ +[user] +name = +project = + +[builder] +mode = local + +[repomgr] +name = aptly +mode = local + +[default] +project = stx-build + diff --git a/stx/lib/stx/helper.py b/stx/lib/stx/helper.py new file mode 100644 index 00000000..64218b4e --- /dev/null +++ b/stx/lib/stx/helper.py @@ -0,0 +1,32 @@ +# Copyright (c) 2021 Wind River Systems, Inc. +# +# 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 sys + + +def help_config(): + return 'Try \'%s config --help\' for more information.\n' % os.path.basename(sys.argv[0]) + + +def help_control(): + return 'Try \'%s control --help\' for more information.\n' % os.path.basename(sys.argv[0]) + + +def help_build(): + return 'Try \'%s build --help\' for more information.\n' % os.path.basename(sys.argv[0]) + + +def help_blurb(): + return 'Try \'%s --help\' for more information.\n' % os.path.basename(sys.argv[0]) diff --git a/stx/lib/stx/stx_configparser.py b/stx/lib/stx/stx_configparser.py new file mode 100644 index 00000000..678478f2 --- /dev/null +++ b/stx/lib/stx/stx_configparser.py @@ -0,0 +1,184 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2021 Wind River Systems, Inc. +# +# 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 configparser +import logging +import os +from stx import helper # pylint: disable=E0611 +from stx import utils # pylint: disable=E0611 +import sys + +logger = logging.getLogger('STX-Config') +utils.set_logger(logger) + + +class STXConfigParser: + + def __init__(self, filepath=None): + if filepath: + configpath = filepath + else: + configpath = os.path.join(os.environ['PRJDIR'], "stx.conf") + + self.configpath = configpath + self.cf = configparser.ConfigParser() + self.cf.read(self.configpath, encoding="utf-8") + + def showAll(self): + '''Output all of contents of the configfile''' + + sections = self.cf.sections() + logger.info("The config file as follows:") + print("[section]") + print("('key' = 'value')") + for section in sections: + print("\r") + print("[%s]" % section) + items = self.cf.items(section) + for item in items: + print("%s" % str(item).replace(',', ' =')) + + def getConfig(self, section, option): + '''Get the value of section.option''' + + if not self.cf.has_section(section): + logger.error("There is no section '%s' in the config file. Please", section) + logger.error("use the command 'stx config --add section.option = value'") + logger.error("add this key/value pair, or select another setion.") + sys.exit(1) + + if not self.cf.has_option(section, option): + logger.error("There is no option '%s' within section '%s'. Please use", option, section) + logger.error("the command 'stx config --add section.option = value' ") + logger.error("to add it or select another option of section.") + sys.exit(1) + + value = self.cf.get(section, option) + if not value: + if self.cf.has_option('default', option): + value = self.cf.get('default', option) + + return value + + def setConfig(self, section, option, value): + '''Set the pair of section.option and value''' + + if not self.cf.has_section(section): + self.cf.add_section(section) + self.cf.set(section, option, value) + self.syncConfigFile() + + def removeSection(self, section): + '''Remove the whole of section from the configfile''' + + if not self.cf.has_section(section): + logger.error("Section [%s] doesn't exist in the configfile.\n", section) + sys.exit(1) + + ret = self.cf.remove_section(section) + self.syncConfigFile() + + return ret + + def removeOption(self, section, option): + '''Remove the option from this section in the configfile''' + + if not self.cf.has_section(section): + logger.error("Section [%s] doesn't exist in the configfile.\n", section) + sys.exit(1) + if not self.cf.has_option(section, option): + logger.error("Option [%s] doesn't exist in the section [%s].\n", option, section) + sys.exit(1) + + ret = self.cf.remove_option(section, option) + + if not self.cf.options(section): + self.cf.remove_section(section) + + self.syncConfigFile() + + return ret + + def syncConfigFile(self): + self.cf.write(open(self.configpath, "w")) + + +class HandleConfigTask: + '''Handle the task for the config sub-command''' + + def __init__(self): + self.stxconfig = STXConfigParser() + + def handleShow(self): + self.stxconfig.showAll() + + def handleGetTask(self, args): + + if args.get[0].count('.') != 1: + logger.error('Please input the correct style for the key. eg: section.option') + sys.exit(1) + + section, option = args.get[0].split('.') + value = self.stxconfig.getConfig(section, option) + print("[%s]" % section) + print("( %s = %s )" % (option, value)) + + def handleAddTask(self, args): + + if args.add[0].count('.') != 1: + logger.error('Please input the correct style for the key. eg: section.option') + print(helper.help_config()) + sys.exit(1) + + section, option = args.add[0].split('.') + value = args.add[1] + + self.stxconfig.setConfig(section, option, value) + + def handleRemoveSectionTask(self, args): + section = args.removesection[0] + return self.stxconfig.removeSection(section) + + def handleUnsetOptionTask(self, args): + + if args.unset[0].count('.') != 1: + logger.error('Please input the correct style to unset. eg: section.option|section') + print(helper.help_config()) + sys.exit(1) + + section, option = args.unset[0].split('.') + return self.stxconfig.removeOption(section, option) + + def handleConfig(self, args): + + if args.add: + self.handleAddTask(args) + + elif args.get: + self.handleGetTask(args) + + elif args.unset: + self.handleUnsetOptionTask(args) + + elif args.removesection: + self.handleRemoveSectionTask(args) + + elif args.show is True: + self.handleShow() + + else: + print(helper.help_config()) diff --git a/stx/lib/stx/stx_main.py b/stx/lib/stx/stx_main.py index 15a04f19..df40c6fc 100644 --- a/stx/lib/stx/stx_main.py +++ b/stx/lib/stx/stx_main.py @@ -13,11 +13,72 @@ # See the License for the specific language governing permissions and # limitations under the License. +import argparse +import logging + +from stx import stx_configparser # pylint: disable=E0611 +from stx import utils # pylint: disable=E0611 + + +logger = logging.getLogger('STX') +utils.set_logger(logger) + class STXMainException(Exception): pass +class CommandLine: + '''Handles parsing the commandline parameters for stx tool''' + + def __init__(self): + self.handleconfig = stx_configparser.HandleConfigTask() + self.parser = self.parseCommandLine() + + def parseCommandLine(self): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + add_help=False, + description='STX Build Tool', + epilog='''Tips: +Use %(prog)s --help to get help for all of parameters\n\n''') + + subparsers = parser.add_subparsers(title='Builtin Commands:', help='sub-command for stx\n\n') + + config_subparser = subparsers.add_parser('config', help='Change stx configuration settings. eg: [--show|--get|--add|--unset|--remove-section]') + config_subparser.add_argument('--show', help='Show all the content of the config file\n\n', action='store_true') + config_subparser.add_argument('--add', help='Add the setting section.key and the value into the config file.\n\n', nargs=2, required=False) + config_subparser.add_argument('--get', help='Get the value of the section.key from the config file.\n\n', nargs=1, required=False) + config_subparser.add_argument('--unset', help='Remove value of the section.key from the config file.\n\n', nargs=1, required=False) + config_subparser.add_argument('--removesection', help='Remove the section from the config file.\n\n', nargs=1, required=False) + config_subparser.set_defaults(handle=self.handleconfig.handleConfig) + + parser.add_argument('-d', '--debug', help='Enable debug output\n\n', + action='store_const', const=logging.DEBUG, dest='loglevel', default=logging.INFO) + + parser.add_argument('-h', '--help', help='Show this help message and exit\n\n', action='help') + + parser.add_argument('-q', '--quiet', help='Hide all output except error messages\n\n', + action='store_const', const=logging.ERROR, dest='loglevel') + + parser.add_argument('-v', '--version', help='Stx build tools version\n\n', + action='version', version='%(prog)s 1.0.0') + return parser + + def parseArgs(self): + args = self.parser.parse_args() + logger.setLevel(args.loglevel) + return args + + def stx_main(): + command_line = CommandLine() + args = command_line.parseArgs() + + if hasattr(args, 'handle'): + args.handle(args) + else: + command_line.parser.print_help() + return 0 diff --git a/stx/lib/stx/utils.py b/stx/lib/stx/utils.py new file mode 100644 index 00000000..5d245f27 --- /dev/null +++ b/stx/lib/stx/utils.py @@ -0,0 +1,61 @@ +# Copyright (c) 2021 Wind River Systems, Inc. +# +# 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 logging + + +def set_logger(logger): + logger.setLevel(logging.DEBUG) + + class ColorFormatter(logging.Formatter): + FORMAT = ("$BOLD%(name)-s$RESET - %(levelname)s: %(message)s") + + BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = list(range(8)) + + RESET_SEQ = "\033[0m" + COLOR_SEQ = "\033[1;%dm" + BOLD_SEQ = "\033[1m" + + COLORS = { + 'WARNING': YELLOW, + 'INFO': GREEN, + 'DEBUG': BLUE, + 'ERROR': RED + } + + def formatter_msg(self, msg, use_color=True): + if use_color: + msg = msg.replace("$RESET", self.RESET_SEQ).replace("$BOLD", self.BOLD_SEQ) + else: + msg = msg.replace("$RESET", "").replace("$BOLD", "") + return msg + + def __init__(self, use_color=True): + msg = self.formatter_msg(self.FORMAT, use_color) + logging.Formatter.__init__(self, msg) + self.use_color = use_color + + def format(self, record): + levelname = record.levelname + if self.use_color and levelname in self.COLORS: + fore_color = 30 + self.COLORS[levelname] + levelname_color = self.COLOR_SEQ % fore_color + levelname + self.RESET_SEQ + record.levelname = levelname_color + return logging.Formatter.format(self, record) + + # create console handler and set level to debug + ch = logging.StreamHandler() + ch.setLevel(logging.DEBUG) + ch.setFormatter(ColorFormatter()) + logger.addHandler(ch)