Skip to content

Linking cxx shared libraries may fail when CC != CXX #355

Open
@vfazio

Description

@vfazio

Noticed this while building greenlet, which uses c++.

Starting with 52cd70b

The LDCXXSHARED variable gets read out of the sysconfig vars from the python build:

br-user@02db68569e9b:/tmp/tmp.SWYRHhln2t/buildroot$ /tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/python3 -m sysconfig | grep LDC
	LDCXXSHARED = "/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache /usr/bin/g++ -shared -L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -Wl,--enable-new-dtags"

This feeds the value of linker_so_cxx:

        compiler.set_executables(
            preprocessor=cpp,
            compiler=cc_cmd,
            compiler_so=cc_cmd + ' ' + ccshared,
            compiler_cxx=cxx_cmd,
            compiler_so_cxx=cxx_cmd + ' ' + ccshared,
            linker_so=ldshared,
            linker_so_cxx=ldcxxshared,
            linker_exe=cc,
            linker_exe_cxx=cxx,
            archiver=archiver,
        )

When linking is performed:

linker = (
self.linker_exe
if building_exe
else (
self.linker_so_cxx if target_lang == "c++" else self.linker_so
)
)[:]

linker=['/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache', '/usr/bin/g++', '-shared', '-L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-Wl,--enable-new-dtags', '-L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-O2', '-I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include', '-I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include']

However, this ultimately fails:

/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache /usr/bin/g++ -O2 -I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include -I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include /usr/bin/g++ -shared -L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -Wl,--enable-new-dtags -L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -O2 -I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include -I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include -g build/temp.linux-x86_64-cpython-313/src/greenlet/greenlet.o -L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib -o build/lib.linux-x86_64-cpython-313/greenlet/_greenlet.cpython-313-x86_64-linux-gnu.so
/usr/bin/ld: cannot use executable file '/usr/bin/g++' as input to a link
collect2: error: ld returned 1 exit status
error: command '/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache' failed with exit code 1

linker=['/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache', '/usr/bin/g++', '-O2', '-I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include', '-I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include', '/usr/bin/g++', '-shared', '-L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-Wl,--enable-new-dtags', '-L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-Wl,-rpath,/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-O2', '-I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include', '-I/tmp/tmp.SWYRHhln2t/buildroot/output/host/include']
ld_args=['-g', 'build/temp.linux-x86_64-cpython-313/src/greenlet/greenlet.o', '-L/tmp/tmp.SWYRHhln2t/buildroot/output/host/lib', '-o', 'build/lib.linux-x86_64-cpython-313/greenlet/_greenlet.cpython-313-x86_64-linux-gnu.so']

The linking command has a spurious /usr/bin/g++ in the invocation, causing problems.

This looks to be because LDCXXSHARED is always generated using CXX in the CPython build:
https://github.com/python/cpython/blob/e42bda9441119c7952ada23e88e6f4ab79df86c2/configure.ac#L3469

However, when parsing this out in unix.py:

_, linker_exe_ne = _split_env(self.linker_exe)
params = _linker_params(linker_na, linker_exe_ne)
linker = env + aix + compiler_cxx_ne + params

self.linker_exe is used instead of self.linker_exe_cxx. self.linker_exe is driven by CC where as self.linker_exe_cxx is driven by CXX.

br-user@02db68569e9b:/tmp/tmp.SWYRHhln2t/buildroot$ /tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/python3 -m sysconfig | egrep "(CXX|CC) =" 
	CC = "/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache /usr/bin/gcc"
	CXX = "/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache /usr/bin/g++"
	LINKCC = "/tmp/tmp.SWYRHhln2t/buildroot/output/host/bin/ccache /usr/bin/gcc"

Since the LDCXXSHARED value is prefixed with CXX and not CC, only the first item from the list is dropped via _linker_params, which is the path to ccache, and the spurious /usr/bin/g++ ref is left around.

So, I think maybe the linker_exe_cxx attribute should be used instead so the proper prefix is parsed out of the flag:

                if target_lang == "c++" and self.compiler_cxx:
                    env, linker_ne = _split_env(linker)
                    aix, linker_na = _split_aix(linker_ne)
                    _, compiler_cxx_ne = _split_env(self.compiler_cxx)
                    _, linker_exe_ne = _split_env(self.linker_exe_cxx)

There may also be issues with c++ executables:

self.linker_exe
if building_exe

There is no branch to use self.linker_exe_cxx when target_lang == "c++"

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions