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/ 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/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..95c7c034 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 ------------------ @@ -113,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). 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