Skip to content

Commit 958a7c3

Browse files
committed
refactor: use single entry for SDist builds
Signed-off-by: Henry Schreiner <[email protected]>
1 parent 91fae82 commit 958a7c3

File tree

11 files changed

+103
-141
lines changed

11 files changed

+103
-141
lines changed

cibuildwheel/__main__.py

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
import shutil
44
import sys
5+
import tarfile
6+
import tempfile
57
import textwrap
68
from pathlib import Path
79
from tempfile import mkdtemp
@@ -20,15 +22,14 @@
2022
CIBW_CACHE_PATH,
2123
BuildSelector,
2224
Unbuffered,
25+
chdir,
2326
detect_ci_provider,
27+
format_safe,
2428
)
2529

2630

2731
def main() -> None:
28-
platform: PlatformName
29-
3032
parser = argparse.ArgumentParser(
31-
prog="cibuildwheel",
3233
description="Build wheels for all the platforms.",
3334
epilog="""
3435
Most options are supplied via environment variables or in
@@ -66,6 +67,7 @@ def main() -> None:
6667

6768
parser.add_argument(
6869
"--output-dir",
70+
type=Path,
6971
help="Destination folder for the wheels. Default: wheelhouse.",
7072
)
7173

@@ -74,19 +76,24 @@ def main() -> None:
7476
default="",
7577
help="""
7678
TOML config file. Default: "", meaning {package}/pyproject.toml,
77-
if it exists.
79+
if it exists. To refer to a project inside your project, use {package}
80+
or {project}.
7881
""",
7982
)
8083

8184
parser.add_argument(
8285
"package_dir",
83-
default=".",
86+
default=Path("."),
87+
type=Path,
8488
nargs="?",
8589
help="""
86-
Path to the package that you want wheels for. Must be a subdirectory of
87-
the working directory. When set, the working directory is still
88-
considered the 'project' and is copied into the Docker container on
89-
Linux. Default: the working directory.
90+
Path to the package that you want wheels for. Must be a
91+
subdirectory of the working directory. When set, the working
92+
directory is still considered the 'project' and is copied into the
93+
Docker container on Linux. Default: the working directory. This can
94+
also be a tar.gz file - if it is, then --config-file and
95+
--output-dir are relative to the current directory, and other paths
96+
are relative to the expanded SDist directory.
9097
""",
9198
)
9299

@@ -110,6 +117,50 @@ def main() -> None:
110117

111118
args = parser.parse_args(namespace=CommandLineArguments())
112119

120+
# These are always relative to the base directory, even in SDist builds
121+
args.package_dir = args.package_dir.resolve()
122+
args.output_dir = Path(
123+
args.output_dir
124+
if args.output_dir is not None
125+
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
126+
).resolve()
127+
128+
# Standard builds if a directory or non-existent path is given
129+
if not args.package_dir.is_file() and not args.package_dir.name.endswith("tar.gz"):
130+
build_in_directory(args)
131+
return
132+
133+
if not args.package_dir.name.endswith("tar.gz"):
134+
raise SystemExit("Must be a tar.gz file if a file is given.")
135+
136+
# Tarfile builds require extraction and changing the directory
137+
with tempfile.TemporaryDirectory(prefix="cibw-sdist-") as temp_dir_str:
138+
temp_dir = Path(temp_dir_str)
139+
with tarfile.open(args.package_dir) as tar:
140+
tar.extractall(path=temp_dir)
141+
142+
# The extract directory is now the project dir
143+
try:
144+
(project_dir,) = temp_dir.iterdir()
145+
except ValueError:
146+
raise SystemExit("invalid sdist: didn't contain a single dir") from None
147+
148+
args.package_dir = project_dir.resolve()
149+
150+
if args.config_file:
151+
# expand the placeholders if they're used
152+
config_file_path = format_safe(
153+
args.config_file,
154+
project=project_dir,
155+
package=project_dir,
156+
)
157+
args.config_file = str(Path(config_file_path).resolve())
158+
159+
with chdir(temp_dir):
160+
build_in_directory(args)
161+
162+
163+
def build_in_directory(args: CommandLineArguments) -> None:
113164
if args.platform != "auto":
114165
platform = args.platform
115166
else:

cibuildwheel/from_sdist.py

Lines changed: 0 additions & 103 deletions
This file was deleted.

cibuildwheel/options.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
class CommandLineArguments:
4747
platform: Literal["auto", "linux", "macos", "windows"]
4848
archs: Optional[str]
49-
output_dir: Optional[str]
49+
output_dir: Optional[Path]
5050
config_file: str
51-
package_dir: str
51+
package_dir: Path
5252
print_build_identifiers: bool
5353
allow_empty: bool
5454
prerelease_pythons: bool
@@ -361,12 +361,9 @@ def package_requires_python_str(self) -> Optional[str]:
361361
@property
362362
def globals(self) -> GlobalOptions:
363363
args = self.command_line_arguments
364-
package_dir = Path(args.package_dir)
365-
output_dir = Path(
366-
args.output_dir
367-
if args.output_dir is not None
368-
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
369-
)
364+
assert args.output_dir is not None, "Must be resolved"
365+
package_dir = args.package_dir
366+
output_dir = args.output_dir
370367

371368
build_config = self.reader.get("build", env_plat=False, sep=" ") or "*"
372369
skip_config = self.reader.get("skip", env_plat=False, sep=" ")

cibuildwheel/util.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@
1919
Any,
2020
ClassVar,
2121
Dict,
22+
Generator,
2223
Iterable,
23-
Iterator,
2424
List,
2525
NamedTuple,
2626
Optional,
2727
Sequence,
2828
TextIO,
29+
Union,
2930
cast,
3031
overload,
3132
)
@@ -58,6 +59,7 @@
5859
"selector_matches",
5960
"strtobool",
6061
"cached_property",
62+
"chdir",
6163
]
6264

6365
resources_dir: Final = Path(__file__).parent / "resources"
@@ -414,7 +416,7 @@ def unwrap(text: str) -> str:
414416

415417

416418
@contextlib.contextmanager
417-
def print_new_wheels(msg: str, output_dir: Path) -> Iterator[None]:
419+
def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
418420
"""
419421
Prints the new items in a directory upon exiting. The message to display
420422
can include {n} for number of wheels, {s} for total number of seconds,
@@ -570,3 +572,16 @@ def virtualenv(
570572
from functools import cached_property
571573
else:
572574
from .functools_cached_property_38 import cached_property
575+
576+
577+
# Can be replaced by contextlib.chdir in Python 3.11
578+
@contextlib.contextmanager
579+
def chdir(new_path: Union[Path, str]) -> Generator[None, None, None]:
580+
"""Non thread-safe context manager to change the current working directory."""
581+
582+
cwd = os.getcwd()
583+
try:
584+
os.chdir(new_path)
585+
yield
586+
finally:
587+
os.chdir(cwd)

setup.cfg

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ include =
5151
[options.entry_points]
5252
console_scripts =
5353
cibuildwheel = cibuildwheel.__main__:main
54-
cibuildwheel-from-sdist = cibuildwheel.from_sdist:main
5554

5655
[options.package_data]
5756
cibuildwheel = resources/*

test/test_from_sdist.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ def make_sdist(project: TestProject, working_dir: Path) -> Path:
1818

1919
sdist_dir = working_dir / "sdist"
2020
subprocess.run(
21-
[sys.executable, "-m", "build", "--sdist", "--outdir", sdist_dir, project_dir], check=True
21+
[sys.executable, "-m", "build", "--sdist", "--outdir", str(sdist_dir), str(project_dir)],
22+
check=True,
2223
)
2324

2425
return next(sdist_dir.glob("*.tar.gz"))
@@ -35,11 +36,11 @@ def cibuildwheel_from_sdist_run(sdist_path, add_env=None, config_file=None):
3536
[
3637
sys.executable,
3738
"-m",
38-
"cibuildwheel.from_sdist",
39+
"cibuildwheel",
3940
*(["--config-file", config_file] if config_file else []),
4041
"--output-dir",
41-
tmp_output_dir,
42-
sdist_path,
42+
str(tmp_output_dir),
43+
str(sdist_path),
4344
],
4445
env=env,
4546
check=True,
@@ -92,7 +93,7 @@ def test_external_config_file_argument(tmp_path, capfd):
9293
actual_wheels = cibuildwheel_from_sdist_run(
9394
sdist_path,
9495
add_env={"CIBW_BUILD": "cp39-*"},
95-
config_file=config_file,
96+
config_file=str(config_file),
9697
)
9798

9899
# check that the expected wheels are produced
@@ -186,8 +187,8 @@ def test_argument_passthrough(tmp_path, capfd):
186187
[
187188
sys.executable,
188189
"-m",
189-
"cibuildwheel.from_sdist",
190-
sdist_path,
190+
"cibuildwheel",
191+
str(sdist_path),
191192
"--platform",
192193
"linux",
193194
"--archs",

unit_test/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def fake_package_dir(monkeypatch):
3232
real_path_exists = Path.exists
3333

3434
def mock_path_exists(path):
35-
if path == MOCK_PACKAGE_DIR / "setup.py":
35+
if str(path).endswith(str(MOCK_PACKAGE_DIR / "setup.py")):
3636
return True
3737
else:
3838
return real_path_exists(path)

unit_test/main_tests/main_options_test.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):
2424

2525
main()
2626

27-
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR
27+
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve()
2828

2929

3030
def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
3131
main()
3232

33-
assert intercepted_build_args.args[0].globals.output_dir == Path("wheelhouse")
33+
assert intercepted_build_args.args[0].globals.output_dir == Path("wheelhouse").resolve()
3434

3535

3636
@pytest.mark.parametrize("also_set_environment", [False, True])
@@ -43,7 +43,7 @@ def test_output_dir_argument(also_set_environment, platform, intercepted_build_a
4343

4444
main()
4545

46-
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR
46+
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve()
4747

4848

4949
def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_empty):

0 commit comments

Comments
 (0)