[CMake] [HOWTO] Easily Cross-Compile CMake (with xstatic)

Zach van Rijn me at zv.io
Fri Oct 26 12:46:32 EDT 2018


On Fri, 2018-10-26 at 11:09 +0200, Rolf Eike Beer wrote:
> ...

Hi Eike,

Thank you for writing. I agree that my approach is a bit brutish
and aim to revise/reduce it to a minimum example. My goal was to
describe a process that is uniform between compilers, easily
automated, and less likely to break between CMake releases.

> So, the 2 things that I do not understand why you need them at
> all:
> 
> -why do the bootstrap thing and move compilers? Just build a
> recent CMake with your host toolchain, package that, and drop
> it into the build container. The need for the host compiler
> should be gone at this point

While it would make sense to build a complete (sufficiently-
recent) CMake that performs the final build, I have not had
consistent success (using the same exact compilers and systems)
in doing so. For example, even specifying all of the settings as
in your 'toolchain.cmake' file (below in this email), omitting
the 'CMAKE_SYSTEM_NAME=Linux' part:

...
cmake-3.12.3/Utilities/cmcurl/lib/strerror.c:32:6: error: #error
"strerror_r MUST be either POSIX, glibc or vxworks-style"
...

and the build fatally aborts. If we add this back,

...
CMake Error: TRY_RUN() invoked in cross-compiling mode, please
set the following cache variables appropriately:
...

which means, potentially, that each future release of CMake may
require updates to the script. I'm not sure if there's a way to
cache all of these variables at once?

CMake Error: TRY_RUN() invoked in cross-compiling mode, please
set the following cache variables appropriately:
   KWSYS_LFS_WORKS (advanced)                                 
   KWSYS_LFS_WORKS__TRYRUN_OUTPUT (advanced) 

CMake Error: TRY_RUN() invoked in cross-compiling mode, please
set the following cache variables appropriately:
   HAVE_FSETXATTR_5
(advanced)                                         
   HAVE_FSETXATTR_5__TRYRUN_OUTPUT (advanced) 

CMake Error: TRY_RUN() invoked in cross-compiling mode, please
set the following cache variables appropriately:
   HAVE_GLIBC_STRERROR_R
(advanced)                               
   HAVE_GLIBC_STRERROR_R__TRYRUN_OUTPUT (advanced)

CMake Error: TRY_RUN() invoked in cross-compiling mode, please
set the following cache variables appropriately:
   HAVE_POSIX_STRERROR_R (advanced)                            
   HAVE_POSIX_STRERROR_R__TRYRUN_OUTPUT (advanced) 

CMake Error: TRY_RUN() invoked in cross-compiling mode, please
set the following cache variables appropriately:
   HAVE_POLL_FINE_EXITCODE
(advanced)                                
   HAVE_POLL_FINE_EXITCODE__TRYRUN_OUTPUT (advanced) 

So it is certainly possible to do this... assuming you are able
to obtain a recent copy of CMake for your build platform:

# In an empty 'build/' directory in the root of CMake source:

$ cmake ..                                                     \
    -DCMAKE_SYSTEM_NAME=Linux                                  \
    -DCMAKE_C_COMPILER=/bin/gcc                      `# cross` \
    -DCMAKE_C_FLAGS="-static --static -g0 -s -Os"              \
    -DCMAKE_CXX_COMPILER=/bin/g++                    `# cross` \
    -DCMAKE_CXX_FLAGS="-static --static -g0 -s -Os"            \
    -DCMAKE_FIND_ROOT_PATH=/armv7l-linux-musleabihf  `# cross` \
    -DCMAKE_SYSROOT=/armv7l-linux-musleabihf         `# cross` \
    -DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER                  \
    -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY                   \
    -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY                   \
    -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=ONLY                   \
    -DKWSYS_LFS_WORKS=ON                                       \
    -DKWSYS_LFS_WORKS__TRYRUN_OUTPUT=""                        \
    -DHAVE_FSETXATTR_5=ON                                      \
    -DHAVE_FSETXATTR_5__TRYRUN_OUTPUT=""                       \
    -DHAVE_GLIBC_STRERROR_R=OFF               `# because musl` \
    -DHAVE_GLIBC_STRERROR_R__TRYRUN_OUTPUT=""                  \
    -DHAVE_POSIX_STRERROR_R=ON                `# because musl` \
    -DHAVE_POSIX_STRERROR_R__TRYRUN_OUTPUT=""                  \
    -DHAVE_POLL_FINE_EXITCODE=ON                               \
    -DHAVE_POLL_FINE_EXITCODE__TRYRUN_OUTPUT=""

...
CMake Warning:
  Manually-specified variables were not used by the project:

    KWSYS_LFS_WORKS__TRYRUN_OUTPUT
...

$ make -j$(nproc)
$ make install

$ file /usr/local/bin *
cmake: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
statically linked, stripped
cpack: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
statically linked, stripped
ctest: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV),
statically linked, stripped

> 
> -why do you need to overwrite the compiler with the target
> compiler? Just drop it anywhere and tell CMake with CC and CXX
> variables where it is. Or better, pass it a toolchain file
> like mine:

Specifically because it would either require defining all of the
above variables, including toolchain path(s), or running some
sort of script or command like 'sed' to attempt to replace all
hard-coded occurrences of the original toolchain. You can see
that there are many:

$ cd ~/cmake-3.12.3/build
$ grep -r "/bin/g++" . | wc -l
1247

(This 'grep' command actually takes longer than building CMake!)

This would be further complicated by having to escape toolchain
paths if they are not in similar locations, and could be messy.

> 
> === toolchain.cmake ===
> set(CMAKE_SYSTEM_NAME Linux)
> set(CMAKE_SYSTEM_PROCESSOR arm)
> 
> # specify the cross compiler
> set(CMAKE_C_COMPILER /opt/emlix/test/bin/arm-unknown-linux-
> gnueabi-gcc)
> set(CMAKE_CXX_COMPILER 
> /opt/emlix/test/bin/arm-unknown-linux-gnueabi-g++)
> 
> # where is the target environment
> set(CMAKE_FIND_ROOT_PATH /opt/emlix/test/sysroot)
> set(CMAKE_SYSROOT /opt/emlix/test/sysroot)
> 
> # search for programs in the build host directories
> set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
> # for libraries and headers in the target directories
> set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
> set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
> set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
> ===
> 
> I'm pretty sure you may find some rough edges, especially if
> you do not want to have the dependencies pre-build but use the
> embedded copies and do static builds with them. But otherwise
> you should hopefully be able to simplify your setup a lot.

Once all this is done, how can this be easily automated? Pasting
in the appropriate 'CMAKE_SYSTEM_NAME' (and others) via some
script? Is it possible to determine the 'HAVE_' and '_WORKS'
variables ahead of time (which ones are actually need TRY_RUN)?

In summary, while I agree my original method could be
simplified, I propose it for the following reasons:

(1) Avoid building two full CMake binaries (one for build system
    and one for target host system)

(2) Avoid breakage between CMake versions, if new tests are
    are added, as script automation would become difficult

(3) As an alternative to the "standard" approach which you
    mention, as some users may have difficulty in determining
    exactly how to populate these variables.


> Eike
> -- 

The approach outlined in this email (based off of yours) might
work for Windows (MinGW) builds. I will try it for the remaining
architectures which did not work with the original approach.


ZV



More information about the CMake mailing list