From 55dd56ee200a236e7bd688d1d97724e65c9e00da Mon Sep 17 00:00:00 2001 From: Manjunath Matti Date: Fri, 17 Jul 2026 12:32:06 +0530 Subject: [PATCH 1/4] Fix build failures with GCC 12+ and Python 3.7+, add GCC 10+ plugin events GCC 12 removed ggc_force_collect in favor of an enum-based ggc_collect() API. Python 3.7 made PyEval_InitThreads() a no-op, Python 3.12 deprecated Py_UnbufferedStdioFlag, and Python 3.13+ provides PyUnicode_AsUTF8 as the public replacement for _PyUnicode_AsString. GCC 10 added PLUGIN_START_PARSE_FUNCTION, PLUGIN_FINISH_PARSE_FUNCTION, and PLUGIN_INCLUDE_FILE events that were not yet wired up. gcc-python-compat.h: * Guard extern declaration of ggc_force_collect with #if (GCC_VERSION < 12000); the variable was removed in GCC 12 (gcc/ggc-internal.h ChangeLog-2021). gcc-python-wrapper.c (force_gcc_gc): * For GCC >= 12, call ggc_collect(GGC_COLLECT_FORCE) instead of setting ggc_force_collect and calling ggc_collect(). gcc-python.c (plugin_init): * Guard Py_UnbufferedStdioFlag assignment with #if PY_VERSION_HEX < 0x030c0000; deprecated in Python 3.12. * Guard PyEval_InitThreads() call with #if PY_VERSION_HEX < 0x03070000; no-op since Python 3.7, deprecated since Python 3.9. gcc-python.h: * For Python >= 3.13, define PyGccString_AsString as PyUnicode_AsUTF8 (public API) instead of _PyUnicode_AsString (private alias). gcc-python-callbacks.c: * Add PyGcc_CallbackFor_PLUGIN_INCLUDE_FILE to pass the included filename as a Python string to registered callbacks. * Wire up PLUGIN_START_PARSE_FUNCTION and PLUGIN_FINISH_PARSE_FUNCTION (gcc_data is a tree function decl) using the existing PyGcc_CallbackFor_tree handler. * Wire up PLUGIN_INCLUDE_FILE using the new handler. All three events were added in GCC 10. Co-Authored-By: Claude Sonnet 4.6 --- gcc-python-callbacks.c | 42 ++++++++++++++++++++++++++++++++++++++++++ gcc-python-compat.h | 5 ++++- gcc-python-wrapper.c | 5 ++++- gcc-python.c | 9 +++++++++ gcc-python.h | 5 +++++ 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/gcc-python-callbacks.c b/gcc-python-callbacks.c index e8a42aa8..cb004633 100644 --- a/gcc-python-callbacks.c +++ b/gcc-python-callbacks.c @@ -198,6 +198,24 @@ PyGcc_CallbackFor_tree(void *gcc_data, void *user_data) } +static void +PyGcc_CallbackFor_PLUGIN_INCLUDE_FILE(void *gcc_data, void *user_data) +{ + PyGILState_STATE gstate; + const char *filename = (const char *)gcc_data; + PyObject *filename_obj; + + gstate = PyGILState_Ensure(); + + filename_obj = filename ? PyGccString_FromString(filename) : Py_None; + if (filename == NULL) + Py_INCREF(Py_None); + + PyGcc_FinishInvokingCallback(gstate, + 1, filename_obj, + user_data); +} + static void PyGcc_CallbackFor_PLUGIN_ATTRIBUTES(void *gcc_data, void *user_data) { @@ -386,6 +404,30 @@ PyGcc_RegisterCallback(PyObject *self, PyObject *args, PyObject *kwargs) break; #endif /* GCC_PYTHON_PLUGIN_CONFIG_has_PLUGIN_FINISH_DECL */ + /* PLUGIN_START_PARSE_FUNCTION and PLUGIN_FINISH_PARSE_FUNCTION were + added in GCC 10; gcc_data is a tree (the function decl). */ + case PLUGIN_START_PARSE_FUNCTION: + register_callback("python", // FIXME + (enum plugin_event)event, + PyGcc_CallbackFor_tree, + closure); + break; + + case PLUGIN_FINISH_PARSE_FUNCTION: + register_callback("python", // FIXME + (enum plugin_event)event, + PyGcc_CallbackFor_tree, + closure); + break; + + /* PLUGIN_INCLUDE_FILE: gcc_data is a const char* filename. */ + case PLUGIN_INCLUDE_FILE: + register_callback("python", // FIXME + (enum plugin_event)event, + PyGcc_CallbackFor_PLUGIN_INCLUDE_FILE, + closure); + break; + default: PyErr_Format(PyExc_ValueError, "event type %i invalid (or not wired up yet)", event); return NULL; diff --git a/gcc-python-compat.h b/gcc-python-compat.h index bcb733fc..ff201110 100644 --- a/gcc-python-compat.h +++ b/gcc-python-compat.h @@ -61,8 +61,11 @@ dump_generic_node (pretty_printer *buffer, tree node, int spc, dump_flags_t flag bool is_stmt); #endif -/* Within gcc/gcc-internal.h, not exposed by plugin API */ +/* Within gcc/gcc-internal.h, not exposed by plugin API. + Removed in GCC 12; replaced by ggc_collect(GGC_COLLECT_FORCE). */ +#if (GCC_VERSION < 12000) extern bool ggc_force_collect; +#endif /* From c-family/c-common.h */ #if GCC_VERSION < 4008 diff --git a/gcc-python-wrapper.c b/gcc-python-wrapper.c index 76e1db35..da55729c 100644 --- a/gcc-python-wrapper.c +++ b/gcc-python-wrapper.c @@ -337,11 +337,14 @@ PyGcc_wrapper_init(void) static void force_gcc_gc(void) { +#if (GCC_VERSION >= 12000) + ggc_collect(GGC_COLLECT_FORCE); +#else bool stored = ggc_force_collect; - ggc_force_collect = true; ggc_collect(); ggc_force_collect = stored; +#endif } PyObject * diff --git a/gcc-python.c b/gcc-python.c index 104d412a..6228d377 100644 --- a/gcc-python.c +++ b/gcc-python.c @@ -781,7 +781,12 @@ plugin_init (struct plugin_name_args *plugin_info, Suppress the buffering, to better support mixed gcc/python output: */ + /* Py_UnbufferedStdioFlag was deprecated in Python 3.12; no replacement + needed since Python 3.7+ initializes in unbuffered mode by default + when PYTHONUNBUFFERED is set, and plugins can set it in the env. */ +#if PY_VERSION_HEX < 0x030c0000 Py_UnbufferedStdioFlag = 1; +#endif #endif PyImport_AppendInittab("gcc", PyInit_gcc); @@ -794,7 +799,11 @@ plugin_init (struct plugin_name_args *plugin_info, PyGcc_globals.module = PyImport_ImportModule("gcc"); + /* PyEval_InitThreads() was made a no-op in Python 3.7 and is + deprecated since 3.9; skip it on 3.7+. */ +#if PY_VERSION_HEX < 0x03070000 PyEval_InitThreads(); +#endif if (!PyGcc_init_gcc_module(plugin_info)) { return 1; diff --git a/gcc-python.h b/gcc-python.h index 599036b0..b91a6e02 100644 --- a/gcc-python.h +++ b/gcc-python.h @@ -386,7 +386,12 @@ PyGcc_GetReprOfAttribute(PyObject *obj, const char *attrname); #define PyGccString_FromFormat PyUnicode_FromFormat #define PyGccString_FromString PyUnicode_FromString #define PyGccString_FromString_and_size PyUnicode_FromStringAndSize +/* _PyUnicode_AsString is a deprecated private alias for PyUnicode_AsUTF8 */ +#if PY_VERSION_HEX >= 0x030d0000 +#define PyGccString_AsString PyUnicode_AsUTF8 +#else #define PyGccString_AsString _PyUnicode_AsString +#endif #define PyGccInt_FromLong PyLong_FromLong #define PyGccInt_Check PyLong_Check #define PyGccInt_AsLong PyLong_AsLong From d3039d65f599deb03a27a3de2c4d957f0f9ca62b Mon Sep 17 00:00:00 2001 From: Manjunath Matti Date: Tue, 15 Sep 2026 23:47:49 +0530 Subject: [PATCH 2/4] README: document current maintenance baseline The README and the Sphinx requirements page still describe the plugin as supporting GCC 4.6 or later (tested up to GCC 8) and Python 2.7 or 3.2+. That support is historical: current GCC releases no longer build the plugin, and Python 2 is long end-of-life. Document the maintenance baseline instead: Python 3 is the target, GCC plugin compatibility is established per GCC release, no current GCC release is claimed as supported until it has been built and tested, and x86_64 and ppc64le are the intended test platforms. README.rst: * Add a "Maintenance status" section. * Requirements: replace the GCC 4.6+ and Python 2.7/3.2+ claims. Note that the plugin headers must match the exact GCC version, and that the plugin is compiled as C++, so the matching C++ compiler is required. * Usage: replace the x86_64-only note with the intended test platforms. docs/basics.rst: * Requirements: same GCC and Python changes as README.rst. * Build-time dependencies: drop the Python 2 package list; use dnf and add gcc-c++. Co-Authored-By: Claude Opus 5 --- README.rst | 39 ++++++++++++++++++++++++++++++--------- docs/basics.rst | 20 +++++++++----------- 2 files changed, 39 insertions(+), 20 deletions(-) diff --git a/README.rst b/README.rst index 714177b9..651b1734 100644 --- a/README.rst +++ b/README.rst @@ -28,17 +28,38 @@ The documentation can be seen at: http://gcc-python-plugin.readthedocs.io/en/latest/index.html -Requirements ------------- +Maintenance status +------------------ + +The plugin is being modernized and maintained at +https://github.com/mmatti-sw/gcc-python-plugin. + +It was originally developed against GCC 4.6 through 8 and Python 2.7 and 3.x. +That support is historical and is no longer maintained. + +GCC does not provide a stable plugin API: its internals change from one +release to the next, so compatibility has to be established, and tested, +separately for each GCC release. Compatibility with current GCC releases is +being re-established, and no GCC release is claimed as supported until the +plugin has been built and its test suite run against it. Verified GCC +releases will be listed here. -* GCC: 4.6 or later (it uses APIs that weren't exposed to plugins in 4.5) +Python 3 is the maintenance target; Python 2 is no longer supported. - * tested with 4.8, 4.9, 5, 6, 7, and 8. +x86_64 and ppc64le are the intended test platforms. + + +Requirements +------------ -* GCC plugin development package: usually available in distribution packages - such as ``gcc-N-plugin-dev`` or ``gcc-plugin-devel``. +* GCC, with the plugin development headers for that exact GCC version: usually + available in distribution packages such as ``gcc-N-plugin-dev`` or + ``gcc-plugin-devel``. The plugin is compiled as C++, so the matching C++ + compiler (e.g. ``g++-N``) is needed as well. See "Maintenance status" above + for which GCC releases have been verified. -* Python: requires 2.7 or 3.2 or later +* Python 3, with its development headers (e.g. ``python3-dev`` or + ``python3-devel``) * "six": The libcpychecker code uses the "six_" Python compatibility library to smooth over Python 2 vs Python 3 differences, both at build-time and @@ -62,8 +83,8 @@ You can also use:: to demonstrate the new compiler errors. -Development has been on x86_64 and I don't know to what extent it will be -compatible with other architectures. +The plugin was originally developed on x86_64. x86_64 and ppc64le are the +intended test platforms; other architectures have not been verified. There isn't an installer yet. In theory you should be able to add these arguments to the gcc invocation:: diff --git a/docs/basics.rst b/docs/basics.rst index c91c86d7..d5a690ce 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -23,9 +23,15 @@ Requirements The plugin has the following requirements: - * GCC: 4.6 or later (it uses APIs that weren't exposed to plugins in 4.5) + * GCC, with the plugin development headers for that exact GCC version + (e.g. the ``gcc-N-plugin-dev`` or ``gcc-plugin-devel`` package), and the + matching C++ compiler, since the plugin is compiled as C++. - * Python: requires 2.7 or 3.2 or later + GCC's internals change between releases, so compatibility is established + and tested separately for each GCC release. Support for GCC 4.6 through + 8 is historical; see the README for the GCC releases currently verified. + + * Python 3, with its development headers. Python 2 is no longer supported. * "six": The libcpychecker code uses the "six" Python compatibility library to smooth over Python 2 vs Python 3 differences, both at build-time and @@ -79,15 +85,7 @@ On a Fedora box you can install them by running the following as root: .. code-block:: bash - yum install gcc-plugin-devel python-devel python-six python-pygments graphviz - -for building against Python 2, or: - -.. code-block:: bash - - yum install gcc-plugin-devel python3-devel python3-six python3-pygments graphviz - -when building for Python 3. + dnf install gcc-c++ gcc-plugin-devel python3-devel python3-six python3-pygments graphviz Building the code ------------------ From 133f1c80562083d96dd2974759908d42648896d4 Mon Sep 17 00:00:00 2001 From: Manjunath Matti Date: Tue, 15 Sep 2026 23:47:50 +0530 Subject: [PATCH 3/4] Makefile: default to Python 3 The Makefile defaults to "python" and "python-config". Many current distributions provide neither command, and where they exist they may refer to Python 2 or to an unintended interpreter. Default to python3 and python3-config instead. PYTHON and PYTHON_CONFIG can still be overridden on the make command line. Makefile: * Default PYTHON to python3 and PYTHON_CONFIG to python3-config. * Update the comment describing the defaults, and drop the commented-out python3 example, which is now the default. docs/basics.rst: * Describe the new default and how to select another Python 3. Co-Authored-By: Claude Opus 5 --- Makefile | 11 ++++------- docs/basics.rst | 11 +++++------ 2 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Makefile b/Makefile index 3a962573..4ef2fe3f 100644 --- a/Makefile +++ b/Makefile @@ -83,19 +83,16 @@ GENERATOR_DEPS=cpybuilder.py wrapperbuilder.py print-gcc-version # as we're linking against, and that the cpychecker will be testing that same # version of Python # -# By default, build against "python", using "python-config" to query for +# By default, build against "python3", using "python3-config" to query for # compilation options. You can override this by passing other values for # PYTHON and PYTHON_CONFIG when invoking "make" (or by simply hacking up this # file): e.g. -# make PYTHON=python3 PYTHON_CONFIG=python3-config all +# make PYTHON=python3.12 PYTHON_CONFIG=python3.12-config all # The python interpreter to use: -PYTHON=python +PYTHON=python3 # The python-config executable to use: -PYTHON_CONFIG=python-config - -#PYTHON=python3 -#PYTHON_CONFIG=python3-config +PYTHON_CONFIG=python3-config #PYTHON=python-debug #PYTHON_CONFIG=python-debug-config diff --git a/docs/basics.rst b/docs/basics.rst index d5a690ce..95c7c034 100644 --- a/docs/basics.rst +++ b/docs/basics.rst @@ -111,15 +111,14 @@ You can also use:: to demonstrate the new compiler errors. -By default, the `Makefile` builds the plugin using the first ``python-config`` -tool found in `$PATH` (e.g. `/usr/bin/python-config`), which is typically the -system copy of Python 2. You can override this (e.g. to build against -Python 3) by overriding the `PYTHON` and `PYTHON_CONFIG` Makefile variables -with: +By default, the `Makefile` builds the plugin with the ``python3`` and +``python3-config`` tools found in `$PATH`. You can select a different Python 3 +installation by overriding the `PYTHON` and `PYTHON_CONFIG` Makefile variables, +e.g.: .. code-block:: bash - make PYTHON=python3 PYTHON_CONFIG=python3-config + make PYTHON=python3.12 PYTHON_CONFIG=python3.12-config There isn't a well-defined process yet for installing the plugin (though the rpm specfile in the source tree contains some work-in-progress towards this). From 071b99c2b8351a345c079de4a66186c67c981473 Mon Sep 17 00:00:00 2001 From: Manjunath Matti Date: Tue, 15 Sep 2026 23:51:24 +0530 Subject: [PATCH 4/4] CI: replace obsolete Travis configuration .travis.yml targets Travis CI's long-retired Ubuntu Precise/Trusty images, builds with GCC 4.8 through 9, and installs Python 2 packages. Replace it with a GitHub Actions workflow. Following the maintenance plan, start with one reproducible job rather than a large matrix: Linux x86_64, Python 3 and a single GCC release, in a pinned ubuntu:22.04 container. GCC 11 and later do not build the current sources (checked with GCC 11 through 16), so the job uses GCC 10, the release before those API changes. Ubuntu 22.04 provides GCC 10 together with Python 3.10; the test tooling still relies on distutils and configparser.SafeConfigParser, which Python 3.12 removed. The job builds the plugin serially, because the Makefile is not yet safe for parallel builds, checks that GCC loads the plugin, and runs the cpybuilder and dejagnu selftests. The main test suite also runs, but does not fail the job for now, since some tests are expected to fail with current Python releases. Further GCC releases and architectures will be added to the matrix as their compatibility fixes land. .travis.yml: Remove. .github/workflows/ci.yml: New file. Co-Authored-By: Claude Opus 5 --- .github/workflows/ci.yml | 81 ++++++++++++++++++++++++++++++++++++ .travis.yml | 90 ---------------------------------------- 2 files changed, 81 insertions(+), 90 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..2f4614e2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,81 @@ +# Continuous integration for gcc-python-plugin +# +# Stage 1 of the CI plan: one reproducible Linux x86_64 job using Python 3 +# and a single GCC release. Further GCC releases (14, 15, 16 and trunk) and +# architectures (ppc64le, aarch64) will be added as their compatibility work +# lands. + +name: CI + +on: + push: + pull_request: + workflow_dispatch: + +permissions: + contents: read + +jobs: + build-and-test: + name: GCC ${{ matrix.gcc }}, Python 3, ${{ matrix.image }} + runs-on: ubuntu-latest + container: ${{ matrix.image }} + strategy: + fail-fast: false + matrix: + include: + # GCC 11 and later do not build the current sources yet, so use + # GCC 10. Ubuntu 22.04 provides it together with Python 3.10; the + # test tooling still relies on distutils and + # configparser.SafeConfigParser, which Python 3.12 removed. + - image: ubuntu:22.04 + gcc: 10 + defaults: + run: + shell: bash + env: + DEBIAN_FRONTEND: noninteractive + steps: + - name: Install build dependencies + run: | + apt-get update + apt-get install -y --no-install-recommends \ + ca-certificates git make \ + gcc-${{ matrix.gcc }} g++-${{ matrix.gcc }} \ + gcc-${{ matrix.gcc }}-plugin-dev \ + python3 python3-dev python-is-python3 \ + python3-six python3-pygments python3-lxml graphviz + # Make plain "gcc" and "g++" refer to the GCC under test + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${{ matrix.gcc }} 100 + update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${{ matrix.gcc }} 100 + + - name: Check out the sources + uses: actions/checkout@v7 + + - name: Show the toolchain + run: | + gcc --version | head -n 1 + python3 --version + + - name: Build the plugin + # Serial build: the Makefile is not yet safe for "make -j" + run: make CC=gcc plugin + + - name: Check that GCC loads the plugin + run: | + printf 'int main (void) { return 0; }\n' > ci-smoke.c + printf 'import gcc\nprint("gcc-python-plugin loaded")\n' > ci-smoke.py + LD_LIBRARY_PATH=gcc-c-api gcc -fplugin="$PWD/python.so" \ + -fplugin-arg-python-script=ci-smoke.py -c ci-smoke.c -o /dev/null \ + | tee ci-smoke.out + grep -q 'gcc-python-plugin loaded' ci-smoke.out + + - name: Run the Python selftests + run: make CC=gcc testcpybuilder testdejagnu + + - name: Run the test suite + # Some tests are expected to fail with current Python releases (for + # example, SyntaxError reports place the caret differently). Report + # the results without failing the job until those are fixed. + continue-on-error: true + run: make CC=gcc test-suite diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index b6b5acdf..00000000 --- a/.travis.yml +++ /dev/null @@ -1,90 +0,0 @@ -matrix: - include: - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.8 gcc-4.8-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-4.8 && CXX=g++-4.8" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-4.9 gcc-4.9-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 gcc-5-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-6 gcc-6-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" - - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 gcc-7-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-8 gcc-8-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" - - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-9 gcc-9-plugin-dev - env: - - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" - -language: c -compiler: - - gcc - -before_install: - - eval "${MATRIX_EVAL}" - - sudo apt-get update -qq - - sudo apt-get install -qq python-six python-pygments graphviz python-lxml - -script: -- pwd=$(pwd -P) -- mkdir build -- cd build -- make -f $pwd/Makefile srcdir=$pwd/