Skip to content

Commit

Permalink
refactor: use single entry for SDist builds
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
  • Loading branch information
henryiii committed Apr 27, 2022
1 parent 91fae82 commit f6e4420
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 140 deletions.
69 changes: 60 additions & 9 deletions cibuildwheel/__main__.py
Expand Up @@ -2,6 +2,8 @@
import os
import shutil
import sys
import tarfile
import tempfile
import textwrap
from pathlib import Path
from tempfile import mkdtemp
Expand All @@ -20,15 +22,14 @@
CIBW_CACHE_PATH,
BuildSelector,
Unbuffered,
chdir,
detect_ci_provider,
format_safe,
)


def main() -> None:
platform: PlatformName

parser = argparse.ArgumentParser(
prog="cibuildwheel",
description="Build wheels for all the platforms.",
epilog="""
Most options are supplied via environment variables or in
Expand Down Expand Up @@ -66,6 +67,7 @@ def main() -> None:

parser.add_argument(
"--output-dir",
type=Path,
help="Destination folder for the wheels. Default: wheelhouse.",
)

Expand All @@ -74,19 +76,24 @@ def main() -> None:
default="",
help="""
TOML config file. Default: "", meaning {package}/pyproject.toml,
if it exists.
if it exists. To refer to a project inside your project, use {package}
or {project}.
""",
)

parser.add_argument(
"package_dir",
default=".",
default=Path("."),
type=Path,
nargs="?",
help="""
Path to the package that you want wheels for. Must be a subdirectory of
the working directory. When set, the working directory is still
considered the 'project' and is copied into the Docker container on
Linux. Default: the working directory.
Path to the package that you want wheels for. Must be a
subdirectory of the working directory. When set, the working
directory is still considered the 'project' and is copied into the
Docker container on Linux. Default: the working directory. This can
also be a tar.gz file - if it is, then --config-file and
--output-dir are relative to the current directory, and other paths
are relative to the expanded SDist directory.
""",
)

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

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

# These are always relative to the base directory, even in SDist builds
args.package_dir = args.package_dir.resolve()
args.output_dir = Path(
args.output_dir
if args.output_dir is not None
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
).resolve()

# Standard builds if a directory or non-existent path is given
if not args.package_dir.is_file() and not args.package_dir.name.endswith("tar.gz"):
build_in_directory(args)
return

if not args.package_dir.name.endswith("tar.gz"):
raise SystemExit("Must be a tar.gz file if a file is given.")

# Tarfile builds require extraction and changing the directory
with tempfile.TemporaryDirectory(prefix="cibw-sdist-") as temp_dir_str:
temp_dir = Path(temp_dir_str)
with tarfile.open(args.package_dir) as tar:
tar.extractall(path=temp_dir)

# The extract directory is now the project dir
try:
(project_dir,) = temp_dir.iterdir()
except ValueError:
raise SystemExit("invalid sdist: didn't contain a single dir") from None

args.package_dir = project_dir.resolve()

if args.config_file:
# expand the placeholders if they're used
config_file_path = format_safe(
args.config_file,
project=project_dir,
package=project_dir,
)
args.config_file = str(Path(config_file_path).resolve())

with chdir(temp_dir):
build_in_directory(args)


def build_in_directory(args: CommandLineArguments) -> None:
if args.platform != "auto":
platform = args.platform
else:
Expand Down
103 changes: 0 additions & 103 deletions cibuildwheel/from_sdist.py

This file was deleted.

13 changes: 5 additions & 8 deletions cibuildwheel/options.py
Expand Up @@ -46,9 +46,9 @@
class CommandLineArguments:
platform: Literal["auto", "linux", "macos", "windows"]
archs: Optional[str]
output_dir: Optional[str]
output_dir: Optional[Path]
config_file: str
package_dir: str
package_dir: Path
print_build_identifiers: bool
allow_empty: bool
prerelease_pythons: bool
Expand Down Expand Up @@ -361,12 +361,9 @@ def package_requires_python_str(self) -> Optional[str]:
@property
def globals(self) -> GlobalOptions:
args = self.command_line_arguments
package_dir = Path(args.package_dir)
output_dir = Path(
args.output_dir
if args.output_dir is not None
else os.environ.get("CIBW_OUTPUT_DIR", "wheelhouse")
)
assert args.output_dir is not None, "Must be resolved"
package_dir = args.package_dir
output_dir = args.output_dir

build_config = self.reader.get("build", env_plat=False, sep=" ") or "*"
skip_config = self.reader.get("skip", env_plat=False, sep=" ")
Expand Down
19 changes: 17 additions & 2 deletions cibuildwheel/util.py
Expand Up @@ -19,13 +19,14 @@
Any,
ClassVar,
Dict,
Generator,
Iterable,
Iterator,
List,
NamedTuple,
Optional,
Sequence,
TextIO,
Union,
cast,
overload,
)
Expand Down Expand Up @@ -58,6 +59,7 @@
"selector_matches",
"strtobool",
"cached_property",
"chdir",
]

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


@contextlib.contextmanager
def print_new_wheels(msg: str, output_dir: Path) -> Iterator[None]:
def print_new_wheels(msg: str, output_dir: Path) -> Generator[None, None, None]:
"""
Prints the new items in a directory upon exiting. The message to display
can include {n} for number of wheels, {s} for total number of seconds,
Expand Down Expand Up @@ -570,3 +572,16 @@ def virtualenv(
from functools import cached_property
else:
from .functools_cached_property_38 import cached_property


# Can be replaced by contextlib.chdir in Python 3.11
@contextlib.contextmanager
def chdir(new_path: Union[Path, str]) -> Generator[None, None, None]:
"""Non thread-safe context manager to change the current working directory."""

cwd = os.getcwd()
try:
os.chdir(new_path)
yield
finally:
os.chdir(cwd)
1 change: 0 additions & 1 deletion setup.cfg
Expand Up @@ -51,7 +51,6 @@ include =
[options.entry_points]
console_scripts =
cibuildwheel = cibuildwheel.__main__:main
cibuildwheel-from-sdist = cibuildwheel.from_sdist:main

[options.package_data]
cibuildwheel = resources/*
Expand Down
13 changes: 7 additions & 6 deletions test/test_from_sdist.py
Expand Up @@ -18,7 +18,8 @@ def make_sdist(project: TestProject, working_dir: Path) -> Path:

sdist_dir = working_dir / "sdist"
subprocess.run(
[sys.executable, "-m", "build", "--sdist", "--outdir", sdist_dir, project_dir], check=True
[sys.executable, "-m", "build", "--sdist", "--outdir", str(sdist_dir), str(project_dir)],
check=True,
)

return next(sdist_dir.glob("*.tar.gz"))
Expand All @@ -35,11 +36,11 @@ def cibuildwheel_from_sdist_run(sdist_path, add_env=None, config_file=None):
[
sys.executable,
"-m",
"cibuildwheel.from_sdist",
"cibuildwheel",
*(["--config-file", config_file] if config_file else []),
"--output-dir",
tmp_output_dir,
sdist_path,
str(tmp_output_dir),
str(sdist_path),
],
env=env,
check=True,
Expand Down Expand Up @@ -186,8 +187,8 @@ def test_argument_passthrough(tmp_path, capfd):
[
sys.executable,
"-m",
"cibuildwheel.from_sdist",
sdist_path,
"cibuildwheel",
str(sdist_path),
"--platform",
"linux",
"--archs",
Expand Down
2 changes: 1 addition & 1 deletion unit_test/conftest.py
Expand Up @@ -32,7 +32,7 @@ def fake_package_dir(monkeypatch):
real_path_exists = Path.exists

def mock_path_exists(path):
if path == MOCK_PACKAGE_DIR / "setup.py":
if str(path).endswith(str(MOCK_PACKAGE_DIR / "setup.py")):
return True
else:
return real_path_exists(path)
Expand Down
6 changes: 3 additions & 3 deletions unit_test/main_tests/main_options_test.py
Expand Up @@ -24,13 +24,13 @@ def test_output_dir(platform, intercepted_build_args, monkeypatch):

main()

assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve()


def test_output_dir_default(platform, intercepted_build_args, monkeypatch):
main()

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


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

main()

assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR
assert intercepted_build_args.args[0].globals.output_dir == OUTPUT_DIR.resolve()


def test_build_selector(platform, intercepted_build_args, monkeypatch, allow_empty):
Expand Down
4 changes: 2 additions & 2 deletions unit_test/main_tests/main_platform_test.py
Expand Up @@ -60,14 +60,14 @@ def test_platform_argument(platform, intercepted_build_args, monkeypatch):

options = intercepted_build_args.args[0]

assert options.globals.package_dir == MOCK_PACKAGE_DIR
assert options.globals.package_dir == MOCK_PACKAGE_DIR.resolve()


def test_platform_environment(platform, intercepted_build_args, monkeypatch):
main()
options = intercepted_build_args.args[0]

assert options.globals.package_dir == MOCK_PACKAGE_DIR
assert options.globals.package_dir == MOCK_PACKAGE_DIR.resolve()


def test_archs_default(platform, intercepted_build_args, monkeypatch):
Expand Down

0 comments on commit f6e4420

Please sign in to comment.