diff --git a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py index ce4e6c5c5e..929029f5b8 100644 --- a/graalpython/com.oracle.graal.python.frozen/freeze_modules.py +++ b/graalpython/com.oracle.graal.python.frozen/freeze_modules.py @@ -112,7 +112,6 @@ def add_graalpython_core(): l = [] l.append("polyglot.arrow : polyglot.arrow = " + os.path.join(lib_graalpython, "modules/_polyglot_arrow.py")) for name in [ - "modules/_sysconfigdata", "modules/_polyglot", "modules/_polyglot_time", ]: diff --git a/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java b/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java index 3894c908ce..4b76ac03e2 100644 --- a/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java +++ b/graalpython/com.oracle.graal.python.resources/src/com/oracle/graal/python/resources/PythonResource.java @@ -43,6 +43,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.nio.file.InvalidPathException; import java.nio.file.Path; import java.util.ArrayList; @@ -66,6 +67,7 @@ public final class PythonResource implements InternalResource { private static final int PYTHON_MINOR; private static final int GRAALVM_MAJOR; private static final int GRAALVM_MINOR; + private static final String PYTHON_ABIFLAGS; /** * The version generated at build time is stored in an ASCII-compatible way. Add build time, we @@ -82,6 +84,13 @@ public final class PythonResource implements InternalResource { is.read(); // skip python micro version GRAALVM_MAJOR = is.read() - VERSION_BASE; GRAALVM_MINOR = is.read() - VERSION_BASE; + is.read(); // skip GraalVM micro version + is.read(); // skip release level + int ch; + while ((ch = is.read()) != '\n' && ch != -1) { + // skip ABI version + } + PYTHON_ABIFLAGS = ch == -1 ? "" : new String(is.readAllBytes(), StandardCharsets.US_ASCII).strip(); } catch (IOException e) { throw new RuntimeException(e); } @@ -117,7 +126,7 @@ public void unpackFiles(Env env, Path targetDirectory) throws IOException { env.unpackResourceFiles(BASE_PATH.resolve(LIBPYTHON_FILES), targetDirectory.resolve("lib").resolve(pythonMajMin), BASE_PATH.resolve(LIBPYTHON), filter); env.unpackResourceFiles(BASE_PATH.resolve(LIBGRAALPY_FILES), targetDirectory.resolve("lib").resolve("graalpy" + GRAALVM_MAJOR + "." + GRAALVM_MINOR), BASE_PATH.resolve(LIBGRAALPY), filter); - env.unpackResourceFiles(BASE_PATH.resolve(INCLUDE_FILES), targetDirectory.resolve("include").resolve(pythonMajMin), BASE_PATH.resolve(INCLUDE), filter); + env.unpackResourceFiles(BASE_PATH.resolve(INCLUDE_FILES), targetDirectory.resolve("include").resolve(pythonMajMin + PYTHON_ABIFLAGS), BASE_PATH.resolve(INCLUDE), filter); } // ni files are in the same place on all platforms env.unpackResourceFiles(BASE_PATH.resolve(NI_FILES), targetDirectory, BASE_PATH, filter); diff --git a/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py b/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py index fd9e01898e..a3fab2f075 100644 --- a/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py +++ b/graalpython/com.oracle.graal.python.test/src/tests/test_sysconfig.py @@ -1,4 +1,4 @@ -# Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -47,7 +47,12 @@ def test_sysconfig(): # must not fail -@unittest.skipIf(sys.implementation.name != 'graalpy', "GraalPy-only test") -def test_sysconfigdata(): - # Maturin loads this directly, make sure the import works - import _sysconfigdata +def test_platform_sysconfigdata(): + import importlib + import sysconfig + mod = importlib.import_module(sysconfig._get_sysconfigdata_name()) + # These flags are parsed directly from the file by maturin + for key in ("ABIFLAGS", "EXT_SUFFIX", "SOABI", "VERSION"): + assert key in mod.build_time_vars + assert mod.build_time_vars["ABIFLAGS"] == sys.abiflags + assert sys.abiflags in sysconfig.get_config_var("INCLUDEPY") diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java index 22a4e70942..f1e831c942 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/PythonLanguage.java @@ -203,6 +203,7 @@ public final class PythonLanguage extends TruffleLanguage { /** See {@code mx_graalpython.py:abi_version} */ public static final String GRAALPY_ABI_VERSION; + public static final String GRAALPY_ABIFLAGS; /* Magic number used to mark pyc files */ public static final int MAGIC_NUMBER = 21000 + Compiler.BYTECODE_VERSION * 10; @@ -252,7 +253,9 @@ public final class PythonLanguage extends TruffleLanguage { default: RELEASE_LEVEL_STRING = tsLiteral("final"); } - GRAALPY_ABI_VERSION = new String(is.readAllBytes(), StandardCharsets.US_ASCII).strip(); + String[] abiParts = new String(is.readAllBytes(), StandardCharsets.US_ASCII).split("\\R", 2); + GRAALPY_ABI_VERSION = abiParts[0].strip(); + GRAALPY_ABIFLAGS = abiParts.length > 1 ? abiParts[1].strip() : ""; } catch (IOException e) { throw new RuntimeException(e); } diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java index 8e3b59e649..71b23f763d 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/SysModuleBuiltins.java @@ -288,6 +288,7 @@ public final class SysModuleBuiltins extends PythonBuiltins { public static final TruffleString T_CACHE_TAG = tsLiteral("cache_tag"); public static final TruffleString T__MULTIARCH = tsLiteral("_multiarch"); + public static final TruffleString T_ABIFLAGS = tsLiteral("abiflags"); static { String compile_time; @@ -497,7 +498,7 @@ public void initialize(Python3Core core) { StructSequence.initType(core, THREAD_INFO_DESC); StructSequence.initType(core, UNRAISABLEHOOK_ARGS_DESC); - addBuiltinConstant("abiflags", T_EMPTY_STRING); + addBuiltinConstant(T_ABIFLAGS, toTruffleStringUncached(PythonLanguage.GRAALPY_ABIFLAGS)); addBuiltinConstant("byteorder", ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? T_LITTLE : T_BIG); addBuiltinConstant("copyright", T_LICENSE); addBuiltinConstant(T_MODULES, PFactory.createDict(language)); diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java index 8c7d926a52..c91a96b6a1 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/objects/module/FrozenModules.java @@ -96,7 +96,6 @@ private static final class Map { private static final PythonFrozenModule __PHELLO___SPAM = new PythonFrozenModule("__PHELLO___SPAM", "__phello__.spam", false); private static final PythonFrozenModule FROZEN_ONLY = new PythonFrozenModule("FROZEN_ONLY", null, false); private static final PythonFrozenModule POLYGLOT_ARROW = new PythonFrozenModule("POLYGLOT_ARROW", null, false); - private static final PythonFrozenModule _SYSCONFIGDATA = new PythonFrozenModule("_SYSCONFIGDATA", null, false); private static final PythonFrozenModule _POLYGLOT = new PythonFrozenModule("_POLYGLOT", null, false); private static final PythonFrozenModule _POLYGLOT_TIME = new PythonFrozenModule("_POLYGLOT_TIME", null, false); private static final PythonFrozenModule GRAALPY___GRAALPYTHON__ = new PythonFrozenModule("GRAALPY___GRAALPYTHON__", null, false); @@ -223,8 +222,6 @@ public static final PythonFrozenModule lookup(String name) { return Map.FROZEN_ONLY; case "polyglot.arrow": return Map.POLYGLOT_ARROW; - case "_sysconfigdata": - return Map._SYSCONFIGDATA; case "_polyglot": return Map._POLYGLOT; case "_polyglot_time": diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java index 0ed9c6099d..b82c5e4b59 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java @@ -30,6 +30,7 @@ import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_DARWIN; import static com.oracle.graal.python.annotations.PythonOS.PLATFORM_WIN32; import static com.oracle.graal.python.builtins.PythonBuiltinClassType.SystemError; +import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T_ABIFLAGS; import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T_CACHE_TAG; import static com.oracle.graal.python.builtins.modules.SysModuleBuiltins.T__MULTIARCH; import static com.oracle.graal.python.builtins.modules.io.IONodes.T_CLOSED; @@ -2918,6 +2919,7 @@ public TruffleString getSoAbi() { Object implementationObj = ReadAttributeFromModuleNode.getUncached().execute(sysModule, T_IMPLEMENTATION); // sys.implementation.cache_tag TruffleString cacheTag = (TruffleString) PyObjectGetAttr.executeUncached(implementationObj, T_CACHE_TAG); + TruffleString abiFlags = (TruffleString) ReadAttributeFromModuleNode.getUncached().execute(sysModule, T_ABIFLAGS); // sys.implementation._multiarch TruffleString multiArch = (TruffleString) PyObjectGetAttr.executeUncached(implementationObj, T__MULTIARCH); @@ -2933,7 +2935,7 @@ public TruffleString getSoAbi() { soExt = T_EXT_SO; } - soABI = cat(T_DOT, cacheTag, T_DASH, T_NATIVE, T_DASH, multiArch, soExt); + soABI = cat(T_DOT, cacheTag, abiFlags, T_DASH, T_NATIVE, T_DASH, multiArch, soExt); } return soABI; } diff --git a/graalpython/graalpy-pyconfig/CMakeLists.txt b/graalpython/graalpy-pyconfig/CMakeLists.txt index 10c3448c73..ee79c2f64f 100644 --- a/graalpython/graalpy-pyconfig/CMakeLists.txt +++ b/graalpython/graalpy-pyconfig/CMakeLists.txt @@ -33,6 +33,23 @@ include(CheckSymbolExists) include(CheckIncludeFile) include (TestBigEndian) +if(NOT DEFINED GRAALPY_SYSCONFIGDATA_NAME) + if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(GRAALPY_SYSCONFIG_PLATFORM "darwin") + elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(GRAALPY_SYSCONFIG_PLATFORM "win32") + else() + set(GRAALPY_SYSCONFIG_PLATFORM "linux") + endif() + + string(TOLOWER "${CMAKE_SYSTEM_PROCESSOR}" GRAALPY_SYSCONFIG_ARCH) + if(GRAALPY_SYSCONFIG_ARCH STREQUAL "amd64") + set(GRAALPY_SYSCONFIG_ARCH "x86_64") + endif() + set(GRAALPY_SYSCONFIG_MULTIARCH "${GRAALPY_SYSCONFIG_ARCH}-${GRAALPY_SYSCONFIG_PLATFORM}") + set(GRAALPY_SYSCONFIGDATA_NAME "_sysconfigdata__${GRAALPY_SYSCONFIG_PLATFORM}_${GRAALPY_SYSCONFIG_MULTIARCH}") +endif() + test_big_endian(IS_BIG_ENDIAN) set(CMAKE_REQUIRED_LINK_OPTIONS "-lm") @@ -70,4 +87,99 @@ else() set(DOUBLE_IS_LITTLE_ENDIAN_IEEE754 1) endif() +function(graalpy_sysconfig_value name default_value) + if(DEFINED ENV{GRAALPY_SYSCONFIG_${name}}) + set(GRAALPY_SYSCONFIG_${name} "$ENV{GRAALPY_SYSCONFIG_${name}}" PARENT_SCOPE) + else() + set(GRAALPY_SYSCONFIG_${name} "${default_value}" PARENT_SCOPE) + endif() +endfunction() + +function(graalpy_sysconfig_empty name) + graalpy_sysconfig_value("${name}" "") +endfunction() + +function(graalpy_sysconfig_env_value name) + if(DEFINED ENV{GRAALPY_SYSCONFIG_${name}}) + set(GRAALPY_SYSCONFIG_${name} "$ENV{GRAALPY_SYSCONFIG_${name}}" PARENT_SCOPE) + else() + set(GRAALPY_SYSCONFIG_${name} "" PARENT_SCOPE) + endif() +endfunction() + +function(graalpy_sysconfig_python_string name) + set(value "${${name}}") + string(REPLACE "\\" "\\\\" value "${value}") + string(REPLACE "\"" "\\\"" value "${value}") + string(REPLACE "\n" "\\n" value "${value}") + string(REPLACE "\r" "\\r" value "${value}") + set(${name}_PYTHON_STRING "${value}" PARENT_SCOPE) +endfunction() + +graalpy_sysconfig_value("OPT" "-DNDEBUG") +graalpy_sysconfig_value("ARFLAGS" "rc") +graalpy_sysconfig_value("USE_GNU_SOURCE" "-D_GNU_SOURCE=1") +set(GRAALPY_SYSCONFIG_CFLAGS_DEFAULT_VALUE "${GRAALPY_SYSCONFIG_OPT}") +if(WIN32) + set(GRAALPY_SYSCONFIG_CFLAGS_DEFAULT_VALUE "${GRAALPY_SYSCONFIG_CFLAGS_DEFAULT_VALUE} -DMS_WINDOWS -DPy_ENABLE_SHARED -DHAVE_DECLSPEC_DLL") +endif() +graalpy_sysconfig_value("CFLAGS_DEFAULT" "${GRAALPY_SYSCONFIG_CFLAGS_DEFAULT_VALUE}") +graalpy_sysconfig_value("CFLAGS" "${GRAALPY_SYSCONFIG_CFLAGS_DEFAULT} ${GRAALPY_SYSCONFIG_USE_GNU_SOURCE}") +graalpy_sysconfig_env_value("AR") +graalpy_sysconfig_env_value("CC") +graalpy_sysconfig_env_value("CXX") +graalpy_sysconfig_env_value("LD") +graalpy_sysconfig_env_value("NM") +graalpy_sysconfig_env_value("RANLIB") +if(APPLE) + graalpy_sysconfig_value("LDFLAGS" "-bundle -undefined dynamic_lookup") + graalpy_sysconfig_value("MACOSX_DEPLOYMENT_TARGET" "11") +else() + graalpy_sysconfig_empty("LDFLAGS") + graalpy_sysconfig_empty("MACOSX_DEPLOYMENT_TARGET") +endif() +if(WIN32) + graalpy_sysconfig_empty("CCSHARED") + graalpy_sysconfig_value("SHLIB_SUFFIX" ".pyd") + graalpy_sysconfig_value("EXE" ".exe") +else() + graalpy_sysconfig_value("CCSHARED" "-fPIC") + graalpy_sysconfig_value("SHLIB_SUFFIX" ".so") + graalpy_sysconfig_empty("EXE") +endif() +graalpy_sysconfig_env_value("LDCXXSHARED") +graalpy_sysconfig_env_value("LDSHARED") +graalpy_sysconfig_value("LDLIBRARY" "libpython.${GRAALPY_SYSCONFIG_SOABI}${GRAALPY_SYSCONFIG_SHLIB_SUFFIX}") + +set(GRAALPY_SYSCONFIG_STRING_NAMES + ABIFLAGS + AR + ARFLAGS + CC + CCSHARED + CFLAGS + CFLAGS_DEFAULT + CXX + EXE + EXT_SUFFIX + LD + LDCXXSHARED + LDLIBRARY + LDFLAGS + LDSHARED + MACOSX_DEPLOYMENT_TARGET + MULTIARCH + NM + OPT + RANLIB + SHLIB_SUFFIX + SOABI + USE_GNU_SOURCE + VERSION +) +foreach(name IN LISTS GRAALPY_SYSCONFIG_STRING_NAMES) + graalpy_sysconfig_python_string("GRAALPY_SYSCONFIG_${name}") +endforeach() + configure_file("pyconfig_template.h" "pyconfig.h" @ONLY) +configure_file("_sysconfigdata_template.py" "${GRAALPY_SYSCONFIGDATA_NAME}.py" @ONLY) diff --git a/graalpython/graalpy-pyconfig/_sysconfigdata_template.py b/graalpython/graalpy-pyconfig/_sysconfigdata_template.py new file mode 100644 index 0000000000..7db9c37367 --- /dev/null +++ b/graalpython/graalpy-pyconfig/_sysconfigdata_template.py @@ -0,0 +1,80 @@ +# Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# The Universal Permissive License (UPL), Version 1.0 +# +# Subject to the condition set forth below, permission is hereby granted to any +# person obtaining a copy of this software, associated documentation and/or +# data (collectively the "Software"), free of charge and under any and all +# copyright rights in the Software, and any and all patent rights owned or +# freely licensable by each licensor hereunder covering either (i) the +# unmodified Software as contributed to or provided by such licensor, or (ii) +# the Larger Works (as defined below), to deal in both +# +# (a) the Software, and +# +# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if +# one is included with the Software each a "Larger Work" to which the Software +# is contributed by such licensors), +# +# without restriction, including without limitation the rights to copy, create +# derivative works of, display, perform, and distribute the Software and make, +# use, sell, offer for sale, import, export, have made, and have sold the +# Software and the Larger Work(s), and to sublicense the foregoing rights on +# either these or other terms. +# +# This license is subject to the following condition: +# +# The above copyright notice and either this complete permission notice or at a +# minimum a reference to the UPL must be included in all copies or substantial +# portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +# system configuration generated and used by the sysconfig module + +build_time_vars = { + "ABIFLAGS": "@GRAALPY_SYSCONFIG_ABIFLAGS_PYTHON_STRING@", + "ARFLAGS": "@GRAALPY_SYSCONFIG_ARFLAGS_PYTHON_STRING@", + "CCSHARED": "@GRAALPY_SYSCONFIG_CCSHARED_PYTHON_STRING@", + "CFLAGS": "@GRAALPY_SYSCONFIG_CFLAGS_PYTHON_STRING@", + "CFLAGS_DEFAULT": "@GRAALPY_SYSCONFIG_CFLAGS_DEFAULT_PYTHON_STRING@", + "EXE": "@GRAALPY_SYSCONFIG_EXE_PYTHON_STRING@", + "EXT_SUFFIX": "@GRAALPY_SYSCONFIG_EXT_SUFFIX_PYTHON_STRING@", + "LDLIBRARY": "@GRAALPY_SYSCONFIG_LDLIBRARY_PYTHON_STRING@", + "LDFLAGS": "@GRAALPY_SYSCONFIG_LDFLAGS_PYTHON_STRING@", + "LIBPYTHON": "", + "LIBS": "", + "MACOSX_DEPLOYMENT_TARGET": "@GRAALPY_SYSCONFIG_MACOSX_DEPLOYMENT_TARGET_PYTHON_STRING@", + "MULTIARCH": "@GRAALPY_SYSCONFIG_MULTIARCH_PYTHON_STRING@", + "OPT": "@GRAALPY_SYSCONFIG_OPT_PYTHON_STRING@", + "Py_GIL_DISABLED": @GRAALPY_SYSCONFIG_GIL_DISABLED@, + "Py_DEBUG": 0, + "Py_ENABLE_SHARED": 0, + "Py_HASH_ALGORITHM": 0, + "SHLIB_SUFFIX": "@GRAALPY_SYSCONFIG_SHLIB_SUFFIX_PYTHON_STRING@", + "SO": "@GRAALPY_SYSCONFIG_EXT_SUFFIX_PYTHON_STRING@", + "SOABI": "@GRAALPY_SYSCONFIG_SOABI_PYTHON_STRING@", + "SYSLIBS": "", + "USE_GNU_SOURCE": "@GRAALPY_SYSCONFIG_USE_GNU_SOURCE_PYTHON_STRING@", + "VERSION": "@GRAALPY_SYSCONFIG_VERSION_PYTHON_STRING@", +} + +for _key, _value in { + "AR": "@GRAALPY_SYSCONFIG_AR_PYTHON_STRING@", + "CC": "@GRAALPY_SYSCONFIG_CC_PYTHON_STRING@", + "CXX": "@GRAALPY_SYSCONFIG_CXX_PYTHON_STRING@", + "LD": "@GRAALPY_SYSCONFIG_LD_PYTHON_STRING@", + "LDCXXSHARED": "@GRAALPY_SYSCONFIG_LDCXXSHARED_PYTHON_STRING@", + "LDSHARED": "@GRAALPY_SYSCONFIG_LDSHARED_PYTHON_STRING@", + "NM": "@GRAALPY_SYSCONFIG_NM_PYTHON_STRING@", + "RANLIB": "@GRAALPY_SYSCONFIG_RANLIB_PYTHON_STRING@", +}.items(): + if _value: + build_time_vars[_key] = _value diff --git a/graalpython/graalpy-versions/CMakeLists.txt b/graalpython/graalpy-versions/CMakeLists.txt index aff3acc34c..a2ba93cafe 100644 --- a/graalpython/graalpy-versions/CMakeLists.txt +++ b/graalpython/graalpy-versions/CMakeLists.txt @@ -32,8 +32,11 @@ project(graalpy-versions LANGUAGES NONE) if (NOT DEFINED GRAALPY_VER) message(FATAL_ERROR "GRAALPY_VER needs to be set") endif() +if (NOT DEFINED GRAALPY_ABIFLAGS) + set(GRAALPY_ABIFLAGS "") +endif() # Generates file 'graalpy_versions' with the given content. # The file will be created if it does not exist and will only be updated if the # content changes. -file(GENERATE OUTPUT "graalpy_versions" CONTENT "${GRAALPY_VER}") +file(GENERATE OUTPUT "graalpy_versions" CONTENT "${GRAALPY_VER}\n${GRAALPY_ABIFLAGS}") diff --git a/graalpython/lib-graalpython/_sysconfig.py b/graalpython/lib-graalpython/_sysconfig.py index 15e55419d1..992fd3f3d1 100644 --- a/graalpython/lib-graalpython/_sysconfig.py +++ b/graalpython/lib-graalpython/_sysconfig.py @@ -1,4 +1,4 @@ -# Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # The Universal Permissive License (UPL), Version 1.0 @@ -38,27 +38,32 @@ # SOFTWARE. -def _get_posix_vars(): - """Initialize the config vars as appropriate for POSIX systems.""" - import _imp - import sys +def _append_flags(g, key, flags): + value = g.get(key, "") + parts = value.split() + for flag in flags: + if flag and flag not in parts: + parts.append(flag) + g[key] = " ".join(parts) + + +def _setdefault(g, key, value): + if key not in g: + g[key] = value + + +def _update_posix_vars(config_vars=None): + """Add runtime path-dependent config vars.""" import os - darwin_native = sys.platform == "darwin" - win32_native = sys.platform == "win32" + import sys - # note: this must be kept in sync with _imp.extension_suffixes - so_abi = sys.implementation.cache_tag + "-native-" + sys.implementation._multiarch - if win32_native: - so_ext = ".pyd" - else: - so_ext = ".so" - assert _imp.extension_suffixes()[0] == "." + so_abi + so_ext, "mismatch between extension suffix to _imp.extension_suffixes" + if config_vars is None: + config_vars = {} + win32_native = sys.platform == "win32" + darwin_native = sys.platform == "darwin" get_toolchain = __graalpython__.determine_system_toolchain().get - toolchain_cxx = get_toolchain('CXX') - have_cxx = toolchain_cxx is not None - if win32_native: python_inc = os.path.join(os.path.normpath(sys.base_prefix), 'Include') else: @@ -68,84 +73,30 @@ def _get_posix_vars(): f'python{sys.version_info[0]}.{sys.version_info[1]}{sys.abiflags}' ) - fpic = "" if win32_native else "-fPIC" - - g = {} - g['CC'] = get_toolchain('CC') - g['CXX'] = toolchain_cxx if have_cxx else get_toolchain('CC') + ' --driver-mode=g++' - opt_flags = ["-DNDEBUG"] - g['OPT'] = ' '.join(opt_flags) - g['INCLUDEPY'] = python_inc - g['CONFINCLUDEPY'] = python_inc - g['CPPFLAGS'] = '-I. -I' + python_inc - gnu_source = "-D_GNU_SOURCE=1" - g['USE_GNU_SOURCE'] = gnu_source - cflags_default = list(opt_flags) + _setdefault(config_vars, 'CC', get_toolchain('CC')) + _setdefault(config_vars, 'CXX', get_toolchain('CXX')) + _setdefault(config_vars, 'AR', get_toolchain('AR')) + _setdefault(config_vars, 'RANLIB', get_toolchain('RANLIB')) + _setdefault(config_vars, 'LD', get_toolchain('LD')) + _setdefault(config_vars, 'NM', get_toolchain('NM')) + _setdefault(config_vars, 'INCLUDEPY', python_inc) + _setdefault(config_vars, 'CONFINCLUDEPY', python_inc) + _append_flags(config_vars, 'CPPFLAGS', ['-I.', '-I' + python_inc]) if win32_native: - cflags_default += ["-DMS_WINDOWS", "-DPy_ENABLE_SHARED", "-DHAVE_DECLSPEC_DLL"] - g['CFLAGS_DEFAULT'] = ' '.join(cflags_default) - g['CFLAGS'] = ' '.join(cflags_default + [gnu_source]) - g['LDFLAGS'] = "" - g['LIBS'] = "" - g['SYSLIBS'] = "" - g['CCSHARED'] = fpic - if darwin_native: - # MACOSX_DEPLOYMENT_TARGET is taken from the minimum version we build - # GraalPy for, which is currently BigSur - g['MACOSX_DEPLOYMENT_TARGET'] = "11" - else: - g['MACOSX_DEPLOYMENT_TARGET'] = "" - if darwin_native: - g['LDFLAGS'] = "-bundle -undefined dynamic_lookup" - ldshared_common = g['LDFLAGS'] - g['LIBPYTHON'] = '' - elif win32_native: - g['LDFLAGS'] = f"-L{__graalpython__.capi_home.replace(os.path.sep, '/')}" - ldshared_common = f"-shared {fpic} {g['LDFLAGS']}" + lib_path = f"-L{__graalpython__.capi_home.replace(os.path.sep, '/')}" + _append_flags(config_vars, 'LDFLAGS', [lib_path]) + if 'LDSHARED' in config_vars: + _append_flags(config_vars, 'LDSHARED', [lib_path]) + if 'LDCXXSHARED' in config_vars: + _append_flags(config_vars, 'LDCXXSHARED', [lib_path]) + ldshared_common = f"-shared {config_vars.get('CCSHARED', '')} {config_vars.get('LDFLAGS', '')}" + elif darwin_native: + ldshared_common = config_vars.get('LDFLAGS', '') else: - ldshared_common = f"-shared {fpic}" - g['LIBPYTHON'] = '' - g['LDSHARED'] = f"{g['CC']} {ldshared_common}" - g['LDCXXSHARED'] = f"{g['CXX']} {ldshared_common}" - g['SOABI'] = so_abi - g['EXT_SUFFIX'] = "." + so_abi + so_ext - g['SHLIB_SUFFIX'] = so_ext - g['SO'] = "." + so_abi + so_ext # deprecated in Python 3, for backward compatibility - g['AR'] = get_toolchain('AR') - g['RANLIB'] = get_toolchain('RANLIB') - g['ARFLAGS'] = "rc" - g['LD'] = get_toolchain('LD') - g['EXE'] = ".exe" if win32_native else "" - g['LIBDIR'] = os.path.join(sys.prefix, 'lib') - g['VERSION'] = ".".join(sys.version.split(".")[:2]) - g['Py_HASH_ALGORITHM'] = 0 # does not give any specific info about the hashing algorithm - g['NM'] = get_toolchain('NM') - g['MULTIARCH'] = sys.implementation._multiarch - g['ABIFLAGS'] = "" - g['Py_DEBUG'] = 0 - g['Py_ENABLE_SHARED'] = 0 - g['LIBDIR'] = __graalpython__.capi_home - g['LIBDEST'] = __graalpython__.capi_home - g['LDLIBRARY'] = 'libpython.' + so_abi + so_ext - g['LIBPL'] = __graalpython__.capi_home.replace(os.path.sep, '/') - return g - - -def make_sysconfigdata(): - # the sysconfigdata module name matches what's computed in stdlib's sysconfig.py - import sys - multiarch = getattr(sys.implementation, '_multiarch', '') - name = f'_sysconfigdata_{sys.abiflags}_{sys.platform}_{multiarch}' - sys.modules[name] = mod = type(sys)(name) - no_default = object() - def __getattr__(key, default=no_default): - if key == "build_time_vars": - return _get_posix_vars() - elif default is no_default: - raise AttributeError(key) - else: - return default - mod.__getattr__ = __getattr__ - - -make_sysconfigdata() + ldshared_common = f"-shared {config_vars.get('CCSHARED', '')}" + _setdefault(config_vars, 'LDSHARED', f"{config_vars['CC']} {ldshared_common}".rstrip()) + _setdefault(config_vars, 'LDCXXSHARED', f"{config_vars['CXX']} {ldshared_common}".rstrip()) + _setdefault(config_vars, 'LIBDIR', __graalpython__.capi_home) + _setdefault(config_vars, 'LIBDEST', __graalpython__.capi_home) + _setdefault(config_vars, 'LIBPL', __graalpython__.capi_home.replace(os.path.sep, '/')) + return config_vars diff --git a/graalpython/lib-graalpython/modules/_sysconfigdata.py b/graalpython/lib-graalpython/modules/_sysconfigdata.py deleted file mode 100644 index 1f1f5860a7..0000000000 --- a/graalpython/lib-graalpython/modules/_sysconfigdata.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# The Universal Permissive License (UPL), Version 1.0 -# -# Subject to the condition set forth below, permission is hereby granted to any -# person obtaining a copy of this software, associated documentation and/or -# data (collectively the "Software"), free of charge and under any and all -# copyright rights in the Software, and any and all patent rights owned or -# freely licensable by each licensor hereunder covering either (i) the -# unmodified Software as contributed to or provided by such licensor, or (ii) -# the Larger Works (as defined below), to deal in both -# -# (a) the Software, and -# -# (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if -# one is included with the Software each a "Larger Work" to which the Software -# is contributed by such licensors), -# -# without restriction, including without limitation the rights to copy, create -# derivative works of, display, perform, and distribute the Software and make, -# use, sell, offer for sale, import, export, have made, and have sold the -# Software and the Larger Work(s), and to sublicense the foregoing rights on -# either these or other terms. -# -# This license is subject to the following condition: -# -# The above copyright notice and either this complete permission notice or at a -# minimum a reference to the UPL must be included in all copies or substantial -# portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - - -from _sysconfig import _get_posix_vars - -build_time_vars = _get_posix_vars() diff --git a/graalpython/lib-python/3/sysconfig.py b/graalpython/lib-python/3/sysconfig.py index b1352ef0b8..4ccf80b2de 100644 --- a/graalpython/lib-python/3/sysconfig.py +++ b/graalpython/lib-python/3/sysconfig.py @@ -530,9 +530,16 @@ def _init_posix(vars): """Initialize the module as appropriate for POSIX systems.""" # _sysconfigdata is generated at build time, see _generate_posix_vars() name = _get_sysconfigdata_name() - _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) - build_time_vars = _temp.build_time_vars - vars.update(build_time_vars) + # GraalPy change: ignore missing _sysconfigdata, it can happen on an unsupported platform + try: + _temp = __import__(name, globals(), locals(), ['build_time_vars'], 0) + build_time_vars = _temp.build_time_vars + vars.update(build_time_vars) + except ModuleNotFoundError as e: + pass + # GraalPy change: merge our runtime vars + import _sysconfig + _sysconfig._update_posix_vars(vars) def _init_non_posix(vars): """Initialize the module as appropriate for NT""" diff --git a/mx.graalpython/mx_graalpython.py b/mx.graalpython/mx_graalpython.py index 8392bc9d86..15089d12c2 100644 --- a/mx.graalpython/mx_graalpython.py +++ b/mx.graalpython/mx_graalpython.py @@ -91,6 +91,9 @@ def get_boolean_env(name, default=False): SUITE_COMPILER = mx.suite("compiler", fatalIfMissing=False) GRAALPY_ABI_VERSION = 'graalpy250' +GRAALPY_ABIFLAGS = os.environ.get('GRAALPY_ABIFLAGS', '') +if not re.fullmatch(r'[A-Za-z0-9_]*', GRAALPY_ABIFLAGS): + mx.abort('GRAALPY_ABIFLAGS may only contain ASCII letters, digits, and underscores') IS_RELEASE = SUITE.is_release() FULL_GRAAL_VERSION = SUITE.release_version() GRAAL_VERSION = FULL_GRAAL_VERSION if IS_RELEASE else FULL_GRAAL_VERSION[:-len('-dev')] @@ -2060,23 +2063,50 @@ def graalpy_cmake_build_type(*_): return 'Debug' if 'GRAALPY_NATIVE_DEBUG_BUILD' in os.environ else 'Release' -def graalpy_ext(*_): - os = mx_subst.path_substitutions.substitute('') +def _graalpy_sysconfig_platform(os): + if os == 'darwin': + return 'darwin' + if os == 'windows': + return 'win32' + return 'linux' + + +def _graalpy_sysconfig_arch(): arch = mx_subst.path_substitutions.substitute('') if arch == 'amd64': # be compatible with CPython's designation # (see also: 'PythonUtils.getPythonArch') arch = 'x86_64' + return arch - # 'pyos' also needs to be compatible with CPython's designation. - # See class 'com.oracle.graal.python.annotations.PythonOS' - # In this case, we can just use 'sys.platform' of the Python running MX. - pyos = sys.platform +def graalpy_soabi(*_): + os = mx_subst.path_substitutions.substitute('') + pyos = _graalpy_sysconfig_platform(os) + return f'{abi_version()}{graalpy_abiflags()}-native-{graalpy_multiarch(os=pyos)}' + + +def graalpy_ext(*_): + os = mx_subst.path_substitutions.substitute('') # on Windows we use '.pyd' else '.so' but never '.dylib' (similar to CPython): # https://github.com/python/cpython/issues/37510 ext = 'pyd' if os == 'windows' else 'so' - return f'.{abi_version()}-native-{arch}-{pyos}.{ext}' + return f'.{graalpy_soabi()}.{ext}' + + +def graalpy_sysconfigdata(*_): + os = mx_subst.path_substitutions.substitute('') + pyos = _graalpy_sysconfig_platform(os) + return f'_sysconfigdata_{graalpy_abiflags()}_{pyos}_{graalpy_multiarch(os=pyos)}' + + +def graalpy_multiarch(*_, os=None): + pyos = os if os is not None else _graalpy_sysconfig_platform(mx_subst.path_substitutions.substitute('')) + return f'{_graalpy_sysconfig_arch()}-{pyos}' + + +def graalpy_abiflags(*_): + return GRAALPY_ABIFLAGS def dev_tag(_=None): @@ -2118,6 +2148,14 @@ def abi_version(): mx_subst.path_substitutions.register_no_arg('graalpy_ext', graalpy_ext) mx_subst.results_substitutions.register_no_arg('graalpy_ext', graalpy_ext) +mx_subst.path_substitutions.register_no_arg('graalpy_abiflags', graalpy_abiflags) +mx_subst.results_substitutions.register_no_arg('graalpy_abiflags', graalpy_abiflags) +mx_subst.path_substitutions.register_no_arg('graalpy_soabi', graalpy_soabi) +mx_subst.results_substitutions.register_no_arg('graalpy_soabi', graalpy_soabi) +mx_subst.path_substitutions.register_no_arg('graalpy_multiarch', graalpy_multiarch) +mx_subst.results_substitutions.register_no_arg('graalpy_multiarch', graalpy_multiarch) +mx_subst.path_substitutions.register_no_arg('graalpy_sysconfigdata', graalpy_sysconfigdata) +mx_subst.results_substitutions.register_no_arg('graalpy_sysconfigdata', graalpy_sysconfigdata) mx_subst.results_substitutions.register_no_arg('graalpy_cmake_build_type', graalpy_cmake_build_type) mx_subst.string_substitutions.register_no_arg('bcflags', bcflags) diff --git a/mx.graalpython/suite.py b/mx.graalpython/suite.py index 8333b42226..9d32268b6c 100644 --- a/mx.graalpython/suite.py +++ b/mx.graalpython/suite.py @@ -652,6 +652,7 @@ "ninja_targets": ["all"], "cmakeConfig": { "GRAALPY_VER": "", + "GRAALPY_ABIFLAGS": "", }, "results": [ "graalpy_versions" @@ -670,9 +671,17 @@ "cmakeConfig": { "GRAALPY_VERSION": "", "GRAALPY_VERSION_NUM": "", + "GRAALPY_SYSCONFIGDATA_NAME": "", + "GRAALPY_SYSCONFIG_ABIFLAGS": "", + "GRAALPY_SYSCONFIG_EXT_SUFFIX": "", + "GRAALPY_SYSCONFIG_GIL_DISABLED": "0", + "GRAALPY_SYSCONFIG_MULTIARCH": "", + "GRAALPY_SYSCONFIG_SOABI": "", + "GRAALPY_SYSCONFIG_VERSION": "", }, "results": [ "pyconfig.h", + ".py", ], }, @@ -1339,6 +1348,7 @@ "./META-INF/resources///Lib/venv/scripts/nt/graalpy.exe": "dependency:python-venvlauncher", "./META-INF/resources///Lib/venv/scripts/nt/python.exe": "dependency:python-venvlauncher", "./META-INF/resources///include/": "dependency:graalpy-pyconfig/-//pyconfig.h", + "./META-INF/resources///lib-graalpython/modules/": "dependency:graalpy-pyconfig/-//.py", }, }, }, @@ -1349,7 +1359,8 @@ "dependency:GRAALPYTHON_NATIVE_LIBS///*", ], "./META-INF/resources///lib/python/venv/scripts/macos/graalpy": "dependency:python-macos-launcher", - "./META-INF/resources///include/python/": "dependency:graalpy-pyconfig/-//pyconfig.h", + "./META-INF/resources///include/python/": "dependency:graalpy-pyconfig/-//pyconfig.h", + "./META-INF/resources///lib/graalpy/modules/": "dependency:graalpy-pyconfig/-//.py", } } }, @@ -1359,7 +1370,8 @@ "./META-INF/resources///lib/graalpy/": [ "dependency:GRAALPYTHON_NATIVE_LIBS///*", ], - "./META-INF/resources///include/python/": "dependency:graalpy-pyconfig/-//pyconfig.h", + "./META-INF/resources///include/python/": "dependency:graalpy-pyconfig/-//pyconfig.h", + "./META-INF/resources///lib/graalpy/modules/": "dependency:graalpy-pyconfig/-//.py", }, }, }, @@ -1424,7 +1436,7 @@ "./lib/graalpy/": [ "dependency:GRAALPYTHON_LIBGRAALPY_RESOURCES/META-INF/resources/libgraalpy/*", ], - "./include/python/": [ + "./include/python/": [ "dependency:GRAALPYTHON_INCLUDE_RESOURCES/META-INF/resources/include/*", ], "./": [