diff --git a/compiler/src/dmd/frontend.h b/compiler/src/dmd/frontend.h index a5b710cfd503..1671043977ac 100644 --- a/compiler/src/dmd/frontend.h +++ b/compiler/src/dmd/frontend.h @@ -7331,6 +7331,7 @@ struct Target final OS os; uint8_t osMajor; + uint32_t osVersionLong; uint8_t ptrsize; uint8_t realsize; uint8_t realpad; @@ -7396,6 +7397,8 @@ struct Target final floatAbi = 2, objectFormat = 3, CET = 4, + OSXVersion = 5, + FreeBSDVersion = 6, }; public: @@ -7405,6 +7408,7 @@ struct Target final bool supportsLinkerDirective() const; Target() : osMajor(), + osVersionLong(), ptrsize(), realsize(), realpad(), @@ -7430,9 +7434,10 @@ struct Target final params() { } - Target(OS os, uint8_t osMajor = 0u, uint8_t ptrsize = 0u, uint8_t realsize = 0u, uint8_t realpad = 0u, uint8_t realalignsize = 0u, uint8_t classinfosize = 0u, uint64_t maxStaticDataSize = 0LLU, TargetC c = TargetC(), TargetCPP cpp = TargetCPP(), TargetObjC objc = TargetObjC(), _d_dynamicArray< const char > architectureName = {}, CPU cpu = (CPU)0u, bool isAArch64 = false, bool isX86_64 = false, bool isX86 = false, bool isLP64 = false, _d_dynamicArray< const char > obj_ext = {}, _d_dynamicArray< const char > lib_ext = {}, _d_dynamicArray< const char > dll_ext = {}, bool run_noext = false, FPTypeProperties FloatProperties = FPTypeProperties(), FPTypeProperties DoubleProperties = FPTypeProperties(), FPTypeProperties<_d_real > RealProperties = FPTypeProperties<_d_real >(), Type* tvalist = nullptr, const Param* params = nullptr) : + Target(OS os, uint8_t osMajor = 0u, uint32_t osVersionLong = 0u, uint8_t ptrsize = 0u, uint8_t realsize = 0u, uint8_t realpad = 0u, uint8_t realalignsize = 0u, uint8_t classinfosize = 0u, uint64_t maxStaticDataSize = 0LLU, TargetC c = TargetC(), TargetCPP cpp = TargetCPP(), TargetObjC objc = TargetObjC(), _d_dynamicArray< const char > architectureName = {}, CPU cpu = (CPU)0u, bool isAArch64 = false, bool isX86_64 = false, bool isX86 = false, bool isLP64 = false, _d_dynamicArray< const char > obj_ext = {}, _d_dynamicArray< const char > lib_ext = {}, _d_dynamicArray< const char > dll_ext = {}, bool run_noext = false, FPTypeProperties FloatProperties = FPTypeProperties(), FPTypeProperties DoubleProperties = FPTypeProperties(), FPTypeProperties<_d_real > RealProperties = FPTypeProperties<_d_real >(), Type* tvalist = nullptr, const Param* params = nullptr) : os(os), osMajor(osMajor), + osVersionLong(osVersionLong), ptrsize(ptrsize), realsize(realsize), realpad(realpad), diff --git a/compiler/src/dmd/target.d b/compiler/src/dmd/target.d index a6b497d72b03..56bf9cba5575 100644 --- a/compiler/src/dmd/target.d +++ b/compiler/src/dmd/target.d @@ -26,6 +26,7 @@ module dmd.target; import core.stdc.stdio; +import core.stdc.stdlib; import dmd.astenums : CHECKENABLE; import dmd.globals : Param; @@ -350,6 +351,7 @@ extern (C++) struct Target OS os; ubyte osMajor; + uint osVersionLong; /// major, minor, patch version for getTargetInfo. // D ABI ubyte ptrsize; /// size of a pointer in bytes @@ -1275,14 +1277,17 @@ extern (C++) struct Target } } - // this guarantees `getTargetInfo` and `allTargetInfos` remain in sync + // NOTE: If `allTargetInfos` gets implemented, the platform-specific keys + // should be moved to a separate enum. private enum TargetInfoKeys { cppRuntimeLibrary, cppStd, floatAbi, objectFormat, - CET + CET, + OSXVersion, + FreeBSDVersion, } /** @@ -1303,6 +1308,11 @@ extern (C++) struct Target { return new StringExp(loc, sval); } + IntegerExp osVersion(uint v) + { + osVersionLong = v; + return new IntegerExp(v); + } switch (name.toDString) with (TargetInfoKeys) { @@ -1323,6 +1333,91 @@ extern (C++) struct Target case CET.stringof: return new IntegerExp(driverParams.ibt); + case OSXVersion.stringof: + if (os == Target.OS.OSX) + { + // Format of the version: XX_YY_ZZ + if (osVersionLong) + return new IntegerExp(osVersionLong); + + static uint macOSVersion(uint darwin) + { + // Darwin25 maps to macOS 26 + if (darwin >= 25) + return (darwin + 1) * 1_00_00; + // Darwin20 to 24 maps to macOS 11 to 15 + else if (darwin >= 20) + return (darwin - 9) * 1_00_00; + // Darwin4 to 19 map to macOS 10.0 to 10.15 + else if (darwin >= 4) + return 10_00_00 + ((darwin - 4) * 1_00); + return 0; + } + // Infer from the requested deployment target. + if (const env = getenv("MACOSX_DEPLOYMENT_TARGET")) + { + uint major, minor, tiny; + if (sscanf (env, "%u.%u.%u", &major, &minor, &tiny) >= 0) + return osVersion((major * 1_00_00) + (minor * 1_00) + tiny); + } + // Infer from -target= os version (i.e: darwin20) + if (auto macVersion = macOSVersion(osMajor)) + return osVersion(macVersion); + // Infer from the running system. + version (OSX) + { + char[32] buf = 0; + size_t length = buf.length; + uint major, minor, tiny; + if (sysctlbyname("kern.osproductversion", buf.ptr, &length, null, 0) == 0) + { + // Returns the macOS version string, e.g: 12.6.0 + if (sscanf(buf.ptr, "%u.%u.%u", &major, &minor, &tiny) >= 0) + return osVersion((major * 1_00_00) + (minor * 1_00) + tiny); + } + if (sysctlbyname("kern.osrelease", buf.ptr, &length, null, 0) == 0) + { + // Returns the darwin version string, map to macOS. + if (sscanf(buf.ptr, "%u.%u.%u", &major, &minor, &tiny) >= 0) + { + if (auto macVersion = macOSVersion(major)) + return osVersion(macVersion); + } + } + } + // Fallback on guessing appropriate minimum version. + if (isAArch64) + return osVersion(11_00_00); + else if (isX86_64) + return osVersion(10_06_00); + else + return osVersion(10_05_00); + } + goto default; + + case FreeBSDVersion.stringof: + if (os == Target.OS.FreeBSD) + { + // Format of the version: XX_YY_ZZZ + if (osVersionLong) + return new IntegerExp(osVersionLong); + + // Infer from -target= os version (i.e: freebsd13) + if (osMajor) + return osVersion(osMajor * 1_00_000); + // Infer from the running system. + version (FreeBSD) + { + int osreldate; + size_t length = int.sizeof; + if (sysctlbyname("kern.osreldate", &osreldate, &length, null, 0) == 0) + return osVersion(osreldate); + } + // Fallback to minimum supported version. + return osVersion(10_00_000); + } + goto default; + default: return null; } @@ -1718,3 +1813,19 @@ struct TargetObjC //////////////////////////////////////////////////////////////////////////////// extern (C++) __gshared Target target; + +private +{ + // Define our own version of C prototypes, as older compilers didn't have + // the core.sys.*.sysctl module. + version (OSX) + { + extern (C) int sysctlbyname(const char* name, void* oldp, size_t* oldlenp, + const void* newp, size_t newlen) nothrow @nogc; + } + version (FreeBSD) + { + extern (C) int sysctlbyname(const char* name, void* oldp, size_t* oldlenp, + const void* newp, size_t newlen) nothrow @nogc; + } +} diff --git a/compiler/src/dmd/target.h b/compiler/src/dmd/target.h index f7a267ab08ae..be355cae5a38 100644 --- a/compiler/src/dmd/target.h +++ b/compiler/src/dmd/target.h @@ -137,6 +137,7 @@ struct Target OS os; uint8_t osMajor; + uint32_t osVersionLong; // D ABI uint8_t ptrsize; uint8_t realsize; // size a real consumes in memory diff --git a/druntime/src/core/internal/qsort.d b/druntime/src/core/internal/qsort.d index e9982262aef5..c8d9ac5b73ae 100644 --- a/druntime/src/core/internal/qsort.d +++ b/druntime/src/core/internal/qsort.d @@ -56,9 +56,7 @@ static if (Glibc_Qsort_R) } else version (FreeBSD) { - import core.sys.freebsd.config : __FreeBSD_version; - - static if (__FreeBSD_version >= 1400000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1400000) { // FreeBSD changed qsort_r function signature to POSIX in FreeBSD 14.0 alias Cmp = extern (C) int function(scope const void*, scope const void*, scope void*); diff --git a/druntime/src/core/sys/freebsd/config.d b/druntime/src/core/sys/freebsd/config.d index 144ef0553727..899f7e9603ec 100644 --- a/druntime/src/core/sys/freebsd/config.d +++ b/druntime/src/core/sys/freebsd/config.d @@ -14,17 +14,7 @@ public import core.sys.posix.config; // NOTE: When adding newer versions of FreeBSD, verify all current versioned // bindings are still compatible with the release. - version (CoreDdoc) enum __FreeBSD_version = 1600011; // keep at latest -else version (FreeBSD_16) enum __FreeBSD_version = 1600011; -else version (FreeBSD_15) enum __FreeBSD_version = 1500063; -else version (FreeBSD_14) enum __FreeBSD_version = 1400097; -else version (FreeBSD_13) enum __FreeBSD_version = 1301000; -else version (FreeBSD_12) enum __FreeBSD_version = 1203000; -else version (FreeBSD_11) enum __FreeBSD_version = 1104000; -else version (FreeBSD_10) enum __FreeBSD_version = 1004000; -else version (FreeBSD_9) enum __FreeBSD_version = 903000; -else version (FreeBSD_8) enum __FreeBSD_version = 804000; -else static assert(false, "Unsupported version of FreeBSD"); +enum __FreeBSD_version = __traits(getTargetInfo, "FreeBSDVersion"); // First version of FreeBSD to support 64-bit stat buffer. -enum INO64_FIRST = 1200031; +enum INO64_FIRST = 1200000; diff --git a/druntime/src/core/sys/freebsd/sys/event.d b/druntime/src/core/sys/freebsd/sys/event.d index 7f5786219b7f..1397177c64c0 100644 --- a/druntime/src/core/sys/freebsd/sys/event.d +++ b/druntime/src/core/sys/freebsd/sys/event.d @@ -39,7 +39,7 @@ enum EVFILT_SYSCOUNT = 11, } -static if (__FreeBSD_version >= 1200000) +static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) { struct kevent_t { @@ -170,7 +170,7 @@ version (GNU) } else { - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) pragma(mangle, "kevent@@FBSD_1.5") int kevent(int kq, const kevent_t *changelist, int nchanges, kevent_t *eventlist, int nevents, diff --git a/druntime/src/core/sys/freebsd/sys/mount.d b/druntime/src/core/sys/freebsd/sys/mount.d index 1b0f0423001f..9214147e6a60 100644 --- a/druntime/src/core/sys/freebsd/sys/mount.d +++ b/druntime/src/core/sys/freebsd/sys/mount.d @@ -34,7 +34,7 @@ struct fid enum MFSNAMELEN = 16; -static if (__FreeBSD_version >= 1200000) +static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) { enum MNAMELEN = 1024; enum STATFS_VERSION = 0x20140518; @@ -316,7 +316,7 @@ version (GNU) } else { - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) { pragma(mangle, "fhstat@FBSD_1.5") int fhstat(const fhandle_t*, stat_t*); pragma(mangle, "fhstatfs@FBSD_1.5") int fhstatfs(const fhandle_t*, statfs_t*); diff --git a/druntime/src/core/sys/posix/dirent.d b/druntime/src/core/sys/posix/dirent.d index c77d6da22b20..e6803ab5263f 100644 --- a/druntime/src/core/sys/posix/dirent.d +++ b/druntime/src/core/sys/posix/dirent.d @@ -82,9 +82,7 @@ else version (Darwin) } else version (FreeBSD) { - import core.sys.freebsd.config; - - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) { struct dirent { @@ -240,8 +238,6 @@ else version (Darwin) } else version (FreeBSD) { - import core.sys.freebsd.config; - // https://github.com/freebsd/freebsd/blob/master/sys/sys/dirent.h enum { @@ -264,7 +260,7 @@ else version (FreeBSD) } else { - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) pragma(mangle, "readdir@FBSD_1.5") dirent* readdir(DIR*); else pragma(mangle, "readdir@FBSD_1.0") dirent* readdir(DIR*); @@ -536,7 +532,7 @@ else version (FreeBSD) } else { - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) pragma(mangle, "readdir_r@FBSD_1.5") int readdir_r(DIR*, dirent*, dirent**); else pragma(mangle, "readdir_r@FBSD_1.0") int readdir_r(DIR*, dirent*, dirent**); diff --git a/druntime/src/core/sys/posix/stdio.d b/druntime/src/core/sys/posix/stdio.d index eceef7f5d446..1a75a5a17a84 100644 --- a/druntime/src/core/sys/posix/stdio.d +++ b/druntime/src/core/sys/posix/stdio.d @@ -364,14 +364,12 @@ else version (Darwin) } else version (FreeBSD) { - import core.sys.freebsd.config; - enum L_ctermid = 1024; int fseeko(FILE*, off_t, int); off_t ftello(FILE*); - static if (__FreeBSD_version >= 800000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 800000) { ssize_t getdelim(char**, size_t*, int, FILE*); ssize_t getline(char**, size_t*, FILE*); diff --git a/druntime/src/core/sys/posix/sys/stat.d b/druntime/src/core/sys/posix/sys/stat.d index a14fc149d707..9f583f671b87 100644 --- a/druntime/src/core/sys/posix/sys/stat.d +++ b/druntime/src/core/sys/posix/sys/stat.d @@ -1227,7 +1227,7 @@ else version (FreeBSD) import core.sys.freebsd.config; // https://github.com/freebsd/freebsd/blob/master/sys/sys/stat.h - static if (__FreeBSD_version >= INO64_FIRST) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= INO64_FIRST) { struct stat_t { @@ -2045,7 +2045,7 @@ else version (FreeBSD) } else { - static if (__FreeBSD_version >= INO64_FIRST) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= INO64_FIRST) { pragma(mangle, "fstat@FBSD_1.5") int fstat(int, stat_t*); pragma(mangle, "lstat@FBSD_1.5") int lstat(const scope char*, stat_t*); @@ -2058,14 +2058,14 @@ else version (FreeBSD) pragma(mangle, "stat@FBSD_1.0") int stat(const scope char*, stat_t*); } } - static if (__FreeBSD_version >= 800000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 800000) { int fchmodat(int, const scope char*, mode_t, int); int fstatat(int, const scope char*, stat_t*, int); int mkdirat(int, const scope char*, mode_t); int mkfifoat(int, const scope char*, mode_t); } - static if (__FreeBSD_version >= 1003000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1000000) { int futimens(int, ref const(timespec)[2]); int utimensat(int, const scope char*, ref const(timespec)[2], int); @@ -2345,7 +2345,7 @@ else version (FreeBSD) } else { - static if (__FreeBSD_version >= INO64_FIRST) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= INO64_FIRST) pragma(mangle, "mknod@FBSD_1.5") int mknod(const scope char*, mode_t, dev_t); else pragma(mangle, "mknod@FBSD_1.0") int mknod(const scope char*, mode_t, dev_t); diff --git a/druntime/src/core/sys/posix/sys/types.d b/druntime/src/core/sys/posix/sys/types.d index cad60b4da4d2..5a6776c2a010 100644 --- a/druntime/src/core/sys/posix/sys/types.d +++ b/druntime/src/core/sys/posix/sys/types.d @@ -209,13 +209,11 @@ else version (Darwin) } else version (FreeBSD) { - import core.sys.freebsd.config; - // https://github.com/freebsd/freebsd/blob/master/sys/sys/_types.h alias blkcnt_t = long; alias blksize_t = uint; - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) { alias dev_t = ulong; alias ino_t = ulong; diff --git a/druntime/src/core/sys/posix/unistd.d b/druntime/src/core/sys/posix/unistd.d index ea6d866999ca..a7e78a4370ee 100644 --- a/druntime/src/core/sys/posix/unistd.d +++ b/druntime/src/core/sys/posix/unistd.d @@ -159,8 +159,7 @@ else version (FreeBSD) int setresgid(gid_t, gid_t, gid_t) @trusted; int setresuid(uid_t, uid_t, uid_t) @trusted; - import core.sys.freebsd.config : __FreeBSD_version; - static if (__FreeBSD_version >= 800000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 800000) { int faccessat(int, const scope char*, int, int); int fchownat(int, const scope char*, uid_t, gid_t, int); @@ -170,16 +169,16 @@ else version (FreeBSD) int symlinkat(const scope char*, int, const scope char*); int unlinkat(int, const scope char*, int); } - static if (__FreeBSD_version >= 1000000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1000000) { int dup3(int, int, int) @trusted; int pipe2(ref int[2], int) @trusted; } - static if (__FreeBSD_version >= 1200000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1200000) { int getentropy(void*, size_t); } - static if (__FreeBSD_version >= 1301000) + static if (__traits(getTargetInfo, "FreeBSDVersion") >= 1300000) { pid_t _Fork() @trusted; } diff --git a/spec/traits.dd b/spec/traits.dd index 9fe70a011900..d95571cea493 100644 --- a/spec/traits.dd +++ b/spec/traits.dd @@ -1776,6 +1776,15 @@ version (CppRuntime_Microsoft) $(LI $(D "objectFormat") - Target object format) ) + $(P + These keys are platform-specific, and availability depends on the target the compiler is building for: + ) + + $(UL + $(LI $(D "OSXVersion") - The target MacOS X version, similar to the `__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__` macros in a C compiler) + $(LI $(D "FreeBSDVersion") - The target FreeBSD version, similar to the `__FreeBSD_version` macro in a C compiler) + ) + $(H3 $(GNAME getUnitTests)) $(P