aboutsummaryrefslogtreecommitdiff
path: root/yapymake/args.py
blob: 60aafe806962bf9b7215e02c59d1495dec657cef (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
import argparse
from dataclasses import dataclass
import os
import sys
from typing import List, Optional, TextIO

from . import DESCRIPTION, VERSION

__all__ = [
    'parse',
    'Args',
]

parser = argparse.ArgumentParser(
    description=f'{DESCRIPTION} - version {VERSION}',
)

parser.add_argument('--environment-overrides', '-e',
                    action='store_true',
                    help='Cause environment variables, including those with null values, to override macro assignments '
                         'within makefiles.')
parser.add_argument('--makefile', '--file', '-f',
                    action='append',
                    type=argparse.FileType('r'),
                    help="Specify a different makefile (or '-' for standard input).")
parser.add_argument('--ignore-errors', '-i',
                    action='store_true',
                    help='Ignore error codes returned by invoked commands.')
# TODO implement keep-going
parser.add_argument('--keep-going', '-k',
                    action='store_true',
                    help='Continue to update other targets that do not depend on the current target if a non-ignored '
                         'error occurs while executing the commands to bring a target up-to-date.')
parser.add_argument('--dry-run', '--just-print', '--recon', '-n',
                    action='store_true',
                    help="Write commands that would be executed on standard output, but do not execute them (but "
                         "execute lines starting with '+').")
parser.add_argument('--print-everything', '--print-data-base', '-p',
                    action='store_true',
                    help='Write to standard output the complete set of macro definitions and target descriptions.')
# TODO implement question
parser.add_argument('--question', '-q',
                    action='store_true',
                    help='Return a zero exit value if the target file is up-to-date; otherwise, return an exit value '
                         'of 1.')
parser.add_argument('--no-builtin-rules', '-r',
                    action='store_false',
                    dest='builtin_rules',
                    help='Clear the suffix list and do not use the built-in rules.')
parser.add_argument('--no-keep-going', '--stop', '-S',
                    action='store_false',
                    dest='keep_going',
                    help='Terminate make if an error occurs while executing the commands to bring a target up-to-date '
                         '(default behavior, required by POSIX to be also a flag for some reason).')
parser.add_argument('--silent', '--quiet', '-s',
                    action='store_true',
                    help='Do not write makefile command lines or touch messages to standard output before executing.')
# TODO implement touch
parser.add_argument('--touch', '-t',
                    action='store_true',
                    help='Update the modification time of each target as though a touch target had been executed.')
parser.add_argument('targets_or_macros',
                    nargs='*',
                    metavar='target_or_macro',
                    help='Target name or macro definition.')

@dataclass()
class Args:
    environment_overrides: bool
    makefile: List[TextIO]
    ignore_errors: bool
    keep_going: bool
    dry_run: bool
    print_everything: bool
    question: bool
    builtin_rules: bool
    silent: bool
    touch: bool
    targets_or_macros: List[str]

    def __init__(self, parsed_args: argparse.Namespace):
        self.environment_overrides = parsed_args.environment_overrides
        if parsed_args.makefile is not None and len(parsed_args.makefile) > 0:
            self.makefile = parsed_args.makefile
        else:
            try:
                self.makefile = [open('./makefile', 'r')]
            except FileNotFoundError:
                self.makefile = [open('./Makefile', 'r')]
        self.ignore_errors = parsed_args.ignore_errors
        self.keep_going = parsed_args.keep_going
        self.dry_run = parsed_args.dry_run
        self.print_everything = parsed_args.print_everything
        self.question = parsed_args.question
        self.builtin_rules = parsed_args.builtin_rules
        self.silent = parsed_args.silent
        self.touch = parsed_args.touch
        self.targets_or_macros = parsed_args.targets_or_macros

def parse(cli_args: Optional[List[str]] = None, env_makeflags: Optional[str] = None) -> Args:
    if cli_args is None:
        real_cli_args = sys.argv[1:]
    else:
        real_cli_args = cli_args
    if env_makeflags is None:
        real_env_makeflags = os.environ.get('MAKEFLAGS', '')
    else:
        real_env_makeflags = env_makeflags

    # per POSIX, we accept option letters without a leading -, so to simplify we prepend a - now
    # TODO allow macro definitions in MAKEFLAGS
    if len(real_env_makeflags) > 0 and not real_env_makeflags.startswith('-'):
        real_env_makeflags = '-' + real_env_makeflags

    if len(real_env_makeflags) > 0:
        all_args = [real_env_makeflags, *real_cli_args]
    else:
        all_args = real_cli_args

    return Args(parser.parse_args(all_args))

# TODO test any of this