Bug 1701340 [wpt PR 28265] - Merge constants.js and websocket.sub.js, a=testonly
[gecko.git] / build / midl.py
blobadd17006d61c1921e5c3eff516f7af5b4046dba3
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 import buildconfig
6 import subprocess
7 import os
8 import sys
11 def relativize(path, base=None):
12 # For absolute path in Unix builds, we need relative paths because
13 # Windows programs run via Wine don't like these Unix absolute paths
14 # (they look like command line arguments).
15 if path.startswith("/"):
16 return os.path.relpath(path, base)
17 # For Windows absolute paths, we can just use the unmodified path.
18 # And if the path starts with '-', it's a command line argument.
19 if os.path.isabs(path) or path.startswith("-"):
20 return path
21 # Remaining case is relative paths, which may be relative to a different
22 # directory (os.getcwd()) than the needed `base`, so we "rebase" it.
23 return os.path.relpath(path, base)
26 def midl(out, input, *flags):
27 out.avoid_writing_to_file()
28 midl = buildconfig.substs["MIDL"]
29 wine = buildconfig.substs.get("WINE")
30 base = os.path.dirname(out.name) or "."
31 if midl.lower().endswith(".exe") and wine:
32 command = [wine, midl]
33 else:
34 command = [midl]
35 command.extend(buildconfig.substs["MIDL_FLAGS"])
36 command.extend([relativize(f, base) for f in flags])
37 command.append("-Oicf")
38 command.append(relativize(input, base))
39 print("Executing:", " ".join(command))
40 result = subprocess.run(command, cwd=base)
41 return result.returncode
44 # midl outputs dlldata to a single dlldata.c file by default. This prevents running
45 # midl in parallel in the same directory for idl files that would generate dlldata.c
46 # because of race conditions updating the file. Instead, we ask midl to create
47 # separate files, and we merge them manually.
48 def merge_dlldata(out, *inputs):
49 inputs = [open(i) for i in inputs]
50 read_a_line = [True] * len(inputs)
51 while True:
52 lines = [
53 f.readline() if read_a_line[n] else lines[n] for n, f in enumerate(inputs)
55 unique_lines = set(lines)
56 if len(unique_lines) == 1:
57 # All the lines are identical
58 if not lines[0]:
59 break
60 out.write(lines[0])
61 read_a_line = [True] * len(inputs)
62 elif (
63 len(unique_lines) == 2
64 and len([l for l in unique_lines if "#define" in l]) == 1
66 # Most lines are identical. When they aren't, it's typically because some
67 # files have an extra #define that others don't. When that happens, we
68 # print out the #define, and get a new input line from the files that had
69 # a #define on the next iteration. We expect that next line to match what
70 # the other files had on this iteration.
71 # Note: we explicitly don't support the case where there are different
72 # defines across different files, except when there's a different one
73 # for each file, in which case it's handled further below.
74 a = unique_lines.pop()
75 if "#define" in a:
76 out.write(a)
77 else:
78 out.write(unique_lines.pop())
79 read_a_line = ["#define" in l for l in lines]
80 elif len(unique_lines) != len(lines):
81 # If for some reason, we don't get lines that are entirely different
82 # from each other, we have some unexpected input.
83 print(
84 "Error while merging dlldata. Last lines read: {}".format(lines),
85 file=sys.stderr,
87 return 1
88 else:
89 for line in lines:
90 out.write(line)
91 read_a_line = [True] * len(inputs)
93 return 0