fix: use `exec` to invoke the stage-2 bootstrap for non-zip case (#2047) When the two-stage bootstrap is used, the parent shell process runs python as a child process, which changes how signals are propagated. Specifically, if a signal is sent _directly_ to the parent (e.g. `kill $parent`), the child process (python) won't receive it and it will appear to be ignored. This is because the parent process is busy waiting for the child process. To fix, invoke the python process using `exec` instead. Because the process is entirely replaced, signals are sent directly to the replacement. This can't be used for zip files, though, because they rely on a catching the exit signal to perform cleanup of the extracted files. Fixes https://github.com/bazelbuild/rules_python/issues/2043 --------- Co-authored-by: Richard Levasseur <rlevasseur@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md index ac11e14..cc44a47 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -28,6 +28,9 @@ * Nothing yet ### Fixed +* (rules) Signals are properly received when using {obj}`--bootstrap_impl=script` + (for non-zip builds). + ([#2043](https://github.com/bazelbuild/rules_python/issues/2043)) * (rules) Fixes python builds when the `--build_python_zip` is set to `false` on Windows. See [#1840](https://github.com/bazelbuild/rules_python/issues/1840). * (pip) Fixed pypi parse_simpleapi_html function for feeds with package metadata containing ">" sign
diff --git a/python/private/stage1_bootstrap_template.sh b/python/private/stage1_bootstrap_template.sh index fb46cc6..48711aa 100644 --- a/python/private/stage1_bootstrap_template.sh +++ b/python/private/stage1_bootstrap_template.sh
@@ -106,13 +106,28 @@ interpreter_env+=("PYTHONSAFEPATH=1") export RUNFILES_DIR -# NOTE: We use <(...) to pass the Python program as a file so that stdin can -# still be passed along as normal. -env \ - "${interpreter_env[@]}" \ - "$python_exe" \ - "${interpreter_args[@]}" \ - "$stage2_bootstrap" \ - "$@" -exit $? +command=( + env + "${interpreter_env[@]}" + "$python_exe" + "${interpreter_args[@]}" + "$stage2_bootstrap" + "$@" +) + +# We use `exec` instead of a child process so that signals sent directly (e.g. +# using `kill`) to this process (the PID seen by the calling process) are +# received by the Python process. Otherwise, this process receives the signal +# and would have to manually propagate it. +# See https://github.com/bazelbuild/rules_python/issues/2043#issuecomment-2215469971 +# for more information. +# +# However, when running a zip file, we need to clean up the workspace after the +# process finishes so control must return here. +if [[ "$IS_ZIPFILE" == "1" ]]; then + "${command[@]}" + exit $? +else + exec "${command[@]}" +fi