Detect 32-bit x86 processor in CMakeList.txt?

2019-06-08 19:35发布

问题:

We are catching errors in our CMake makefiles due to lack of -fPIC. Her's one from a ci20 MIPS dev-board:

...
[ 92%] Built target cryptopp-object
Scanning dependencies of target cryptopp-shared
Scanning dependencies of target cryptopp-static
Linking CXX static library libcryptopp.a
Linking CXX shared library libcryptopp.so
/usr/bin/ld: CMakeFiles/cryptopp-object.dir/cryptlib.cpp.o: relocation R_MIPS_HI16 against
`a local symbol' can not be used when making a shared object; recompile with -fPIC
CMakeFiles/cryptopp-object.dir/cryptlib.cpp.o: could not read symbols: Bad value
collect2: ld returned 1 exit status

The project's policy is to us PIC everywhere except 32-bit x86 due to register pressures. That means x86_64, ARM-32, Aarch32, Aarch64, MIPS, MIPS64, UltraSparc, etc get PIC.

I believe the target processor is provided in CMAKE_SYSTEM_PROCESSOR. The problem I am having is the docs don't tell me the values, so I can't figure out how to craft a "not 32-bit x86" test.

How do I detect 32-bit x86 processor in CMakeList.txt?

Even better, I would like to see a comprehensive list of processors that CMake sets CMAKE_SYSTEM_PROCESSOR to. If anyone has the list, then it would be great to provide it.

回答1:

I probably would build something around the compiler.

A close approximation using existing variables/modules would be:

include(TestBigEndian)

if (NOT WIN32)
    TEST_BIG_ENDIAN(_bigendian)
    if((CMAKE_SIZEOF_VOID_P GREATER 4) OR (_bigendian))
        message(
            STATUS "Setting ${CMAKE_CXX_COMPILE_OPTIONS_PIC} "
                   "for machine ${CMAKE_HOST_SYSTEM_PROCESSOR}"
        )
        set(CMAKE_POSITION_INDEPENDENT_CODE 1)
    endif()
endif()

In short what I have done:

  • WIN32 is also valid for 64 Bit Windows compilers/environments
  • CMAKE_SIZEOF_VOID_P GREATER 4 checks for "greater then 32 Bit"
  • The last is the biggest assumption: take all little endian processors as Intel/AMD based
  • Used more generic CMAKE_POSITION_INDEPENDENT_CODE to set -fPIC

I admit a more accurate method would be to build something around a pre-defined macros test.

Edit: Added "Predefined Macros Check" Alternative

Here is the more precise check for predefined macros:

include(CheckCXXSourceCompiles)

if (CMAKE_CXX_COMPILE_OPTIONS_PIC)
    set(
        _preDefMacrosX86 
            __i386 __i386__ __i486__ __i586__ __i686__      
            _M_I86 _M_IX86 __X86__ _X86_ __THW_INTEL__
            __I86__ __INTEL__ __386
    )
    set(_code "void main() {}")
    foreach(_macro IN LISTS _preDefMacrosX86)
        set(
            _code
            "${_code}\n\#ifdef ${_macro}\n\#error ${_macro} is defined\n\#endif"
        )
    endforeach()
    CHECK_CXX_SOURCE_COMPILES("${_code}" _canCompileX86DoesFailCheck)

    if((CMAKE_SIZEOF_VOID_P GREATER 4) OR (_canCompileX86DoesFailCheck))
        message(STATUS "Setting ${CMAKE_CXX_COMPILE_OPTIONS_PIC}")
        set(CMAKE_POSITION_INDEPENDENT_CODE 1)
    endif()
endif()

References

  • How to detect if 64 bit MSVC with cmake?
  • Detecting CPU architecture compile-time


回答2:

I believe this performs the detection on nearly everything except Windows. Windows does not consume -fPIC, so it does not matter to me. The pieces were glued together from three Stack Overflow answers.

# Stop hiding the damn output...
set(CMAKE_VERBOSE_MAKEFILE on)

# Enable PIC for all targets except Windows and 32-bit x86
if (NOT (WINDOWS OR WINDOWS_STORE OR WINDOWS_PHONE))

    set (UNAME_CMD "uname")
    set (UNAME_ARG "-m")
    execute_process(COMMAND ${UNAME_CMD} ${UNAME_ARG}
        WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
        RESULT_VARIABLE UNAME_RESULT
        OUTPUT_VARIABLE UNAME_MACHINE)

    # Use Regex; match i386, i486, i586 and i686
    IF (NOT (${UNAME_MACHINE} MATCHES "i.86"))
        # message(STATUS "Setting -fPIC for machine ${UNAME_MACHINE}")
        if (CMAKE_VERSION VERSION_LESS 2.8.12)
            add_definitions(-fPIC)
        else()
            add_compile_options(-fPIC)
        endif()
    endif()
endif()

You get the machine with uname -m, and its mostly accurate, even on OS X. For example, on OS X, uname -p returns i386 while uname -m returns x86_64. I seem to recall 10.6 or 10.7 was a little flaky as the transition was being made to 64-bit Macs.

You get the processor with uname -p sometimes, but it fails on many dev-boards. For example my ci20 dev-board returns "mips" for the machine and "unknown" for the processor. Another example is my LeMaker HiKey. It returns "aarch64" for the machine and "unknown" for the processor.


I'd still like to see the list of processors provided by Cmake.