refactor: decompose large modules into focused submodules#3052
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR decomposes several large source files into focused submodules for better maintainability, with no intended behavioral changes (pure structural refactoring).
Changes:
- Extracts
BuildContextBuildertosrc/build_context/builder.rs,get_platform_taglogic tosrc/target/platform_tag.rs,InstallBackendtosrc/develop/install_backend.rs, andhash_file/zip_mtimeutilities tosrc/util.rs - Consolidates 4 near-identical non-bin wheel-writing pipelines into a single
write_wheel()method, and deduplicates repeatedabi3/platform-tag resolution patterns - Decomposes the monolithic
cargo_build_command()into focused helpers (configure_bin_lib_flags,configure_platform_linker_args,configure_macos_pyo3_linker_args,configure_emscripten_args,configure_debuginfo_flags,create_build_command,configure_pyo3_env)
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/util.rs |
New module with hash_file and zip_mtime utilities extracted from build_context.rs |
src/upload.rs |
Import updated from build_context::hash_file to util::hash_file |
src/target/platform_tag.rs |
New module with get_platform_tag, rustc_macosx_target_version, and all platform-tag helpers extracted from build_context.rs; includes moved tests |
src/target/mod.rs |
Re-exports get_platform_tag and rustc_macosx_target_version from new submodule |
src/source_distribution.rs |
unpack_sdist moved here from build_context.rs, paired with sdist creation logic |
src/lib.rs |
Public re-exports updated: unpack_sdist now from source_distribution, BuildContextBuilder exposed; util declared as new pub(crate) module |
src/develop/mod.rs |
InstallBackend and helpers extracted to install_backend submodule; imports consolidated |
src/develop/install_backend.rs |
New module housing InstallBackend, find_uv_bin, find_uv_python, check_pip_exists, is_uv_venv |
src/compile.rs |
cargo_build_command decomposed into 7 focused helper functions; rustc_macosx_target_version import updated |
src/build_options.rs |
BuildContextBuilder and related helpers removed (moved to builder.rs); test imports updated |
src/build_context/mod.rs |
Unified write_wheel() pipeline, build_abi3_wheels() helper, and resolve_platform_tags() deduplication; get_platform_tag delegates to target module |
src/build_context/builder.rs |
New module with BuildContextBuilder, resolve_target, resolve_platform_tags, validate_bridge_type, filter_cargo_targets |
src/binding_generator/mod.rs |
?Sized bound added to generate_binding's generator parameter to support Box<dyn BindingGenerator> dispatch |
Comments suppressed due to low confidence (1)
src/build_context/mod.rs:144
- This refactoring introduces a subtle behavioral change for
Abi3Version::CurrentPythonwhen multiple abi3-capable interpreters exist. In the old code, all interpreters withhas_stable_api()were partitioned only byhas_stable_api(). In the new code,build_abi3_wheelsadditionally filters abi3 candidates byversion >= (major, minor), meaning any abi3-capable interpreter with a version lower than the first abi3 interpreter's version will now fall intonon_abi3_interpsand receive a version-specific wheel rather than being silently included in the abi3 wheel build. While the resulting abi3 wheel is the same (sincebuild_pyo3_wheel_abi3only usesinterpreters.first()anyway), the PR description claims "no behavioral changes" — this is not strictly true in this edge case.
Extract the utility functions hash_file() and zip_mtime() from build_context.rs into a dedicated src/util.rs module. These functions are general-purpose utilities with no dependency on BuildContext, so they belong in a shared module. Update references in build_context.rs and upload.rs to import from the new location.
Remove the standalone get_py3_tags() method which was nearly identical to get_universal_tags(). The only difference was that get_universal_tags() returned an additional (tag, tags) tuple while get_py3_tags() returned just tags. Inline the single-element vec construction directly into get_universal_tags(), eliminating the redundant method.
Move InstallBackend enum, its impl block, and the related helper functions (find_uv_bin, find_uv_python, check_pip_exists, is_uv_venv) from develop.rs into a dedicated install_backend.rs submodule. This separates the pip/uv backend detection and command construction logic (~200 lines) from the develop() orchestration function, making both easier to understand and maintain. The develop module is converted from a single file (src/develop.rs) to a directory module (src/develop/mod.rs + src/develop/install_backend.rs).
Extract build_abi3_wheels() helper that splits interpreters into abi3-capable and non-abi3 groups, then builds the appropriate wheel type for each group. This collapses two near-identical ~30-line match arms (Abi3Version::Version and Abi3Version::CurrentPython) into concise calls to the shared helper.
…ile.rs Break the ~300-line cargo_build_command() function into focused helpers: - configure_bin_lib_flags(): --bin/--lib flags and musl crt-static - configure_platform_linker_args(): dispatches to platform-specific helpers - configure_macos_pyo3_linker_args(): macOS LC_ID_DYLIB and dynamic_lookup - configure_emscripten_args(): Emscripten SIDE_MODULE and WASM_BIGINT - configure_debuginfo_flags(): split-debuginfo rustflags - create_build_command(): xwin/zig/plain cargo backend selection - configure_pyo3_env(): PYO3_* environment variables and config files The main cargo_build_command() function now reads as a clear sequence of configuration steps, while each helper is independently testable and documented.
Introduce a single write_wheel() method that encapsulates the common 7-step wheel-building pipeline (create writer, add external libs, generate bindings, add pth, add data, write SBOMs, finish). The method takes a closure for generator creation, allowing each binding type to provide its specific generator while sharing all boilerplate. The 4 non-bin write methods (write_pyo3_wheel_abi3, write_pyo3_wheel, write_cffi_wheel, write_uniffi_wheel) now delegate to write_wheel(), reducing each to ~15 lines of tag computation + closure. The bin wheel method is kept separate due to its unique metadata mutation pattern. Also add ?Sized bound to generate_binding()'s generator parameter to support trait object dispatch through Box<dyn BindingGenerator>.
Move BuildContextBuilder and its associated resolution functions (resolve_target, resolve_platform_tags, validate_bridge_type, filter_cargo_targets) from build_options.rs into a new build_context/builder.rs submodule. This separates the CLI/config type definitions (CargoOptions, BuildOptions, TargetTriple) in build_options.rs from the build context construction logic, making each file more focused: - build_options.rs (~400 lines): CLI types, serde, clap definitions - build_context/builder.rs (~500 lines): BuildContextBuilder + resolution - build_context/mod.rs: BuildContext struct and wheel-building methods The build_context module is converted from a single file to a directory module (mod.rs + builder.rs) to accommodate the new submodule.
The unpack_sdist() function is a standalone utility for extracting sdist tarballs — it has no dependency on BuildContext. Moving it to source_distribution.rs pairs sdist creation and consumption in the same module. The public re-export in lib.rs is updated accordingly.
Move get_platform_tag() and its helper functions (macosx_deployment_target, iphoneos_deployment_target, rustc_macosx_target_version, emscripten_version, find_android_api_level) out of build_context/mod.rs into a new src/target/platform_tag.rs module. These are pure target/platform functions with no dependency on BuildContext fields beyond target, universal2, pyproject_toml, and manifest_path. The standalone get_platform_tag() function takes these as parameters, and BuildContext::get_platform_tag() becomes a thin delegate. This removes ~400 lines from build_context/mod.rs (1579 → 1118).
- Extract resolve_platform_tags() helper in BuildContext to deduplicate
the 5-way repeated pattern:
if self.platform_tag.is_empty() { vec![policy.platform_tag()] }
else { self.platform_tag.clone() }
- Simplify zip_mtime() in util.rs: replace the .context("") hack for
error type unification with a cleaner closure returning anyhow::Result.
e80dc9c to
1c2252b
Compare
build_abi3_wheels now takes Option<(u8, u8)> instead of (major, minor). When None (CurrentPython), all has_stable_api() interpreters stay in the abi3 group with the baseline derived from the first one—matching the pre-refactor behavior. When Some (explicit Version), the version filter is applied as before. This fixes two behavioral regressions from the abi3 deduplication: 1. Stable-API interpreters below the first interpreter's version were incorrectly moved to non_abi3_interps and built as version-specific. 2. When no interpreter had has_stable_api(), the warning was silently dropped.
Use .context() or .ok_or_else() instead of .unwrap() on root_package() in filter_cargo_targets() and source_distribution(), matching the pattern already used in bridge/detection.rs and metadata.rs.
write_wheel() always received tag and tags where tags was just vec![tag.clone()]. Compute tags inside write_wheel() from the single tag parameter instead.
Return a single String instead of (String, Vec<String>) since the vec was always just vec![tag.clone()]. Callers that need a slice construct it locally.
All write_*_wheel() methods returned (PathBuf, String) where the String was always a hardcoded tag known by the caller. Return just PathBuf and let callers construct the metadata tuple directly.
…paths test Both pyo3_mixed_include_exclude_wheel_files and pyo3_wheel_record_has_normalized_paths were sharing the same unique_name "wheel-files-pyo3-mixed-include-exclude", causing them to use the same target directory. When run in parallel, stage_artifact() in one test renames the .so out of debug/, so the other test can't find it there, leading to "No such file or directory" failures.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Break apart several oversized source files (
build_context.rs,build_options.rs,develop.rs,compile.rs) into focused submodules with clear responsibilities. No behavioral changes — pure structural refactoring across incremental commits.Motivation
Several core modules had grown to 1500+ lines with mixed concerns:
build_context.rscontained theBuildContextstruct, wheel-writing pipelines, platform tag resolution, and sdist unpacking — all in one 1600-line file.build_options.rsmixed CLI/serde type definitions with the entireBuildContextBuilderconstruction logic (~500 lines).develop.rsinterleaved thedevelop()orchestration function with pip/uv backend detection (~200 lines).compile.rshad a single ~300-linecargo_build_command()function handling every platform's linker args, env vars, and build flags.What changed
Module extractions
src/build_context/builder.rsbuild_options.rssrc/target/platform_tag.rsbuild_context/mod.rsget_platform_tag()and its helpers are pure target/platform functions with no dependency onBuildContextfields — they belong with the rest of the target logicsrc/develop/install_backend.rsdevelop.rsdevelop()orchestrationsrc/util.rsbuild_context.rshash_file()andzip_mtime()are general-purpose utilities with no dependency onBuildContextCode deduplication
Unified
write_wheel()pipeline: The 4 non-bin wheel writers (write_pyo3_wheel_abi3,write_pyo3_wheel,write_cffi_wheel,write_uniffi_wheel) all followed the same 7-step pattern. A newwrite_wheel()method captures the shared pipeline and takes a closure for binding generator creation:Each caller is now ~15 lines of tag computation + closure instead of ~60 lines of duplicated boilerplate.
resolve_platform_tags()helper: Deduplicates the 5× repeated pattern:Consolidated
get_py3_tags()intoget_universal_tags(): The two methods were nearly identical — removed the redundant one.build_abi3_wheels()helper: Collapses two near-identical ~30-line match arms forAbi3Version::VersionandAbi3Version::CurrentPythoninto a shared helper.cargo_build_command()decompositionThe monolithic function is split into focused helpers, each handling one concern:
configure_bin_lib_flags()--bin/--libflags and muslcrt-staticconfigure_platform_linker_args()configure_macos_pyo3_linker_args()LC_ID_DYLIBanddynamic_lookupconfigure_emscripten_args()SIDE_MODULEandWASM_BIGINTconfigure_debuginfo_flags()create_build_command()configure_pyo3_env()PYO3_*environment variables and config filesMinor changes
?Sizedbound togenerate_binding()'s generator parameter to supportBox<dyn BindingGenerator>dispatch.unpack_sdist()tosource_distribution.rsto pair sdist creation and consumption.