Wrong headers are used for building native x86 code on x86_64 system


(Scott Palmer) #1

When I have Gradle build for both x86 and x86_64 architectures it uses the exact same include paths for both. However, output of “cpp -v” suggests this should not be the case.
Looking at the default include paths by running:
cpp -x c++ -m32 -v
and
cpp -x c++ -m64 -v

shows a couple differences. E.g.
64-bit has /usr/include/x86_64-linux-gnu/c++/5
32 bit has /usr/include/x86_64-linux-gnu/c++/5/32
instead.

However the compiler options.txt file produced by Gradle for both architectures has exactly the same list of include paths. On the system I have configured to build for ‘x86’, ‘x86_64’ and ‘arm’ this results in an error in random.h where the 32-bit build tries to use __int128, which is not supported for 32-bit.

Strangely on some systems the include path issue doesn’t actually trigger that error.

Anyway, I would like to know if this is something I can easily work around. I think it is a bug but would like confirmation.


(Sterling Greene) #2

Do you have a sample build for this? Which set of C++ plugins are you using?


(Scott Palmer) #3

Just using ‘c’ or ‘cpp’ plugins on Gradle 4.7.

build.gradle:

apply plugin: 'cpp'
apply plugin: 'c'

model {
    platforms {
        x86 {
            architecture "x86"
        }
        x64 {
            architecture "x86_64"
        }
    }

    toolChains {
        gcc(Gcc) {
            target('x64') {
                cCompiler.withArguments { args ->
                    args << '-m64'
                }
                cppCompiler.withArguments { args ->
                    args << '-m64'
                }
            }
            target('x86') {
                cCompiler.withArguments { args ->
                    args << '-m32' << '-march=i686'
                }
                cppCompiler.withArguments { args ->
                    args << '-m32' << '-march=i686'
                }
                linker.withArguments { args ->
                    args << '-m32' << '-march=i686'
                }
            }
        }

    }


    components {
        main(NativeExecutableSpec) {
            targetPlatform 'x64'
            targetPlatform 'x86'
        }
    }
}

src/main/cpp/main.cpp:

#include <algorithm>

int main(int argc, char **argv) {
    // Intentionally fail the build. Error msg will show if the macro is defined or not
    #ifdef _GLIBCXX_USE_INT128
        fail("_GLIBCXX_USE_INT128 is defined")
    #else
        fail("_GLIBCXX_USE_INT128 is not defined")
    #endif
    return 0;
}

You can see the difference running:

g++ -m32 src/main/cpp/main.cpp
g++ -m64 src/main/cpp/main.cpp

vs

gradle build

Using older Gradle 4.3 gives expected results.


(Scott Palmer) #4

It may be that the compiler puts the include folders in some sort of hash set, so the order searched could depend on the actual path value.
On some systems everything works.
I can reproduce the problem every time on a system where the include path is as above, but never on my newer Ubuntu 18.04 system GCC 7 and these paths:
/usr/include/x86_64-linux-gnu/c++/7
/usr/include/x86_64-linux-gnu/c++/7/32

I tried being explicit with -I arguments, running a g++ command line, but the order doesn’t matter.

g++ -I /usr/include/x86_64-linux-gnu/c++/5/32 -I /usr/include/x86_64-linux-gnu/c++/5 main.cpp

fails, as does:

g++ -I /usr/include/x86_64-linux-gnu/c++/5 -I /usr/include/x86_64-linux-gnu/c++/5/32 main.cpp

As soon as I add -I /usr/include/x86_64-linux-gnu/c++/7 it seems to get picked up before -I /usr/include/x86_64-linux-gnu/c++/7/32 regardless of the order.

I discovered this trying to work around the original problem, thinking I could force options.txt to have the correct path first. But order doesn’t matter.


(Daniel Lacasse) #5

Thanks for all the details Scott. It seems that since you are overwriting the target configuration for the Gcc toolchain, the default probing compiler argument are lost. This cause Gradle to probe the compiler using no -m32/-m64 flag. You can work around this by adding compilerProbeArgs '-m32', '-march=i686' or compilerProbeArgs '-m64' inside the target bloc.


(Scott Palmer) #6

Thanks! I didn’t know about compilerProbeArgs.

I also understand now why my previous workaround didn’t work the -I that I tried to add before the bad one that Gradle was adding was ignored because it matched a system include path.

Adding the appropriate compilerProbeArgs has fixed the issue for me.