diff --git a/src/poetry/console/commands/build.py b/src/poetry/console/commands/build.py
index 5339a865200..0d1bd15af45 100644
--- a/src/poetry/console/commands/build.py
+++ b/src/poetry/console/commands/build.py
@@ -1,11 +1,17 @@
from __future__ import annotations
+from typing import TYPE_CHECKING
+
from cleo.helpers import option
from poetry.console.commands.env_command import EnvCommand
from poetry.utils.env import build_environment
+if TYPE_CHECKING:
+ from pathlib import Path
+
+
class BuildCommand(EnvCommand):
name = "build"
description = "Builds a package, as a tarball and a wheel by default."
@@ -20,9 +26,26 @@ class BuildCommand(EnvCommand):
"poetry.core.masonry.builders.wheel",
]
- def handle(self) -> int:
- from poetry.core.masonry.builder import Builder
+ def _build(
+ self,
+ fmt: str,
+ executable: str | Path | None = None,
+ *,
+ target_dir: Path | None = None,
+ ) -> None:
+ from poetry.masonry.builders import BUILD_FORMATS
+
+ if fmt in BUILD_FORMATS:
+ builders = [BUILD_FORMATS[fmt]]
+ elif fmt == "all":
+ builders = list(BUILD_FORMATS.values())
+ else:
+ raise ValueError(f"Invalid format: {fmt}")
+
+ for builder in builders:
+ builder(self.poetry, executable=executable).build(target_dir)
+ def handle(self) -> int:
with build_environment(poetry=self.poetry, env=self.env, io=self.io) as env:
fmt = self.option("format") or "all"
package = self.poetry.package
@@ -30,7 +53,6 @@ def handle(self) -> int:
f"Building {package.pretty_name} ({package.version})"
)
- builder = Builder(self.poetry)
- builder.build(fmt, executable=env.python)
+ self._build(fmt, executable=env.python)
return 0
diff --git a/src/poetry/masonry/builders/__init__.py b/src/poetry/masonry/builders/__init__.py
index 61662422c39..4fdd87d96e0 100644
--- a/src/poetry/masonry/builders/__init__.py
+++ b/src/poetry/masonry/builders/__init__.py
@@ -1,6 +1,16 @@
from __future__ import annotations
+from poetry.core.masonry.builders.sdist import SdistBuilder
+from poetry.core.masonry.builders.wheel import WheelBuilder
+
from poetry.masonry.builders.editable import EditableBuilder
-__all__ = ["EditableBuilder"]
+__all__ = ["BUILD_FORMATS", "EditableBuilder"]
+
+
+# might be extended by plugins
+BUILD_FORMATS = {
+ "sdist": SdistBuilder,
+ "wheel": WheelBuilder,
+}
diff --git a/tests/console/commands/test_build.py b/tests/console/commands/test_build.py
index ed9f3a3c5fa..3b378d1e4e5 100644
--- a/tests/console/commands/test_build.py
+++ b/tests/console/commands/test_build.py
@@ -5,17 +5,63 @@
from typing import TYPE_CHECKING
+import pytest
+
from poetry.factory import Factory
if TYPE_CHECKING:
from pathlib import Path
+ from cleo.testers.command_tester import CommandTester
+
+ from poetry.poetry import Poetry
from poetry.utils.env import VirtualEnv
from tests.types import CommandTesterFactory
from tests.types import FixtureDirGetter
+@pytest.fixture
+def tmp_project_path(tmp_path: Path) -> Path:
+ return tmp_path / "project"
+
+
+@pytest.fixture
+def tmp_poetry(tmp_project_path: Path, fixture_dir: FixtureDirGetter) -> Poetry:
+ # copy project so that we start with a clean directory
+ shutil.copytree(fixture_dir("simple_project"), tmp_project_path)
+ poetry = Factory().create_poetry(tmp_project_path)
+ return poetry
+
+
+@pytest.fixture
+def tmp_tester(
+ tmp_poetry: Poetry, command_tester_factory: CommandTesterFactory
+) -> CommandTester:
+ return command_tester_factory("build", tmp_poetry)
+
+
+def get_package_glob(poetry: Poetry) -> str:
+ return f"{poetry.package.name.replace('-', '_')}-{poetry.package.version}*"
+
+
+def test_build_format_is_not_valid(tmp_tester: CommandTester) -> None:
+ with pytest.raises(ValueError, match=r"Invalid format.*"):
+ tmp_tester.execute("--format not_valid")
+
+
+@pytest.mark.parametrize("format", ["sdist", "wheel", "all"])
+def test_build_creates_packages_in_dist_directory_if_no_output_is_specified(
+ tmp_tester: CommandTester, tmp_project_path: Path, tmp_poetry: Poetry, format: str
+) -> None:
+ tmp_tester.execute(f"--format {format}")
+ build_artifacts = tuple(
+ (tmp_project_path / "dist").glob(get_package_glob(tmp_poetry))
+ )
+ assert len(build_artifacts) > 0
+ assert all(archive.exists() for archive in build_artifacts)
+
+
def test_build_with_multiple_readme_files(
fixture_dir: FixtureDirGetter,
tmp_path: Path,