mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-10-31 08:18:54 +02:00 
			
		
		
		
	 c8d97baa2c
			
		
	
	
		c8d97baa2c
		
	
	
	
	
		
			
			Some were automatic fixes, but most were done manually. Differential Revision: https://phabricator.services.mozilla.com/D253375
		
			
				
	
	
		
			223 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			223 lines
		
	
	
	
		
			8.4 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # This Source Code Form is subject to the terms of the Mozilla Public
 | |
| # License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 | |
| 
 | |
| import functools
 | |
| import os
 | |
| import shutil
 | |
| import subprocess
 | |
| import sys
 | |
| 
 | |
| import buildconfig
 | |
| 
 | |
| 
 | |
| def relativize(path, base=None):
 | |
|     # For absolute path in Unix builds, we need relative paths because
 | |
|     # Windows programs run via Wine don't like these Unix absolute paths
 | |
|     # (they look like command line arguments).
 | |
|     if path.startswith("/"):
 | |
|         return os.path.relpath(path, base)
 | |
|     # For Windows absolute paths, we can just use the unmodified path.
 | |
|     # And if the path starts with '-', it's a command line argument.
 | |
|     if os.path.isabs(path) or path.startswith("-"):
 | |
|         return path
 | |
|     # Remaining case is relative paths, which may be relative to a different
 | |
|     # directory (os.getcwd()) than the needed `base`, so we "rebase" it.
 | |
|     return os.path.relpath(path, base)
 | |
| 
 | |
| 
 | |
| @functools.cache
 | |
| def files_in(path):
 | |
|     return {p.lower(): os.path.join(path, p) for p in os.listdir(path)}
 | |
| 
 | |
| 
 | |
| def search_path(paths, path):
 | |
|     for p in paths:
 | |
|         f = os.path.join(p, path)
 | |
|         if os.path.isfile(f):
 | |
|             return f
 | |
|         # try an case-insensitive match
 | |
|         maybe_match = files_in(p).get(path.lower())
 | |
|         if maybe_match:
 | |
|             return maybe_match
 | |
|     raise RuntimeError(f"Cannot find {path}")
 | |
| 
 | |
| 
 | |
| # Filter-out -std= flag from the preprocessor command, as we're not preprocessing
 | |
| # C or C++, and the command would fail with the flag.
 | |
| def filter_preprocessor(cmd):
 | |
|     prev = None
 | |
|     for arg in cmd:
 | |
|         if arg == "-Xclang":
 | |
|             prev = arg
 | |
|             continue
 | |
|         if not arg.startswith("-std="):
 | |
|             if prev:
 | |
|                 yield prev
 | |
|             yield arg
 | |
|         prev = None
 | |
| 
 | |
| 
 | |
| # Preprocess all the direct and indirect inputs of midl, and put all the
 | |
| # preprocessed inputs in the given `base` directory. Returns a tuple containing
 | |
| # the path of the main preprocessed input, and the modified flags to use instead
 | |
| # of the flags given as argument.
 | |
| def preprocess(base, input, flags):
 | |
|     import argparse
 | |
|     import re
 | |
|     from collections import deque
 | |
| 
 | |
|     IMPORT_RE = re.compile(r'import\s*"([^"]+)";')
 | |
| 
 | |
|     parser = argparse.ArgumentParser()
 | |
|     parser.add_argument("-I", action="append")
 | |
|     parser.add_argument("-D", action="append")
 | |
|     parser.add_argument("-acf")
 | |
|     args, remainder = parser.parse_known_args(flags)
 | |
|     preprocessor = (
 | |
|         list(filter_preprocessor(buildconfig.substs["CXXCPP"]))
 | |
|         # Ideally we'd use the real midl version, but querying it adds a
 | |
|         # significant overhead to configure. In practice, the version number
 | |
|         # doesn't make a difference at the moment.
 | |
|         + ["-D__midl=801"]
 | |
|         + [f"-D{d}" for d in args.D or ()]
 | |
|         + [f"-I{i}" for i in args.I or ()]
 | |
|     )
 | |
|     includes = ["."] + buildconfig.substs["INCLUDE"].split(";") + (args.I or [])
 | |
|     seen = set()
 | |
|     queue = deque([input])
 | |
|     if args.acf:
 | |
|         queue.append(args.acf)
 | |
|     output = os.path.join(base, os.path.basename(input))
 | |
|     while True:
 | |
|         try:
 | |
|             input = queue.popleft()
 | |
|         except IndexError:
 | |
|             break
 | |
|         if os.path.basename(input) in seen:
 | |
|             continue
 | |
|         seen.add(os.path.basename(input))
 | |
|         input = search_path(includes, input)
 | |
|         # If there is a .acf file corresponding to the .idl we're processing,
 | |
|         # we also want to preprocess that file because midl might look for it too.
 | |
|         if input.lower().endswith(".idl"):
 | |
|             try:
 | |
|                 acf = search_path(
 | |
|                     [os.path.dirname(input)], os.path.basename(input)[:-4] + ".acf"
 | |
|                 )
 | |
|                 if acf:
 | |
|                     queue.append(acf)
 | |
|             except RuntimeError:
 | |
|                 pass
 | |
|         command = preprocessor + [input]
 | |
|         preprocessed = os.path.join(base, os.path.basename(input))
 | |
|         subprocess.run(command, stdout=open(preprocessed, "wb"), check=True)
 | |
|         # Read the resulting file, and search for imports, that we'll want to
 | |
|         # preprocess as well.
 | |
|         with open(preprocessed) as fh:
 | |
|             for line in fh:
 | |
|                 if not line.startswith("import"):
 | |
|                     continue
 | |
|                 m = IMPORT_RE.match(line)
 | |
|                 if not m:
 | |
|                     continue
 | |
|                 imp = m.group(1)
 | |
|                 queue.append(imp)
 | |
|     flags = []
 | |
|     # Add -I<base> first in the flags, so that midl resolves imports to the
 | |
|     # preprocessed files we created.
 | |
|     for i in [base] + (args.I or []):
 | |
|         flags.extend(["-I", i])
 | |
|     # Add the preprocessed acf file if one was given on the command line.
 | |
|     if args.acf:
 | |
|         flags.extend(["-acf", os.path.join(base, os.path.basename(args.acf))])
 | |
|     flags.extend(remainder)
 | |
|     return output, flags
 | |
| 
 | |
| 
 | |
| def midl(out, input, *flags):
 | |
|     out.avoid_writing_to_file()
 | |
|     midl_flags = buildconfig.substs["MIDL_FLAGS"]
 | |
|     base = os.path.dirname(out.name) or "."
 | |
|     tmpdir = None
 | |
|     try:
 | |
|         # If the build system is asking to not use the preprocessor to midl,
 | |
|         # we need to do the preprocessing ourselves.
 | |
|         if "-no_cpp" in midl_flags:
 | |
|             # Normally, we'd use tempfile.TemporaryDirectory, but in this specific
 | |
|             # case, we actually want a deterministic directory name, because it's
 | |
|             # recorded in the code midl generates.
 | |
|             tmpdir = os.path.join(base, os.path.basename(input) + ".tmp")
 | |
|             os.makedirs(tmpdir, exist_ok=True)
 | |
|             try:
 | |
|                 input, flags = preprocess(tmpdir, input, flags)
 | |
|             except subprocess.CalledProcessError as e:
 | |
|                 return e.returncode
 | |
|         midl = buildconfig.substs["MIDL"]
 | |
|         wine = buildconfig.substs.get("WINE")
 | |
|         if midl.lower().endswith(".exe") and wine:
 | |
|             command = [wine, midl]
 | |
|         else:
 | |
|             command = [midl]
 | |
|         command.extend(midl_flags)
 | |
|         command.extend([relativize(f, base) for f in flags])
 | |
|         command.append("-Oicf")
 | |
|         command.append(relativize(input, base))
 | |
|         print("Executing:", " ".join(command))
 | |
|         result = subprocess.run(command, cwd=base)
 | |
|         return result.returncode
 | |
|     finally:
 | |
|         if tmpdir:
 | |
|             shutil.rmtree(tmpdir)
 | |
| 
 | |
| 
 | |
| # midl outputs dlldata to a single dlldata.c file by default. This prevents running
 | |
| # midl in parallel in the same directory for idl files that would generate dlldata.c
 | |
| # because of race conditions updating the file. Instead, we ask midl to create
 | |
| # separate files, and we merge them manually.
 | |
| def merge_dlldata(out, *inputs):
 | |
|     inputs = [open(i) for i in inputs]
 | |
|     read_a_line = [True] * len(inputs)
 | |
|     while True:
 | |
|         lines = [
 | |
|             f.readline() if read_a_line[n] else lines[n] for n, f in enumerate(inputs)
 | |
|         ]
 | |
|         unique_lines = set(lines)
 | |
|         if len(unique_lines) == 1:
 | |
|             # All the lines are identical
 | |
|             if not lines[0]:
 | |
|                 break
 | |
|             out.write(lines[0])
 | |
|             read_a_line = [True] * len(inputs)
 | |
|         elif (
 | |
|             len(unique_lines) == 2
 | |
|             and len([l for l in unique_lines if "#define" in l]) == 1
 | |
|         ):
 | |
|             # Most lines are identical. When they aren't, it's typically because some
 | |
|             # files have an extra #define that others don't. When that happens, we
 | |
|             # print out the #define, and get a new input line from the files that had
 | |
|             # a #define on the next iteration. We expect that next line to match what
 | |
|             # the other files had on this iteration.
 | |
|             # Note: we explicitly don't support the case where there are different
 | |
|             # defines across different files, except when there's a different one
 | |
|             # for each file, in which case it's handled further below.
 | |
|             a = unique_lines.pop()
 | |
|             if "#define" in a:
 | |
|                 out.write(a)
 | |
|             else:
 | |
|                 out.write(unique_lines.pop())
 | |
|             read_a_line = ["#define" in l for l in lines]
 | |
|         elif len(unique_lines) != len(lines):
 | |
|             # If for some reason, we don't get lines that are entirely different
 | |
|             # from each other, we have some unexpected input.
 | |
|             print(
 | |
|                 f"Error while merging dlldata. Last lines read: {lines}",
 | |
|                 file=sys.stderr,
 | |
|             )
 | |
|             return 1
 | |
|         else:
 | |
|             for line in lines:
 | |
|                 out.write(line)
 | |
|             read_a_line = [True] * len(inputs)
 | |
| 
 | |
|     return 0
 |