Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cross-compilation support #1203

Closed
musm opened this issue Nov 16, 2021 · 42 comments
Closed

Cross-compilation support #1203

musm opened this issue Nov 16, 2021 · 42 comments
Assignees
Labels
Component - Build CMake, Autotools Priority - 1. High 🔼 These are important issues that should be resolved in the next release Type - Improvement Improvements that don't add a new feature or functionality

Comments

@musm
Copy link

musm commented Nov 16, 2021

Lack of cross-compilation from linux to other platforms would significantly help distribution of binaries in the Julia ecosystem. This has been a long-standing issue. See e.g. JuliaPackaging/Yggdrasil#567, https://forum.hdfgroup.org/t/cross-compiling-for-windows/6735, to mention two threads. There have been several attempts at it including the fork https://github.com/stevengj/hdf5 and in addition Steven Varga has worked on looking at this in the past.

@musm
Copy link
Author

musm commented Feb 28, 2022

any status or thoughts?

@gnuoyd
Copy link
Contributor

gnuoyd commented Feb 28, 2022 via email

@musm
Copy link
Author

musm commented Feb 28, 2022

Thanks for the update, looks quite promising and glad to see progress

@simonbyrne
Copy link

Any updates on this? @gnuoyd I noticed both PRs have now been merged: what other pieces need to get done?

@byrnHDF
Copy link
Contributor

byrnHDF commented Sep 28, 2022

#1410 also is related and I will document the CMake process there and then create a cross-compile document in the release_docs folder.

@giordano
Copy link

@byrnHDF trying to build eac2cd5 (most recent commit in develop as of now), I still get plenty of TRY_RUNs:

[23:55:44] CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
[23:55:44]    TEST_LFS_WORKS_RUN (advanced)
[23:55:44] For details see /workspace/srcdir/hdf5/build/TryRunResults.cmake
[...]
[23:55:52] CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
[23:55:52]    H5_LDOUBLE_TO_LONG_SPECIAL_RUN (advanced)
[23:55:52]    H5_LDOUBLE_TO_LONG_SPECIAL_RUN__TRYRUN_OUTPUT (advanced)
[23:55:52] For details see /workspace/srcdir/hdf5/build/TryRunResults.cmake
[23:55:52] CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
[23:55:52]    H5_LONG_TO_LDOUBLE_SPECIAL_RUN (advanced)
[23:55:52]    H5_LONG_TO_LDOUBLE_SPECIAL_RUN__TRYRUN_OUTPUT (advanced)
[23:55:52] For details see /workspace/srcdir/hdf5/build/TryRunResults.cmake
[23:55:52] CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
[23:55:52]    H5_LDOUBLE_TO_LLONG_ACCURATE_RUN (advanced)
[23:55:52]    H5_LDOUBLE_TO_LLONG_ACCURATE_RUN__TRYRUN_OUTPUT (advanced)
[23:55:52] For details see /workspace/srcdir/hdf5/build/TryRunResults.cmake
[23:55:53] CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
[23:55:53]    H5_LLONG_TO_LDOUBLE_CORRECT_RUN (advanced)
[23:55:53]    H5_LLONG_TO_LDOUBLE_CORRECT_RUN__TRYRUN_OUTPUT (advanced)
[23:55:53] For details see /workspace/srcdir/hdf5/build/TryRunResults.cmake
[23:55:53] CMake Error: TRY_RUN() invoked in cross-compiling mode, please set the following cache variables appropriately:
[23:55:53]    H5_DISABLE_SOME_LDOUBLE_CONV_RUN (advanced)
[23:55:53]    H5_DISABLE_SOME_LDOUBLE_CONV_RUN__TRYRUN_OUTPUT (advanced)
[23:55:53] For details see /workspace/srcdir/hdf5/build/TryRunResults.cmake

See for example https://dev.azure.com/JuliaPackaging/Yggdrasil/_build/results?buildId=23874&view=logs&j=1da34be1-494f-551e-5051-6ec6842bd3b0&t=cb3052b7-7b92-5226-336b-5a172e41a1c3&l=242.

For reference, CMake invocation:

cmake .. -DCMAKE_INSTALL_PREFIX=${prefix} \
    -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TARGET_TOOLCHAIN} \
    -DHDF5_BUILD_CPP_LIB=OFF \
    -DONLY_SHARED_LIBS=ON \
    -DHDF5_BUILD_HL_LIB=ON \
    -DHDF5_ENABLE_Z_LIB_SUPPORT=ON \
    -DHDF5_ENABLE_SZIP_SUPPORT=OFF \
    -DHDF5_ENABLE_SZIP_ENCODING=OFF \
    -DBUILD_TESTING=OFF

Am I missing something?

@byrnHDF
Copy link
Contributor

byrnHDF commented Nov 28, 2022

The next step is to set those try-run values in the your preset H5Tinit.c file. I think H5Tinit.c is created by the above. See src/CMakeLists.txt about line 1035.
HDF5_USE_PREGEN needs to be set to TRUE.
HDF5_USE_PREGEN_DIR needs to be where the pregenerated files are located.

References:
https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html
https://gitlab.com/embeddedlinux/libs/platform

@giordano
Copy link

giordano commented Nov 28, 2022

I thought the entire point of supporting cross-compilation was to not have to manually set the output of try-run values for all possible target platforms.

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

I think we just need the CMakeCache.txt files, no?

https://cmake.org/cmake/help/latest/command/try_run.html#behavior-when-cross-compiling

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

Here's my src/build-MINGW64/CMakeCache.txt from a MINGW64 run:
msys2/MINGW-packages#15033

//Checking IF your system converts long double to (unsigned) long
// values with special algorithm
H5_LDOUBLE_TO_LONG_SPECIAL:INTERNAL=
//Result of TRY_COMPILE
H5_LDOUBLE_TO_LONG_SPECIAL_COMPILE:INTERNAL=TRUE
//Result of try_run()
H5_LDOUBLE_TO_LONG_SPECIAL_RUN:INTERNAL=1
//Checking IF correctly converting (unsigned) long long to long
// double values
H5_LLONG_TO_LDOUBLE_CORRECT:INTERNAL=1
//Result of TRY_COMPILE
H5_LLONG_TO_LDOUBLE_CORRECT_COMPILE:INTERNAL=TRUE
//Result of try_run()
H5_LLONG_TO_LDOUBLE_CORRECT_RUN:INTERNAL=0
//Checking IF your system can convert (unsigned) long to long double
// values with special algorithm
H5_LONG_TO_LDOUBLE_SPECIAL:INTERNAL=
//Result of TRY_COMPILE
H5_LONG_TO_LDOUBLE_SPECIAL_COMPILE:INTERNAL=TRUE
//Result of try_run()
H5_LONG_TO_LDOUBLE_SPECIAL_RUN:INTERNAL=1
...
//Checking IF the cpu is power9 and cannot correctly converting
// long double values
H5_DISABLE_SOME_LDOUBLE_CONV:INTERNAL=
//Result of TRY_COMPILE
H5_DISABLE_SOME_LDOUBLE_CONV_COMPILE:INTERNAL=TRUE
//Result of try_run()
H5_DISABLE_SOME_LDOUBLE_CONV_RUN:INTERNAL=1

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

Sounds like we need to copy the values into the generated TryRunResults.cmake and then pass those settings into cmake -C TryRunResults.cmake per https://cmake.org/cmake/help/book/mastering-cmake/chapter/Cross%20Compiling%20With%20CMake.html#using-compile-checks

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Our latest develop has no errors compiling with mingw64 on an ubuntu 22.04 system.
We do have an issue (not with try/run configuration) compiling with aarch64 on an ubuntu 22.04 system. The compile error: "Assumed value of MB_LEN_MAX wrong" along with a number of macro redefined [-Wmacro-redefined] warnings.
the aarch64 path is "/opt/android-ndk-r25b-linux/android-ndk-r25b/toolchains/llvm/prebuilt/linux-x86_64/bin/clang"

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Is your mingw64 on a windows machine? I haven't got one of those workers available.

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

Yes, I'm running mingw64 under msys2 on windows.

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Maybe what is needed is some more preset values in Configure for mingw on windows environments?
There is a section at the top that presets known values for windows platforms - around line 55.

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

long double has always been trouble in cygwin, mingw on windows and such. dtarith test usually fails on those as well.

I think it has to do with compiler tricks. (Not official statement, just a personal opinion).

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

ConversionTests.c file is an interesting read.

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

We may be getting a bit distracted by the mingw-w64 aspect. As far as I can tell we have a solution to build on that platform. That is we have a functional native build solution for msys2/mingw-w64.

What I would like to do is send @giordano a file, my CMakeCache.txt for example, so that we can also build mingw-w64 binaries from Linux via cross compilation.

Cmake seems to have some very rudimentary infrastructure to support this. Basically, if we can transmit information cmake -C to populate the cmake cache, then cmake can just use that information.

The process seems to be as follows:

  1. On the target platform, perform the cmake configuration step, which will populate a CMakeCache.txt. That CMakeCache.txt should contain all the values generated from the try_run and try_compile steps on the target platform. Rename this to CMakeCache-<target platform>.txt.
  2. On the build platform, do an initial configuration build with cmake -DCMAKE_CROSSCOMPILING=ON. This will generate a TryRunResults.cmake file.
  3. On the build platform, write and execute a script to move values from CMakeCache-<target platform>.txt to TryRunResults.cmake.
  4. On the build platform, invoke cmake -C TryRunResults.cmake to populate the cmake cache.

@giordano
Copy link

TryRun isn't a great way to do cross-compilation though. Quite the opposite in fact. What stevengj/hdf5@6b05455 showed is that most of those checks are actually compiler checks which don't need to run code in the first place.

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Yes that is basically the process.
However, we have a mingw64.cmake toolchain file in the config/toolchain folder - have you used that?

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

TryRun isn't a great way to do cross-compilation though. Quite the opposite in fact. What stevengj/hdf5@6b05455 showed is that most of those checks are actually compiler checks which don't need to run code in the first place.

If we wanted to bypass try_run and try_compile, we could use CMAKE_CROSSCOMPILING_EMULATOR to run autotools in order to return the result of the programs.

Per @derobins , The HDF Group is leaning towards retiring autotools in favor of cmake. I've commented there that an issue with this is it closing off Steven G. Johnson's route of populating this.

Perhaps the "emulation" route might be a way to salvage this?

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Eliminating tryrun has been discussed and is preferred. It is the details in doing it for all the platforms where hdf is used.
I think that is where using the toolchain files can be part of the solution.

@mkitti
Copy link
Contributor

mkitti commented Jan 13, 2023

Are you suggesting that we can the toolchain files located at the link below to include type information that try_run and try_compile are trying to obtain?

https://github.com/HDFGroup/hdf5/tree/develop/config/toolchain

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Maybe, I think it would be a good place for specific settings that are specific to that build env.

@byrnHDF
Copy link
Contributor

byrnHDF commented Jan 13, 2023

Actually, a toolchain could be gotten from a URL using the FetchContent (CMake has an example)

@mkitti
Copy link
Contributor

mkitti commented Jan 31, 2023

The Java CPP project has cross compiling setup:

https://github.com/bytedeco/javacpp-presets/tree/master/hdf5

@derobins derobins self-assigned this May 4, 2023
@derobins derobins added Priority - 1. High 🔼 These are important issues that should be resolved in the next release Component - Build CMake, Autotools Type - Improvement Improvements that don't add a new feature or functionality labels May 4, 2023
@DoneListen
Copy link

Hello, has this problem been solved

@DoneListen
Copy link

I set these variables to OFF, and cmake succeeds. But there was a tricky problem when make: Generated files H5detect and H5make_libsettings, and report an error "H5make_libsettings: cannot execute binary file";
What is the current solution to this

@mkitti
Copy link
Contributor

mkitti commented May 8, 2023

As far as I know this has not been done. Preferably, the need to run an executable on the target platform could be replaced by some static assertions in C and Fortran so that the settings could be detected by compilation alone.

https://en.cppreference.com/w/c/language/_Static_assert
https://en.cppreference.com/w/cpp/language/static_assert

https://fortran-lang.discourse.group/t/static-assert-in-fortran/5687

@DoneListen
Copy link

H5make_libsettings

H5make_libsettings and H5detect is used to generate some files, I am not sure what you mean.

@mkitti
Copy link
Contributor

mkitti commented May 8, 2023

As you noticed, these programs need to be run on the target platform. To make this work you need to compile the program, run it on the target platform, and then transfer some files back to the host compilation environment. You can see an example of this being done for the Julia ecosystem here:
JuliaPackaging/Yggdrasil#6551

Rather than detecting floating point properties at runtime as in H5detect.c, we might be able to detect some of these properties at compile time.

For example, if one were using GCC one may be able to detect if we are using IEEE 754 compliant floating point by checking some macro values.

#include<assert.h>
#include<float.h>
int main(void) {
    static_assert(sizeof(float)  ==                        4);
    static_assert(FLT_RADIX      ==                        2);
    static_assert(FLT_MANT_DIG   ==                       24);
    static_assert(FLT_DIG        ==                        6);
    static_assert(FLT_MIN_EXP    ==                     -125);
    static_assert(FLT_MIN_10_EXP ==                      -37);
    static_assert(FLT_MAX_EXP    ==                      128);
    static_assert(FLT_MAX_10_EXP ==                      +38);
    static_assert(FLT_MIN        ==          1.17549435E-38F);
    static_assert(FLT_MAX        ==          3.40282347E+38F);
    static_assert(FLT_EPSILON    ==          1.19209290E-07F);

    static_assert(sizeof(double) ==                        8);
    static_assert(DBL_MANT_DIG   ==                       53);
    static_assert(DBL_DIG        ==                       15);
    static_assert(DBL_MIN_EXP    ==                    -1021);
    static_assert(DBL_MIN_10_EXP ==                     -307);
    static_assert(DBL_MAX_EXP    ==                     1024);
    static_assert(DBL_MAX_10_EXP ==                      308);
    static_assert(DBL_MAX        ==  1.7976931348623157E+308);
    static_assert(DBL_MIN        ==  2.2250738585072014E-308);
    static_assert(DBL_EPSILON    ==  2.2204460492503131E-016);
}

@byrnHDF
Copy link
Contributor

byrnHDF commented May 15, 2023

We can make the existing cmake configure process simpler, caching the values of the try-run, see https://cmake.org/cmake/help/latest/command/try_run.html
`When cross compiling, the executable compiled in the first step usually cannot be run on the build host. The try_run command checks the CMAKE_CROSSCOMPILING variable to detect whether CMake is in cross-compiling mode. If that is the case, it will still try to compile the executable, but it will not try to run the executable unless the CMAKE_CROSSCOMPILING_EMULATOR variable is set. Instead it will create cache variables which must be filled by the user or by presetting them in some CMake script file to the values the executable would have produced if it had been run on its actual target platform. These cache entries are:

Exit code if the executable were to be run on the target platform.

__TRYRUN_OUTPUT
Output from stdout and stderr if the executable were to be run on the target platform. This is created only if the RUN_OUTPUT_VARIABLE or OUTPUT_VARIABLE option was used.

`
So we identify the variables to be cached and my suggestion is to add them to a toolchain file (or preset file for cmake 3.25+)

@mkitti
Copy link
Contributor

mkitti commented May 15, 2023

You could eliminate the need for most of H5detect.c by checking if these compile.

float_assert.cpp:

#include<assert.h>
#include<float.h>
int main(void) {
    static_assert(sizeof(float)  ==                        4, "");
    static_assert(FLT_RADIX      ==                        2, "");
    static_assert(FLT_MANT_DIG   ==                       24, "");
    static_assert(FLT_DIG        ==                        6, "");
    static_assert(FLT_MIN_EXP    ==                     -125, "");
    static_assert(FLT_MIN_10_EXP ==                      -37, "");
    static_assert(FLT_MAX_EXP    ==                      128, "");
    static_assert(FLT_MAX_10_EXP ==                      +38, "");
    static_assert(FLT_MIN        ==          1.17549435E-38F, "");
    static_assert(FLT_MAX        ==          3.40282347E+38F, "");
    static_assert(FLT_EPSILON    ==          1.19209290E-07F, "");

    static_assert(sizeof(double) ==                        8, "");
    static_assert(DBL_MANT_DIG   ==                       53, "");
    static_assert(DBL_DIG        ==                       15, "");
    static_assert(DBL_MIN_EXP    ==                    -1021, "");
    static_assert(DBL_MIN_10_EXP ==                     -307, "");
    static_assert(DBL_MAX_EXP    ==                     1024, "");
    static_assert(DBL_MAX_10_EXP ==                      308, "");
    static_assert(DBL_MAX        ==  1.7976931348623157E+308, "");
    static_assert(DBL_MIN        ==  2.2250738585072014E-308, "");
    static_assert(DBL_EPSILON    ==  2.2204460492503131E-016, "");
}

long_double_assert.cpp (might not work on 32-bit systems)

#include<float.h>
int main(void) {
    static_assert(sizeof(long double) ==                         16, "");
    static_assert(LDBL_MANT_DIG   ==                             64, "");
    static_assert(LDBL_DIG        ==                             18, "");
    static_assert(LDBL_MIN_EXP    ==                         -16381, "");
    static_assert(LDBL_MIN_10_EXP ==                          -4931, "");
    static_assert(LDBL_MAX_EXP    ==                          16384, "");
    static_assert(LDBL_MAX_10_EXP ==                           4932, "");
    static_assert(LDBL_MAX        ==  1.18973149535723176502E+4932L, "");
    static_assert(LDBL_MIN        ==  3.36210314311209350626E-4932L, "");
    static_assert(LDBL_EPSILON    ==    1.08420217248550443401E-19L, "");
}

For 32-bit systems not using MSVC:
long_double_assert32.cpp:

#include<float.h>
int main(void) {
    static_assert(sizeof(long double) ==                         16, "");
    static_assert(LDBL_MANT_DIG   ==                             64, "");
    static_assert(LDBL_DIG        ==                             18, "");
    static_assert(LDBL_MIN_EXP    ==                         -16381, "");
    static_assert(LDBL_MIN_10_EXP ==                          -4931, "");
    static_assert(LDBL_MAX_EXP    ==                          16384, "");
    static_assert(LDBL_MAX_10_EXP ==                           4932, "");
    static_assert(LDBL_MAX        ==  1.18973149535723176502E+4932L, "");
    static_assert(LDBL_MIN        ==  3.36210314311209350626E-4932L, "");
    static_assert(LDBL_EPSILON    ==    1.08420217248550443401E-19L, "");
}

For Microsoft Visual Studio compiler (MSVC):
long_double_assert_msvc.cpp:

#include <float.h>
int main(void {
    static_assert(sizeof(long double) ==                    8, "");
    static_assert(LDBL_MANT_DIG   ==                       53, "");
    static_assert(LDBL_DIG        ==                       15, "");
    static_assert(LDBL_MIN_EXP    ==                    -1021, "");
    static_assert(LDBL_MIN_10_EXP ==                     -307, "");
    static_assert(LDBL_MAX_EXP    ==                     1024, "");
    static_assert(LDBL_MAX_10_EXP ==                      308, "");
    static_assert(LDBL_MAX        ==  1.7976931348623157E+308, "");
    static_assert(LDBL_MIN        ==  2.2250738585072014E-308, "");
    static_assert(LDBL_EPSILON    ==  2.2204460492503131E-016, "");
}

@qkoziol
Copy link
Contributor

qkoziol commented May 15, 2023

H5detect is designed to allow for cross-compilation (also)

@giordano
Copy link

Yes, the problem of cross-compilation isn't caching the result of try_run, but the try_run itself! And most of the try_run checks can be replaced with compiler checks, as suggested by Mark, and demonstrated in the now-5-year-old fork https://github.com/stevengj/hdf5 linked in the original post of this issue

@mkitti
Copy link
Contributor

mkitti commented May 15, 2023

H5detect is designed to allow for cross-compilation (also)

Say we're trying to cross-compile everything on x86_64 based cloud instance host for continuous integration and we do not have access to the target hardware or an emulator for that environment. How many platforms could we accurately populate H5init.c with via C preprocessor macros alone given gcc or clang based toolchains?

@mkitti
Copy link
Contributor

mkitti commented May 15, 2023

To check this, we could use Godbolt to see how many toolchains are able to compile the static assertions.

@derobins
Copy link
Member

Can this issue be closed?

  • The long double checks are gone
  • AC_RUN_IFELSE in the Autotools has defaults for everything that is left (just szip encoding and checking pthread_attr_setscope)
  • H5detect is gone
  • H5make_libsettings is gone

Cross compiling should basically just work now

@musm
Copy link
Author

musm commented Jul 12, 2023

Yep should be fixed now

@musm musm closed this as completed Jul 12, 2023
@musm
Copy link
Author

musm commented Jul 12, 2023

In Julia this is the relevant PR that made the changes to enable cross-compilation JuliaPackaging/Yggdrasil#6551

@giordano
Copy link

To be clear, that PR predates #3104 and it was hardcoding try_run results with a huge collection of scripts to be run on all platforms before getting to a build. I haven't tried #3104 yet but hopefully it'll make all of that redundant and we can greatly simplify the build script.

@musm
Copy link
Author

musm commented Jul 12, 2023

@giordano should a new issue to update the script in Yggrasil be opened instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component - Build CMake, Autotools Priority - 1. High 🔼 These are important issues that should be resolved in the next release Type - Improvement Improvements that don't add a new feature or functionality
Projects
None yet
Development

No branches or pull requests

9 participants