summaryrefslogtreecommitdiff
path: root/xnt/xenant.py
blob: 3dc4a70ebfe599ed964f3977f368bedec4342d0e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
#!/usr/bin/env python
"""Xnt Runner Script"""

#   Xnt -- A Wrapper Build Tool
#   Copyright (C) 2013  Kenny Ballou

#   This program is free software: you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.

#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.

#   You should have received a copy of the GNU General Public License
#   along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os
import sys
import time
import logging
import argparse
import xnt
from xnt.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR

logging.basicConfig(format="%(message)s")
LOGGER = logging.Logger(name=__name__)
LOGGER.addHandler(logging.StreamHandler())
LOGGER.setLevel(logging.WARNING)

def main():
    """Xnt Entry Point"""
    start_time = time.time()
    args = parse_args(sys.argv[1:])
    build_file = "./build.py"
    if args["verbose"]:
        xnt.VERBOSE = True
        LOGGER.setLevel(logging.INFO)
        logging.getLogger("xnt.tasks").setLevel(logging.INFO)
    if args["build-file"]:
        build_file = args["build-file"]
    if args["list-targets"]:
        error_code = list_targets(load_build(build_file))
    else:
        error_code = invoke_build(load_build(build_file),
                                  args["targets"],
                                  args["properties"])
    elapsed_time = time.time() - start_time
    print("Execution time: %.3f" % elapsed_time)
    if error_code != 0:
        LOGGER.error("Failure")
    from xnt.tasks import rm
    rm("build.pyc",
       "__pycache__",
       build_file + "c",
       os.path.join(os.path.dirname(build_file), "__pycache__"))
    if error_code != 0:
        sys.exit(error_code)

def load_build(buildfile="./build.py"):
    """Load build file

    Load the build.py and return the resulting import
    """
    path = os.path.dirname(buildfile)
    build = os.path.basename(buildfile)
    buildmodule = os.path.splitext(build)[0]
    if not path:
        path = os.getcwd()
    else:
        path = os.path.abspath(path)
    sys.path.append(path)
    cwd = os.getcwd()
    os.chdir(path)
    if not os.path.exists(build):
        LOGGER.error("There was no build file")
        sys.exit(1)
    try:
        return __import__(buildmodule, fromlist=[])
    except ImportError:
        LOGGER.error("HOW?!")
        return None
    finally:
        sys.path.remove(path)
        del sys.modules[buildmodule]
        os.chdir(cwd)

def invoke_build(build, targets=None, props=None):
    """Invoke Build with `targets` passing `props`"""
    def call_target(target_name, props):
        """Call target on build module"""
        def process_params(params, existing_props=None):
            """Parse and separate properties and append to build module"""
            properties = existing_props if existing_props is not None else {}
            for param in params:
                name, value = param.split("=")
                properties[name] = value
            return properties
        def __get_properties():
            """Return the properties dictionary of the build module"""
            try:
                return getattr(build, "PROPERTIES")
            except AttributeError:
                LOGGER.warning("Build file specifies no properties")
                return None
        try:
            if props and len(props) > 0:
                setattr(build,
                        "PROPERTIES",
                        process_params(props, __get_properties()))
            target = getattr(build, target_name)
            error_code = target()
            return error_code if error_code else 0
        except AttributeError:
            LOGGER.error("There was no target: %s", target_name)
            return ERROR
        except Exception as ex:
            LOGGER.critical(ex)
            return UNKNOWN_ERROR
    if targets and len(targets) > 0:
        for target in targets:
            error_code = call_target(target, props)
            if error_code:
                return error_code
        return SUCCESS
    else:
        return call_target("default", props)

def list_targets(build):
    """List targets (and doctstrings) of the provided build module"""
    try:
        for attr in dir(build):
            try:
                func = getattr(build, attr)
                if func.decorator == "target":
                    print(attr + ":")
                    if func.__doc__:
                        print(func.__doc__)
                    print("")
            except AttributeError:
                pass
    except AttributeError as ex:
        LOGGER.error(ex)
        return ERROR
    return SUCCESS

def parse_args(args_in):
    """Parse and group arguments"""
    parser = argparse.ArgumentParser(prog="Xnt")
    parser.add_argument("-v", "--verbose",
                        help="be verbose",
                        action="store_true",
                        dest="verbose")
    parser.add_argument(
        "--version",
        action="version",
        version=xnt.__version__,
        help="print the version information and quit")
    parser.add_argument(
        "-b", "--build-file",
        dest="build-file",
        help="use the given buildfile")
    parser.add_argument("-l", "--list-targets",
                        action="store_true",
                        dest="list-targets",
                        help="print build targets")
    # Properties Group
    params_group = parser.add_argument_group("Properties")
    params_group.add_argument(
        "-D", dest="properties", action="append",
        help="use value for gvien property")
    target_group = parser.add_argument_group("Targets")

    # Targets Group
    target_group.add_argument("targets", nargs=argparse.REMAINDER,
                              help="name(s) of targets to invoke")
    return vars(parser.parse_args(args_in))

if __name__ == "__main__":
    main()