pyconfig/main.py
# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com)
# All rights reserved.
#
# https://github.com/samdmarshall/pyconfig
#
# Redistribution and use in source and binary forms, with or without modification,
# are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation and/or
# other materials provided with the distribution.
#
# 3. Neither the name of Samantha Marshall nor the names of its contributors may
# be used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
import sys
import argparse
from .version import __version__ as PYCONFIG_VERSION
from .Interpreter import Consumer
from .Graph import Searcher
from .Graph import Grapher
from .Helpers.Logger import Logger
from .Serializer import Serializer
from .Analyzer import Engine
from .SCM import SCM
# Main
def main(argv=sys.argv[1:]):
# setup the argument parsing
parser = argparse.ArgumentParser(description='pyconfig is a tool to generate xcconfig files from a simple DSL')
parser.add_argument(
'--version',
help='Displays the version information',
action='version',
version=PYCONFIG_VERSION
)
parser.add_argument(
'file',
metavar='<path>',
help='Path to the pyconfig file to use to generate a xcconfig file',
)
parser.add_argument(
'--scheme',
metavar='name',
action='store',
help='Optional argument to supply the scheme name'
)
parser.add_argument(
'--no-analyze',
help='Skips the step of analyzing the pyconfig files before writing to disk',
default=False,
action='store_true'
)
parser.add_argument(
'--dry-run',
help='Runs normally except will not write out a file',
default=False,
action='store_true'
)
parser.add_argument(
'--scm-info',
help='Generate an additional xcconfig that contains metadata about the current source control environment',
choices=['detect', 'git', 'svn', 'hg'],
action='store'
)
parser.add_argument(
'--quiet',
help='Silences all logging output',
default=False,
action='store_true'
)
parser.add_argument(
'--verbose',
help='Adds verbosity to logging output',
default=False,
action='store_true'
)
parser.add_argument(
'--no-ansi',
help='Disables the ANSI color codes as part of the logger',
default=False,
action='store_true'
)
parser.add_argument(
'--debug',
help=argparse.SUPPRESS,
default=False,
action='store_true'
)
args = parser.parse_args(argv)
# perform the logging modifications before we do any other operations
Logger.disableANSI(args.no_ansi)
Logger.enableDebugLogger(args.debug)
Logger.isVerbose(args.verbose)
Logger.isSilent(args.quiet)
# take the input path and search for pyconfig files, returning an array of
## file paths.
found_pyconfig_files = Searcher.locateConfigs(args.file)
# once we have the array of files, parse each file and attach the results
## to a graph node object. Return all of the created nodes as a set.
parsed_configs = Consumer.CreateGraphNodes(found_pyconfig_files)
# if pyconfig is running as a script and the number of files returned by the
## consumer is not equal to the number of files located originally, then
## this should be interpreted as a linter error and the script should finish
## with a non-zero exit code.
running_as_script = __name__ == '__main__'
encountered_linter_error = len(found_pyconfig_files) != len(parsed_configs)
if running_as_script and encountered_linter_error:
sys.exit(1) # pragma: no cover
# detect if there was an option to generate data from the SCM used for this repo
## if there is, then it should be inserted into the list of files so that it can
## be in graphed as part of the dependency tree.
if args.scm_info is not None:
Logger.write().info('SCM method: %s' % args.scm_info)
scm_node = SCM.CreateNodeForSCM(args.scm_info, args.file)
parsed_configs.add(scm_node)
# after all the nodes have been constructed, the file paths that each node
## has should be resolved to the full file path. This is done as a for loop
## instead of a map() call to be compatible with Python 3.
for node in parsed_configs:
node.resolvePaths(parsed_configs)
# once the file paths have been resolved, then transform the set of files into
## an array that is ordered from root config to highest level config. Since the
## map of config files has many roots and many children, this will traverse one
## branch then move onto the next root and navigate to a child, and repeat this
## until it has consumed all of the nodes.
mapped_nodes = Grapher.TraverseNodes(parsed_configs)
# initialize the analyzer engine, there is only one instance of this across all
## of the files used in this pass. The intended behavior here is to raise any
## issues that could impact the outcome of a particular build.
analyzer_engine = Engine.Engine()
# iterate through the ordered nodes
for current_config in mapped_nodes:
# unless the `--no-analyze` flag was passed, the analysis engine should be
## used to process each configuration.
if not args.no_analyze:
analyzer_engine.process(current_config)
# unless the `--dry-run` flag was passed, each configuration file should be
## serialized to disk as an xcconfig file.
if not args.dry_run:
Serializer.writeFile(current_config, args.scheme)
if __name__ == "__main__": # pragma: no cover
main()