ponyc: 0.64.0 -> 0.67.0

Upstream ponyc has made two significant changes since our last update:

1. Removal of the ability to use an external linker.
2. Moving from make to cmake for build.

Previously we had relied on being able to call gcc as an external wrapper,
which would take care of all of the /nix/store linking paths, selecting
the correct dynamic linker etc etc…

Now that the wrapper isn't available anymore, we have modified the embedded
linker to read the flags from nix's environment.

Assisted-By: Opus 4.8
This commit is contained in:
Redvers Davies
2026-07-21 15:46:16 +00:00
parent adaba46e08
commit b360de16fc
8 changed files with 679 additions and 158 deletions
+34
View File
@@ -0,0 +1,34 @@
diff --git a/CMakePresets.json b/CMakePresets.json
index d12d71eb5..d337e7339 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -9,11 +9,7 @@
{
"name": "base",
"hidden": true,
- "binaryDir": "${sourceDir}/build/build_${presetName}",
- "cacheVariables": {
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
+ "binaryDir": "${sourceDir}/build/build_${presetName}"
},
{
"name": "native",
diff --git a/lib/CMakePresets.json b/lib/CMakePresets.json
index 12d797b87..92196d931 100644
--- a/lib/CMakePresets.json
+++ b/lib/CMakePresets.json
@@ -19,11 +19,7 @@
{
"name": "libs",
"displayName": "Vendored LLVM + support libraries (clang)",
- "inherits": "libs-base",
- "cacheVariables": {
- "CMAKE_C_COMPILER": "clang",
- "CMAKE_CXX_COMPILER": "clang++"
- }
+ "inherits": "libs-base"
},
{
"name": "libs-windows-x86-64",
@@ -1,41 +1,80 @@
From c1283b82daff94ebac21fc20cb8df0b05aa080f8 Mon Sep 17 00:00:00 2001
From: Morgan Jones <me@numin.it>
Date: Fri, 22 May 2026 23:06:52 -0700
Subject: [PATCH] net: disable networking tests
---
packages/net/_test.pony | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/packages/net/_test.pony b/packages/net/_test.pony
index 1d7b2d6f..69cbeea6 100644
index 081ce67a0..99942c05e 100644
--- a/packages/net/_test.pony
+++ b/packages/net/_test.pony
@@ -20,24 +20,6 @@ actor \nodoc\ Main is TestList
@@ -26,14 +26,14 @@ actor \nodoc\ Main is TestList
fun tag tests(test: PonyTest) =>
// Tests below function across all systems and are listed alphabetically
- test(_TestDNSBroadcastIP4)
- test(_TestDNSBroadcastIP6)
- test(_TestDNSUnresolvableEmpty)
- test(_TestMulticastIP4)
- test(_TestMulticastIP6)
- test(_TestNetAddressIP6Scope)
- test(_TestNetAddressNameRoundTripIP4)
- test(_TestNetAddressNameRoundTripIP6)
+// test(_TestDNSBroadcastIP4)
+// test(_TestDNSBroadcastIP6)
+// test(_TestDNSUnresolvableEmpty)
+// test(_TestMulticastIP4)
+// test(_TestMulticastIP6)
+// test(_TestNetAddressIP6Scope)
+// test(_TestNetAddressNameRoundTripIP4)
+// test(_TestNetAddressNameRoundTripIP6)
test(_TestOsIpString)
test(_TestSocketResultDecoder)
test(_TestTCPConnectionFailed)
- test(_TestTCPExpect)
- test(_TestTCPExpectOverBufferSize)
- test(_TestTCPExpectSetToZero)
- test(_TestTCPMute)
- test(_TestTCPProxy)
- test(_TestTCPUnmute)
- test(_TestTCPWritev)
-
- // Tests below exclude windows and are listed alphabetically
- ifdef not windows then
- test(_TestTCPConnectionToClosedServerFailed)
- test(_TestTCPThrottle)
@@ -48,36 +48,36 @@ actor \nodoc\ Main is TestList
test(_TestTCPThrottle)
test(_TestTCPUnmute)
test(_TestTCPWritev)
- test(_TestUDPEmptyDatagramDelivered)
+// test(_TestUDPEmptyDatagramDelivered)
test(_TestUDPListenFailure)
- test(_TestUDPOversizedDatagramTruncated)
- test(_TestUDPUndersizedDatagramDelivered)
- test(_TestUDPZeroSizeReadBufferDelivers)
+// test(_TestUDPOversizedDatagramTruncated)
+// test(_TestUDPUndersizedDatagramDelivered)
+// test(_TestUDPZeroSizeReadBufferDelivers)
test(_TestUnicastIP6Loopback)
// The deterministic send-failure trigger (send to broadcast without
// SO_BROADCAST -> EACCES/WSAEACCES) is verified on linux and windows.
- ifdef linux or windows then
- test(_TestUDPCloseOnSendFailure)
- end
-
- // Tests below exclude osx and are listed alphabetically
+// ifdef linux or windows then
+// test(_TestUDPCloseOnSendFailure)
+// end
// Tests below run only on linux and are listed alphabetically
- ifdef linux then
- test(_TestBroadcastReceive)
- end
+// ifdef linux then
+// test(_TestBroadcastReceive)
+// end
// Tests below exclude osx and are listed alphabetically
- ifdef not osx then
- test(_TestBroadcast)
- end
+// ifdef not osx then
+// test(_TestBroadcast)
+// end
// Tests below exclude osx and bsd and are listed alphabetically.
// They read IPv4 multicast options back with getsockopt_u32, which
// expects a 4-byte value; osx and bsd return these options as a 1-byte
// u_char, so the read-back fails there regardless of correctness.
- ifdef (not osx) and (not bsd) then
- test(_TestMulticastSockopt)
- end
+// ifdef (not osx) and (not bsd) then
+// test(_TestMulticastSockopt)
+// end
class \nodoc\ _TestPing is UDPNotify
let _h: TestHelper
--
2.53.0
@@ -1,26 +0,0 @@
From 77d703b11d298f6be88b04f7e8ca85de139e82be Mon Sep 17 00:00:00 2001
From: Morgan Jones <me@numin.it>
Date: Mon, 5 May 2025 20:34:02 -0700
Subject: [PATCH] process: disable KillLongRunningChild test
---
packages/process/_test.pony | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/packages/process/_test.pony b/packages/process/_test.pony
index fe9fdb04..756588f9 100644
--- a/packages/process/_test.pony
+++ b/packages/process/_test.pony
@@ -18,7 +18,8 @@ actor \nodoc\ Main is TestList
test(_TestChdir)
test(_TestExpect)
test(_TestFileExecCapabilityIsRequired)
- test(_TestKillLongRunningChild)
+ // (@booxter/@numinit) Appears to be flaky.
+ // test(_TestKillLongRunningChild)
test(_TestLongRunningChild)
test(_TestNonExecutablePathResultsInExecveError)
test(_TestPrintvOrdering)
--
2.47.0
@@ -1,13 +0,0 @@
diff --git a/src/libponyc/codegen/genexe.cc b/src/libponyc/codegen/genexe.cc
index 3f0348eaa1..76b03030bf 100644
--- a/src/libponyc/codegen/genexe.cc
+++ b/src/libponyc/codegen/genexe.cc
@@ -308,7 +308,7 @@
snprintf(ld_cmd, ld_len,
"%s -execute -arch %.*s "
"-o %s %s %s %s "
- "-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib -lSystem %s -platform_version macos '" STR(PONY_OSX_PLATFORM) "' '0.0.0'",
+ "-L\"${SDKROOT:-${DEVELOPER_DIR:-@apple-sdk@}/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk}/usr/lib\" -lSystem %s -platform_version macos '" STR(PONY_OSX_PLATFORM) "' '0.0.0'",
linker, (int)arch_len, c->opt->triple, file_exe, file_o,
lib_args, ponyrt, sanitizer_arg
);
@@ -0,0 +1,61 @@
diff --git a/src/libponyc/codegen/gencshim.cc b/src/libponyc/codegen/gencshim.cc
index a1722ad01a22..db742c6adaa5 100644
--- a/src/libponyc/codegen/gencshim.cc
+++ b/src/libponyc/codegen/gencshim.cc
@@ -7,6 +7,7 @@
#include "../ast/stringtab.h"
#include "paths.h"
#include "ponyassert.h"
+#include "nix.h"
#ifdef _MSC_VER
# pragma warning(push)
@@ -307,8 +308,11 @@ static const char* find_macos_sdk_include(pass_opt_t* opt)
pclose(f);
}
+ // Fall back to the Nix apple-sdk when xcrun is unavailable, mirroring the
+ // link side in genexe.cc. The unversioned MacOSX.sdk symlink keeps this
+ // independent of the SDK version.
const char* fallback =
- "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include";
+ "@apple-sdk@/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include";
if(dir_exists(fallback))
{
@@ -349,6 +353,25 @@ static bool push_isystem(pass_opt_t* opt, std::vector<const char*>& args,
}
+/*
+ Add the Nix header search directories (see nix.h) as -internal-isystem
+ entries. push_isystem skips any directory that doesn't exist, so a partial
+ toolchain degrades gracefully.
+*/
+static bool add_nix_include_dirs(pass_opt_t* opt,
+ std::vector<const char*>& args, errors_t* errors)
+{
+ std::vector<const char*> dirs;
+ nix_collect_include_dirs(opt->strtab, dirs);
+
+ bool any = false;
+ for(size_t i = 0; i < dirs.size(); i++)
+ any |= push_isystem(opt, args, errors, "-internal-isystem", dirs[i], "");
+
+ return any;
+}
+
+
// System include directories for the target, derived from the same sysroot
// the embedded linker uses — hand-rolled like LLD rather than driven through
// clang's Driver, so the headers the shim compiles against stay consistent
@@ -488,6 +511,9 @@ static bool add_system_include_args(pass_opt_t* opt, const char* sysroot,
any |= push_isystem(opt, args, errors, "-internal-externc-isystem",
sysroot, "/usr/include");
+ if(sysroot[0] == '\0')
+ any |= add_nix_include_dirs(opt, args, errors);
+
// Per-directory skipping is right (multiarch and /usr/local are
// legitimately absent on many systems), but an explicit --sysroot that
// yields nothing at all deserves a direct answer, like the macOS branch
+153 -21
View File
@@ -1,26 +1,158 @@
From ac4b2a65f997f7f779b9c63dbe683ba10a26fc7f Mon Sep 17 00:00:00 2001
From: Morgan Jones <me@numin.it>
Date: Fri, 22 May 2026 23:58:29 -0700
Subject: [PATCH] genexe: take PONY_LINKER into account
---
src/libponyc/codegen/genexe.cc | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/libponyc/codegen/genexe.cc b/src/libponyc/codegen/genexe.cc
index 48f97578..5b9f36f1 100644
index 826d69f0bcba..897afba953bb 100644
--- a/src/libponyc/codegen/genexe.cc
+++ b/src/libponyc/codegen/genexe.cc
@@ -1448,6 +1448,9 @@ static bool link_exe(compile_t* c, ast_t* program,
{
errors_t* errors = c->opt->check.errors;
@@ -22,6 +22,7 @@ LLD_HAS_DRIVER(wasm)
#include "../type/lookup.h"
#include "../../libponyrt/mem/pool.h"
#include "ponyassert.h"
+#include "nix.h"
#include <string.h>
+ if(c->opt->linker == NULL)
+ c->opt->linker = getenv("PONY_LINKER");
#include <llvm/Support/raw_ostream.h>
@@ -38,6 +39,8 @@ LLD_HAS_DRIVER(wasm)
# include <llvm/TargetParser/Triple.h>
#endif
+static bool dir_has_arch_libc_crt(const char* dir, uint16_t target_machine);
+
// Use embedded LLD for Linux, macOS, and Windows targets unless --linker
// escape hatch is specified. Sanitizer builds fall back to the system
// compiler driver for native compilation since sanitizer runtime libraries
--
2.53.0
#if defined(PONY_SANITIZER)
// Generated at configure time (top-level CMakeLists.txt, PONY_SANITIZERS_ENABLED
// block): the sanitizer runtime link fragment captured from the compiler driver.
@@ -965,6 +968,9 @@ static bool link_exe_lld_elf(compile_t* c, ast_t* program,
program_lib_build_args_embedded(program, c->opt);
+ std::vector<const char*> env_libdirs;
+ nix_collect_link_libdirs(c->opt->strtab, env_libdirs);
+
const char* sys_triple = system_triple(c->opt);
bool is_freebsd = target_is_freebsd(c->opt->triple);
bool is_dragonfly = target_is_dragonfly(c->opt->triple);
@@ -996,7 +1002,11 @@ static bool link_exe_lld_elf(compile_t* c, ast_t* program,
return false;
}
- const char* dynlinker = dynamic_linker_path(c);
+ // Prefer a Nix-provided ELF interpreter (baked default or environment);
+ // fall back to the built-in FHS search when none is configured.
+ const char* dynlinker = nix_dynamic_linker(c->opt->strtab);
+ if(dynlinker[0] == '\0')
+ dynlinker = dynamic_linker_path(c);
if(!c->opt->staticbin && dynlinker == NULL)
{
errorf(errors, NULL,
@@ -1009,10 +1019,23 @@ static bool link_exe_lld_elf(compile_t* c, ast_t* program,
const char* libc_crt_dir = find_libc_crt_dir(sysroot, sys_triple,
target_machine, c->opt->strtab);
if(libc_crt_dir == NULL)
+ {
+ for(size_t i = 0; i < env_libdirs.size(); i++)
+ {
+ if(dir_has_arch_libc_crt(env_libdirs[i], target_machine))
+ {
+ libc_crt_dir = env_libdirs[i];
+ break;
+ }
+ }
+ }
+ if(libc_crt_dir == NULL)
{
errorf(errors, NULL,
"could not find a libc crt startup object (crt1.o or crt0.o) "
- "matching target architecture '%s' in sysroot '%s'",
+ "matching target architecture '%s' in sysroot '%s'\n"
+ " On NixOS systems, set PONY_LINK_LIBDIRS to the directory holding "
+ "the arch-matching libc startup object (crt1.o or crt0.o).",
c->opt->triple, sysroot);
return false;
}
@@ -1305,6 +1328,21 @@ static bool link_exe_lld_elf(compile_t* c, ast_t* program,
}
}
+/*
+ Add /nix/store lib directories to the embedded linker args
+*/
+ for(size_t i = 0; i < env_libdirs.size(); i++)
+ {
+ snprintf(buf, sizeof(buf), "-L%s", env_libdirs[i]);
+ args.push_back(stringtab(c->opt->strtab, buf));
+
+ if(!c->opt->staticbin)
+ {
+ args.push_back("-rpath");
+ args.push_back(env_libdirs[i]);
+ }
+ }
+
// Standard system library fallback paths. The paths above cover
// distro-specific locations (libc_crt_dir, gcc_lib_dir, etc.) but miss
// common install locations like /usr/local/lib (where libraries built
@@ -1732,9 +1770,10 @@ static const char* find_macos_sdk_path(strtable_t* strtab)
pclose(f);
}
- // Hardcoded fallback.
+ // Fall back to the Nix apple-sdk when xcrun is unavailable. The unversioned
+ // MacOSX.sdk symlink keeps this independent of the SDK version.
const char* fallback =
- "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib";
+ "@apple-sdk@/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/lib";
struct stat st;
if(stat(fallback, &st) == 0 && S_ISDIR(st.st_mode))
@@ -1947,6 +1986,31 @@ static bool link_exe_lld_macho(compile_t* c, ast_t* program,
}
}
+/*
+ Add /nix/store lib directories to the embedded Mach-O linker args, mirroring
+ the ELF path. On NixOS the C++ standard library, buildInputs and user FFI
+ libraries live under /nix/store, not in the SDK's usr/lib or the Homebrew and
+ /usr/local fallbacks above. nix_collect_link_libdirs() (see nix.h) gathers
+ them from the baked PONY_NIX_LINK_LIBDIRS default, the PONY_LINK_LIBDIRS /
+ NIX_LDFLAGS env vars, and the cc-wrapper's $NIX_CC/nix-support/cc-ldflags
+ (which carries the -L<libc++> path on Darwin). Without this,
+ `use "lib:c++" if osx` (as used by pony-compiler) fails to link with
+ "library not found for -lc++".
+*/
+ std::vector<const char*> env_libdirs;
+ nix_collect_link_libdirs(c->opt->strtab, env_libdirs);
+ for(size_t i = 0; i < env_libdirs.size(); i++)
+ {
+ snprintf(buf, sizeof(buf), "-L%s", env_libdirs[i]);
+ args.push_back(stringtab(c->opt->strtab, buf));
+
+ if(!c->opt->staticbin)
+ {
+ args.push_back("-rpath");
+ args.push_back(env_libdirs[i]);
+ }
+ }
+
// Object file.
args.push_back(file_o);
@@ -2417,3 +2481,21 @@ bool genexe(compile_t* c, ast_t* program)
return true;
}
+
+// Does `dir` hold a libc startup object (crt1.o / crt0.o) whose ELF machine
+// matches the target? Used to pick the crt directory out of the Nix libdirs
+// gathered by nix_collect_link_libdirs().
+static bool dir_has_arch_libc_crt(const char* dir, uint16_t target_machine)
+{
+ static const char* sentinels[] = { "crt1.o", "crt0.o" };
+ char buf[PATH_MAX];
+
+ for(size_t j = 0; j < sizeof(sentinels) / sizeof(sentinels[0]); j++)
+ {
+ snprintf(buf, sizeof(buf), "%s/%s", dir, sentinels[j]);
+ if(file_exists(buf) && elf_matches_target(buf, target_machine))
+ return true;
+ }
+
+ return false;
+}
+291
View File
@@ -0,0 +1,291 @@
diff --git a/src/libponyc/codegen/nix.h b/src/libponyc/codegen/nix.h
new file mode 100644
index 000000000000..b1368f222b91
--- /dev/null
+++ b/src/libponyc/codegen/nix.h
@@ -0,0 +1,47 @@
+#ifndef CODEGEN_NIX_H
+#define CODEGEN_NIX_H
+
+#include "../ast/stringtab.h"
+
+#include <vector>
+
+/*
+ Nix/NixOS toolchain path discovery for the embedded LLD linker (genexe.cc)
+ and the embedded clang C-shim compiler (gencshim.cc).
+
+ On Nix there is no FHS layout: no /usr/lib, no /lib64/ld-linux-*.so, no
+ /usr/include. The libc, libgcc, C++ runtime, buildInputs and user FFI
+ libraries all live under /nix/store, and their locations reach the compiler
+ through baked-in defaults (the -DPONY_NIX_* macros set at configure time) and
+ the cc-wrapper environment (NIX_LDFLAGS, NIX_CFLAGS_COMPILE,
+ $NIX_CC/nix-support/*). These helpers gather those paths so the embedded
+ tools link and compile without a wrapper script.
+
+ On a plain non-Nix build every source is empty, so the collectors append
+ nothing and behaviour is unchanged.
+*/
+
+// Library search directories for the linker, in priority order:
+// PONY_NIX_LINK_LIBDIRS (baked default), PONY_LINK_LIBDIRS, NIX_LDFLAGS,
+// NIX_LDFLAGS_BEFORE, $NIX_CC/nix-support {orig-libc, cc-ldflags}.
+// Directories are interned into `strtab` and appended to `dirs`; existing
+// entries are left in place.
+void nix_collect_link_libdirs(strtable_t* strtab,
+ std::vector<const char*>& dirs);
+
+// Header search directories for the C-shim compiler, in priority order:
+// PONY_NIX_INCLUDE_DIRS (baked default), PONY_INCLUDE_DIRS,
+// NIX_CFLAGS_COMPILE, $NIX_CC/nix-support/libc-cflags.
+// Directories are interned into `strtab` and appended to `dirs`; existing
+// entries are left in place.
+void nix_collect_include_dirs(strtable_t* strtab,
+ std::vector<const char*>& dirs);
+
+// The ELF dynamic linker (interpreter) path, in priority order:
+// PONY_DYNAMIC_LINKER, PONY_NIX_DYNAMIC_LINKER (baked default),
+// $NIX_CC/nix-support/dynamic-linker.
+// Returns "" (never NULL) when none is configured, so the caller can test the
+// first byte and fall back to the built-in FHS search.
+const char* nix_dynamic_linker(strtable_t* strtab);
+
+#endif
diff --git a/src/libponyc/codegen/nix.cc b/src/libponyc/codegen/nix.cc
new file mode 100644
index 000000000000..eddfd7833103
--- /dev/null
+++ b/src/libponyc/codegen/nix.cc
@@ -0,0 +1,220 @@
+#include "nix.h"
+
+#include "../ast/stringtab.h"
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef PATH_MAX
+# define PATH_MAX 4096
+#endif
+
+/*
+ The baked-in Nix defaults. package.nix passes each of these through
+ -DPONY_NIX_*="..." in NIX_CFLAGS_COMPILE at configure time, capturing the
+ cc-wrapper's libc/libgcc/pcre2/openssl lib dirs, the ELF interpreter and the
+ libc/gcc include dirs. They are empty strings on a plain source build, which
+ disables every Nix-specific path below.
+*/
+#ifndef PONY_NIX_LINK_LIBDIRS
+# define PONY_NIX_LINK_LIBDIRS ""
+#endif
+#ifndef PONY_NIX_DYNAMIC_LINKER
+# define PONY_NIX_DYNAMIC_LINKER ""
+#endif
+#ifndef PONY_NIX_INCLUDE_DIRS
+# define PONY_NIX_INCLUDE_DIRS ""
+#endif
+
+/*
+ Split `s` into tokens on any delimiter character in `delims`, interning each
+ token into `strtab` and appending it to `toks`. A token that would overflow
+ the buffer is dropped whole rather than truncated to a wrong path.
+*/
+static void tokenize(const char* s, const char* delims, strtable_t* strtab,
+ std::vector<const char*>& toks)
+{
+ if(s == NULL || s[0] == '\0')
+ return;
+
+ char buf[PATH_MAX];
+ size_t len = 0;
+ bool overflow = false;
+ for(const char* p = s; ; p++)
+ {
+ // The '\0' test is first so strchr is never asked to match the terminator.
+ if(*p == '\0' || strchr(delims, *p) != NULL)
+ {
+ if(len > 0 && !overflow)
+ {
+ buf[len] = '\0';
+ toks.push_back(stringtab(strtab, buf));
+ }
+ len = 0;
+ overflow = false;
+ if(*p == '\0')
+ break;
+ }
+ else if(len < sizeof(buf) - 1)
+ {
+ buf[len++] = *p;
+ }
+ else
+ {
+ overflow = true;
+ }
+ }
+}
+
+/*
+ Interpret a whitespace-separated linker-flag string (NIX_LDFLAGS,
+ $NIX_CC/nix-support/cc-ldflags) for library directories, handling the joined
+ -L<dir>, the separated -L <dir>, and -rpath / -rpath-link <dir> forms.
+
+ This is how /nix/store libraries (and directories injected by pkg-config) are
+ found where other distros would look in /usr/lib and the like.
+*/
+static void append_ldflags_dirs(const char* flags, strtable_t* strtab,
+ std::vector<const char*>& dirs)
+{
+ std::vector<const char*> toks;
+ tokenize(flags, " \t\n\r", strtab, toks);
+
+ for(size_t i = 0; i < toks.size(); i++)
+ {
+ const char* t = toks[i];
+ if(strncmp(t, "-L", 2) == 0)
+ {
+ if(t[2] != '\0')
+ dirs.push_back(stringtab(strtab, t + 2));
+ else if(i + 1 < toks.size())
+ dirs.push_back(toks[++i]);
+ }
+ else if(strcmp(t, "-rpath") == 0 || strcmp(t, "-rpath-link") == 0)
+ {
+ if(i + 1 < toks.size())
+ dirs.push_back(toks[++i]);
+ }
+ }
+}
+
+/*
+ Interpret a whitespace-separated compiler-flag string (NIX_CFLAGS_COMPILE,
+ $NIX_CC/nix-support/libc-cflags) for header search directories, handling the
+ joined -I<dir>, the separated -I <dir>, and -isystem / -idirafter / -iquote
+ <dir> forms. This covers directories injected by pkg-config too.
+*/
+static void append_cflags_include_dirs(const char* flags, strtable_t* strtab,
+ std::vector<const char*>& dirs)
+{
+ std::vector<const char*> toks;
+ tokenize(flags, " \t\n\r", strtab, toks);
+
+ for(size_t i = 0; i < toks.size(); i++)
+ {
+ const char* t = toks[i];
+ if(strcmp(t, "-isystem") == 0 || strcmp(t, "-idirafter") == 0 ||
+ strcmp(t, "-iquote") == 0 || strcmp(t, "-I") == 0)
+ {
+ if(i + 1 < toks.size())
+ dirs.push_back(toks[++i]);
+ }
+ else if(strncmp(t, "-I", 2) == 0 && t[2] != '\0')
+ {
+ dirs.push_back(stringtab(strtab, t + 2));
+ }
+ }
+}
+
+/*
+ Read $NIX_CC/nix-support/<name> into `out`, trimming trailing whitespace.
+ Returns false if the file is missing, empty, or so long it would be
+ truncated -- acting on a partial flag string would be worse than ignoring it.
+*/
+static bool read_nix_support_file(const char* nix_cc, const char* name,
+ char* out, size_t outsz)
+{
+ char path[PATH_MAX];
+ snprintf(path, sizeof(path), "%s/nix-support/%s", nix_cc, name);
+
+ FILE* f = fopen(path, "rb");
+ if(f == NULL)
+ return false;
+
+ size_t n = fread(out, 1, outsz - 1, f);
+ // If the buffer filled and the file still has bytes, the content was
+ // truncated; reject it rather than acting on a partial flag string.
+ bool truncated = (n == outsz - 1) && (fgetc(f) != EOF);
+ fclose(f);
+ if(truncated)
+ return false;
+ out[n] = '\0';
+
+ while(n > 0 && (out[n - 1] == '\n' || out[n - 1] == '\r' ||
+ out[n - 1] == ' ' || out[n - 1] == '\t'))
+ out[--n] = '\0';
+
+ return n > 0;
+}
+
+void nix_collect_link_libdirs(strtable_t* strtab,
+ std::vector<const char*>& dirs)
+{
+ tokenize(PONY_NIX_LINK_LIBDIRS, ":", strtab, dirs);
+ tokenize(getenv("PONY_LINK_LIBDIRS"), ":", strtab, dirs);
+ append_ldflags_dirs(getenv("NIX_LDFLAGS"), strtab, dirs);
+ append_ldflags_dirs(getenv("NIX_LDFLAGS_BEFORE"), strtab, dirs);
+
+ const char* nix_cc = getenv("NIX_CC");
+ if(nix_cc == NULL || nix_cc[0] == '\0')
+ return;
+
+ char buf[PATH_MAX];
+ if(read_nix_support_file(nix_cc, "orig-libc", buf, sizeof(buf)))
+ {
+ char libdir[PATH_MAX];
+ snprintf(libdir, sizeof(libdir), "%s/lib", buf);
+ dirs.push_back(stringtab(strtab, libdir));
+ }
+
+ if(read_nix_support_file(nix_cc, "cc-ldflags", buf, sizeof(buf)))
+ append_ldflags_dirs(buf, strtab, dirs);
+}
+
+void nix_collect_include_dirs(strtable_t* strtab,
+ std::vector<const char*>& dirs)
+{
+ tokenize(PONY_NIX_INCLUDE_DIRS, ":", strtab, dirs);
+ tokenize(getenv("PONY_INCLUDE_DIRS"), ":", strtab, dirs);
+ append_cflags_include_dirs(getenv("NIX_CFLAGS_COMPILE"), strtab, dirs);
+
+ const char* nix_cc = getenv("NIX_CC");
+ if(nix_cc != NULL && nix_cc[0] != '\0')
+ {
+ char cflags[PATH_MAX * 4];
+ if(read_nix_support_file(nix_cc, "libc-cflags", cflags, sizeof(cflags)))
+ append_cflags_include_dirs(cflags, strtab, dirs);
+ }
+}
+
+const char* nix_dynamic_linker(strtable_t* strtab)
+{
+ const char* dl = getenv("PONY_DYNAMIC_LINKER");
+ if(dl != NULL && dl[0] != '\0')
+ return stringtab(strtab, dl);
+
+ if(PONY_NIX_DYNAMIC_LINKER[0] != '\0')
+ return stringtab(strtab, PONY_NIX_DYNAMIC_LINKER);
+
+ const char* nix_cc = getenv("NIX_CC");
+ if(nix_cc != NULL && nix_cc[0] != '\0')
+ {
+ char buf[PATH_MAX];
+ if(read_nix_support_file(nix_cc, "dynamic-linker", buf, sizeof(buf)))
+ return stringtab(strtab, buf);
+ }
+
+ return "";
+}
diff --git a/src/libponyc/CMakeLists.txt b/src/libponyc/CMakeLists.txt
index f4e3e42ef48f..4a477e306ac1 100644
--- a/src/libponyc/CMakeLists.txt
+++ b/src/libponyc/CMakeLists.txt
@@ -27,6 +27,7 @@ add_library(libponyc STATIC
codegen/gendesc.c
codegen/gencshim.cc
codegen/genexe.cc
+ codegen/nix.cc
codegen/genexpr.c
codegen/genfun.c
codegen/genheader.c
+73 -70
View File
@@ -7,14 +7,11 @@
cmake,
coreutils,
libxml2,
lto ? true,
makeWrapper,
openssl,
pcre2,
pony-corral,
python3,
zlib,
# Not really used for anything real, just at build time.
git,
replaceVars,
which,
@@ -25,13 +22,13 @@
stdenv.mkDerivation (finalAttrs: {
pname = "ponyc";
version = "0.64.0";
version = "0.67.0";
src = fetchFromGitHub {
owner = "ponylang";
repo = "ponyc";
tag = finalAttrs.version;
hash = "sha256-CdsfJO+7y7nvlDdCXdWRB4vmP9pB1Jz5CVwJuha+yds=";
hash = "sha256-9X2xaQ5nCV/sTL3WjXNDswIfNhlwgfV4/X70p1zTd2U=";
fetchSubmodules = true;
};
@@ -53,7 +50,6 @@ stdenv.mkDerivation (finalAttrs: {
nativeBuildInputs = [
cmake
makeWrapper
which
python3
git
@@ -64,23 +60,36 @@ stdenv.mkDerivation (finalAttrs: {
buildInputs = [
libxml2
openssl
pcre2
z3
zlib
];
patches = [
./cmake-presets.patch
# Sandbox disallows network access, so disabling problematic networking tests
./disable-networking-tests.patch
./disable-process-tests.patch
# Take PONY_LINKER into account
./genexe-pony-linker.patch
# Adds codegen/nix.cc + nix.h (the Nix toolchain-path helpers shared by the
# embedded linker and C-shim compiler) and wires them into libponyc's
# CMakeLists. Platform-independent, so applied verbatim everywhere.
./nix-codegen.patch
]
++ lib.optionals stdenv.hostPlatform.isDarwin [
(replaceVars ./fix-darwin-build.patch {
inherit apple-sdk;
})
];
++ (
let
# These patch the embedded LLD/clang codegen and reference the macOS SDK
# via @apple-sdk@; substitute it on Darwin, apply verbatim elsewhere (the
# placeholder sits in macOS-only code that other platforms never compile).
sdkPatches = [
./gencshim-pony-cc.patch
./genexe-pony-linker.patch
];
in
if stdenv.hostPlatform.isDarwin then
map (p: replaceVars p { inherit apple-sdk; }) sdkPatches
else
sdkPatches
);
postUnpack = ''
mkdir -p $NIX_BUILD_TOP/deps
@@ -88,8 +97,6 @@ stdenv.mkDerivation (finalAttrs: {
tar -C "$googletest" -cf $NIX_BUILD_TOP/deps/googletest-$googletestRev.tar .
'';
dontConfigure = true;
postPatch = ''
substituteInPlace packages/process/_test.pony \
--replace-fail '"/bin/' '"${coreutils}/bin/' \
@@ -105,8 +112,7 @@ stdenv.mkDerivation (finalAttrs: {
'';
# We do not concern ourselves with darwin as the ponyc compiler
# has logic which overrides this environmental variable in this
# case.
# has logic which overrides this environment variable.
env.arch =
if stdenv.hostPlatform.isx86_64 then
"x86-64"
@@ -118,32 +124,41 @@ stdenv.mkDerivation (finalAttrs: {
this may result in crashes on incompatible CPUs!
'' "native";
preBuild = ''
extraFlags=(build_flags=-j$NIX_BUILD_CORES)
''
+ lib.optionalString stdenv.hostPlatform.isLinux ''
export PONY_LINKER="$CC"
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
export PONY_LINKER=ld
''
+ lib.optionalString stdenv.hostPlatform.isAarch64 ''
# See this relnote about building on Raspbian:
# https://github.com/ponylang/ponyc/blob/0.46.0/.release-notes/0.45.2.md
extraFlags+=(pic_flag=-fPIC)
''
+ ''
make libs "''${extraFlags[@]}"
make configure "''${extraFlags[@]}"
# Bake the Nix link/include paths into the compiler before it is built.
preConfigure =
lib.optionalString stdenv.hostPlatform.isLinux ''
libcDir=$(dirname "$($CC -print-file-name=crt1.o)")
gccDir=$(dirname "$($CC -print-libgcc-file-name)")
gccSharedDir=$(dirname "$($CC -print-file-name=libgcc_s.so)")
dynamicLinker=$(cat "$NIX_CC/nix-support/dynamic-linker")
libcIncDir="$(cat "$NIX_CC/nix-support/orig-libc-dev")/include"
# pcre2 and openssl are baked into the compiler's link path so an installed
# ponyc can link `use "regex"` (pcre2) and `use "net/ssl"` (openssl)
# programs without a wrapper this replaces the old wrapProgram PONYPATH.
export NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -DPONY_NIX_LINK_LIBDIRS=\"$libcDir:$gccDir:$gccSharedDir:${lib.getLib pcre2}/lib:${lib.getLib openssl}/lib\" -DPONY_NIX_DYNAMIC_LINKER=\"$dynamicLinker\" -DPONY_NIX_INCLUDE_DIRS=\"$libcIncDir:$gccDir/include:$gccDir/include-fixed\""
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
# Bake pcre2/openssl into the compiler's link path so an installed ponyc can
# link `use "regex"` / `use "net/ssl"` without a wrapper; libc and the C++
# standard library come from the SDK (via xcrun / apple-sdk) at link time.
export NIX_CFLAGS_COMPILE="$NIX_CFLAGS_COMPILE -DPONY_NIX_LINK_LIBDIRS=\"${lib.getLib pcre2}/lib:${lib.getLib openssl}/lib\""
'';
# Upstream drives the build through CMakePresets (which fix the binaryDir and
# compiler), so we bypass the cmake setup hook's configurePhase and invoke the
# presets directly rather than fight its flags.
configurePhase = ''
runHook preConfigure
cmake -DJOBS=$NIX_BUILD_CORES -P lib/build-libs.cmake
cmake --preset release
runHook postConfigure
'';
enableParallelBuilding = true;
makeFlags = [
"PONYC_VERSION=${finalAttrs.version}"
"prefix=${placeholder "out"}"
]
++ lib.optionals stdenv.hostPlatform.isDarwin ([ "bits=64" ] ++ lib.optional (!lto) "lto=no");
buildPhase = ''
runHook preBuild
cmake --build --preset release --parallel $NIX_BUILD_CORES
runHook postBuild
'';
env.NIX_CFLAGS_COMPILE = toString [
"-Wno-error=redundant-move"
@@ -152,35 +167,21 @@ stdenv.mkDerivation (finalAttrs: {
doCheck = true;
enableParallelChecking = true;
nativeCheckInputs = [ procps ];
installPhase = ''
makeArgs=(config=release prefix=$out)
''
+ lib.optionalString stdenv.hostPlatform.isDarwin ''
makeArgs+=(bits=64)
''
+ lib.optionalString (stdenv.hostPlatform.isDarwin && !lto) ''
makeArgs+=(lto=no)
''
+ ''
make "''${makeArgs[@]}" install
wrapProgram $out/bin/ponyc \
--prefix PATH ":" "${stdenv.cc}/bin" \
--set-default CC "$CC" \
--set-default PONY_LINKER "$PONY_LINKER" \
--prefix PONYPATH : "${
lib.makeLibraryPath [
pcre2
openssl
(placeholder "out")
]
}"
checkPhase = ''
runHook preCheck
ctest --preset release -L ci-core -j$NIX_BUILD_CORES
runHook postCheck
'';
# Stripping breaks linking for ponyc
installPhase = ''
runHook preInstall
cmake --install build/build_release --prefix=$out
runHook postInstall
'';
# Stripping breaks linking for ponyc.
dontStrip = true;
passthru = {
@@ -189,17 +190,19 @@ stdenv.mkDerivation (finalAttrs: {
};
meta = {
description = "Pony is an Object-oriented, actor-model, capabilities-secure, high performance programming language";
description = "Object-oriented, actor-model, capabilities-secure, high performance programming language";
homepage = "https://www.ponylang.io";
license = lib.licenses.bsd2;
mainProgram = "ponyc";
maintainers = with lib.maintainers; [
kamilchm
redvers
numinit
];
# Intel macOS (x86_64-darwin) is intentionally unsupported; only Apple
# Silicon is supported on Darwin.
platforms = [
"x86_64-linux"
"x86_64-darwin"
"aarch64-linux"
"aarch64-darwin"
];