[docs] Add warning for `os.hostarch`
What does this PR do?
Currently, there are issues with both the os.hostarch function and its documentation. The problem with the function is that it doesn't actually retrieve the runtime architecture, and the issue with the documentation is that it fails to point this out.
How does this PR change Premake's behavior?
Nothing.
Anything else we should know?
I think it is better to fix os.hostarch to reflect the actual arch.
Did you check all the boxes?
- [x] Focus on a single fix or feature; remove any unrelated formatting or code changes
- [x] Add unit tests showing fix or feature works; all tests pass
- [x] Mention any related issues (put
closes #XXXXin comment to auto-close issue when PR is merged) - [x] Follow our coding conventions
- [x] Minimize the number of commits
- [x] Align documentation to your changes
You can now support Premake on our OpenCollective. Your contributions help us spend more time responding to requests like these!
Possible fix, untested:
/**
* \file os_hostarch.c
* \brief Get the architecture for the current host OS we're executing on.
* \author Copyright (c) 2014-2024 Jess Perkins and the Premake project
*/
#include "premake.h"
#if defined(_WIN32)
#include <windows.h>
#elif defined(__unix__) || defined(__APPLE__)
#include <sys/utsname.h>
#include <string.h>
#endif
/**
* Detect the actual CPU architecture at runtime.
* Falls back to compile-time PLATFORM_ARCHITECTURE on failure.
*/
static const char *detect_runtime_arch(void)
{
#if defined(_WIN32)
// Try the newer IsWow64Process2 (Windows 10+)
HMODULE hKernel = GetModuleHandleA("kernel32");
if (hKernel) {
typedef BOOL (WINAPI *LPFN_IsWow64Process2)(HANDLE, USHORT*, USHORT*);
LPFN_IsWow64Process2 fnWow2 =
(LPFN_IsWow64Process2)GetProcAddress(hKernel, "IsWow64Process2");
if (fnWow2) {
USHORT procMachine = 0, nativeMachine = 0;
if (fnWow2(GetCurrentProcess(), &procMachine, &nativeMachine)) {
switch (nativeMachine) {
case IMAGE_FILE_MACHINE_AMD64: return "x86_64";
case IMAGE_FILE_MACHINE_I386: return "x86";
case IMAGE_FILE_MACHINE_ARM64: return "ARM64";
case IMAGE_FILE_MACHINE_ARM: return "ARM";
default: return "unknown";
}
}
}
}
// Fallback to GetNativeSystemInfo
{
SYSTEM_INFO si;
typedef void (WINAPI *LPFN_GetNativeSystemInfo)(LPSYSTEM_INFO);
LPFN_GetNativeSystemInfo fnNSI =
(LPFN_GetNativeSystemInfo)GetProcAddress(hKernel, "GetNativeSystemInfo");
if (fnNSI)
fnNSI(&si);
else
GetSystemInfo(&si);
switch (si.wProcessorArchitecture) {
case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64";
case PROCESSOR_ARCHITECTURE_INTEL: return "x86";
case PROCESSOR_ARCHITECTURE_ARM64: return "ARM64";
case PROCESSOR_ARCHITECTURE_ARM: return "ARM";
default: return "unknown";
}
}
#elif defined(__unix__) || defined(__APPLE__)
struct utsname u;
if (uname(&u) == 0 && u.machine[0] != '\0') {
const char *m = u.machine;
// Normalize uname values to our PLATFORM_ARCHITECTURE strings
if (strcmp(m, "x86_64") == 0 || strcmp(m, "amd64") == 0)
return "x86_64";
else if (strcmp(m, "i386") == 0 || strcmp(m, "i486") == 0 || strcmp(m, "i586") == 0 || strcmp(m, "i686") == 0)
return "x86";
else if (strcmp(m, "aarch64") == 0 || strcmp(m, "arm64") == 0)
return "ARM64";
else if (strncmp(m, "arm", 3) == 0)
return "ARM";
else if (strcmp(m, "riscv64") == 0)
return "RISCV64";
else if (strcmp(m, "loongarch64") == 0)
return "loongarch64";
else if (strcmp(m, "e2k") == 0)
return "e2k";
// Unknown or less common: return raw uname
return m;
} else {
/* Fallback to compile-time macro */
return PLATFORM_ARCHITECTURE;
}
#else
/* Other platforms: fallback */
return PLATFORM_ARCHITECTURE;
#endif
}
int os_hostarch(lua_State* L)
{
const char *arch = detect_runtime_arch();
lua_pushstring(L, arch);
return 1;
}
Possible fix, untested:
/** * \file os_hostarch.c * \brief Get the architecture for the current host OS we're executing on. * \author Copyright (c) 2014-2024 Jess Perkins and the Premake project */ #include "premake.h" #if defined(_WIN32) #include <windows.h> #elif defined(__unix__) || defined(__APPLE__) #include <sys/utsname.h> #include <string.h> #endif /** * Detect the actual CPU architecture at runtime. * Falls back to compile-time PLATFORM_ARCHITECTURE on failure. */ static const char *detect_runtime_arch(void) { #if defined(_WIN32) // Try the newer IsWow64Process2 (Windows 10+) HMODULE hKernel = GetModuleHandleA("kernel32"); if (hKernel) { typedef BOOL (WINAPI *LPFN_IsWow64Process2)(HANDLE, USHORT*, USHORT*); LPFN_IsWow64Process2 fnWow2 = (LPFN_IsWow64Process2)GetProcAddress(hKernel, "IsWow64Process2"); if (fnWow2) { USHORT procMachine = 0, nativeMachine = 0; if (fnWow2(GetCurrentProcess(), &procMachine, &nativeMachine)) { switch (nativeMachine) { case IMAGE_FILE_MACHINE_AMD64: return "x86_64"; case IMAGE_FILE_MACHINE_I386: return "x86"; case IMAGE_FILE_MACHINE_ARM64: return "ARM64"; case IMAGE_FILE_MACHINE_ARM: return "ARM"; default: return "unknown"; } } } } // Fallback to GetNativeSystemInfo { SYSTEM_INFO si; typedef void (WINAPI *LPFN_GetNativeSystemInfo)(LPSYSTEM_INFO); LPFN_GetNativeSystemInfo fnNSI = (LPFN_GetNativeSystemInfo)GetProcAddress(hKernel, "GetNativeSystemInfo"); if (fnNSI) fnNSI(&si); else GetSystemInfo(&si); switch (si.wProcessorArchitecture) { case PROCESSOR_ARCHITECTURE_AMD64: return "x86_64"; case PROCESSOR_ARCHITECTURE_INTEL: return "x86"; case PROCESSOR_ARCHITECTURE_ARM64: return "ARM64"; case PROCESSOR_ARCHITECTURE_ARM: return "ARM"; default: return "unknown"; } } #elif defined(__unix__) || defined(__APPLE__) struct utsname u; if (uname(&u) == 0 && u.machine[0] != '\0') { const char *m = u.machine; // Normalize uname values to our PLATFORM_ARCHITECTURE strings if (strcmp(m, "x86_64") == 0 || strcmp(m, "amd64") == 0) return "x86_64"; else if (strcmp(m, "i386") == 0 || strcmp(m, "i486") == 0 || strcmp(m, "i586") == 0 || strcmp(m, "i686") == 0) return "x86"; else if (strcmp(m, "aarch64") == 0 || strcmp(m, "arm64") == 0) return "ARM64"; else if (strncmp(m, "arm", 3) == 0) return "ARM"; else if (strcmp(m, "riscv64") == 0) return "RISCV64"; else if (strcmp(m, "loongarch64") == 0) return "loongarch64"; else if (strcmp(m, "e2k") == 0) return "e2k"; // Unknown or less common: return raw uname return m; } else { /* Fallback to compile-time macro */ return PLATFORM_ARCHITECTURE; } #else /* Other platforms: fallback */ return PLATFORM_ARCHITECTURE; #endif } int os_hostarch(lua_State* L) { const char *arch = detect_runtime_arch(); lua_pushstring(L, arch); return 1; }
Unfortunately, this would break any existing code relying on the existing behavior (for some reason). We need to first deprecate the API before changing its functionality entirely.
Unfortunately, this would break any existing code relying on the existing behavior (for some reason). We need to first deprecate the API before changing its functionality entirely.
This seems such a niche edge-case that I think it's reasonable to just fix it. The fact no one else has complained about it in years suggests maybe no one else has found this before?
And even then, it's not that common to filter builds by architecture, and if one did, such filtering with the current behaviour would be wrong anyway, with no alternative to properly filter it as you could not differentiate between the right architecture.
Going through a deprecating cycle for this seems overkill. What would you replace it with anyway? Just add an os.hostarch2 and break everything else? Seems more reasonable to just fix it and document the behaviour in the release notes, but just my 2 cents.
Doing things based on premake host architecture also feels like an anti-pattern. I agree, this is definitely an edge case. I don't know what we would name and deprecate with. The correct solution may be to just fix it directly, since we are still just in beta and can have the breaking change.
I'm afraid filtering by architecture isn't particularly uncommon. Macs, for example, come with either Apple Silicon or Intel CPUs, corresponding to ARM64 and x86_64 architectures respectively. Certain x86-specific optimizations, such as SSE, may not be includable in builds targeting the ARM64 architecture, and vice versa.
I understand that in such cases it might be more appropriate to use os.targetarch, but that function doesn't work either—though the documentation clearly states this.
https://github.com/mercury233/premake-macos-arch-test/actions/runs/15089710004/job/42416629418#step:5:1
Moreover, the architecture directive itself doesn't function fully as expected; see issue #2136.
You should likely be filtering using filter "architecture:arm", for example. I'm saying it's probably an antipattern to filter based on the architecture of the computer running premake. It's perfectly reasonable (and expected) to filter based on the target system's architecture (via the filter tag)
You should likely be filtering using
filter "architecture:arm", for example. I'm saying it's probably an antipattern to filter based on the architecture of the computer running premake. It's perfectly reasonable (and expected) to filter based on the target system's architecture (via the filter tag)
https://github.com/mercury233/premake-macos-arch-test/actions/runs/15089901144/job/42417030453#step:7:8
The filter only takes effect when architecture has been set. As for how to determine the architecture, if we want the compiled program to match the architecture of the machine doing the compiling (This is the current default behavior of Premake on macOS.), we need to use os.hostarch.
Okay, yeah, let's fix the actual issue then rather than updating docs (as @tritao suggested)
Can you try the suggested code @mercury233?
Just wanted to poke on this. An update to the docs is better than nothing, but if we can fix the root of the problem, that would be best.
Can you try the suggested code @mercury233?
I finally had the chance to test your patch. I set up an ARM-based TV box and installed the Armbian Linux system on it.
https://github.com/mercury233/premake-arch-test
This repository contains the program I compiled using your patch. Here are the test results:
https://github.com/mercury233/premake-arch-test/actions/runs/15649386571/job/44091879546
It successfully detected the current architecture on both Linux and Windows, but not on macOS. The original beta7 version, however, is a universal build, so it can correctly detect the architecture on macOS.