Skip to content

Cross-compiled library assumes wrong binaries directory #209

@cbu-hw

Description

@cbu-hw

Describe the bug
When cross-compiling FMIL for Windows on a Linux Host, an incorrect CMake configuration causes the library to search for FMU binaries in the linux64 directory instead of win64.

To Reproduce
Run the following Dockerfile on Windows, with an output directory at the same level. This checks out the latest FMIL, fetches reference FMUs, sets up the cross-compilation toolchain, and exports the build folder to output.
docker build --progress=plain -t fmil-test:latest --output output .

Then, run in the output folder: .\fmi_import_test.exe .\fmus\2.0\BouncingBall.fmu

# Troubleshoot cross-compiling fmi-library
FROM ubuntu:24.04 AS build

RUN apt update && export DEBIAN_FRONTEND=noninteractive \
&& apt -y install --no-install-recommends wget git ninja-build cmake g++-mingw-w64 zip unzip \
&& apt clean all \
&& rm -rf /var/lib/apt/lists

# Check out b711f5d
RUN git clone --depth 1 --branch master https://github.com/modelon-community/fmi-library.git  \
	&& cd fmi-library \
	&& git reset --hard b711f5d
WORKDIR /fmi-library

RUN wget https://github.com/modelica/Reference-FMUs/releases/download/v0.0.39/Reference-FMUs-0.0.39.zip
RUN unzip Reference-FMUs-0.0.39.zip -d ./fmus

RUN <<HEREDOC cat > mingw-w64-x86_64.cmake
	set(CMAKE_SYSTEM_NAME Windows)
	set(TOOLCHAIN_PREFIX x86_64-w64-mingw32)
	set(CMAKE_SYSTEM_PROCESSOR x86_64)
	# cross compilers to use for C, C++
	set(CMAKE_C_COMPILER \${TOOLCHAIN_PREFIX}-gcc)
	set(CMAKE_CXX_COMPILER \${TOOLCHAIN_PREFIX}-g++)
	set(CMAKE_RC_COMPILER \${TOOLCHAIN_PREFIX}-windres)
	# target environment on the build host system
	set(CMAKE_FIND_ROOT_PATH /usr/\${TOOLCHAIN_PREFIX})
	# modify default behavior of FIND_XXX() commands
	# search for programs in the build host environment
	set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
	# search for headers/libs in the target environment
	set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
	set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
HEREDOC
RUN cat -e mingw-w64-x86_64.cmake
ENV CMAKE_TOOLCHAIN_FILE="/fmi-library/mingw-w64-x86_64.cmake"
RUN printenv

# Patch fmi_import_test.c to avoid non-portable temp dir
RUN cat -e Test/fmi_import_test.c
RUN <<"HEREDOC" cat > patch-tempdir.diff
diff --git a/Test/fmi_import_test.c b/Test/fmi_import_test.c
index c24190d..b8139d4 100644
--- a/Test/fmi_import_test.c
+++ b/Test/fmi_import_test.c
@@ -69 +69 @@ int main(int argc, char *argv[])
-    tmpPath = fmi_import_mk_temp_dir(&callbacks, FMU_UNPACK_DIR, NULL);
+    tmpPath = fmi_import_mk_temp_dir(&callbacks, NULL, NULL);
HEREDOC

RUN cat -e patch-tempdir.diff
# This needed to avoid line-ending differences making a patch unapplicable
RUN apt update && apt install -y dos2unix patch
RUN unix2dos patch-tempdir.diff
# patch is more resilient w.r.t. line endings than git apply
RUN patch -p1 --binary < patch-tempdir.diff

RUN ls -la
# this produces a failing executable
RUN cmake -S . -B crossbuild -G Ninja
# this works around the problem, demonstrating that cross-compilation works
#RUN cmake -S . -B crossbuild -G Ninja -D FMILIB_FMI2_PLATFORM=win64

RUN cmake --build crossbuild --target fmi_import_test 

# Export build artifacts with --output argument
FROM scratch
COPY --from=build /fmi-library/crossbuild /
COPY --from=build /fmi-library/fmus /fmus

Observed behavior
The exe run fails because it's wrongly looking for the dll in the linux64 directory:

 .\fmi_import_test.exe .\fmus\2.0\BouncingBall.fmu
module = FMILIB, log level = 5: Allocating FMIL context
module = FMILIB, log level = 5: Detecting FMI standard version
module = FMIZIP, log level = 5: Unpacking FMU into C:\Users\myuser\AppData\Local\Temp\fmila20492
module = FMIXML, log level = 5: Parsing XML to detect FMI standard version
module = FMIXML, log level = 5: XML specifies FMI 2.0
module = FMILIB, log level = 4: XML specifies FMI standard version 2.0
module = FMILIB, log level = 5: Detecting FMI standard version
module = FMIXML, log level = 5: Parsing XML to detect FMI standard version
module = FMIXML, log level = 5: XML specifies FMI 2.0
module = FMILIB, log level = 4: XML specifies FMI standard version 2.0
module = FMILIB, log level = 5: Parsing model description XML
module = FMI2XML, log level = 5: Parsing XML element fmiModelDescription
module = FMI2XML, log level = 5: Parsing XML element ModelExchange
module = FMI2XML, log level = 5: Parsing XML element CoSimulation
module = FMI2XML, log level = 5: Parsing XML element UnitDefinitions
module = FMI2XML, log level = 5: Parsing XML element TypeDefinitions
module = FMI2XML, log level = 5: Parsing XML element LogCategories
module = FMI2XML, log level = 5: Parsing XML element ModelVariables
module = FMI2XML, log level = 5: Building alias index
module = FMI2XML, log level = 5: Parsing XML element ModelStructure
module = FMI2XML, log level = 5: Parsing XML element Outputs
module = FMI2XML, log level = 5: Parsing XML element Derivatives
module = FMI2XML, log level = 5: Parsing XML element InitialUnknowns
module = FMI2XML, log level = 4: Found model identifiers for ModelExchange and CoSimulation
module = FMIXML, log level = 4: Could not find or open terminalsAndIcons.xml: 'C:\Users\myuser\AppData\Local\Temp\fmila20492/terminalsAndIcons\terminalsAndIcons.xml'. Continuing.
module = FMILIB, log level = 5: Parsing finished successfully
Model name: BouncingBall
Model identifier for ME: BouncingBall
Model GUID: {1AE5E10D-9521-4DE3-80B9-D0EAAA7D5AF1}
module = FMILIB, log level = 4: Loading 'linux64' binary with 'default' platform types
module = FMICAPI, log level = 1: Could not load the FMU binary: The specified module could not be found.

Could not create the DLL loading mechanism(C-API).
module = JMPRT, log level = 5: Removing C:\Users\myuser\AppData\Local\Temp\fmila20492
Press 'Enter' to exit

Expected behavior
This is expected to run correctly, and can be made so by manually overriding the platform variable as a workaround:

#RUN cmake -S . -B crossbuild -G Ninja -D FMILIB_FMI2_PLATFORM=win64
module = FMILIB, log level = 4: Loading 'win64' binary with 'default' platform types
module = FMICAPI, log level = 5: Loaded FMU binary from C:\Users\myuser\AppData\Local\Temp\fmila29512\binaries\win64\BouncingBall.dll
module = FMICAPI, log level = 5: Loading functions for the model exchange interface
module = FMILIB, log level = 5: Successfully loaded all the interface functions
Version returned from FMU:   2.0
module = FMILIB, log level = 5: Releasing FMU CAPI interface
module = FMICAPI, log level = 5: Successfully unloaded FMU binary
module = FMILIB, log level = 5: Releasing allocated library resources
module = JMPRT, log level = 5: Removing C:\Users\myuser\AppData\Local\Temp\fmila29512
Everything seems to be OK since you got this far=)!
Press 'Enter' to exit

The reason behind the error is that

  • platform = FMI2_PLATFORM ends up being "linux64", not as expected "win64", because
  • fmilib_config.h:57 has #define FMI2_PLATFORM "linux64" (FMI_FILE_SEP \\ and FMI_DLL_EXT .dll are correct, though!), because
  • config_fmilib.h.cmake has #define FMI2_PLATFORM "@FMI2_PLATFORM@", which is ultimately set by
  • Config.cmake/fmiplatform.cmake, which uses
    • CMAKE_HOST_WIN32 instead of WIN32 and
    • CMAKE_HOST_SYSTEM_NAME instead CMAKE_SYSTEM_NAME (AFAICT both changes would be correct, as it's not the host system that is relevant)

Versions

Additional context/problems

  • There is a non-fatal problem because of a mangled path, could probably be cleaned up at the same time:

Could not find or open terminalsAndIcons.xml: 'C:\Users\myuser\AppData\Local\Temp\fmila20492/terminalsAndIcons\terminalsAndIcons.xml'. Continuing.

  • The proposed workaround is only for FMI2
  • I don't know if HOST variables should be adapted elsewhere in the CMake setup
  • Contrary to @jschueller's remark at Cross compilation compatible #96 (comment), -DFMILIB_EXTERNAL_LIBS=ON is not necessary, and in fact needs to be OFF, because cross-compiled external libs are not available (at least not on Ubuntu). I think the root cause behind this recommendation was that no other way was visible to propagate the cross-compilation toolchain settings to the whole build. Exporting a CMAKE_TOOLCHAIN_FILE env variable as in the repro above solves that problem.
  • fmi_import_test.c contains a temporary path (via FMU_UNPACK_DIR) that is hardcoded to somewhere on the (Linux) host system, making it un-runnable on Windows. The rough patch avoid that issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions