Skip to content

Commit

Permalink
446 fixed aarch64 cross compiling (#448)
Browse files Browse the repository at this point in the history
* Fixed aarch64 cross-compiling;
* Updated documentations;
* Updated troubleshooting.
  • Loading branch information
aregtech authored Nov 16, 2024
1 parent 44d9a0c commit 18240f8
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 48 deletions.
11 changes: 4 additions & 7 deletions conf/cmake/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,17 @@ elseif("${AREG_COMPILER_SHORT}" STREQUAL "")

endif()

# Set identified OS
set(AREG_OS ${CMAKE_SYSTEM_NAME})
# Set processor, if not identified yet.
if ("${AREG_PROCESSOR}" STREQUAL "")
set(AREG_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
endif()
# Set bitness, if not identified yet.
if ("${AREG_BITNESS}" STREQUAL "" OR AREG_BITNESS EQUAL 0)
macro_system_bitness(AREG_BITNESS)
endif()

# Identify the OS
set(AREG_OS ${CMAKE_SYSTEM_NAME})
# Identify CPU platform
if ("${AREG_PROCESSOR}" STREQUAL "")
set(AREG_PROCESSOR ${CMAKE_SYSTEM_PROCESSOR})
endif()

# -----------------------------------------------------
# areg specific internal variable settings
# -----------------------------------------------------
Expand Down
99 changes: 79 additions & 20 deletions conf/cmake/functions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ macro(macro_parse_arguments res_sources res_libs res_resources)
endforeach()
endmacro(macro_parse_arguments)

# Read-only variable of 32-bit 'x86' processor name
set(_proc_x86 "x86")
# Read-only variable of 64-bit 'x64' processor name
set(_proc_x64 "x86_64")
# Read-only variable of 32-bit 'arm' processor name
set(_proc_arm32 "ARM")
# Read-only variable of 64-bit 'aarch64' processor name
set(_proc_arm64 "AARCH64")

# ---------------------------------------------------------------------------
# Macro ......: macro_guess_processor_architecture
# Purpose ....: Tries to guess the CPU architecture and bitness from the given cross-compiler.
Expand All @@ -179,17 +188,24 @@ endmacro(macro_parse_arguments)
# Example ....: macro_guess_processor_architecture("arm-linux-gnueabihf-g++" cpu_architect cpu_bitness)
# ---------------------------------------------------------------------------
macro(macro_guess_processor_architecture compiler_path target_processor target_bitness)
foreach(_proc "arm;32;arm" "aarch64;64;arm64")
list(GET _proc 0 _arch)
list(GET _proc 1 _bits)
list(GET _proc 2 _name)
string(FIND "${compiler_path}" ${_arch} _proc_pos)
foreach(_entry "arm;${_proc_arm};32" "aarch64;${_proc_arm64};64")
list(GET _entry 0 _proc)
list(GET _entry 1 _arch)
list(GET _entry 2 _bits)
string(FIND "${compiler_path}" ${_proc} _proc_pos)
if (_proc_pos GREATER -1)
set(${target_processor} ${_name})
set(${target_processor} ${_arch})
set(${target_bitness} ${_bits})
break()
endif()
endforeach()

unset(_entry)
unset(_proc)
unset(_arch)
unset(_bits)
unset(_proc_pos)

endmacro(macro_guess_processor_architecture)

# ---------------------------------------------------------------------------
Expand All @@ -211,6 +227,38 @@ macro(macro_system_bitness var_bitness)
endif()
endmacro(macro_system_bitness)

# ---------------------------------------------------------------------------
# Macro ......: macro_get_processor
# Purpose ....: Identifies and validates the processor architecture based on a provided name.
# If a match is found in the supported processor list, it extracts:
# - The canonical architecture name.
# - The bitness (e.g., 32 or 64 bits).
# Parameters ..: ${processor_name} [in] -- Input processor architecture name to search for.
# ${var_processor} [out] -- Variable to store the canonical processor architecture name.
# ${var_bitness} [out] -- Variable to store the bitness (32/64) of the processor.
# ${var_found} [out] -- Variable to indicate if the processor is supported (TRUE/FALSE).
# Usage .......: macro_get_processor(<processor-name> <var_processor> <var_bitness> <var_entry_found>)
# Example .....: macro_get_processor("arm64" AREG_PROCESSOR AREG_BITNESS _entry_found)
# ---------------------------------------------------------------------------
macro(macro_get_processor processor_name var_processor var_bitness var_found)
set(${var_found} FALSE)
foreach(_entry "x86;${_proc_x86};32" "x64;${_proc_x64};64" "x86_64;${_proc_x64};64" "amd64;${_proc_x64};64" "arm;${_proc_arm32};32" "arm32;${_proc_arm32};32" "arm64;${_proc_arm64};64" "aarch64;${_proc_arm64};64")
list(GET _entry 0 _proc)
list(GET _entry 1 _arch)
list(GET _entry 2 _bits)
if (${_proc} STREQUAL ${processor_name})
set(${var_processor} ${_arch})
set(${var_bitness} ${_bits})
set(${var_found} TRUE)
break()
endif()
endforeach()
unset(_entry)
unset(_proc)
unset(_arch)
unset(_bits)
endmacro(macro_get_processor)

# ---------------------------------------------------------------------------
# Macro ......: macro_setup_compilers_data
# Purpose ....: Identifies and configures compiler family, short names, and paths.
Expand Down Expand Up @@ -244,13 +292,13 @@ macro(macro_setup_compilers_data compiler_path compiler_family compiler_short co

# Iterate over known compilers to identify the compiler type
foreach(_entry "clang-cl;llvm;clang-cl" "clang++;llvm;clang" "clang;llvm;clang" "g++;gnu;gcc" "gcc;gnu;gcc" "c++;gnu;cc" "cc;gnu;cc" "cl;msvc;cl")
list(GET _entry 0 _cxx_compiler)
list(GET _entry 0 _cxx_comp)
list(GET _entry 1 _family)
list(GET _entry 2 _c_compiler)
list(GET _entry 2 _cc_comp)

# Check if the provided compiler matches the known C++ compiler
string(TOLOWER "${compiler_path}" _comp_path)
string(FIND "${_comp_path}" "${_cxx_compiler}" _found_pos REVERSE)
string(FIND "${_comp_path}" "${_cxx_comp}" _found_pos REVERSE)
if (_found_pos GREATER -1)
# Handle special case for CYGWIN and GNU family compilers
if (CYGWIN AND ("${_family}" STREQUAL "gnu"))
Expand All @@ -262,14 +310,14 @@ macro(macro_setup_compilers_data compiler_path compiler_family compiler_short co
set(${compiler_family} "${_family}")
endif()

set(${compiler_short} "${_cxx_compiler}")
set(${compiler_short} "${_cxx_comp}")
set(${compiler_cxx} "${compiler_path}")

# Determine the corresponding C compiler path or name
if ("${_cxx_compiler}" STREQUAL "${_c_compiler}")
if ("${_cxx_comp}" STREQUAL "${_cc_comp}")
set(${compiler_c} "${compiler_path}")
else()
string(REPLACE "${_cxx_compiler}" "${_c_compiler}" ${compiler_c} "${compiler_path}")
string(REPLACE "${_cxx_comp}" "${_cc_comp}" ${compiler_c} "${compiler_path}")
endif()

# Mark compiler as found
Expand All @@ -280,6 +328,12 @@ macro(macro_setup_compilers_data compiler_path compiler_family compiler_short co
endif()
endforeach()

unset(_entry)
unset(_cxx_comp)
unset(_family)
unset(_cc_comp)
unset(_found_pos)

endmacro(macro_setup_compilers_data)

# ---------------------------------------------------------------------------
Expand All @@ -305,28 +359,28 @@ macro(macro_setup_compilers_data_by_family compiler_family compiler_short compil

# Iterate over known compilers and match the family
foreach(_entry "clang++;llvm;clang" "g++;gnu;gcc" "cl;msvc;cl" "g++;cygwin;gcc")
list(GET _entry 0 _compiler)
list(GET _entry 0 _cxx_comp)
list(GET _entry 1 _family)
list(GET _entry 2 _c_compiler)
list(GET _entry 2 _cc_comp)

if ("${_family}" STREQUAL "${compiler_family}")
# Special case for Windows
if (WIN32 AND "${_family}" STREQUAL "llvm")
set(${compiler_short} "clang-cl")
set(${compiler_cxx} "clang-cl")
set(${compiler_c} "clang-cl")
elseif (AREG_PROCESSOR STREQUAL "arm" AND "${_family}" STREQUAL "gnu")
elseif ("${AREG_PROCESSOR}" STREQUAL "${_proc_arm32}" AND "${_family}" STREQUAL "gnu")
set(${compiler_short} "g++")
set(${compiler_cxx} "arm-linux-gnueabihf-g++")
set(${compiler_c} "arm-linux-gnueabihf-gcc")
elseif (AREG_PROCESSOR STREQUAL "aarch64" AND "${_family}" STREQUAL "gnu")
elseif ("${AREG_PROCESSOR}" STREQUAL "${_proc_arm64}" AND "${_family}" STREQUAL "gnu")
set(${compiler_short} "g++")
set(${compiler_cxx} "aarch64-linux-gnu-g++")
set(${compiler_c} "aarch64-linux-gnu-gcc")
else()
set(${compiler_short} "${_compiler}")
set(${compiler_cxx} "${_compiler}")
set(${compiler_c} "${_c_compiler}")
set(${compiler_short} "${_cxx_comp}")
set(${compiler_cxx} "${_cxx_comp}")
set(${compiler_c} "${_cc_comp}")
endif()

# Mark compiler as found
Expand All @@ -337,6 +391,11 @@ macro(macro_setup_compilers_data_by_family compiler_family compiler_short compil
endif()
endforeach()

unset(_entry)
unset(_cxx_comp)
unset(_family)
unset(_cc_comp)

endmacro(macro_setup_compilers_data_by_family)

# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -886,7 +945,7 @@ function(printAregConfigStatus var_make_print var_prefix var_header var_footer)

# Print detailed configuration status, each with the defined prefix
message(STATUS "${var_prefix}: >>> CMAKE_SOURCE_DIR = '${CMAKE_SOURCE_DIR}', build type '${CMAKE_BUILD_TYPE}'")
message(STATUS "${var_prefix}: >>> Build Environment ..: System '${CMAKE_SYSTEM_NAME}', ${AREG_BITNESS}-bit platform, '${AREG_PROCESSOR}' CPU")
message(STATUS "${var_prefix}: >>> Build Environment ..: System '${CMAKE_SYSTEM_NAME}', ${AREG_BITNESS}-bit platform, '${AREG_PROCESSOR}' CPU, Environment '${AREG_DEVELOP_ENV}'")
message(STATUS "${var_prefix}: >>> Compiler ...........: '${CMAKE_CXX_COMPILER}'")
message(STATUS "${var_prefix}: >>> Compiler Version ...: C++ standard 'c++${CMAKE_CXX_STANDARD}', compiler family '${AREG_COMPILER_FAMILY}'")
message(STATUS "${var_prefix}: >>> AREG SDK Root ......: '${AREG_SDK_ROOT}'")
Expand Down
2 changes: 1 addition & 1 deletion conf/cmake/gnu.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ else()
list(APPEND AREG_COMPILER_OPTIONS -O0 -g3)
endif()

if (NOT ${AREG_PROCESSOR} STREQUAL "arm" AND NOT ${AREG_PROCESSOR} STREQUAL "aarch64")
if (${AREG_PROCESSOR} STREQUAL ${_proc_x86} OR ${AREG_PROCESSOR} STREQUAL ${_proc_x64})
if(${AREG_BITNESS} EQUAL 32)
list(APPEND AREG_COMPILER_OPTIONS -m32)
list(APPEND AREG_LDFLAGS -m32)
Expand Down
22 changes: 3 additions & 19 deletions conf/cmake/user.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -82,25 +82,9 @@ set(AREG_C_COMPILER)
set(AREG_COMPILER_SHORT)

# Specify CPU platform here, the system CPU platform is detected in 'commmon.cmake'
if (DEFINED AREG_PROCESSOR)
if (AREG_PROCESSOR STREQUAL "x86")
set(CMAKE_SYSTEM_PROCESSOR x86)
set(AREG_BITNESS 32)
elseif(AREG_PROCESSOR STREQUAL "x64" OR AREG_PROCESSOR STREQUAL "x86_64" OR AREG_PROCESSOR STREQUAL "amd64")
set(CMAKE_SYSTEM_PROCESSOR x86_64)
set(AREG_PROCESSOR x64)
set(AREG_BITNESS 64)
elseif (AREG_PROCESSOR STREQUAL "arm" OR AREG_PROCESSOR STREQUAL "arm32")
set(CMAKE_SYSTEM_PROCESSOR ARM)
set(AREG_PROCESSOR arm)
set(AREG_BITNESS 32)
elseif (AREG_PROCESSOR STREQUAL "aarch64" OR AREG_PROCESSOR STREQUAL "arm64")
set(CMAKE_SYSTEM_PROCESSOR AARCH64)
set(AREG_PROCESSOR aarch64)
set(AREG_BITNESS 64)
else()
set(CMAKE_SYSTEM_PROCESSOR ${AREG_PROCESSOR})
endif()
if (DEFINED AREG_PROCESSOR AND NOT "${AREG_PROCESSOR}" STREQUAL "")
macro_get_processor(${AREG_PROCESSOR} AREG_PROCESSOR AREG_BITNESS _found_proc)
set(CMAKE_SYSTEM_PROCESSOR ${AREG_PROCESSOR})
endif()

# If CMake compilers are specified, use them
Expand Down
2 changes: 2 additions & 0 deletions docs/wiki/01b-cmake-build.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ Cross-compiling allows building applications for architectures different from th
| MSVC | Windows | Win32 | x86, x86_64 |
| Cygwin GNU| Windows | POSIX | x86, x86_64 |

> [!IMPORTANT]
> The AREG SDK itself does not require any external libraries. However, the extended library, `aregextend`, may have dependencies. When cross-compiling, it is recommended to set `AREG_EXTENDED` to `OFF` if you are unsure whether all required libraries are available for the target platform. Alternatively, ensure that dependencies such as `ncurses` or `sqlite3` (if the SQLite3 package is used) are accessible for the target. Otherwise, the build of AREG SKD sources will fail.
### Cross-Compiling for 32-bit Systems

Expand Down
17 changes: 17 additions & 0 deletions docs/wiki/02b-cmake-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ The [functions.cmake](./../../conf/cmake/functions.cmake) file in AREG SDK conta
- [`macro_setup_compilers_data_by_family`](#macro_setup_compilers_data_by_family)
- [`macro_guess_processor_architecture`](#macro_guess_processor_architecture)
- [`macro_system_bitness`](#macro_system_bitness)
- [`macro_get_processor`](#macro_get_processor)
3. [CMake Functions Overview](#3-cmake-functions-overview)
- [`setAppOptions`](#setappoptions)
- [`addExecutableEx`](#addexecutableex)
Expand Down Expand Up @@ -233,6 +234,22 @@ The [functions.cmake](./../../conf/cmake/functions.cmake) file includes reusable
macro_system_bitness(_sys_bitness)
```

### `macro_get_processor`
- **Syntax**: `macro_get_processor(processor_name var_processor var_bitness var_found)`
- **Purpose**: Identifies and validates the processor architecture based on a provided name. If a match is found in the supported processor list, it extracts:
- The canonical architecture name.
- The bitness (e.g., 32 or 64 bits).
- **Parameters**:
- `processor_name` [in]: Input processor architecture name to search for.
- `var_processor` [out]: Variable to store the canonical processor architecture name.
- `var_bitness` [out]: Variable to store the bitness (32/64) of the processor.
- `var_found` [out]: Variable to indicate if the processor is supported (TRUE/FALSE).
- **Usage**: `macro_get_processor(<processor-name> <var_processor> <var_bitness> <var_entry_found>)`
- **Example**:
```cmake
macro_get_processor("arm64" AREG_PROCESSOR AREG_BITNESS _entry_found)
```

---

## 3. CMake Functions Overview
Expand Down
28 changes: 28 additions & 0 deletions docs/wiki/07b-troubleshooting-cmake-linux-builds.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ This guide provides solutions for common issues encountered when compiling proje
2. [Issue 2: Values of `AREG_XXX` Variables are Ignored](#issue-2-values-of-areg_xxx-variables-are-ignored)
- [Solution 1: Fetch Sources Before Project Declaration](#solution-fetch-sources-before-project-declaration)
- [Solution 2: Use Standard CMake Variables](#solution-2-use-standard-cmake-variables)
3. [Issue 3: Failure to Cross-Compile with Extended Objects Enabled](#issue-3-failure-to-cross-compile-with-extended-objects-enabled)
- [Solution 1: Use Target-Compatible Libraries](#solution-1-use-target-compatible-libraries)
- [Solution 2: Pre-Build Required Libraries](#solution*2-pre-build-required-libraries)
- [Solution 3: Disable Extended Objects](#solution-3-disable-extended-objects)

---

Expand Down Expand Up @@ -55,3 +59,27 @@ Fetch the AREG SDK sources *before* calling `project()`. Additionally, you can s
Alternatively, use standard CMake variables such as `CMAKE_CXX_COMPILER` to specify the compiler and `CMAKE_BUILD_TYPE` to define the build type. These variables are compatible with AREG-specific CMake configurations and help avoid conflicts.
---
## Issue 3: Failure to Cross-Compile with Extended Objects Enabled
The build process fails during cross-compilation when AREG SDK extended objects are enabled (option `-DAREG_EXTENDED=ON`). The error message typically states: `cannot find -lncurses: No such file or directory`, even when `ncurses` library is installed. Similar may happend with `sqlite3` library, wich is not connected to extended objects.
This issue occurs when the system lacks target-compatible libraries. For example, if your host system runs on an `x86_64` platform but you are cross-compiling for 32-bit ARM, this error will appear due to missing ARM-compatible versions of the required libraries.
### Solution 1: Use Target-Compatible Libraries
Ensure that the necessary libraries are available for your target platform. You can install them directly or use a package manager, such as `vcpkg`, to compile the dependencies for your target architecture. For example, to install `sqlite3` for an ARM Linux target, use the following command:
```bash
vcpkg install sqlite3:linux-arm
```
For `ncurses`, similarly ensure you compile or install the library for the target platform.
### Solution 2: Pre-Build Required Libraries
Manually build and install the required libraries (`ncurses`, `sqlite3`, etc.) for the target platform using your cross-compilation toolchain. Ensure the paths to these libraries are correctly set in your build environment. For simplicity and portability, consider building statically linked versions of `ncurses` and `sqlite3` for your target platform. This approach avoids runtime dependency issues on the target system.
### Solution 3: Disable Extended Objects
If the extended features of the AREG SDK are not required for your application, you can disable them by setting `-DAREG_EXTENDED=OFF`. This removes dependencies on `ncurses` library. And for `sqlite3` library, use AREG SDK built-in SQLite3 sources available in the `thirdparty` subdirectory.
---
2 changes: 1 addition & 1 deletion tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
if (AREG_BUILD_TESTS)

# Disable unit tests post-build for ARM processor
if (${AREG_PROCESSOR} STREQUAL "arm" OR ${AREG_PROCESSOR} STREQUAL "aarch64")
if (${AREG_PROCESSOR} STREQUAL ${_proc_arm32} OR ${AREG_PROCESSOR} STREQUAL ${_proc_arm64})
set(CMAKE_GTEST_DISCOVER_TESTS_DISCOVERY_MODE PRE_TEST)
endif()

Expand Down

0 comments on commit 18240f8

Please sign in to comment.