Guidance on cross compiling LLVM with mingw-w64 and cmake

I need to build libLLVM (individual static libraries are fine at the
moment) using mingw-w64 cross compilers, i686-w64-mingw32-gcc and
(separately) x86_64-w64-mingw32-gcc. I'd like this to work from both
Linux and Cygwin build environments. With autotools, this worked fine:
../configure --host=i686-w64-mingw32 and that's it (with mingw32-gcc-c++
installed on Fedora 23, also works fine on Ubuntu, Cygwin, etc).
I'm trying to recreate this with cmake so we don't get stuck on 3.8
indefinitely. Here's what I've got so far:

cmake .. -DCMAKE_C_COMPILER=/usr/bin/i686-w64-mingw32-gcc \
  -DCMAKE_CXX_COMPILER=/usr/bin/i686-w64-mingw32-g++ \
  -DCMAKE_SYSTEM_NAME=Windows \
  -DCMAKE_RC_COMPILER=/usr/bin/i686-w64-mingw32-windres
# (some older versions of cmake have issues if you don't
# specify an absolute path to windres)

When this gets to "Configuring NATIVE targets" it calls cmake again
trying to use the same mingw compilers, but without CMAKE_SYSTEM_NAME
set so it doesn't succeed in configuring. During the build I then get
a "No rule to make target 'LLVMSupport'. Stop." Full build log is at
http://sprunge.us/bCUU

I'd like to go with the default behavior of building a native version
of TableGen, but the way that's currently set up in cmake isn't working
here. I'm looking at cmake/modules/CrossCompile.cmake (and I think
"toochain" on the first line is a typo?) and trying to figure out how
to make its call to execute_process(COMMAND ${CMAKE_COMMAND} ...) not
inherit the top-level settings for CMAKE_C_COMPILER etc from the cross
build. Any ideas? I've tried moving my mingw settings from the command
line to a toolchain file but that hasn't done anything different so far.
I've also tried specifically creating a native toolchain file and
tweaking the call to llvm_create_cross_target_internal at the end of
CrossCompile.cmake to use it, but that hasn't worked either - keeps giving
Could not find toolchain file: "/home/llvm/cmake/platforms/NATIVE.cmake"

Is this a configuration anyone else has gotten working with cmake?
Thanks,
Tony

Hi Tony,
I don’t have experience with your specific circumstance, so sorry I can’t be much help there. Thank you though for pointing out what appears to be a typo. CC’ing Chris Bieneman as he may be able to verify if this is correct.

Chris,
I have attached a patch which should fix the typo. Would you mind having a look and committing if you think it is all good?

Hope you both have a good day.

Thanks,
Mike Edwards

patch.txt (478 Bytes)

The CrossCompile module is in a perpetual state of “when I get a chance…”, and desperately needs some cleanup.

The problem you are hitting is caused by setting CMAKE_SYSTEM_NAME. When you set that CMake sets a variable CMAKE_CROSS_COMPILING. That variable should only be set when your host OS doesn’t match your target OS. Since LLVM needs to build host-capable tools there is some goop to call out and configure a new CMake build directory to target the host.

-Chris

The problem you are hitting is caused by setting CMAKE_SYSTEM_NAME.
When you set that CMake sets a variable CMAKE_CROSS_COMPILING. That
variable should only be set when your host OS doesn't match your target
OS. Since LLVM needs to build host-capable tools there is some goop to
call out and configure a new CMake build directory to target the host.

Right. My build OS doesn't match my target OS, my build is Linux or Cygwin,
and the target is Win32. So setting CMAKE_CROSS_COMPILING is correct.
It's the "goop" that needs some work to get back to parity with the
functionality that was working on autotools. Sorry I didn't try running
this configuration earlier on in the autotools-deprecation process.

The new CMake build directory for the native tools needs to not use the
same settings for compiler names as the parent cross-compile build.
Any ideas how to accomplish this? I'm happy to help test out patches
and dig into cmake docs as necessary to get this working. If this would
be more appropriate to work through in an issue than on the mailing
list, do say so.

-Tony

Sadly you are way out in uncharted territory. I’m unaware of anyone using gcc to cross-compile LLVM. At Apple we extensively cross-compile, but we use clang as our host compiler.

I’m not sure why you’re seeing the native CMake picking up your cross-compiler instead of your host compiler. Our CMake scripts don’t pass through he compiler from one configuration to the next. I’m guessing the “native” CMake is probably picking up the cross-compiler from the environment somehow (maybe the CC and CXX envar).

You can probably work around the issue by configuring a host LLVM directory and passing that directory into your cross-configured CMake invocation with the LLVM_NATIVE_BUILD option.

Because you are off in uncharted territory I expect you’re mostly going to be on your own for debugging and diagnosing. I can answer specific questions, but I haven’t used a gcc toolchain for cross-compiling since the early days of the PS3 (and I’ve pretty much blocked that out of my memory).

-Chris

We’ve been doing this for years in Julia with no troubles, but all in autotools. This is our primary build configuration used to create Windows installers. We have to target mingw because of Lapack and a few smaller Fortran dependencies.

Fedora, OpenSUSE, Arch, and Cygwin all have sets of mingw cross-compiled library packages but as far as I’m aware only Cygwin has packaged a cross-built llvm yet.

This may not be a very high profile build configuration, but it’s a definite regression. I’ll try to figure out if cmake is setting the env vars in places it shouldn’t be. Building all of LLVM twice doesn’t sound like a satisfying workaround, but if it’s the only thing we can get to work…

Sadly you are way out in uncharted territory. I’m unaware of anyone using gcc to cross-compile LLVM. At Apple we extensively cross-compile, but we use clang as our host compiler.

I’m not sure why you’re seeing the native CMake picking up your cross-compiler instead of your host compiler. Our CMake scripts don’t pass through he compiler from one configuration to the next. I’m guessing the “native” CMake is probably picking up the cross-compiler from the environment somehow (maybe the CC and CXX envar).

You can probably work around the issue by configuring a host LLVM directory and passing that directory into your cross-configured CMake invocation with the LLVM_NATIVE_BUILD option.

Because you are off in uncharted territory I expect you’re mostly going to be on your own for debugging and diagnosing. I can answer specific questions, but I haven’t used a gcc toolchain for cross-compiling since the early days of the PS3 (and I’ve pretty much blocked that out of my memory).

-Chris

Tony Kelman via llvm-dev <llvm-dev@lists.llvm.org> writes:

I need to build libLLVM (individual static libraries are fine at the
moment) using mingw-w64 cross compilers, i686-w64-mingw32-gcc and
(separately) x86_64-w64-mingw32-gcc. I'd like this to work from both
Linux and Cygwin build environments. With autotools, this worked fine:
../configure --host=i686-w64-mingw32 and that's it (with mingw32-gcc-c++
installed on Fedora 23, also works fine on Ubuntu, Cygwin, etc).
I'm trying to recreate this with cmake so we don't get stuck on 3.8
indefinitely. Here's what I've got so far:

cmake .. -DCMAKE_C_COMPILER=/usr/bin/i686-w64-mingw32-gcc \
  -DCMAKE_CXX_COMPILER=/usr/bin/i686-w64-mingw32-g++ \
  -DCMAKE_SYSTEM_NAME=Windows \
  -DCMAKE_RC_COMPILER=/usr/bin/i686-w64-mingw32-windres
# (some older versions of cmake have issues if you don't
# specify an absolute path to windres)

When this gets to "Configuring NATIVE targets" it calls cmake again
trying to use the same mingw compilers, but without CMAKE_SYSTEM_NAME
set so it doesn't succeed in configuring. During the build I then get
a "No rule to make target 'LLVMSupport'. Stop." Full build log is at
http://sprunge.us/bCUU

I'd like to go with the default behavior of building a native version
of TableGen, but the way that's currently set up in cmake isn't working
here. I'm looking at cmake/modules/CrossCompile.cmake (and I think
"toochain" on the first line is a typo?) and trying to figure out how
to make its call to execute_process(COMMAND ${CMAKE_COMMAND} ...) not
inherit the top-level settings for CMAKE_C_COMPILER etc from the cross
build. Any ideas? I've tried moving my mingw settings from the command
line to a toolchain file but that hasn't done anything different so far.
I've also tried specifically creating a native toolchain file and
tweaking the call to llvm_create_cross_target_internal at the end of
CrossCompile.cmake to use it, but that hasn't worked either - keeps giving
Could not find toolchain file: "/home/llvm/cmake/platforms/NATIVE.cmake"

Looks like the (unreachable due to the toochain typo) cmake code to set
the native toolchain file is buggy - it's failing to find the file
because it thinks the quotes are part of the file name. This:

    set(CROSS_TOOLCHAIN_FLAGS_${target_name}
        -DCMAKE_TOOLCHAIN_FILE=\"${LLVM_MAIN_SRC_DIR}/cmake/platforms/${toolchain}.cmake\"

should probably say this:

    set(CROSS_TOOLCHAIN_FLAGS_${target_name}
        -DCMAKE_TOOLCHAIN_FILE=${LLVM_MAIN_SRC_DIR}/cmake/platforms/${toolchain}.cmake

You could probably also try out your native toolchain file without
changing CrossCompile.cmake by setting CROSS_TOOLCHAIN_FLAGS_NATIVE on
your cmake command line:

  cmake .. -DCMAKE_C_COMPILER=/usr/bin/i686-w64-mingw32-gcc \
    -DCMAKE_CXX_COMPILER=/usr/bin/i686-w64-mingw32-g++ \
    -DCMAKE_SYSTEM_NAME=Windows \
    -DCMAKE_RC_COMPILER=/usr/bin/i686-w64-mingw32-windres \
    -DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=/path/to/NATIVE.cmake

If that helps, we should probably figure out a more general way to pass
in or find the native toolchain - it's kind of haphazard right now.

Finally, if you need a workaround to get things going you can probably
configure and build tablegen in another build directory (just run cmake
like you're building for the host and build the llvm-tblgen target).
This can then be passed in with -DLLVM_NATIVE_BUILD=/path/to/builddir
and the cross build *should* pick up the tablegen from there.

-DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=/path/to/NATIVE.cmake

Brilliant. That worked, and was apparently all I was missing. I could have sworn I tried something like this, but apparently not.

For completeness, my NATIVE.cmake toolchain file is just:

set(CMAKE_C_COMPILER cc)
set(CMAKE_CXX_COMPILER c++)

Thanks Justin!

Tony Kelman via llvm-dev llvm-dev@lists.llvm.org writes:

I need to build libLLVM (individual static libraries are fine at the
moment) using mingw-w64 cross compilers, i686-w64-mingw32-gcc and
(separately) x86_64-w64-mingw32-gcc. I’d like this to work from both
Linux and Cygwin build environments. With autotools, this worked fine:
…/configure --host=i686-w64-mingw32 and that’s it (with mingw32-gcc-c++
installed on Fedora 23, also works fine on Ubuntu, Cygwin, etc).
I’m trying to recreate this with cmake so we don’t get stuck on 3.8
indefinitely. Here’s what I’ve got so far:

cmake … -DCMAKE_C_COMPILER=/usr/bin/i686-w64-mingw32-gcc
-DCMAKE_CXX_COMPILER=/usr/bin/i686-w64-mingw32-g++
-DCMAKE_SYSTEM_NAME=Windows
-DCMAKE_RC_COMPILER=/usr/bin/i686-w64-mingw32-windres

(some older versions of cmake have issues if you don’t

specify an absolute path to windres)

When this gets to “Configuring NATIVE targets” it calls cmake again
trying to use the same mingw compilers, but without CMAKE_SYSTEM_NAME
set so it doesn’t succeed in configuring. During the build I then get
a “No rule to make target ‘LLVMSupport’. Stop.” Full build log is at
http://sprunge.us/bCUU

I’d like to go with the default behavior of building a native version
of TableGen, but the way that’s currently set up in cmake isn’t working
here. I’m looking at cmake/modules/CrossCompile.cmake (and I think
“toochain” on the first line is a typo?) and trying to figure out how
to make its call to execute_process(COMMAND ${CMAKE_COMMAND} …) not
inherit the top-level settings for CMAKE_C_COMPILER etc from the cross
build. Any ideas? I’ve tried moving my mingw settings from the command
line to a toolchain file but that hasn’t done anything different so far.
I’ve also tried specifically creating a native toolchain file and
tweaking the call to llvm_create_cross_target_internal at the end of
CrossCompile.cmake to use it, but that hasn’t worked either - keeps giving
Could not find toolchain file: “/home/llvm/cmake/platforms/NATIVE.cmake”

Looks like the (unreachable due to the toochain typo) cmake code to set
the native toolchain file is buggy - it’s failing to find the file
because it thinks the quotes are part of the file name. This:

set(CROSS_TOOLCHAIN_FLAGS_${target_name}
-DCMAKE_TOOLCHAIN_FILE="${LLVM_MAIN_SRC_DIR}/cmake/platforms/${toolchain}.cmake"

should probably say this:

set(CROSS_TOOLCHAIN_FLAGS_${target_name}
-DCMAKE_TOOLCHAIN_FILE=${LLVM_MAIN_SRC_DIR}/cmake/platforms/${toolchain}.cmake

You could probably also try out your native toolchain file without
changing CrossCompile.cmake by setting CROSS_TOOLCHAIN_FLAGS_NATIVE on
your cmake command line:

cmake … -DCMAKE_C_COMPILER=/usr/bin/i686-w64-mingw32-gcc
-DCMAKE_CXX_COMPILER=/usr/bin/i686-w64-mingw32-g++
-DCMAKE_SYSTEM_NAME=Windows
-DCMAKE_RC_COMPILER=/usr/bin/i686-w64-mingw32-windres
-DCROSS_TOOLCHAIN_FLAGS_NATIVE=-DCMAKE_TOOLCHAIN_FILE=/path/to/NATIVE.cmake

If that helps, we should probably figure out a more general way to pass
in or find the native toolchain - it’s kind of haphazard right now.

Finally, if you need a workaround to get things going you can probably
configure and build tablegen in another build directory (just run cmake
like you’re building for the host and build the llvm-tblgen target).
This can then be passed in with -DLLVM_NATIVE_BUILD=/path/to/builddir
and the cross build should pick up the tablegen from there.