We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add lzma
As mentioned by @hoodmane in https://github.com/pyodide/pyodide/discussions/2930#discussioncomment-3316181

> Is there an issue open about lzma? What is our position on it again? That we want it but there is no emscripten port and we haven't gotten to it?

I think the main concern was the size increase for everyone vs few people actually needing it. Depending on the size maybe we could make it an unvendored stdlib package (or include by default if the size is negligible).

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `pyodide-build/pyodide_build/common.py`
Content:
```
1 import contextlib
2 import functools
3 import os
4 import subprocess
5 import sys
6 from collections.abc import Generator, Iterable, Iterator, Mapping
7 from pathlib import Path
8 
9 import tomli
10 from packaging.tags import Tag, compatible_tags, cpython_tags
11 from packaging.utils import parse_wheel_filename
12 
13 from .io import parse_package_config
14 
15 
16 def emscripten_version() -> str:
17     return get_make_flag("PYODIDE_EMSCRIPTEN_VERSION")
18 
19 
20 def platform() -> str:
21     emscripten_version = get_make_flag("PYODIDE_EMSCRIPTEN_VERSION")
22     version = emscripten_version.replace(".", "_")
23     return f"emscripten_{version}_wasm32"
24 
25 
26 def pyodide_tags() -> Iterator[Tag]:
27     """
28     Returns the sequence of tag triples for the Pyodide interpreter.
29 
30     The sequence is ordered in decreasing specificity.
31     """
32     PYMAJOR = get_make_flag("PYMAJOR")
33     PYMINOR = get_make_flag("PYMINOR")
34     PLATFORM = platform()
35     python_version = (int(PYMAJOR), int(PYMINOR))
36     yield from cpython_tags(platforms=[PLATFORM], python_version=python_version)
37     yield from compatible_tags(platforms=[PLATFORM], python_version=python_version)
38 
39 
40 def find_matching_wheels(wheel_paths: Iterable[Path]) -> Iterator[Path]:
41     """
42     Returns the sequence wheels whose tags match the Pyodide interpreter.
43 
44     Parameters
45     ----------
46     wheel_paths
47         A list of paths to wheels
48 
49     Returns
50     -------
51     The subset of wheel_paths that have tags that match the Pyodide interpreter.
52     """
53     wheel_paths = list(wheel_paths)
54     wheel_tags_list: list[frozenset[Tag]] = []
55     for wheel in wheel_paths:
56         _, _, _, tags = parse_wheel_filename(wheel.name)
57         wheel_tags_list.append(tags)
58     for supported_tag in pyodide_tags():
59         for wheel_path, wheel_tags in zip(wheel_paths, wheel_tags_list):
60             if supported_tag in wheel_tags:
61                 yield wheel_path
62 
63 
64 UNVENDORED_STDLIB_MODULES = {"test", "distutils"}
65 
66 ALWAYS_PACKAGES = {
67     "pyparsing",
68     "packaging",
69     "micropip",
70 }
71 
72 CORE_PACKAGES = {
73     "micropip",
74     "pyparsing",
75     "pytz",
76     "packaging",
77     "Jinja2",
78     "regex",
79     "fpcast-test",
80     "sharedlib-test-py",
81     "cpp-exceptions-test",
82     "ssl",
83     "pytest",
84     "tblib",
85 }
86 
87 CORE_SCIPY_PACKAGES = {
88     "numpy",
89     "scipy",
90     "pandas",
91     "matplotlib",
92     "scikit-learn",
93     "joblib",
94     "pytest",
95 }
96 
97 
98 def _parse_package_subset(query: str | None) -> set[str]:
99     """Parse the list of packages specified with PYODIDE_PACKAGES env var.
100 
101     Also add the list of mandatory packages: ["pyparsing", "packaging",
102     "micropip"]
103 
104     Supports following meta-packages,
105      - 'core': corresponds to packages needed to run the core test suite
106        {"micropip", "pyparsing", "pytz", "packaging", "Jinja2", "fpcast-test"}. This is the default option
107        if query is None.
108      - 'min-scipy-stack': includes the "core" meta-package as well as some of the
109        core packages from the scientific python stack and their dependencies:
110        {"numpy", "scipy", "pandas", "matplotlib", "scikit-learn", "joblib", "pytest"}.
111        This option is non exhaustive and is mainly intended to make build faster
112        while testing a diverse set of scientific packages.
113      - '*': corresponds to all packages (returns None)
114 
115     Note: None as input is equivalent to PYODIDE_PACKAGES being unset and leads
116     to only the core packages being built.
117 
118     Returns:
119       a set of package names to build or None (build all packages).
120     """
121     if query is None:
122         query = "core"
123 
124     packages = {el.strip() for el in query.split(",")}
125     packages.update(ALWAYS_PACKAGES)
126     packages.update(UNVENDORED_STDLIB_MODULES)
127     # handle meta-packages
128     if "core" in packages:
129         packages |= CORE_PACKAGES
130         packages.discard("core")
131     if "min-scipy-stack" in packages:
132         packages |= CORE_PACKAGES | CORE_SCIPY_PACKAGES
133         packages.discard("min-scipy-stack")
134 
135     # Hack to deal with the circular dependence between soupsieve and
136     # beautifulsoup4
137     if "beautifulsoup4" in packages:
138         packages.add("soupsieve")
139     packages.discard("")
140     return packages
141 
142 
143 def get_make_flag(name: str) -> str:
144     """Get flags from makefile.envs.
145 
146     For building packages we currently use:
147         SIDE_MODULE_LDFLAGS
148         SIDE_MODULE_CFLAGS
149         SIDE_MODULE_CXXFLAGS
150         TOOLSDIR
151     """
152     return get_make_environment_vars()[name]
153 
154 
155 def get_pyversion() -> str:
156     PYMAJOR = get_make_flag("PYMAJOR")
157     PYMINOR = get_make_flag("PYMINOR")
158     return f"python{PYMAJOR}.{PYMINOR}"
159 
160 
161 def get_hostsitepackages() -> str:
162     return get_make_flag("HOSTSITEPACKAGES")
163 
164 
165 @functools.cache
166 def get_make_environment_vars() -> dict[str, str]:
167     """Load environment variables from Makefile.envs
168 
169     This allows us to set all build vars in one place"""
170 
171     PYODIDE_ROOT = get_pyodide_root()
172     environment = {}
173     result = subprocess.run(
174         ["make", "-f", str(PYODIDE_ROOT / "Makefile.envs"), ".output_vars"],
175         capture_output=True,
176         text=True,
177     )
178     for line in result.stdout.splitlines():
179         equalPos = line.find("=")
180         if equalPos != -1:
181             varname = line[0:equalPos]
182             value = line[equalPos + 1 :]
183             value = value.strip("'").strip()
184             environment[varname] = value
185     return environment
186 
187 
188 def search_pyodide_root(curdir: str | Path, *, max_depth: int = 5) -> Path:
189     """
190     Recursively search for the root of the Pyodide repository,
191     by looking for the pyproject.toml file in the parent directories
192     which contains [tool.pyodide] section.
193     """
194 
195     # We want to include "curdir" in parent_dirs, so add a garbage suffix
196     parent_dirs = (Path(curdir) / "garbage").parents[:max_depth]
197 
198     for base in parent_dirs:
199         pyproject_file = base / "pyproject.toml"
200 
201         if not pyproject_file.is_file():
202             continue
203 
204         try:
205             with pyproject_file.open("rb") as f:
206                 configs = tomli.load(f)
207         except tomli.TOMLDecodeError:
208             raise ValueError(f"Could not parse {pyproject_file}.")
209 
210         if "tool" in configs and "pyodide" in configs["tool"]:
211             return base
212 
213     raise FileNotFoundError(
214         "Could not find Pyodide root directory. If you are not in the Pyodide directory, set `PYODIDE_ROOT=<pyodide-root-directory>`."
215     )
216 
217 
218 def init_environment() -> None:
219     if os.environ.get("__LOADED_PYODIDE_ENV"):
220         return
221     os.environ["__LOADED_PYODIDE_ENV"] = "1"
222     # If we are building docs, we don't need to know the PYODIDE_ROOT
223     if "sphinx" in sys.modules:
224         os.environ["PYODIDE_ROOT"] = ""
225 
226     if "PYODIDE_ROOT" in os.environ:
227         os.environ["PYODIDE_ROOT"] = str(Path(os.environ["PYODIDE_ROOT"]).resolve())
228     else:
229         os.environ["PYODIDE_ROOT"] = str(search_pyodide_root(os.getcwd()))
230 
231     os.environ.update(get_make_environment_vars())
232     try:
233         hostsitepackages = get_hostsitepackages()
234         pythonpath = [
235             hostsitepackages,
236         ]
237         os.environ["PYTHONPATH"] = ":".join(pythonpath)
238     except KeyError:
239         pass
240     os.environ["BASH_ENV"] = ""
241     get_unisolated_packages()
242 
243 
244 @functools.cache
245 def get_pyodide_root() -> Path:
246     init_environment()
247     return Path(os.environ["PYODIDE_ROOT"])
248 
249 
250 @functools.cache
251 def get_unisolated_packages() -> list[str]:
252     import json
253 
254     if "UNISOLATED_PACKAGES" in os.environ:
255         return json.loads(os.environ["UNISOLATED_PACKAGES"])
256     PYODIDE_ROOT = get_pyodide_root()
257     unisolated_file = PYODIDE_ROOT / "unisolated.txt"
258     if unisolated_file.exists():
259         # in xbuild env, read from file
260         unisolated_packages = unisolated_file.read_text().splitlines()
261     else:
262         unisolated_packages = []
263         for pkg in (PYODIDE_ROOT / "packages").glob("**/meta.yaml"):
264             config = parse_package_config(pkg, check=False)
265             if config.get("build", {}).get("cross-build-env", False):
266                 unisolated_packages.append(config["package"]["name"])
267         # TODO: remove setuptools_rust from this when they release the next version.
268         unisolated_packages.append("setuptools_rust")
269     os.environ["UNISOLATED_PACKAGES"] = json.dumps(unisolated_packages)
270     return unisolated_packages
271 
272 
273 @contextlib.contextmanager
274 def replace_env(build_env: Mapping[str, str]) -> Generator[None, None, None]:
275     old_environ = dict(os.environ)
276     os.environ.clear()
277     os.environ.update(build_env)
278     try:
279         yield
280     finally:
281         os.environ.clear()
282         os.environ.update(old_environ)
283
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/pyodide-build/pyodide_build/common.py b/pyodide-build/pyodide_build/common.py
--- a/pyodide-build/pyodide_build/common.py
+++ b/pyodide-build/pyodide_build/common.py
@@ -80,6 +80,7 @@
     "sharedlib-test-py",
     "cpp-exceptions-test",
     "ssl",
+    "lzma",
     "pytest",
     "tblib",
 }

{"golden_diff": "diff --git a/pyodide-build/pyodide_build/common.py b/pyodide-build/pyodide_build/common.py\n--- a/pyodide-build/pyodide_build/common.py\n+++ b/pyodide-build/pyodide_build/common.py\n@@ -80,6 +80,7 @@\n     \"sharedlib-test-py\",\n     \"cpp-exceptions-test\",\n     \"ssl\",\n+    \"lzma\",\n     \"pytest\",\n     \"tblib\",\n }\n", "issue": "Add lzma\nAs mentioned by @hoodmane in https://github.com/pyodide/pyodide/discussions/2930#discussioncomment-3316181\r\n\r\n> Is there an issue open about lzma? What is our position on it again? That we want it but there is no emscripten port and we haven't gotten to it?\r\n\r\nI think the main concern was the size increase for everyone vs few people actually needing it. Depending on the size maybe we could make it an unvendored stdlib package (or include by default if the size is negligible).\n", "before_files": [{"content": "import contextlib\nimport functools\nimport os\nimport subprocess\nimport sys\nfrom collections.abc import Generator, Iterable, Iterator, Mapping\nfrom pathlib import Path\n\nimport tomli\nfrom packaging.tags import Tag, compatible_tags, cpython_tags\nfrom packaging.utils import parse_wheel_filename\n\nfrom .io import parse_package_config\n\n\ndef emscripten_version() -> str:\n    return get_make_flag(\"PYODIDE_EMSCRIPTEN_VERSION\")\n\n\ndef platform() -> str:\n    emscripten_version = get_make_flag(\"PYODIDE_EMSCRIPTEN_VERSION\")\n    version = emscripten_version.replace(\".\", \"_\")\n    return f\"emscripten_{version}_wasm32\"\n\n\ndef pyodide_tags() -> Iterator[Tag]:\n    \"\"\"\n    Returns the sequence of tag triples for the Pyodide interpreter.\n\n    The sequence is ordered in decreasing specificity.\n    \"\"\"\n    PYMAJOR = get_make_flag(\"PYMAJOR\")\n    PYMINOR = get_make_flag(\"PYMINOR\")\n    PLATFORM = platform()\n    python_version = (int(PYMAJOR), int(PYMINOR))\n    yield from cpython_tags(platforms=[PLATFORM], python_version=python_version)\n    yield from compatible_tags(platforms=[PLATFORM], python_version=python_version)\n\n\ndef find_matching_wheels(wheel_paths: Iterable[Path]) -> Iterator[Path]:\n    \"\"\"\n    Returns the sequence wheels whose tags match the Pyodide interpreter.\n\n    Parameters\n    ----------\n    wheel_paths\n        A list of paths to wheels\n\n    Returns\n    -------\n    The subset of wheel_paths that have tags that match the Pyodide interpreter.\n    \"\"\"\n    wheel_paths = list(wheel_paths)\n    wheel_tags_list: list[frozenset[Tag]] = []\n    for wheel in wheel_paths:\n        _, _, _, tags = parse_wheel_filename(wheel.name)\n        wheel_tags_list.append(tags)\n    for supported_tag in pyodide_tags():\n        for wheel_path, wheel_tags in zip(wheel_paths, wheel_tags_list):\n            if supported_tag in wheel_tags:\n                yield wheel_path\n\n\nUNVENDORED_STDLIB_MODULES = {\"test\", \"distutils\"}\n\nALWAYS_PACKAGES = {\n    \"pyparsing\",\n    \"packaging\",\n    \"micropip\",\n}\n\nCORE_PACKAGES = {\n    \"micropip\",\n    \"pyparsing\",\n    \"pytz\",\n    \"packaging\",\n    \"Jinja2\",\n    \"regex\",\n    \"fpcast-test\",\n    \"sharedlib-test-py\",\n    \"cpp-exceptions-test\",\n    \"ssl\",\n    \"pytest\",\n    \"tblib\",\n}\n\nCORE_SCIPY_PACKAGES = {\n    \"numpy\",\n    \"scipy\",\n    \"pandas\",\n    \"matplotlib\",\n    \"scikit-learn\",\n    \"joblib\",\n    \"pytest\",\n}\n\n\ndef _parse_package_subset(query: str | None) -> set[str]:\n    \"\"\"Parse the list of packages specified with PYODIDE_PACKAGES env var.\n\n    Also add the list of mandatory packages: [\"pyparsing\", \"packaging\",\n    \"micropip\"]\n\n    Supports following meta-packages,\n     - 'core': corresponds to packages needed to run the core test suite\n       {\"micropip\", \"pyparsing\", \"pytz\", \"packaging\", \"Jinja2\", \"fpcast-test\"}. This is the default option\n       if query is None.\n     - 'min-scipy-stack': includes the \"core\" meta-package as well as some of the\n       core packages from the scientific python stack and their dependencies:\n       {\"numpy\", \"scipy\", \"pandas\", \"matplotlib\", \"scikit-learn\", \"joblib\", \"pytest\"}.\n       This option is non exhaustive and is mainly intended to make build faster\n       while testing a diverse set of scientific packages.\n     - '*': corresponds to all packages (returns None)\n\n    Note: None as input is equivalent to PYODIDE_PACKAGES being unset and leads\n    to only the core packages being built.\n\n    Returns:\n      a set of package names to build or None (build all packages).\n    \"\"\"\n    if query is None:\n        query = \"core\"\n\n    packages = {el.strip() for el in query.split(\",\")}\n    packages.update(ALWAYS_PACKAGES)\n    packages.update(UNVENDORED_STDLIB_MODULES)\n    # handle meta-packages\n    if \"core\" in packages:\n        packages |= CORE_PACKAGES\n        packages.discard(\"core\")\n    if \"min-scipy-stack\" in packages:\n        packages |= CORE_PACKAGES | CORE_SCIPY_PACKAGES\n        packages.discard(\"min-scipy-stack\")\n\n    # Hack to deal with the circular dependence between soupsieve and\n    # beautifulsoup4\n    if \"beautifulsoup4\" in packages:\n        packages.add(\"soupsieve\")\n    packages.discard(\"\")\n    return packages\n\n\ndef get_make_flag(name: str) -> str:\n    \"\"\"Get flags from makefile.envs.\n\n    For building packages we currently use:\n        SIDE_MODULE_LDFLAGS\n        SIDE_MODULE_CFLAGS\n        SIDE_MODULE_CXXFLAGS\n        TOOLSDIR\n    \"\"\"\n    return get_make_environment_vars()[name]\n\n\ndef get_pyversion() -> str:\n    PYMAJOR = get_make_flag(\"PYMAJOR\")\n    PYMINOR = get_make_flag(\"PYMINOR\")\n    return f\"python{PYMAJOR}.{PYMINOR}\"\n\n\ndef get_hostsitepackages() -> str:\n    return get_make_flag(\"HOSTSITEPACKAGES\")\n\n\n@functools.cache\ndef get_make_environment_vars() -> dict[str, str]:\n    \"\"\"Load environment variables from Makefile.envs\n\n    This allows us to set all build vars in one place\"\"\"\n\n    PYODIDE_ROOT = get_pyodide_root()\n    environment = {}\n    result = subprocess.run(\n        [\"make\", \"-f\", str(PYODIDE_ROOT / \"Makefile.envs\"), \".output_vars\"],\n        capture_output=True,\n        text=True,\n    )\n    for line in result.stdout.splitlines():\n        equalPos = line.find(\"=\")\n        if equalPos != -1:\n            varname = line[0:equalPos]\n            value = line[equalPos + 1 :]\n            value = value.strip(\"'\").strip()\n            environment[varname] = value\n    return environment\n\n\ndef search_pyodide_root(curdir: str | Path, *, max_depth: int = 5) -> Path:\n    \"\"\"\n    Recursively search for the root of the Pyodide repository,\n    by looking for the pyproject.toml file in the parent directories\n    which contains [tool.pyodide] section.\n    \"\"\"\n\n    # We want to include \"curdir\" in parent_dirs, so add a garbage suffix\n    parent_dirs = (Path(curdir) / \"garbage\").parents[:max_depth]\n\n    for base in parent_dirs:\n        pyproject_file = base / \"pyproject.toml\"\n\n        if not pyproject_file.is_file():\n            continue\n\n        try:\n            with pyproject_file.open(\"rb\") as f:\n                configs = tomli.load(f)\n        except tomli.TOMLDecodeError:\n            raise ValueError(f\"Could not parse {pyproject_file}.\")\n\n        if \"tool\" in configs and \"pyodide\" in configs[\"tool\"]:\n            return base\n\n    raise FileNotFoundError(\n        \"Could not find Pyodide root directory. If you are not in the Pyodide directory, set `PYODIDE_ROOT=<pyodide-root-directory>`.\"\n    )\n\n\ndef init_environment() -> None:\n    if os.environ.get(\"__LOADED_PYODIDE_ENV\"):\n        return\n    os.environ[\"__LOADED_PYODIDE_ENV\"] = \"1\"\n    # If we are building docs, we don't need to know the PYODIDE_ROOT\n    if \"sphinx\" in sys.modules:\n        os.environ[\"PYODIDE_ROOT\"] = \"\"\n\n    if \"PYODIDE_ROOT\" in os.environ:\n        os.environ[\"PYODIDE_ROOT\"] = str(Path(os.environ[\"PYODIDE_ROOT\"]).resolve())\n    else:\n        os.environ[\"PYODIDE_ROOT\"] = str(search_pyodide_root(os.getcwd()))\n\n    os.environ.update(get_make_environment_vars())\n    try:\n        hostsitepackages = get_hostsitepackages()\n        pythonpath = [\n            hostsitepackages,\n        ]\n        os.environ[\"PYTHONPATH\"] = \":\".join(pythonpath)\n    except KeyError:\n        pass\n    os.environ[\"BASH_ENV\"] = \"\"\n    get_unisolated_packages()\n\n\n@functools.cache\ndef get_pyodide_root() -> Path:\n    init_environment()\n    return Path(os.environ[\"PYODIDE_ROOT\"])\n\n\n@functools.cache\ndef get_unisolated_packages() -> list[str]:\n    import json\n\n    if \"UNISOLATED_PACKAGES\" in os.environ:\n        return json.loads(os.environ[\"UNISOLATED_PACKAGES\"])\n    PYODIDE_ROOT = get_pyodide_root()\n    unisolated_file = PYODIDE_ROOT / \"unisolated.txt\"\n    if unisolated_file.exists():\n        # in xbuild env, read from file\n        unisolated_packages = unisolated_file.read_text().splitlines()\n    else:\n        unisolated_packages = []\n        for pkg in (PYODIDE_ROOT / \"packages\").glob(\"**/meta.yaml\"):\n            config = parse_package_config(pkg, check=False)\n            if config.get(\"build\", {}).get(\"cross-build-env\", False):\n                unisolated_packages.append(config[\"package\"][\"name\"])\n        # TODO: remove setuptools_rust from this when they release the next version.\n        unisolated_packages.append(\"setuptools_rust\")\n    os.environ[\"UNISOLATED_PACKAGES\"] = json.dumps(unisolated_packages)\n    return unisolated_packages\n\n\n@contextlib.contextmanager\ndef replace_env(build_env: Mapping[str, str]) -> Generator[None, None, None]:\n    old_environ = dict(os.environ)\n    os.environ.clear()\n    os.environ.update(build_env)\n    try:\n        yield\n    finally:\n        os.environ.clear()\n        os.environ.update(old_environ)\n", "path": "pyodide-build/pyodide_build/common.py"}], "after_files": [{"content": "import contextlib\nimport functools\nimport os\nimport subprocess\nimport sys\nfrom collections.abc import Generator, Iterable, Iterator, Mapping\nfrom pathlib import Path\n\nimport tomli\nfrom packaging.tags import Tag, compatible_tags, cpython_tags\nfrom packaging.utils import parse_wheel_filename\n\nfrom .io import parse_package_config\n\n\ndef emscripten_version() -> str:\n    return get_make_flag(\"PYODIDE_EMSCRIPTEN_VERSION\")\n\n\ndef platform() -> str:\n    emscripten_version = get_make_flag(\"PYODIDE_EMSCRIPTEN_VERSION\")\n    version = emscripten_version.replace(\".\", \"_\")\n    return f\"emscripten_{version}_wasm32\"\n\n\ndef pyodide_tags() -> Iterator[Tag]:\n    \"\"\"\n    Returns the sequence of tag triples for the Pyodide interpreter.\n\n    The sequence is ordered in decreasing specificity.\n    \"\"\"\n    PYMAJOR = get_make_flag(\"PYMAJOR\")\n    PYMINOR = get_make_flag(\"PYMINOR\")\n    PLATFORM = platform()\n    python_version = (int(PYMAJOR), int(PYMINOR))\n    yield from cpython_tags(platforms=[PLATFORM], python_version=python_version)\n    yield from compatible_tags(platforms=[PLATFORM], python_version=python_version)\n\n\ndef find_matching_wheels(wheel_paths: Iterable[Path]) -> Iterator[Path]:\n    \"\"\"\n    Returns the sequence wheels whose tags match the Pyodide interpreter.\n\n    Parameters\n    ----------\n    wheel_paths\n        A list of paths to wheels\n\n    Returns\n    -------\n    The subset of wheel_paths that have tags that match the Pyodide interpreter.\n    \"\"\"\n    wheel_paths = list(wheel_paths)\n    wheel_tags_list: list[frozenset[Tag]] = []\n    for wheel in wheel_paths:\n        _, _, _, tags = parse_wheel_filename(wheel.name)\n        wheel_tags_list.append(tags)\n    for supported_tag in pyodide_tags():\n        for wheel_path, wheel_tags in zip(wheel_paths, wheel_tags_list):\n            if supported_tag in wheel_tags:\n                yield wheel_path\n\n\nUNVENDORED_STDLIB_MODULES = {\"test\", \"distutils\"}\n\nALWAYS_PACKAGES = {\n    \"pyparsing\",\n    \"packaging\",\n    \"micropip\",\n}\n\nCORE_PACKAGES = {\n    \"micropip\",\n    \"pyparsing\",\n    \"pytz\",\n    \"packaging\",\n    \"Jinja2\",\n    \"regex\",\n    \"fpcast-test\",\n    \"sharedlib-test-py\",\n    \"cpp-exceptions-test\",\n    \"ssl\",\n    \"lzma\",\n    \"pytest\",\n    \"tblib\",\n}\n\nCORE_SCIPY_PACKAGES = {\n    \"numpy\",\n    \"scipy\",\n    \"pandas\",\n    \"matplotlib\",\n    \"scikit-learn\",\n    \"joblib\",\n    \"pytest\",\n}\n\n\ndef _parse_package_subset(query: str | None) -> set[str]:\n    \"\"\"Parse the list of packages specified with PYODIDE_PACKAGES env var.\n\n    Also add the list of mandatory packages: [\"pyparsing\", \"packaging\",\n    \"micropip\"]\n\n    Supports following meta-packages,\n     - 'core': corresponds to packages needed to run the core test suite\n       {\"micropip\", \"pyparsing\", \"pytz\", \"packaging\", \"Jinja2\", \"fpcast-test\"}. This is the default option\n       if query is None.\n     - 'min-scipy-stack': includes the \"core\" meta-package as well as some of the\n       core packages from the scientific python stack and their dependencies:\n       {\"numpy\", \"scipy\", \"pandas\", \"matplotlib\", \"scikit-learn\", \"joblib\", \"pytest\"}.\n       This option is non exhaustive and is mainly intended to make build faster\n       while testing a diverse set of scientific packages.\n     - '*': corresponds to all packages (returns None)\n\n    Note: None as input is equivalent to PYODIDE_PACKAGES being unset and leads\n    to only the core packages being built.\n\n    Returns:\n      a set of package names to build or None (build all packages).\n    \"\"\"\n    if query is None:\n        query = \"core\"\n\n    packages = {el.strip() for el in query.split(\",\")}\n    packages.update(ALWAYS_PACKAGES)\n    packages.update(UNVENDORED_STDLIB_MODULES)\n    # handle meta-packages\n    if \"core\" in packages:\n        packages |= CORE_PACKAGES\n        packages.discard(\"core\")\n    if \"min-scipy-stack\" in packages:\n        packages |= CORE_PACKAGES | CORE_SCIPY_PACKAGES\n        packages.discard(\"min-scipy-stack\")\n\n    # Hack to deal with the circular dependence between soupsieve and\n    # beautifulsoup4\n    if \"beautifulsoup4\" in packages:\n        packages.add(\"soupsieve\")\n    packages.discard(\"\")\n    return packages\n\n\ndef get_make_flag(name: str) -> str:\n    \"\"\"Get flags from makefile.envs.\n\n    For building packages we currently use:\n        SIDE_MODULE_LDFLAGS\n        SIDE_MODULE_CFLAGS\n        SIDE_MODULE_CXXFLAGS\n        TOOLSDIR\n    \"\"\"\n    return get_make_environment_vars()[name]\n\n\ndef get_pyversion() -> str:\n    PYMAJOR = get_make_flag(\"PYMAJOR\")\n    PYMINOR = get_make_flag(\"PYMINOR\")\n    return f\"python{PYMAJOR}.{PYMINOR}\"\n\n\ndef get_hostsitepackages() -> str:\n    return get_make_flag(\"HOSTSITEPACKAGES\")\n\n\n@functools.cache\ndef get_make_environment_vars() -> dict[str, str]:\n    \"\"\"Load environment variables from Makefile.envs\n\n    This allows us to set all build vars in one place\"\"\"\n\n    PYODIDE_ROOT = get_pyodide_root()\n    environment = {}\n    result = subprocess.run(\n        [\"make\", \"-f\", str(PYODIDE_ROOT / \"Makefile.envs\"), \".output_vars\"],\n        capture_output=True,\n        text=True,\n    )\n    for line in result.stdout.splitlines():\n        equalPos = line.find(\"=\")\n        if equalPos != -1:\n            varname = line[0:equalPos]\n            value = line[equalPos + 1 :]\n            value = value.strip(\"'\").strip()\n            environment[varname] = value\n    return environment\n\n\ndef search_pyodide_root(curdir: str | Path, *, max_depth: int = 5) -> Path:\n    \"\"\"\n    Recursively search for the root of the Pyodide repository,\n    by looking for the pyproject.toml file in the parent directories\n    which contains [tool.pyodide] section.\n    \"\"\"\n\n    # We want to include \"curdir\" in parent_dirs, so add a garbage suffix\n    parent_dirs = (Path(curdir) / \"garbage\").parents[:max_depth]\n\n    for base in parent_dirs:\n        pyproject_file = base / \"pyproject.toml\"\n\n        if not pyproject_file.is_file():\n            continue\n\n        try:\n            with pyproject_file.open(\"rb\") as f:\n                configs = tomli.load(f)\n        except tomli.TOMLDecodeError:\n            raise ValueError(f\"Could not parse {pyproject_file}.\")\n\n        if \"tool\" in configs and \"pyodide\" in configs[\"tool\"]:\n            return base\n\n    raise FileNotFoundError(\n        \"Could not find Pyodide root directory. If you are not in the Pyodide directory, set `PYODIDE_ROOT=<pyodide-root-directory>`.\"\n    )\n\n\ndef init_environment() -> None:\n    if os.environ.get(\"__LOADED_PYODIDE_ENV\"):\n        return\n    os.environ[\"__LOADED_PYODIDE_ENV\"] = \"1\"\n    # If we are building docs, we don't need to know the PYODIDE_ROOT\n    if \"sphinx\" in sys.modules:\n        os.environ[\"PYODIDE_ROOT\"] = \"\"\n\n    if \"PYODIDE_ROOT\" in os.environ:\n        os.environ[\"PYODIDE_ROOT\"] = str(Path(os.environ[\"PYODIDE_ROOT\"]).resolve())\n    else:\n        os.environ[\"PYODIDE_ROOT\"] = str(search_pyodide_root(os.getcwd()))\n\n    os.environ.update(get_make_environment_vars())\n    try:\n        hostsitepackages = get_hostsitepackages()\n        pythonpath = [\n            hostsitepackages,\n        ]\n        os.environ[\"PYTHONPATH\"] = \":\".join(pythonpath)\n    except KeyError:\n        pass\n    os.environ[\"BASH_ENV\"] = \"\"\n    get_unisolated_packages()\n\n\n@functools.cache\ndef get_pyodide_root() -> Path:\n    init_environment()\n    return Path(os.environ[\"PYODIDE_ROOT\"])\n\n\n@functools.cache\ndef get_unisolated_packages() -> list[str]:\n    import json\n\n    if \"UNISOLATED_PACKAGES\" in os.environ:\n        return json.loads(os.environ[\"UNISOLATED_PACKAGES\"])\n    PYODIDE_ROOT = get_pyodide_root()\n    unisolated_file = PYODIDE_ROOT / \"unisolated.txt\"\n    if unisolated_file.exists():\n        # in xbuild env, read from file\n        unisolated_packages = unisolated_file.read_text().splitlines()\n    else:\n        unisolated_packages = []\n        for pkg in (PYODIDE_ROOT / \"packages\").glob(\"**/meta.yaml\"):\n            config = parse_package_config(pkg, check=False)\n            if config.get(\"build\", {}).get(\"cross-build-env\", False):\n                unisolated_packages.append(config[\"package\"][\"name\"])\n        # TODO: remove setuptools_rust from this when they release the next version.\n        unisolated_packages.append(\"setuptools_rust\")\n    os.environ[\"UNISOLATED_PACKAGES\"] = json.dumps(unisolated_packages)\n    return unisolated_packages\n\n\n@contextlib.contextmanager\ndef replace_env(build_env: Mapping[str, str]) -> Generator[None, None, None]:\n    old_environ = dict(os.environ)\n    os.environ.clear()\n    os.environ.update(build_env)\n    try:\n        yield\n    finally:\n        os.environ.clear()\n        os.environ.update(old_environ)\n", "path": "pyodide-build/pyodide_build/common.py"}]}