1# Copyright (C) 2018 and later: Unicode, Inc. and others.
2# License & terms of use: http://www.unicode.org/copyright.html
3
4# Python 2/3 Compatibility (ICU-20299)
5# TODO(ICU-20301): Remove this.
6from __future__ import print_function
7
8from . import *
9from .. import *
10from .. import utils
11from ..request_types import *
12
13def get_gnumake_rules(build_dirs, requests, makefile_vars, **kwargs):
14    makefile_string = ""
15
16    # Common Variables
17    common_vars = kwargs["common_vars"]
18    for key, value in sorted(makefile_vars.items()):
19        makefile_string += "{KEY} = {VALUE}\n".format(
20            KEY = key,
21            VALUE = value
22        )
23    makefile_string += "\n"
24
25    # Directories
26    dirs_timestamp_file = "{TMP_DIR}/dirs.timestamp".format(**common_vars)
27    makefile_string += "DIRS = {TIMESTAMP_FILE}\n\n".format(
28        TIMESTAMP_FILE = dirs_timestamp_file
29    )
30    makefile_string += "{TIMESTAMP_FILE}:\n\t$(MKINSTALLDIRS) {ALL_DIRS}\n\techo timestamp > {TIMESTAMP_FILE}\n\n".format(
31        TIMESTAMP_FILE = dirs_timestamp_file,
32        ALL_DIRS = " ".join(build_dirs).format(**common_vars)
33    )
34
35    # Generate Rules
36    make_rules = []
37    for request in requests:
38        make_rules += get_gnumake_rules_helper(request, **kwargs)
39
40    # Main Commands
41    for rule in make_rules:
42        if isinstance(rule, MakeFilesVar):
43            makefile_string += "{NAME} = {FILE_LIST}\n\n".format(
44                NAME = rule.name,
45                FILE_LIST = files_to_makefile(rule.files, wrap = True, **kwargs),
46            )
47            continue
48
49        if isinstance(rule, MakeStringVar):
50            makefile_string += "define {NAME}\n{CONTENT}\nendef\nexport {NAME}\n\n".format(
51                NAME = rule.name,
52                CONTENT = rule.content
53            )
54            continue
55
56        assert isinstance(rule, MakeRule)
57        header_line = "{OUT_FILE}: {DEP_FILES} {DEP_LITERALS} | $(DIRS)".format(
58            OUT_FILE = files_to_makefile([rule.output_file], **kwargs),
59            DEP_FILES = files_to_makefile(rule.dep_files, wrap = True, **kwargs),
60            DEP_LITERALS = " ".join(rule.dep_literals)
61        )
62
63        if len(rule.cmds) == 0:
64            makefile_string += "%s\n\n" % header_line
65            continue
66
67        makefile_string += "{HEADER_LINE}\n{RULE_LINES}\n\n".format(
68            HEADER_LINE = header_line,
69            RULE_LINES = "\n".join("\t%s" % cmd for cmd in rule.cmds)
70        )
71
72    return makefile_string
73
74def files_to_makefile(files, common_vars, wrap = False, **kwargs):
75    if len(files) == 0:
76        return ""
77    dirnames = [utils.dir_for(file).format(**common_vars) for file in files]
78    join_str = " \\\n\t\t" if wrap and len(files) > 2 else " "
79    if len(files) == 1:
80        return "%s/%s" % (dirnames[0], files[0].filename)
81    elif len(set(dirnames)) == 1:
82        return "$(addprefix %s/,%s)" % (dirnames[0], join_str.join(file.filename for file in files))
83    else:
84        return join_str.join("%s/%s" % (d, f.filename) for d,f in zip(dirnames, files))
85
86def get_gnumake_rules_helper(request, common_vars, **kwargs):
87
88    if isinstance(request, PrintFileRequest):
89        var_name = "%s_CONTENT" % request.name.upper()
90        return [
91            MakeStringVar(
92                name = var_name,
93                content = request.content
94            ),
95            MakeRule(
96                name = request.name,
97                dep_literals = [],
98                dep_files = [],
99                output_file = request.output_file,
100                cmds = [
101                    "echo \"$${VAR_NAME}\" > {MAKEFILENAME}".format(
102                        VAR_NAME = var_name,
103                        MAKEFILENAME = files_to_makefile([request.output_file], common_vars),
104                        **common_vars
105                    )
106                ]
107            )
108        ]
109
110
111    if isinstance(request, CopyRequest):
112        return [
113            MakeRule(
114                name = request.name,
115                dep_literals = [],
116                dep_files = [request.input_file],
117                output_file = request.output_file,
118                cmds = ["cp %s %s" % (
119                    files_to_makefile([request.input_file], common_vars),
120                    files_to_makefile([request.output_file], common_vars))
121                ]
122            )
123        ]
124
125    if isinstance(request, VariableRequest):
126        return [
127            MakeFilesVar(
128                name = request.name.upper(),
129                files = request.input_files
130            )
131        ]
132
133    if request.tool.name == "make":
134        cmd_template = "$(MAKE) {ARGS}"
135    elif request.tool.name == "gentest":
136        cmd_template = "$(INVOKE) $(GENTEST) {ARGS}"
137    else:
138        assert isinstance(request.tool, IcuTool)
139        cmd_template = "$(INVOKE) $(TOOLBINDIR)/{TOOL} {{ARGS}}".format(
140            TOOL = request.tool.name
141        )
142
143    if isinstance(request, SingleExecutionRequest):
144        cmd = utils.format_single_request_command(request, cmd_template, common_vars)
145        dep_files = request.all_input_files()
146
147        if len(request.output_files) > 1:
148            # Special case for multiple output files: Makefile rules should have only one
149            # output file apiece. More information:
150            # https://www.gnu.org/software/automake/manual/html_node/Multiple-Outputs.html
151            timestamp_var_name = "%s_ALL" % request.name.upper()
152            timestamp_file = TmpFile("%s.timestamp" % request.name)
153            rules = [
154                MakeFilesVar(
155                    name = timestamp_var_name,
156                    files = [timestamp_file]
157                ),
158                MakeRule(
159                    name = "%s_all" % request.name,
160                    dep_literals = [],
161                    dep_files = dep_files,
162                    output_file = timestamp_file,
163                    cmds = [
164                        cmd,
165                        "echo timestamp > {MAKEFILENAME}".format(
166                            MAKEFILENAME = files_to_makefile([timestamp_file], common_vars)
167                        )
168                    ]
169                )
170            ]
171            for i, file in enumerate(request.output_files):
172                rules += [
173                    MakeRule(
174                        name = "%s_%d" % (request.name, i),
175                        dep_literals = ["$(%s)" % timestamp_var_name],
176                        dep_files = [],
177                        output_file = file,
178                        cmds = []
179                    )
180                ]
181            return rules
182
183        elif len(dep_files) > 5:
184            # For nicer printing, for long input lists, use a helper variable.
185            dep_var_name = "%s_DEPS" % request.name.upper()
186            return [
187                MakeFilesVar(
188                    name = dep_var_name,
189                    files = dep_files
190                ),
191                MakeRule(
192                    name = request.name,
193                    dep_literals = ["$(%s)" % dep_var_name],
194                    dep_files = [],
195                    output_file = request.output_files[0],
196                    cmds = [cmd]
197                )
198            ]
199
200        else:
201            return [
202                MakeRule(
203                    name = request.name,
204                    dep_literals = [],
205                    dep_files = dep_files,
206                    output_file = request.output_files[0],
207                    cmds = [cmd]
208                )
209            ]
210
211    if isinstance(request, RepeatedExecutionRequest):
212        rules = []
213        dep_literals = []
214        # To keep from repeating the same dep files many times, make a variable.
215        if len(request.common_dep_files) > 0:
216            dep_var_name = "%s_DEPS" % request.name.upper()
217            dep_literals = ["$(%s)" % dep_var_name]
218            rules += [
219                MakeFilesVar(
220                    name = dep_var_name,
221                    files = request.common_dep_files
222                )
223            ]
224        # Add a rule for each individual file.
225        for loop_vars in utils.repeated_execution_request_looper(request):
226            (_, specific_dep_files, input_file, output_file) = loop_vars
227            name_suffix = input_file[input_file.filename.rfind("/")+1:input_file.filename.rfind(".")]
228            cmd = utils.format_repeated_request_command(
229                request,
230                cmd_template,
231                loop_vars,
232                common_vars
233            )
234            rules += [
235                MakeRule(
236                    name = "%s_%s" % (request.name, name_suffix),
237                    dep_literals = dep_literals,
238                    dep_files = specific_dep_files + [input_file],
239                    output_file = output_file,
240                    cmds = [cmd]
241                )
242            ]
243        return rules
244
245    assert False
246