From b77325f54624a1b915bac278f849b8b5a8f41f2b Mon Sep 17 00:00:00 2001 From: Peter M Date: Sun, 8 Mar 2026 11:13:49 +0100 Subject: [PATCH] Normalize system_architecture strings Normalize system_architecture to arch-vendor-os form. Use compiler target strings where available and normalize them to exactly three components across generic Unix, Emscripten, ESP32, RP2, and STM32. Add a shared CMake helper for normalization, keep platform-specific vendor tags for RP2 and STM32, and extend system_architecture tests for generic Unix and ESP32. Signed-off-by: Peter M --- CHANGELOG.md | 7 ++ CMakeModules/SystemArchitecture.cmake | 71 +++++++++++++++++++ src/libAtomVM/CMakeLists.txt | 20 ++++++ src/libAtomVM/nifs.c | 6 +- src/libAtomVM/version.h.in | 1 + src/platforms/emscripten/src/CMakeLists.txt | 3 + .../esp32/components/libatomvm/CMakeLists.txt | 3 + .../test/main/test_erl_sources/CMakeLists.txt | 2 + .../test_system_architecture.erl | 34 +++++++++ src/platforms/esp32/test/main/test_main.c | 6 ++ src/platforms/generic_unix/CMakeLists.txt | 5 ++ src/platforms/rp2/src/CMakeLists.txt | 2 + src/platforms/stm32/src/CMakeLists.txt | 2 + tests/erlang_tests/test_system_info.erl | 15 +++- 14 files changed, 172 insertions(+), 5 deletions(-) create mode 100644 CMakeModules/SystemArchitecture.cmake create mode 100644 src/platforms/esp32/test/main/test_erl_sources/test_system_architecture.erl diff --git a/CHANGELOG.md b/CHANGELOG.md index 542a0825de..3aa31ad5be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.7.0-alpha.1] - Unreleased + +### Added +### Changed +### Fixed +- `erlang:system_info(system_architecture)` now reports normalized `arch-vendor-os` strings + ## [0.7.0-alpha.0] - 2026-03-20 ### Added diff --git a/CMakeModules/SystemArchitecture.cmake b/CMakeModules/SystemArchitecture.cmake new file mode 100644 index 0000000000..660a68748b --- /dev/null +++ b/CMakeModules/SystemArchitecture.cmake @@ -0,0 +1,71 @@ +# +# This file is part of AtomVM. +# +# Copyright 2026 Peter M. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +# + +function(avm_get_system_architecture_string out_var) + set(options) + set(one_value_args PLATFORM_VENDOR PLATFORM_OS) + cmake_parse_arguments(PARSE_ARGV 1 AVM "${options}" "${one_value_args}" "") + + execute_process( + COMMAND ${CMAKE_C_COMPILER} -dumpmachine + OUTPUT_VARIABLE avm_raw_system_architecture + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + ) + + if (avm_raw_system_architecture STREQUAL "") + unset(${out_var} PARENT_SCOPE) + return() + endif() + + string(REPLACE "-" ";" avm_system_architecture_parts "${avm_raw_system_architecture}") + list(LENGTH avm_system_architecture_parts avm_system_architecture_length) + + if (avm_system_architecture_length EQUAL 1) + list(GET avm_system_architecture_parts 0 avm_architecture) + if (DEFINED AVM_PLATFORM_VENDOR) + set(avm_vendor "${AVM_PLATFORM_VENDOR}") + else() + set(avm_vendor "unknown") + endif() + set(avm_os "unknown") + elseif (avm_system_architecture_length EQUAL 2) + list(GET avm_system_architecture_parts 0 avm_architecture) + if (DEFINED AVM_PLATFORM_VENDOR) + set(avm_vendor "${AVM_PLATFORM_VENDOR}") + else() + set(avm_vendor "unknown") + endif() + list(GET avm_system_architecture_parts 1 avm_os) + else() + list(GET avm_system_architecture_parts 0 avm_architecture) + list(GET avm_system_architecture_parts 1 avm_vendor) + if (DEFINED AVM_PLATFORM_VENDOR AND (avm_vendor STREQUAL "none" OR avm_vendor STREQUAL "unknown")) + set(avm_vendor "${AVM_PLATFORM_VENDOR}") + endif() + + list(REMOVE_AT avm_system_architecture_parts 0 1) + string(REPLACE ";" "_" avm_os "${avm_system_architecture_parts}") + endif() + + if (DEFINED AVM_PLATFORM_OS) + set(avm_os "${AVM_PLATFORM_OS}") + endif() + + string(REPLACE "-" "_" avm_architecture "${avm_architecture}") + string(REPLACE "-" "_" avm_vendor "${avm_vendor}") + string(REPLACE "-" "_" avm_os "${avm_os}") + + set(${out_var} "${avm_architecture}-${avm_vendor}-${avm_os}" PARENT_SCOPE) +endfunction() diff --git a/src/libAtomVM/CMakeLists.txt b/src/libAtomVM/CMakeLists.txt index 2f10f23635..a7a6b49729 100644 --- a/src/libAtomVM/CMakeLists.txt +++ b/src/libAtomVM/CMakeLists.txt @@ -319,6 +319,26 @@ else() set(ATOMVM_VERSION ${ATOMVM_BASE_VERSION}) endif() +set(AVM_SYSTEM_ARCHITECTURE_FALLBACK_ARCH "${CMAKE_SYSTEM_PROCESSOR}") +if (AVM_SYSTEM_ARCHITECTURE_FALLBACK_ARCH STREQUAL "") + set(AVM_SYSTEM_ARCHITECTURE_FALLBACK_ARCH "unknown") +endif() +string(REPLACE "-" "_" AVM_SYSTEM_ARCHITECTURE_FALLBACK_ARCH "${AVM_SYSTEM_ARCHITECTURE_FALLBACK_ARCH}") + +set(AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS "${CMAKE_SYSTEM_NAME}") +if (AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS STREQUAL "") + set(AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS "unknown") +endif() +if (NOT CMAKE_SYSTEM_VERSION STREQUAL "") + string(APPEND AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS "_${CMAKE_SYSTEM_VERSION}") +endif() +string(REPLACE "-" "_" AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS "${AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS}") + +if (NOT DEFINED AVM_SYSTEM_ARCHITECTURE_STRING OR AVM_SYSTEM_ARCHITECTURE_STRING STREQUAL "") + set(AVM_SYSTEM_ARCHITECTURE_STRING + "${AVM_SYSTEM_ARCHITECTURE_FALLBACK_ARCH}-unknown-${AVM_SYSTEM_ARCHITECTURE_FALLBACK_OS}") +endif() + # Add include to directory where avm_version.h is generated so targets linking # libAtomVM can access it target_include_directories(libAtomVM PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/src/libAtomVM/nifs.c b/src/libAtomVM/nifs.c index 68c89df47e..1ceadd1a01 100644 --- a/src/libAtomVM/nifs.c +++ b/src/libAtomVM/nifs.c @@ -3173,13 +3173,11 @@ static term nif_erlang_system_info(Context *ctx, int argc, term argv[]) return term_from_int11(sizeof(avm_float_t)); } if (key == SYSTEM_ARCHITECTURE_ATOM) { - char buf[128]; - snprintf(buf, 128, "%s-%s-%s", SYSTEM_NAME, SYSTEM_VERSION, SYSTEM_ARCHITECTURE); - size_t len = strnlen(buf, 128); + size_t len = sizeof(SYSTEM_ARCHITECTURE_STRING) - 1; if (memory_ensure_free_opt(ctx, term_binary_heap_size(len), MEMORY_CAN_SHRINK) != MEMORY_GC_OK) { RAISE_ERROR(OUT_OF_MEMORY_ATOM); } - return term_from_literal_binary((const uint8_t *) buf, len, &ctx->heap, ctx->global); + return term_from_literal_binary((const uint8_t *) SYSTEM_ARCHITECTURE_STRING, len, &ctx->heap, ctx->global); } if (key == ATOMVM_VERSION_ATOM) { size_t len = strlen(ATOMVM_VERSION); diff --git a/src/libAtomVM/version.h.in b/src/libAtomVM/version.h.in index 85849a042a..4b7c31dec6 100644 --- a/src/libAtomVM/version.h.in +++ b/src/libAtomVM/version.h.in @@ -21,4 +21,5 @@ #define SYSTEM_NAME "${CMAKE_SYSTEM_NAME}" #define SYSTEM_VERSION "${CMAKE_SYSTEM_VERSION}" #define SYSTEM_ARCHITECTURE "${CMAKE_SYSTEM_PROCESSOR}" +#define SYSTEM_ARCHITECTURE_STRING "${AVM_SYSTEM_ARCHITECTURE_STRING}" #define ATOMVM_VERSION "${ATOMVM_VERSION}" diff --git a/src/platforms/emscripten/src/CMakeLists.txt b/src/platforms/emscripten/src/CMakeLists.txt index 38da49320f..e989cb035e 100644 --- a/src/platforms/emscripten/src/CMakeLists.txt +++ b/src/platforms/emscripten/src/CMakeLists.txt @@ -24,6 +24,9 @@ add_executable(AtomVM main.c) target_compile_features(AtomVM PUBLIC c_std_11) +include(SystemArchitecture) +avm_get_system_architecture_string(AVM_SYSTEM_ARCHITECTURE_STRING) + add_subdirectory(../../../libAtomVM libAtomVM) target_link_libraries(AtomVM PUBLIC libAtomVM) target_compile_options(libAtomVM PUBLIC -O3 -fno-exceptions -fno-rtti -pthread -sINLINING_LIMIT -sUSE_ZLIB=1) diff --git a/src/platforms/esp32/components/libatomvm/CMakeLists.txt b/src/platforms/esp32/components/libatomvm/CMakeLists.txt index e135c917c3..b516f829c2 100644 --- a/src/platforms/esp32/components/libatomvm/CMakeLists.txt +++ b/src/platforms/esp32/components/libatomvm/CMakeLists.txt @@ -29,6 +29,9 @@ idf_component_register(INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}/../../../../lib # "pedantic" flag should be disabled rather poluting diagnostics with warnings caused from 3rd party option(AVM_PEDANTIC_WARNINGS "Pedantic compiler warnings" OFF) +include(SystemArchitecture) +avm_get_system_architecture_string(AVM_SYSTEM_ARCHITECTURE_STRING PLATFORM_OS esp_idf) + add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../libAtomVM" "libAtomVM") # Add directory with platform_atomic.h if we mean to use it diff --git a/src/platforms/esp32/test/main/test_erl_sources/CMakeLists.txt b/src/platforms/esp32/test/main/test_erl_sources/CMakeLists.txt index ce8d9f62e5..f13dc8e6f9 100644 --- a/src/platforms/esp32/test/main/test_erl_sources/CMakeLists.txt +++ b/src/platforms/esp32/test/main/test_erl_sources/CMakeLists.txt @@ -74,6 +74,7 @@ compile_erlang(test_rtc_slow) compile_erlang(test_select) compile_erlang(test_socket) compile_erlang(test_ssl) +compile_erlang(test_system_architecture) compile_erlang(test_time_and_processes) compile_erlang(test_twdt) compile_erlang(test_tz) @@ -96,6 +97,7 @@ set(erlang_test_beams test_select.beam test_socket.beam test_ssl.beam + test_system_architecture.beam test_time_and_processes.beam test_twdt.beam test_tz.beam diff --git a/src/platforms/esp32/test/main/test_erl_sources/test_system_architecture.erl b/src/platforms/esp32/test/main/test_erl_sources/test_system_architecture.erl new file mode 100644 index 0000000000..3500f3ab59 --- /dev/null +++ b/src/platforms/esp32/test/main/test_erl_sources/test_system_architecture.erl @@ -0,0 +1,34 @@ +% +% This file is part of AtomVM. +% +% Copyright 2026 Peter M. +% +% Licensed under the Apache License, Version 2.0 (the "License"); +% you may not use this file except in compliance with the License. +% You may obtain a copy of the License at +% +% http://www.apache.org/licenses/LICENSE-2.0 +% +% Unless required by applicable law or agreed to in writing, software +% distributed under the License is distributed on an "AS IS" BASIS, +% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +% See the License for the specific language governing permissions and +% limitations under the License. +% +% SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later +% + +-module(test_system_architecture). +-export([start/0]). + +start() -> + SystemArchitecture = erlang:system_info(system_architecture), + ok = + case SystemArchitecture of + <<"xtensa-esp-esp_idf">> -> + ok; + <<"riscv32-esp-esp_idf">> -> + ok + end, + nomatch = binary:match(SystemArchitecture, <<"esp_idf-">>), + ok. diff --git a/src/platforms/esp32/test/main/test_main.c b/src/platforms/esp32/test/main/test_main.c index a42aaeb4d1..680c3baf65 100644 --- a/src/platforms/esp32/test/main/test_main.c +++ b/src/platforms/esp32/test/main/test_main.c @@ -502,6 +502,12 @@ TEST_CASE("test_time_and_processes", "[test_run]") TEST_ASSERT(term_to_int(ret_value) == 6); } +TEST_CASE("test_system_architecture", "[test_run]") +{ + term ret_value = avm_test_case("test_system_architecture.beam"); + TEST_ASSERT(ret_value == OK_ATOM); +} + TEST_CASE("test_tz", "[test_run]") { term ret_value = avm_test_case("test_tz.beam"); diff --git a/src/platforms/generic_unix/CMakeLists.txt b/src/platforms/generic_unix/CMakeLists.txt index 76cc7104fa..2b7e7be272 100644 --- a/src/platforms/generic_unix/CMakeLists.txt +++ b/src/platforms/generic_unix/CMakeLists.txt @@ -45,6 +45,11 @@ endif() add_subdirectory(lib) target_include_directories(AtomVM PUBLIC lib/) +include(SystemArchitecture) +if (NOT DEFINED AVM_SYSTEM_ARCHITECTURE_STRING OR "${AVM_SYSTEM_ARCHITECTURE_STRING}" STREQUAL "") + avm_get_system_architecture_string(AVM_SYSTEM_ARCHITECTURE_STRING) +endif() + add_subdirectory(../../libAtomVM libAtomVM) target_link_libraries(AtomVM PRIVATE libAtomVM) diff --git a/src/platforms/rp2/src/CMakeLists.txt b/src/platforms/rp2/src/CMakeLists.txt index f4ef67cd8f..fe5f7fb2ac 100644 --- a/src/platforms/rp2/src/CMakeLists.txt +++ b/src/platforms/rp2/src/CMakeLists.txt @@ -34,6 +34,8 @@ set(HAVE_PLATFORM_SMP_H ON) if(PICO_RP2040) set(HAVE_PLATFORM_ATOMIC_H ON) endif() +include(SystemArchitecture) +avm_get_system_architecture_string(AVM_SYSTEM_ARCHITECTURE_STRING PLATFORM_VENDOR rp2 PLATFORM_OS none) add_subdirectory(../../../libAtomVM libAtomVM) target_link_libraries(AtomVM PUBLIC libAtomVM) # Also add lib where platform_smp.h and platform_atomic headers are diff --git a/src/platforms/stm32/src/CMakeLists.txt b/src/platforms/stm32/src/CMakeLists.txt index 9f96271202..3a5569bfca 100644 --- a/src/platforms/stm32/src/CMakeLists.txt +++ b/src/platforms/stm32/src/CMakeLists.txt @@ -105,6 +105,8 @@ target_include_directories(${PROJECT_EXECUTABLE} PUBLIC add_dependencies(${PROJECT_EXECUTABLE} picolibc) # Link libAtomVM +include(SystemArchitecture) +avm_get_system_architecture_string(AVM_SYSTEM_ARCHITECTURE_STRING PLATFORM_VENDOR stm32 PLATFORM_OS none) add_subdirectory(../../../libAtomVM libAtomVM) add_dependencies(libAtomVM picolibc) target_link_libraries(${PROJECT_EXECUTABLE} PUBLIC libAtomVM) diff --git a/tests/erlang_tests/test_system_info.erl b/tests/erlang_tests/test_system_info.erl index 4c48898b5c..ba2932e929 100644 --- a/tests/erlang_tests/test_system_info.erl +++ b/tests/erlang_tests/test_system_info.erl @@ -33,7 +33,10 @@ start() -> % beam returns a list and probably so should AtomVM. assert(is_list(erlang:system_info(system_architecture))); _ -> - assert(is_binary(erlang:system_info(system_architecture))) + SystemArchitecture = erlang:system_info(system_architecture), + assert(is_binary(SystemArchitecture)), + 2 = count_hyphens(SystemArchitecture), + false = starts_with_known_os(SystemArchitecture) end, SystemVersion = erlang:system_info(system_version), true = is_list(SystemVersion), @@ -74,6 +77,16 @@ loop(Pid) -> assert(true) -> ok. +count_hyphens(<<>>) -> 0; +count_hyphens(<<"-", Rest/binary>>) -> 1 + count_hyphens(Rest); +count_hyphens(<<_, Rest/binary>>) -> count_hyphens(Rest). + +starts_with_known_os(<<"Darwin-", _/binary>>) -> true; +starts_with_known_os(<<"Linux-", _/binary>>) -> true; +starts_with_known_os(<<"FreeBSD-", _/binary>>) -> true; +starts_with_known_os(<<"DragonFly-", _/binary>>) -> true; +starts_with_known_os(_) -> false. + test_port_count("BEAM") -> N = erlang:system_info(port_count), true = is_integer(N),