diff --git a/ci-targets.yaml b/ci-targets.yaml index 48fc36df3..359d8c469 100644 --- a/ci-targets.yaml +++ b/ci-targets.yaml @@ -11,7 +11,13 @@ darwin: - "3.14" - "3.15" build_options: + - debug - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" x86_64-apple-darwin: arch: x86_64 @@ -23,7 +29,13 @@ darwin: - "3.14" - "3.15" build_options: + - debug - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" run: true linux: @@ -38,7 +50,13 @@ linux: - "3.14" - "3.15" build_options: + - debug - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" x86_64-unknown-linux-gnu: arch: x86_64 @@ -51,7 +69,13 @@ linux: - "3.14" - "3.15" build_options: + - debug - pgo+lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+pgo+lto + minimum-python-version: "3.13" run: true x86_64-unknown-linux-musl: @@ -65,7 +89,18 @@ linux: - "3.14" - "3.15" build_options: + - debug+static + - noopt+static - lto+static + - debug + - noopt + - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" run: true aarch64-unknown-linux-musl: @@ -79,7 +114,19 @@ linux: - "3.14" - "3.15" build_options: + # TODO: Static support is current blocked by some compiler-rt linking issues + # - debug+static + # - noopt+static + # - lto+static + - debug + - noopt - lto + build_options_conditional: + - options: + - freethreaded+debug + - freethreaded+noopt + - freethreaded+lto + minimum-python-version: "3.13" run: true windows: @@ -96,6 +143,10 @@ windows: vs_version: "2022" build_options: - pgo + build_options_conditional: + - options: + - freethreaded+pgo + minimum-python-version: "3.14" x86_64-pc-windows-msvc: arch: x86_64 @@ -113,6 +164,10 @@ windows: minimum-python-version: "3.15" build_options: - pgo + build_options_conditional: + - options: + - freethreaded+pgo + minimum-python-version: "3.14" aarch64-pc-windows-msvc: arch: aarch64 @@ -128,3 +183,7 @@ windows: vs_version: "2022" build_options: - pgo + build_options_conditional: + - options: + - freethreaded+pgo + minimum-python-version: "3.14" diff --git a/cpython-windows/build.py b/cpython-windows/build.py index 5b9d40ba5..7d37f3e02 100644 --- a/cpython-windows/build.py +++ b/cpython-windows/build.py @@ -1387,20 +1387,9 @@ def find_additional_dependencies(project: pathlib.Path): return set() - if arch == "amd64": - abi_platform = "win_amd64" - elif arch == "win32": - abi_platform = "win32" - elif arch == "arm64": - abi_platform = "win_arm64" - else: - raise Exception("unhandled architecture: %s" % arch) - if freethreaded: - abi_tag = ".cp%st-%s" % (python_majmin, abi_platform) lib_suffix = "t" else: - abi_tag = "" lib_suffix = "" # Copy object files for core sources into their own directory. @@ -1515,15 +1504,14 @@ def find_additional_dependencies(project: pathlib.Path): res["extensions"][ext] = [entry] # Copy the extension static library. - ext_static = outputs_path / ("%s%s.lib" % (ext, abi_tag)) - dest = dest_dir / ("%s%s.lib" % (ext, abi_tag)) + ext_static = outputs_path / ("%s.lib" % ext) + dest = dest_dir / ("%s.lib" % ext) log("copying static extension %s" % ext_static) shutil.copyfile(ext_static, dest) - res["extensions"][ext][0]["static_lib"] = "build/extensions/%s/%s%s.lib" % ( + res["extensions"][ext][0]["static_lib"] = "build/extensions/%s/%s.lib" % ( ext, ext, - abi_tag, ) lib_dir = out_dir / "build" / "lib" diff --git a/pystandalone/src/Modules/_pystandalone.c b/pystandalone/src/Modules/_pystandalone.c index 0639563b6..d2d965b0c 100644 --- a/pystandalone/src/Modules/_pystandalone.c +++ b/pystandalone/src/Modules/_pystandalone.c @@ -15,6 +15,14 @@ #define EVP_CTRL_AEAD_SET_TAG EVP_CTRL_GCM_SET_TAG #endif +// Some compatibility defines for CPython < 3.13 + +#if PY_VERSION_HEX < 0x030d0000 +typedef struct { int v; } PyMutex; +#define PyMutex_Lock(m) ((void)(m)) +#define PyMutex_Unlock(m) ((void)(m)) +#endif + // Some compatibility defines for CPython < 3.11 #ifndef _PyCFunction_CAST @@ -59,6 +67,7 @@ get_pystandalone_state(PyObject *module) typedef struct { PyObject_HEAD + PyMutex mutex; EVP_CIPHER_CTX *ctx; /* OpenSSL cipher context */ unsigned char key[EVP_MAX_KEY_LENGTH]; unsigned char iv[EVP_MAX_IV_LENGTH]; @@ -68,6 +77,7 @@ typedef struct { typedef struct { PyObject_HEAD + PyMutex mutex; EVP_PKEY_CTX *ctx; /* OpenSSL pkey context */ } PublicKey; @@ -240,6 +250,8 @@ _Cipher_new(PyObject *module, const EVP_CIPHER *cipher, Py_buffer *key, Py_buffe return NULL; } + self->mutex = (PyMutex){0}; + self->ctx = EVP_CIPHER_CTX_new(); if (self->ctx == NULL) { Py_DECREF(self); @@ -480,7 +492,10 @@ static PyObject * _pystandalone_Cipher_encrypt_impl(Cipher *self, Py_buffer *data) /*[clinic end generated code: output=905ce94ccf6e0082 input=17d62fae407625b9]*/ { - return _Cipher_crypt(self, data, CIPHER_MODE_ENCRYPT); + PyMutex_Lock(&self->mutex); + PyObject *res = _Cipher_crypt(self, data, CIPHER_MODE_ENCRYPT); + PyMutex_Unlock(&self->mutex); + return res; } /*[clinic input] @@ -497,17 +512,23 @@ _pystandalone_Cipher_encrypt_and_digest_impl(Cipher *self, Py_buffer *data) { PyObject *buf, *digest; + PyMutex_Lock(&self->mutex); + buf = _Cipher_crypt(self, data, CIPHER_MODE_ENCRYPT); if (buf == NULL) { + PyMutex_Unlock(&self->mutex); return NULL; } digest = _Cipher_get_tag(self); if (digest == NULL) { + PyMutex_Unlock(&self->mutex); Py_DECREF(buf); return NULL; } + PyMutex_Unlock(&self->mutex); + return PyTuple_Pack(2, buf, digest); } @@ -523,7 +544,10 @@ static PyObject * _pystandalone_Cipher_decrypt_impl(Cipher *self, Py_buffer *data) /*[clinic end generated code: output=dd94f41ddbeaccf7 input=363abf129f94df00]*/ { - return _Cipher_crypt(self, data, CIPHER_MODE_DECRYPT); + PyMutex_Lock(&self->mutex); + PyObject *res = _Cipher_crypt(self, data, CIPHER_MODE_DECRYPT); + PyMutex_Unlock(&self->mutex); + return res; } /*[clinic input] @@ -544,16 +568,22 @@ _pystandalone_Cipher_decrypt_and_verify_impl(Cipher *self, Py_buffer *data, { PyObject *buf; + PyMutex_Lock(&self->mutex); + buf = _Cipher_crypt(self, data, CIPHER_MODE_DECRYPT); if (buf == NULL) { + PyMutex_Unlock(&self->mutex); return NULL; } if (_Cipher_verify_tag(self, tag) == NULL) { + PyMutex_Unlock(&self->mutex); Py_DECREF(buf); return NULL; } + PyMutex_Unlock(&self->mutex); + return buf; } @@ -569,7 +599,10 @@ static PyObject * _pystandalone_Cipher_update_impl(Cipher *self, Py_buffer *data) /*[clinic end generated code: output=8aea00e2c0e763eb input=ce774ce7ddc5e8c9]*/ { - return _Cipher_update_ad(self, data); + PyMutex_Lock(&self->mutex); + PyObject *res = _Cipher_update_ad(self, data); + PyMutex_Unlock(&self->mutex); + return res; } /*[clinic input] @@ -582,7 +615,10 @@ static PyObject * _pystandalone_Cipher_digest_impl(Cipher *self) /*[clinic end generated code: output=ca0c92f67a3aac6d input=d92ff14e2380d3e4]*/ { - return _Cipher_get_tag(self); + PyMutex_Lock(&self->mutex); + PyObject *res = _Cipher_get_tag(self); + PyMutex_Unlock(&self->mutex); + return res; } /*[clinic input] @@ -597,7 +633,10 @@ static PyObject * _pystandalone_Cipher_verify_impl(Cipher *self, Py_buffer *tag) /*[clinic end generated code: output=2c8e2d72c015c078 input=cf4d4fd1a495c7f7]*/ { - return _Cipher_verify_tag(self, tag); + PyMutex_Lock(&self->mutex); + PyObject *res = _Cipher_verify_tag(self, tag); + PyMutex_Unlock(&self->mutex); + return res; } /*[clinic input] @@ -610,7 +649,9 @@ static PyObject * _pystandalone_Cipher_clean_impl(Cipher *self) /*[clinic end generated code: output=fa8d7c88cbdea4c8 input=e334120cdcc73ca7]*/ { + PyMutex_Lock(&self->mutex); _Cipher_clean(self); + PyMutex_Unlock(&self->mutex); Py_RETURN_NONE; } @@ -629,22 +670,28 @@ static PyMethodDef Cipher_methods[] = { static PyObject * Cipher_get_initialized(Cipher *self, void *closure) { - PyObject * res = self->state == CIPHER_STATE_INIT ? Py_True : Py_False; - return Py_INCREF(res), res; + PyMutex_Lock(&self->mutex); + int initialized = self->state == CIPHER_STATE_INIT; + PyMutex_Unlock(&self->mutex); + return PyBool_FromLong(initialized); } static PyObject * Cipher_get_finalized(Cipher *self, void *closure) { - PyObject * res = self->state == CIPHER_STATE_FINAL ? Py_True : Py_False; - return Py_INCREF(res), res; + PyMutex_Lock(&self->mutex); + int finalized = self->state == CIPHER_STATE_FINAL; + PyMutex_Unlock(&self->mutex); + return PyBool_FromLong(finalized); } static PyObject * Cipher_get_cleaned(Cipher *self, void *closure) { - PyObject * res = self->state == CIPHER_STATE_CLEAR ? Py_True : Py_False; - return Py_INCREF(res), res; + PyMutex_Lock(&self->mutex); + int cleaned = self->state == CIPHER_STATE_CLEAR; + PyMutex_Unlock(&self->mutex); + return PyBool_FromLong(cleaned); } static PyObject * @@ -700,11 +747,13 @@ Cipher_repr(Cipher *self) static void Cipher_dealloc(Cipher *self) { + PyTypeObject *tp = Py_TYPE(self); EVP_CIPHER_CTX_cleanup(self->ctx); EVP_CIPHER_CTX_free(self->ctx); OPENSSL_cleanse(self->key, sizeof(self->key)); OPENSSL_cleanse(self->iv, sizeof(self->iv)); - PyObject_Del(self); + PyObject_Free(self); + Py_DECREF(tp); } static int @@ -810,6 +859,8 @@ _PublicKey_new(PyObject *module, EVP_PKEY *key) return NULL; } + self->mutex = (PyMutex){0}; + self->ctx = EVP_PKEY_CTX_new(key, NULL); if (self->ctx == NULL) { PyErr_NoMemory(); @@ -850,24 +901,31 @@ _pystandalone_PublicKey_encrypt_impl(PublicKey *self, Py_buffer *data) size_t out_len; unsigned char *out_buf; + PyMutex_Lock(&self->mutex); + if (!EVP_PKEY_encrypt(self->ctx, NULL, &out_len, data->buf, data->len)) { + PyMutex_Unlock(&self->mutex); _set_exception(PyExc_ValueError); return NULL; } buf = PyBytes_FromStringAndSize(NULL, out_len); if (buf == NULL) { + PyMutex_Unlock(&self->mutex); PyErr_NoMemory(); return NULL; } out_buf = (unsigned char *)PyBytes_AS_STRING(buf); if (!EVP_PKEY_encrypt(self->ctx, out_buf, &out_len, data->buf, data->len)) { + PyMutex_Unlock(&self->mutex); _set_exception(PyExc_ValueError); Py_DECREF(buf); return NULL; } + PyMutex_Unlock(&self->mutex); + return buf; } @@ -977,8 +1035,10 @@ PublicKey_repr(PublicKey *self) static void PublicKey_dealloc(PublicKey *self) { + PyTypeObject *tp = Py_TYPE(self); EVP_PKEY_CTX_free(self->ctx); - PyObject_Del(self); + PyObject_Free(self); + Py_DECREF(tp); } static int @@ -1132,8 +1192,7 @@ static PyObject * _pystandalone_has_library_impl(PyObject *module) /*[clinic end generated code: output=04238eaa01e29446 input=3272862d1d74a71a]*/ { - PyObject *res = PyStandalone_HasLibrary() ? Py_True : Py_False; - return Py_INCREF(res), res; + return PyBool_FromLong(PyStandalone_HasLibrary()); } /*[clinic input] @@ -1146,8 +1205,7 @@ static PyObject * _pystandalone_has_bootstrap_impl(PyObject *module) /*[clinic end generated code: output=a5e616490f5e50c9 input=24807adbd6092d18]*/ { - PyObject *res = PyStandalone_HasBootstrap() ? Py_True : Py_False; - return Py_INCREF(res), res; + return PyBool_FromLong(PyStandalone_HasBootstrap()); } /*[clinic input] @@ -1160,8 +1218,7 @@ static PyObject * _pystandalone_has_payload_impl(PyObject *module) /*[clinic end generated code: output=3f6b9eeea5cb6ba3 input=338fbe7842bbb2dd]*/ { - PyObject *res = PyStandalone_HasPayload() ? Py_True : Py_False; - return Py_INCREF(res), res; + return PyBool_FromLong(PyStandalone_HasPayload()); } /* List of functions exported by this module */ @@ -1306,6 +1363,9 @@ _pystandalone_free(void *module) static struct PyModuleDef_Slot _pystandalone_slots[] = { {Py_mod_exec, _pystandalone_init_types}, {Py_mod_exec, _pystandalone_init_cipher_names}, +#ifdef Py_GIL_DISABLED + {Py_mod_gil, Py_MOD_GIL_NOT_USED}, +#endif {0, NULL} }; diff --git a/src/github.rs b/src/github.rs index 6f7dcfe21..c856fa5fc 100644 --- a/src/github.rs +++ b/src/github.rs @@ -474,6 +474,16 @@ pub async fn command_upload_release_distributions(args: &ArgMatches) -> Result<( let missing = wanted_filenames .keys() + // PYSTANDALONE: filter out distributions we don't build + .filter(|x| { + !x.contains("-armv7-unknown-linux-") + && !x.contains("-ppc64le-unknown-linux-") + && !x.contains("-riscv64-unknown-linux-") + && !x.contains("-s390x-unknown-linux-") + && !x.contains("-x86_64_v2-unknown-linux-") + && !x.contains("-x86_64_v3-unknown-linux-") + && !x.contains("-x86_64_v4-unknown-linux-") + }) .filter(|x| !filenames.contains(*x)) .collect::>(); diff --git a/src/release.rs b/src/release.rs index af45ad5a6..e45e9957e 100644 --- a/src/release.rs +++ b/src/release.rs @@ -109,6 +109,7 @@ pub static RELEASE_TRIPLES: Lazy> = Lazy:: ); // Windows. + // PYSTANDALONE: we can only build freethreaded on >= 3.14 h.insert( "i686-pc-windows-msvc", TripleRelease { @@ -117,7 +118,7 @@ pub static RELEASE_TRIPLES: Lazy> = Lazy:: freethreaded_install_only_suffix: "freethreaded+pgo", python_version_requirement: None, conditional_suffixes: vec![ConditionalSuffixes { - python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + python_version_requirement: VersionSpecifier::from_str(">=3.14").unwrap(), suffixes: vec!["freethreaded+pgo"], }], }, @@ -130,7 +131,7 @@ pub static RELEASE_TRIPLES: Lazy> = Lazy:: freethreaded_install_only_suffix: "freethreaded+pgo", python_version_requirement: None, conditional_suffixes: vec![ConditionalSuffixes { - python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + python_version_requirement: VersionSpecifier::from_str(">=3.14").unwrap(), suffixes: vec!["freethreaded+pgo"], }], }, @@ -143,7 +144,7 @@ pub static RELEASE_TRIPLES: Lazy> = Lazy:: freethreaded_install_only_suffix: "freethreaded+pgo", python_version_requirement: Some(VersionSpecifier::from_str(">=3.11").unwrap()), conditional_suffixes: vec![ConditionalSuffixes { - python_version_requirement: VersionSpecifier::from_str(">=3.13").unwrap(), + python_version_requirement: VersionSpecifier::from_str(">=3.14").unwrap(), suffixes: vec!["freethreaded+pgo"], }], }, @@ -312,7 +313,8 @@ pub static RELEASE_TRIPLES: Lazy> = Lazy:: "x86_64-unknown-linux-musl", TripleRelease { suffixes: linux_suffixes_musl.clone(), - install_only_suffix: "lto", + // PYSTANDALONE: promote the static build to the install_only artifact + install_only_suffix: "lto+static", freethreaded_install_only_suffix: "freethreaded+lto", python_version_requirement: None, conditional_suffixes: vec![ConditionalSuffixes {