summaryrefslogtreecommitdiffstatshomepage
path: root/3rdparty/zstd/contrib/freestanding_lib
diff options
context:
space:
mode:
Diffstat (limited to '3rdparty/zstd/contrib/freestanding_lib')
-rwxr-xr-x3rdparty/zstd/contrib/freestanding_lib/freestanding.py774
1 files changed, 774 insertions, 0 deletions
diff --git a/3rdparty/zstd/contrib/freestanding_lib/freestanding.py b/3rdparty/zstd/contrib/freestanding_lib/freestanding.py
new file mode 100755
index 00000000000..df6983245d6
--- /dev/null
+++ b/3rdparty/zstd/contrib/freestanding_lib/freestanding.py
@@ -0,0 +1,774 @@
+#!/usr/bin/env python3
+# ################################################################
+# Copyright (c) Meta Platforms, Inc. and affiliates.
+# All rights reserved.
+#
+# This source code is licensed under both the BSD-style license (found in the
+# LICENSE file in the root directory of this source tree) and the GPLv2 (found
+# in the COPYING file in the root directory of this source tree).
+# You may select, at your option, one of the above-listed licenses.
+# ##########################################################################
+
+import argparse
+import contextlib
+import os
+import re
+import shutil
+import sys
+from typing import Optional
+
+
+INCLUDED_SUBDIRS = ["common", "compress", "decompress"]
+
+SKIPPED_FILES = [
+ "common/mem.h",
+ "common/zstd_deps.h",
+ "common/pool.c",
+ "common/pool.h",
+ "common/threading.c",
+ "common/threading.h",
+ "common/zstd_trace.h",
+ "compress/zstdmt_compress.h",
+ "compress/zstdmt_compress.c",
+]
+
+XXHASH_FILES = [
+ "common/xxhash.c",
+ "common/xxhash.h",
+]
+
+
+class FileLines(object):
+ def __init__(self, filename):
+ self.filename = filename
+ with open(self.filename, "r") as f:
+ self.lines = f.readlines()
+
+ def write(self):
+ with open(self.filename, "w") as f:
+ f.write("".join(self.lines))
+
+
+class PartialPreprocessor(object):
+ """
+ Looks for simple ifdefs and ifndefs and replaces them.
+ Handles && and ||.
+ Has fancy logic to handle translating elifs to ifs.
+ Only looks for macros in the first part of the expression with no
+ parens.
+ Does not handle multi-line macros (only looks in first line).
+ """
+ def __init__(self, defs: [(str, Optional[str])], replaces: [(str, str)], undefs: [str]):
+ MACRO_GROUP = r"(?P<macro>[a-zA-Z_][a-zA-Z_0-9]*)"
+ ELIF_GROUP = r"(?P<elif>el)?"
+ OP_GROUP = r"(?P<op>&&|\|\|)?"
+
+ self._defs = {macro:value for macro, value in defs}
+ self._replaces = {macro:value for macro, value in replaces}
+ self._defs.update(self._replaces)
+ self._undefs = set(undefs)
+
+ self._define = re.compile(r"\s*#\s*define")
+ self._if = re.compile(r"\s*#\s*if")
+ self._elif = re.compile(r"\s*#\s*(?P<elif>el)if")
+ self._else = re.compile(r"\s*#\s*(?P<else>else)")
+ self._endif = re.compile(r"\s*#\s*endif")
+
+ self._ifdef = re.compile(fr"\s*#\s*if(?P<not>n)?def {MACRO_GROUP}\s*")
+ self._if_defined = re.compile(
+ fr"\s*#\s*{ELIF_GROUP}if\s+(?P<not>!)?\s*defined\s*\(\s*{MACRO_GROUP}\s*\)\s*{OP_GROUP}"
+ )
+ self._if_defined_value = re.compile(
+ fr"\s*#\s*{ELIF_GROUP}if\s+defined\s*\(\s*{MACRO_GROUP}\s*\)\s*"
+ fr"(?P<op>&&)\s*"
+ fr"(?P<openp>\()?\s*"
+ fr"(?P<macro2>[a-zA-Z_][a-zA-Z_0-9]*)\s*"
+ fr"(?P<cmp>[=><!]+)\s*"
+ fr"(?P<value>[0-9]*)\s*"
+ fr"(?P<closep>\))?\s*"
+ )
+ self._if_true = re.compile(
+ fr"\s*#\s*{ELIF_GROUP}if\s+{MACRO_GROUP}\s*{OP_GROUP}"
+ )
+
+ self._c_comment = re.compile(r"/\*.*?\*/")
+ self._cpp_comment = re.compile(r"//")
+
+ def _log(self, *args, **kwargs):
+ print(*args, **kwargs)
+
+ def _strip_comments(self, line):
+ # First strip c-style comments (may include //)
+ while True:
+ m = self._c_comment.search(line)
+ if m is None:
+ break
+ line = line[:m.start()] + line[m.end():]
+
+ # Then strip cpp-style comments
+ m = self._cpp_comment.search(line)
+ if m is not None:
+ line = line[:m.start()]
+
+ return line
+
+ def _fixup_indentation(self, macro, replace: [str]):
+ if len(replace) == 0:
+ return replace
+ if len(replace) == 1 and self._define.match(replace[0]) is None:
+ # If there is only one line, only replace defines
+ return replace
+
+
+ all_pound = True
+ for line in replace:
+ if not line.startswith('#'):
+ all_pound = False
+ if all_pound:
+ replace = [line[1:] for line in replace]
+
+ min_spaces = len(replace[0])
+ for line in replace:
+ spaces = 0
+ for i, c in enumerate(line):
+ if c != ' ':
+ # Non-preprocessor line ==> skip the fixup
+ if not all_pound and c != '#':
+ return replace
+ spaces = i
+ break
+ min_spaces = min(min_spaces, spaces)
+
+ replace = [line[min_spaces:] for line in replace]
+
+ if all_pound:
+ replace = ["#" + line for line in replace]
+
+ return replace
+
+ def _handle_if_block(self, macro, idx, is_true, prepend):
+ """
+ Remove the #if or #elif block starting on this line.
+ """
+ REMOVE_ONE = 0
+ KEEP_ONE = 1
+ REMOVE_REST = 2
+
+ if is_true:
+ state = KEEP_ONE
+ else:
+ state = REMOVE_ONE
+
+ line = self._inlines[idx]
+ is_if = self._if.match(line) is not None
+ assert is_if or self._elif.match(line) is not None
+ depth = 0
+
+ start_idx = idx
+
+ idx += 1
+ replace = prepend
+ finished = False
+ while idx < len(self._inlines):
+ line = self._inlines[idx]
+ # Nested if statement
+ if self._if.match(line):
+ depth += 1
+ idx += 1
+ continue
+ # We're inside a nested statement
+ if depth > 0:
+ if self._endif.match(line):
+ depth -= 1
+ idx += 1
+ continue
+
+ # We're at the original depth
+
+ # Looking only for an endif.
+ # We've found a true statement, but haven't
+ # completely elided the if block, so we just
+ # remove the remainder.
+ if state == REMOVE_REST:
+ if self._endif.match(line):
+ if is_if:
+ # Remove the endif because we took the first if
+ idx += 1
+ finished = True
+ break
+ idx += 1
+ continue
+
+ if state == KEEP_ONE:
+ m = self._elif.match(line)
+ if self._endif.match(line):
+ replace += self._inlines[start_idx + 1:idx]
+ idx += 1
+ finished = True
+ break
+ if self._elif.match(line) or self._else.match(line):
+ replace += self._inlines[start_idx + 1:idx]
+ state = REMOVE_REST
+ idx += 1
+ continue
+
+ if state == REMOVE_ONE:
+ m = self._elif.match(line)
+ if m is not None:
+ if is_if:
+ idx += 1
+ b = m.start('elif')
+ e = m.end('elif')
+ assert e - b == 2
+ replace.append(line[:b] + line[e:])
+ finished = True
+ break
+ m = self._else.match(line)
+ if m is not None:
+ if is_if:
+ idx += 1
+ while self._endif.match(self._inlines[idx]) is None:
+ replace.append(self._inlines[idx])
+ idx += 1
+ idx += 1
+ finished = True
+ break
+ if self._endif.match(line):
+ if is_if:
+ # Remove the endif because no other elifs
+ idx += 1
+ finished = True
+ break
+ idx += 1
+ continue
+ if not finished:
+ raise RuntimeError("Unterminated if block!")
+
+ replace = self._fixup_indentation(macro, replace)
+
+ self._log(f"\tHardwiring {macro}")
+ if start_idx > 0:
+ self._log(f"\t\t {self._inlines[start_idx - 1][:-1]}")
+ for x in range(start_idx, idx):
+ self._log(f"\t\t- {self._inlines[x][:-1]}")
+ for line in replace:
+ self._log(f"\t\t+ {line[:-1]}")
+ if idx < len(self._inlines):
+ self._log(f"\t\t {self._inlines[idx][:-1]}")
+
+ return idx, replace
+
+ def _preprocess_once(self):
+ outlines = []
+ idx = 0
+ changed = False
+ while idx < len(self._inlines):
+ line = self._inlines[idx]
+ sline = self._strip_comments(line)
+ m = self._ifdef.fullmatch(sline)
+ if_true = False
+ if m is None:
+ m = self._if_defined_value.fullmatch(sline)
+ if m is None:
+ m = self._if_defined.match(sline)
+ if m is None:
+ m = self._if_true.match(sline)
+ if_true = (m is not None)
+ if m is None:
+ outlines.append(line)
+ idx += 1
+ continue
+
+ groups = m.groupdict()
+ macro = groups['macro']
+ op = groups.get('op')
+
+ if not (macro in self._defs or macro in self._undefs):
+ outlines.append(line)
+ idx += 1
+ continue
+
+ defined = macro in self._defs
+
+ # Needed variables set:
+ # resolved: Is the statement fully resolved?
+ # is_true: If resolved, is the statement true?
+ ifdef = False
+ if if_true:
+ if not defined:
+ outlines.append(line)
+ idx += 1
+ continue
+
+ defined_value = self._defs[macro]
+ is_int = True
+ try:
+ defined_value = int(defined_value)
+ except TypeError:
+ is_int = False
+ except ValueError:
+ is_int = False
+
+ resolved = is_int
+ is_true = (defined_value != 0)
+
+ if resolved and op is not None:
+ if op == '&&':
+ resolved = not is_true
+ else:
+ assert op == '||'
+ resolved = is_true
+
+ else:
+ ifdef = groups.get('not') is None
+ elseif = groups.get('elif') is not None
+
+ macro2 = groups.get('macro2')
+ cmp = groups.get('cmp')
+ value = groups.get('value')
+ openp = groups.get('openp')
+ closep = groups.get('closep')
+
+ is_true = (ifdef == defined)
+ resolved = True
+ if op is not None:
+ if op == '&&':
+ resolved = not is_true
+ else:
+ assert op == '||'
+ resolved = is_true
+
+ if macro2 is not None and not resolved:
+ assert ifdef and defined and op == '&&' and cmp is not None
+ # If the statement is true, but we have a single value check, then
+ # check the value.
+ defined_value = self._defs[macro]
+ are_ints = True
+ try:
+ defined_value = int(defined_value)
+ value = int(value)
+ except TypeError:
+ are_ints = False
+ except ValueError:
+ are_ints = False
+ if (
+ macro == macro2 and
+ ((openp is None) == (closep is None)) and
+ are_ints
+ ):
+ resolved = True
+ if cmp == '<':
+ is_true = defined_value < value
+ elif cmp == '<=':
+ is_true = defined_value <= value
+ elif cmp == '==':
+ is_true = defined_value == value
+ elif cmp == '!=':
+ is_true = defined_value != value
+ elif cmp == '>=':
+ is_true = defined_value >= value
+ elif cmp == '>':
+ is_true = defined_value > value
+ else:
+ resolved = False
+
+ if op is not None and not resolved:
+ # Remove the first op in the line + spaces
+ if op == '&&':
+ opre = op
+ else:
+ assert op == '||'
+ opre = r'\|\|'
+ needle = re.compile(fr"(?P<if>\s*#\s*(el)?if\s+).*?(?P<op>{opre}\s*)")
+ match = needle.match(line)
+ assert match is not None
+ newline = line[:match.end('if')] + line[match.end('op'):]
+
+ self._log(f"\tHardwiring partially resolved {macro}")
+ self._log(f"\t\t- {line[:-1]}")
+ self._log(f"\t\t+ {newline[:-1]}")
+
+ outlines.append(newline)
+ idx += 1
+ continue
+
+ # Skip any statements we cannot fully compute
+ if not resolved:
+ outlines.append(line)
+ idx += 1
+ continue
+
+ prepend = []
+ if macro in self._replaces:
+ assert not ifdef
+ assert op is None
+ value = self._replaces.pop(macro)
+ prepend = [f"#define {macro} {value}\n"]
+
+ idx, replace = self._handle_if_block(macro, idx, is_true, prepend)
+ outlines += replace
+ changed = True
+
+ return changed, outlines
+
+ def preprocess(self, filename):
+ with open(filename, 'r') as f:
+ self._inlines = f.readlines()
+ changed = True
+ iters = 0
+ while changed:
+ iters += 1
+ changed, outlines = self._preprocess_once()
+ self._inlines = outlines
+
+ with open(filename, 'w') as f:
+ f.write(''.join(self._inlines))
+
+
+class Freestanding(object):
+ def __init__(
+ self, zstd_deps: str, mem: str, source_lib: str, output_lib: str,
+ external_xxhash: bool, xxh64_state: Optional[str],
+ xxh64_prefix: Optional[str], rewritten_includes: [(str, str)],
+ defs: [(str, Optional[str])], replaces: [(str, str)],
+ undefs: [str], excludes: [str], seds: [str], spdx: bool,
+ ):
+ self._zstd_deps = zstd_deps
+ self._mem = mem
+ self._src_lib = source_lib
+ self._dst_lib = output_lib
+ self._external_xxhash = external_xxhash
+ self._xxh64_state = xxh64_state
+ self._xxh64_prefix = xxh64_prefix
+ self._rewritten_includes = rewritten_includes
+ self._defs = defs
+ self._replaces = replaces
+ self._undefs = undefs
+ self._excludes = excludes
+ self._seds = seds
+ self._spdx = spdx
+
+ def _dst_lib_file_paths(self):
+ """
+ Yields all the file paths in the dst_lib.
+ """
+ for root, dirname, filenames in os.walk(self._dst_lib):
+ for filename in filenames:
+ filepath = os.path.join(root, filename)
+ yield filepath
+
+ def _log(self, *args, **kwargs):
+ print(*args, **kwargs)
+
+ def _copy_file(self, lib_path):
+ suffixes = [".c", ".h", ".S"]
+ if not any((lib_path.endswith(suffix) for suffix in suffixes)):
+ return
+ if lib_path in SKIPPED_FILES:
+ self._log(f"\tSkipping file: {lib_path}")
+ return
+ if self._external_xxhash and lib_path in XXHASH_FILES:
+ self._log(f"\tSkipping xxhash file: {lib_path}")
+ return
+
+ src_path = os.path.join(self._src_lib, lib_path)
+ dst_path = os.path.join(self._dst_lib, lib_path)
+ self._log(f"\tCopying: {src_path} -> {dst_path}")
+ shutil.copyfile(src_path, dst_path)
+
+ def _copy_source_lib(self):
+ self._log("Copying source library into output library")
+
+ assert os.path.exists(self._src_lib)
+ os.makedirs(self._dst_lib, exist_ok=True)
+ self._copy_file("zstd.h")
+ self._copy_file("zstd_errors.h")
+ for subdir in INCLUDED_SUBDIRS:
+ src_dir = os.path.join(self._src_lib, subdir)
+ dst_dir = os.path.join(self._dst_lib, subdir)
+
+ assert os.path.exists(src_dir)
+ os.makedirs(dst_dir, exist_ok=True)
+
+ for filename in os.listdir(src_dir):
+ lib_path = os.path.join(subdir, filename)
+ self._copy_file(lib_path)
+
+ def _copy_zstd_deps(self):
+ dst_zstd_deps = os.path.join(self._dst_lib, "common", "zstd_deps.h")
+ self._log(f"Copying zstd_deps: {self._zstd_deps} -> {dst_zstd_deps}")
+ shutil.copyfile(self._zstd_deps, dst_zstd_deps)
+
+ def _copy_mem(self):
+ dst_mem = os.path.join(self._dst_lib, "common", "mem.h")
+ self._log(f"Copying mem: {self._mem} -> {dst_mem}")
+ shutil.copyfile(self._mem, dst_mem)
+
+ def _hardwire_preprocessor(self, name: str, value: Optional[str] = None, undef=False):
+ """
+ If value=None then hardwire that it is defined, but not what the value is.
+ If undef=True then value must be None.
+ If value='' then the macro is defined to '' exactly.
+ """
+ assert not (undef and value is not None)
+ for filepath in self._dst_lib_file_paths():
+ file = FileLines(filepath)
+
+ def _hardwire_defines(self):
+ self._log("Hardwiring macros")
+ partial_preprocessor = PartialPreprocessor(self._defs, self._replaces, self._undefs)
+ for filepath in self._dst_lib_file_paths():
+ partial_preprocessor.preprocess(filepath)
+
+ def _remove_excludes(self):
+ self._log("Removing excluded sections")
+ for exclude in self._excludes:
+ self._log(f"\tRemoving excluded sections for: {exclude}")
+ begin_re = re.compile(f"BEGIN {exclude}")
+ end_re = re.compile(f"END {exclude}")
+ for filepath in self._dst_lib_file_paths():
+ file = FileLines(filepath)
+ outlines = []
+ skipped = []
+ emit = True
+ for line in file.lines:
+ if emit and begin_re.search(line) is not None:
+ assert end_re.search(line) is None
+ emit = False
+ if emit:
+ outlines.append(line)
+ else:
+ skipped.append(line)
+ if end_re.search(line) is not None:
+ assert begin_re.search(line) is None
+ self._log(f"\t\tRemoving excluded section: {exclude}")
+ for s in skipped:
+ self._log(f"\t\t\t- {s}")
+ emit = True
+ skipped = []
+ if not emit:
+ raise RuntimeError("Excluded section unfinished!")
+ file.lines = outlines
+ file.write()
+
+ def _rewrite_include(self, original, rewritten):
+ self._log(f"\tRewriting include: {original} -> {rewritten}")
+ regex = re.compile(f"\\s*#\\s*include\\s*(?P<include>{original})")
+ for filepath in self._dst_lib_file_paths():
+ file = FileLines(filepath)
+ for i, line in enumerate(file.lines):
+ match = regex.match(line)
+ if match is None:
+ continue
+ s = match.start('include')
+ e = match.end('include')
+ file.lines[i] = line[:s] + rewritten + line[e:]
+ file.write()
+
+ def _rewrite_includes(self):
+ self._log("Rewriting includes")
+ for original, rewritten in self._rewritten_includes:
+ self._rewrite_include(original, rewritten)
+
+ def _replace_xxh64_prefix(self):
+ if self._xxh64_prefix is None:
+ return
+ self._log(f"Replacing XXH64 prefix with {self._xxh64_prefix}")
+ replacements = []
+ if self._xxh64_state is not None:
+ replacements.append(
+ (re.compile(r"([^\w]|^)(?P<orig>XXH64_state_t)([^\w]|$)"), self._xxh64_state)
+ )
+ if self._xxh64_prefix is not None:
+ replacements.append(
+ (re.compile(r"([^\w]|^)(?P<orig>XXH64)[\(_]"), self._xxh64_prefix)
+ )
+ for filepath in self._dst_lib_file_paths():
+ file = FileLines(filepath)
+ for i, line in enumerate(file.lines):
+ modified = False
+ for regex, replacement in replacements:
+ match = regex.search(line)
+ while match is not None:
+ modified = True
+ b = match.start('orig')
+ e = match.end('orig')
+ line = line[:b] + replacement + line[e:]
+ match = regex.search(line)
+ if modified:
+ self._log(f"\t- {file.lines[i][:-1]}")
+ self._log(f"\t+ {line[:-1]}")
+ file.lines[i] = line
+ file.write()
+
+ def _parse_sed(self, sed):
+ assert sed[0] == 's'
+ delim = sed[1]
+ match = re.fullmatch(f's{delim}(.+){delim}(.*){delim}(.*)', sed)
+ assert match is not None
+ regex = re.compile(match.group(1))
+ format_str = match.group(2)
+ is_global = match.group(3) == 'g'
+ return regex, format_str, is_global
+
+ def _process_sed(self, sed):
+ self._log(f"Processing sed: {sed}")
+ regex, format_str, is_global = self._parse_sed(sed)
+
+ for filepath in self._dst_lib_file_paths():
+ file = FileLines(filepath)
+ for i, line in enumerate(file.lines):
+ modified = False
+ while True:
+ match = regex.search(line)
+ if match is None:
+ break
+ replacement = format_str.format(match.groups(''), match.groupdict(''))
+ b = match.start()
+ e = match.end()
+ line = line[:b] + replacement + line[e:]
+ modified = True
+ if not is_global:
+ break
+ if modified:
+ self._log(f"\t- {file.lines[i][:-1]}")
+ self._log(f"\t+ {line[:-1]}")
+ file.lines[i] = line
+ file.write()
+
+ def _process_seds(self):
+ self._log("Processing seds")
+ for sed in self._seds:
+ self._process_sed(sed)
+
+ def _process_spdx(self):
+ if not self._spdx:
+ return
+ self._log("Processing spdx")
+ SPDX_C = "// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause\n"
+ SPDX_H_S = "/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */\n"
+ for filepath in self._dst_lib_file_paths():
+ file = FileLines(filepath)
+ if file.lines[0] == SPDX_C or file.lines[0] == SPDX_H_S:
+ continue
+ for line in file.lines:
+ if "SPDX-License-Identifier" in line:
+ raise RuntimeError(f"Unexpected SPDX license identifier: {file.filename} {repr(line)}")
+ if file.filename.endswith(".c"):
+ file.lines.insert(0, SPDX_C)
+ elif file.filename.endswith(".h") or file.filename.endswith(".S"):
+ file.lines.insert(0, SPDX_H_S)
+ else:
+ raise RuntimeError(f"Unexpected file extension: {file.filename}")
+ file.write()
+
+
+
+ def go(self):
+ self._copy_source_lib()
+ self._copy_zstd_deps()
+ self._copy_mem()
+ self._hardwire_defines()
+ self._remove_excludes()
+ self._rewrite_includes()
+ self._replace_xxh64_prefix()
+ self._process_seds()
+ self._process_spdx()
+
+
+def parse_optional_pair(defines: [str]) -> [(str, Optional[str])]:
+ output = []
+ for define in defines:
+ parsed = define.split('=')
+ if len(parsed) == 1:
+ output.append((parsed[0], None))
+ elif len(parsed) == 2:
+ output.append((parsed[0], parsed[1]))
+ else:
+ raise RuntimeError(f"Bad define: {define}")
+ return output
+
+
+def parse_pair(rewritten_includes: [str]) -> [(str, str)]:
+ output = []
+ for rewritten_include in rewritten_includes:
+ parsed = rewritten_include.split('=')
+ if len(parsed) == 2:
+ output.append((parsed[0], parsed[1]))
+ else:
+ raise RuntimeError(f"Bad rewritten include: {rewritten_include}")
+ return output
+
+
+
+def main(name, args):
+ parser = argparse.ArgumentParser(prog=name)
+ parser.add_argument("--zstd-deps", default="zstd_deps.h", help="Zstd dependencies file")
+ parser.add_argument("--mem", default="mem.h", help="Memory module")
+ parser.add_argument("--source-lib", default="../../lib", help="Location of the zstd library")
+ parser.add_argument("--output-lib", default="./freestanding_lib", help="Where to output the freestanding zstd library")
+ parser.add_argument("--xxhash", default=None, help="Alternate external xxhash include e.g. --xxhash='<xxhash.h>'. If set xxhash is not included.")
+ parser.add_argument("--xxh64-state", default=None, help="Alternate XXH64 state type (excluding _) e.g. --xxh64-state='struct xxh64_state'")
+ parser.add_argument("--xxh64-prefix", default=None, help="Alternate XXH64 function prefix (excluding _) e.g. --xxh64-prefix=xxh64")
+ parser.add_argument("--rewrite-include", default=[], dest="rewritten_includes", action="append", help="Rewrite an include REGEX=NEW (e.g. '<stddef\\.h>=<linux/types.h>')")
+ parser.add_argument("--sed", default=[], dest="seds", action="append", help="Apply a sed replacement. Format: `s/REGEX/FORMAT/[g]`. REGEX is a Python regex. FORMAT is a Python format string formatted by the regex dict.")
+ parser.add_argument("--spdx", action="store_true", help="Add SPDX License Identifiers")
+ parser.add_argument("-D", "--define", default=[], dest="defs", action="append", help="Pre-define this macro (can be passed multiple times)")
+ parser.add_argument("-U", "--undefine", default=[], dest="undefs", action="append", help="Pre-undefine this macro (can be passed multiple times)")
+ parser.add_argument("-R", "--replace", default=[], dest="replaces", action="append", help="Pre-define this macro and replace the first ifndef block with its definition")
+ parser.add_argument("-E", "--exclude", default=[], dest="excludes", action="append", help="Exclude all lines between 'BEGIN <EXCLUDE>' and 'END <EXCLUDE>'")
+ args = parser.parse_args(args)
+
+ # Always remove threading
+ if "ZSTD_MULTITHREAD" not in args.undefs:
+ args.undefs.append("ZSTD_MULTITHREAD")
+
+ args.defs = parse_optional_pair(args.defs)
+ for name, _ in args.defs:
+ if name in args.undefs:
+ raise RuntimeError(f"{name} is both defined and undefined!")
+
+ # Always set tracing to 0
+ if "ZSTD_NO_TRACE" not in (arg[0] for arg in args.defs):
+ args.defs.append(("ZSTD_NO_TRACE", None))
+ args.defs.append(("ZSTD_TRACE", "0"))
+
+ args.replaces = parse_pair(args.replaces)
+ for name, _ in args.replaces:
+ if name in args.undefs or name in args.defs:
+ raise RuntimeError(f"{name} is both replaced and (un)defined!")
+
+ args.rewritten_includes = parse_pair(args.rewritten_includes)
+
+ external_xxhash = False
+ if args.xxhash is not None:
+ external_xxhash = True
+ args.rewritten_includes.append(('"(\\.\\./common/)?xxhash.h"', args.xxhash))
+
+ if args.xxh64_prefix is not None:
+ if not external_xxhash:
+ raise RuntimeError("--xxh64-prefix may only be used with --xxhash provided")
+
+ if args.xxh64_state is not None:
+ if not external_xxhash:
+ raise RuntimeError("--xxh64-state may only be used with --xxhash provided")
+
+ Freestanding(
+ args.zstd_deps,
+ args.mem,
+ args.source_lib,
+ args.output_lib,
+ external_xxhash,
+ args.xxh64_state,
+ args.xxh64_prefix,
+ args.rewritten_includes,
+ args.defs,
+ args.replaces,
+ args.undefs,
+ args.excludes,
+ args.seds,
+ args.spdx,
+ ).go()
+
+if __name__ == "__main__":
+ main(sys.argv[0], sys.argv[1:])