forked from python/cpython
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathMakefile.nanvix
More file actions
335 lines (313 loc) · 13.8 KB
/
Copy pathMakefile.nanvix
File metadata and controls
335 lines (313 loc) · 13.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# Makefile.nanvix - CPython cross-compilation for Nanvix
#
# Copyright(c) The Maintainers of Nanvix.
# Licensed under the MIT License.
#
# Usage:
# make -f Makefile.nanvix CONFIG_NANVIX=y NANVIX_HOME=/path/to/nanvix all
#
# Thin cross-compilation wrapper: configure, build, install, clean.
# All other concerns (tests, ramfs, packaging) are handled by the Python
# modules in .nanvix/ and invoked via ./z.
#
# Dependencies:
# - zlib (libz.a in NANVIX_HOME/lib)
# - SQLite (libsqlite3.a in NANVIX_HOME/lib)
# - OpenSSL (libssl.a, libcrypto.a in NANVIX_HOME/lib)
# - bzip2 (libbz2.a in NANVIX_HOME/lib)
# - libffi (libffi.a in NANVIX_HOME/lib)
# - liblzma (liblzma.a in NANVIX_HOME/lib)
# Nanvix Docker image for cross-compilation
NANVIX_DOCKER_IMAGE ?= ghcr.io/nanvix/toolchain-python:latest
# Platform and deployment configuration
PLATFORM ?= microvm
PROCESS_MODE ?= standalone
MEMORY_SIZE ?= 256mb
# Install prefix baked into the python binary (sys.prefix, sys.path)
INSTALL_PREFIX ?= /sysroot
# Set to 'yes' for release packaging
NANVIX_RELEASE ?= no
# Nanvix cross-compilation configuration
ifdef CONFIG_NANVIX
NANVIX_TOOLCHAIN ?= /opt/nanvix
NANVIX_HOME ?= $(HOME)/nanvix
# Check if Docker is explicitly requested or native toolchain is not available
ifndef CONFIG_NANVIX_DOCKER
ifeq ($(wildcard $(NANVIX_TOOLCHAIN)/bin/i686-nanvix-gcc),)
# Native toolchain not found, check for Docker
DOCKER_AVAILABLE := $(shell command -v docker 2>/dev/null)
ifdef DOCKER_AVAILABLE
DOCKER_IMAGE_EXISTS := $(shell docker image inspect $(NANVIX_DOCKER_IMAGE) >/dev/null 2>&1 && echo yes)
ifdef DOCKER_IMAGE_EXISTS
CONFIG_NANVIX_DOCKER := y
$(info [INFO] Native toolchain not found at $(NANVIX_TOOLCHAIN), using Docker image $(NANVIX_DOCKER_IMAGE))
else
$(error Docker image $(NANVIX_DOCKER_IMAGE) not found. Run: docker pull $(NANVIX_DOCKER_IMAGE))
endif
else
$(error Nanvix toolchain not found at $(NANVIX_TOOLCHAIN) and Docker is not available)
endif
endif
else
# Docker explicitly requested via CONFIG_NANVIX_DOCKER=y
DOCKER_AVAILABLE := $(shell command -v docker 2>/dev/null)
ifndef DOCKER_AVAILABLE
$(error CONFIG_NANVIX_DOCKER=y but Docker is not installed)
endif
DOCKER_IMAGE_EXISTS := $(shell docker image inspect $(NANVIX_DOCKER_IMAGE) >/dev/null 2>&1 && echo yes)
ifndef DOCKER_IMAGE_EXISTS
$(error Docker image $(NANVIX_DOCKER_IMAGE) not found. Run: docker pull $(NANVIX_DOCKER_IMAGE))
endif
$(info [INFO] Using Docker image $(NANVIX_DOCKER_IMAGE) (explicitly requested))
endif
EXE=.elf
ifdef CONFIG_NANVIX_DOCKER
DOCKER_TOOLCHAIN_PATH := /opt/nanvix
DOCKER_SYSROOT_PATH := /mnt/sysroot
DOCKER_WORKSPACE_PATH := /mnt/workspace
DOCKER_UID := $(shell id -u)
DOCKER_GID := $(shell id -g)
DOCKER_RUN := docker run --rm --user $(DOCKER_UID):$(DOCKER_GID) \
-v $(CURDIR):$(DOCKER_WORKSPACE_PATH) \
-v $(abspath $(NANVIX_HOME)):$(DOCKER_SYSROOT_PATH):ro \
-w $(DOCKER_WORKSPACE_PATH) \
-e HOME=/tmp \
$(NANVIX_DOCKER_IMAGE)
TOOLCHAIN_PREFIX := $(DOCKER_TOOLCHAIN_PATH)
SYSROOT_PATH := $(DOCKER_SYSROOT_PATH)
LIBC := $(DOCKER_TOOLCHAIN_PATH)/i686-nanvix/lib/libc.a
LIBM := $(DOCKER_TOOLCHAIN_PATH)/i686-nanvix/lib/libm.a
LIBPOSIX := $(DOCKER_SYSROOT_PATH)/lib/libposix.a
LIBZ := $(DOCKER_SYSROOT_PATH)/lib/libz.a
LIBSQLITE3 := $(DOCKER_SYSROOT_PATH)/lib/libsqlite3.a
LIBSSL := $(DOCKER_SYSROOT_PATH)/lib/libssl.a
LIBCRYPTO := $(DOCKER_SYSROOT_PATH)/lib/libcrypto.a
# libnvx_crt0 ships the executable startup symbols (`_do_start`, `_start`,
# `c_trampoline`). It must be present in the Nanvix sysroot ahead of this
# cpython build; the existence check below fails loudly when it is not.
LIBNVX_CRT0 := $(DOCKER_SYSROOT_PATH)/lib/libnvx_crt0.a
BUILD_PYTHON := $(DOCKER_TOOLCHAIN_PATH)/bin/python3
else
TOOLCHAIN_PREFIX := $(NANVIX_TOOLCHAIN)
SYSROOT_PATH := $(abspath $(NANVIX_HOME))
LIBC := $(NANVIX_TOOLCHAIN)/i686-nanvix/lib/libc.a
LIBM := $(NANVIX_TOOLCHAIN)/i686-nanvix/lib/libm.a
LIBPOSIX := $(abspath $(NANVIX_HOME))/lib/libposix.a
LIBZ := $(abspath $(NANVIX_HOME))/lib/libz.a
LIBSQLITE3 := $(abspath $(NANVIX_HOME))/lib/libsqlite3.a
LIBSSL := $(abspath $(NANVIX_HOME))/lib/libssl.a
LIBCRYPTO := $(abspath $(NANVIX_HOME))/lib/libcrypto.a
LIBNVX_CRT0 := $(abspath $(NANVIX_HOME))/lib/libnvx_crt0.a
BUILD_PYTHON := $(NANVIX_TOOLCHAIN)/bin/python3
endif
# libstdc++ / libgcc are referenced via `-l` rather than absolute paths so
# the GCC driver resolves them: libgcc lives under a versioned dir
# (`lib/gcc/i686-nanvix/<gcc-version>/libgcc.a`) and hardcoding a version
# would be fragile across toolchain upgrades. Defined once at top-level
# because the `-l` form is identical between the docker and host build
# paths above.
LIBSTDCXX := -lstdc++
LIBGCC := -lgcc
else
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),distclean)
$(error CONFIG_NANVIX must be set to 'y'. Usage: make -f Makefile.nanvix CONFIG_NANVIX=y NANVIX_HOME=/path/to/nanvix)
endif
endif
EXE=.elf
endif
# Existence check for libnvx_crt0.a.
#
# libnvx_crt0.a is the executable startup archive (`_do_start` / `_start` /
# `c_trampoline`) introduced by the Nanvix `nvx-crt0` crate split. cpython
# requires a Nanvix sysroot that ships it. If the user is building against
# an older sysroot snapshot, fail at make-parse time with an actionable
# message rather than producing a python.elf with no entry point.
ifdef CONFIG_NANVIX
ifneq ($(filter clean distclean,$(MAKECMDGOALS)),$(MAKECMDGOALS))
ifeq ($(wildcard $(LIBNVX_CRT0)),)
$(error libnvx_crt0.a not found at $(LIBNVX_CRT0). Update the Nanvix sysroot to one that ships libnvx_crt0.a (the nvx-crt0 crate must be present and built into the sysroot lib/ directory).)
endif
endif
endif
# Configure environment variables
#
# Linker flag rationale:
#
# `-Wl,--export-dynamic`: put every globally-defined symbol from python.elf
# into its `.dynsym`. Extension `.so`s (numpy's `_multiarray_umath.so`, ssl,
# etc.) leave C/C++ runtime symbols UND and resolve them against python.elf
# at dlopen() time. Without this, those `.so`s fail to load.
#
# `-Wl,--allow-multiple-definition`: tolerates duplicate symbol definitions
# that arise from forcing every libposix / libc / libm / libstdc++ / libgcc
# object into python.elf via `--whole-archive` below. The biggest set is
# newlib's long-double math helpers (`frexpl`, `llrintl`, `lrintl`, `rintl`
# are defined in three different newlib directories simultaneously — a
# newlib build-system bug). Other known overlaps include libposix vs. libc
# (`_start`, `copysign[f]`, `getenv`, `setenv`, `unsetenv`, `environ`,
# `isatty`), libc vs. libm (`frexp`, `ldexp`, `modf`, `isnan`, `isinf`,
# `scalbn`, …), libm vs. libstdc++ (`hypotf`), libgcc internal duplicates
# (`__x86.get_pc_thunk.*`), and a libc / libgcc `__eprintf`. The set is
# large and toolchain-build-version-dependent; treating the link as
# multiple-definition-tolerant is the only practical workaround until each
# upstream is fixed. Remove this flag once the contributing duplicates are
# resolved upstream.
#
# `LIBS` segment 1 (`--whole-archive ... --no-whole-archive`): force every
# object from libnvx_crt0, libposix, libc, libm, libstdc++, and libgcc into
# python.elf so the runtime symbols extension `.so`s depend on are embedded
# and re-exported via `--export-dynamic`. Without `--whole-archive`, the
# static linker drops unreferenced objects (e.g. `fscanf`, `longjmp`,
# `strtold_l` for numpy; `operator new/delete[]`, `__cxa_*`, `_Unwind_*`,
# `std::type_info` vtables for any C++ extension) and subsequent dlopen()
# of those `.so`s fails with "symbol not found".
#
# `libnvx_crt0` is listed first inside `--whole-archive`. Today, libposix.a
# still ships its own copy of the startup symbols (`_start`, `_do_start`,
# `c_trampoline`) because `nvx` builds them in under the `staticlib`
# feature. With `--allow-multiple-definition` (above) the linker takes the
# first definition, so listing libnvx_crt0 first selects the standalone
# crt0 copy of `_start` and friends. This is an intentional behaviour
# change: future Nanvix versions remove the duplicate from libposix, after
# which libnvx_crt0 is the sole provider. Listing it first today keeps
# python.elf using a consistent `_start` source across both states.
#
# `LIBS` segment 2 (empty after the Wave 7 unbundling): with libffi /
# libssl / libcrypto / libsqlite3 / libz / libbz2 / liblzma all
# shipped as .so under sysroot/lib/, python.elf no longer bundles any
# external Nanvix-ported libraries. The per-module .so wrappers
# (_ssl, _hashlib, _ctypes, _sqlite3, zlib, _bz2, _lzma) reference
# the corresponding system .so via DT_NEEDED; the Nanvix dynamic
# loader walks the chain at dlopen time and binds UND symbols against
# python.elf .dynsym. The per-module *_LIBS environment variables
# (LIBSQLITE3_LIBS, ZLIB_LIBS, BZIP2_LIBS, LIBLZMA_LIBS, LIBFFI_LIBS)
# in -L/-l form make ld emit the DT_NEEDED entry rather than
# embedding the .a -- exactly the upstream cpython behavior for
# system-library builds (Linux distro default).
CONFIGURE_ENV = \
CC="$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-gcc" \
CXX="$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-g++" \
LD="$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-ld" \
AR="$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-ar" \
RANLIB="$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-ranlib" \
CFLAGS="-O3 -fomit-frame-pointer -fno-unwind-tables -fno-asynchronous-unwind-tables -I$(SYSROOT_PATH)/include" \
CFLAGS_NODIST="-fno-semantic-interposition" \
LDFLAGS="-L$(SYSROOT_PATH)/lib -T$(SYSROOT_PATH)/lib/user.ld -Wl,--allow-multiple-definition -no-pie -Wl,--export-dynamic -Wl,--no-dynamic-linker" \
LIBS="-Wl,--whole-archive $(LIBNVX_CRT0) $(LIBPOSIX) $(LIBC) $(LIBM) $(LIBSTDCXX) $(LIBGCC) -Wl,--no-whole-archive" \
LIBSQLITE3_LIBS="-L$(SYSROOT_PATH)/lib -lsqlite3" \
LIBSQLITE3_CFLAGS="-I$(SYSROOT_PATH)/include" \
ZLIB_LIBS="-L$(SYSROOT_PATH)/lib -lz" \
ZLIB_CFLAGS="-I$(SYSROOT_PATH)/include" \
BZIP2_LIBS="-L$(SYSROOT_PATH)/lib -lbz2" \
BZIP2_CFLAGS="-I$(SYSROOT_PATH)/include" \
LIBLZMA_LIBS="-L$(SYSROOT_PATH)/lib -llzma" \
LIBLZMA_CFLAGS="-I$(SYSROOT_PATH)/include" \
LIBFFI_LIBS="-L$(SYSROOT_PATH)/lib -lffi" \
LIBFFI_CFLAGS="-I$(SYSROOT_PATH)/include"
# Configure options for Nanvix
CONFIGURE_OPTS = \
--disable-shared \
--build=x86_64-pc-linux-gnux32 \
--host=i686-nanvix \
--with-build-python="$(BUILD_PYTHON)" \
$(if $(filter yes,$(NANVIX_RELEASE)),--disable-test-modules,) \
--with-libc="$(LIBC)" \
--with-libm= \
--prefix="$(INSTALL_PREFIX)" \
--exec-prefix="$(INSTALL_PREFIX)" \
--with-ensurepip=no \
--with-pkg-config=no \
--with-openssl="$(SYSROOT_PATH)" \
--disable-ipv6 \
$(if $(filter yes,$(NANVIX_RELEASE)),--without-doc-strings,) \
--with-computed-gotos \
ac_cv_file__dev_ptmx=no \
ac_cv_file__dev_ptc=no \
ac_cv_pthread_is_default=yes \
ac_cv_pthread=yes \
ac_cv_kthread=no \
ac_cv_func_dlopen=yes \
ac_cv_header_dlfcn_h=yes \
ac_cv_header_sys_socket_h=yes \
ac_cv_header_netinet_in_h=yes \
ac_cv_header_arpa_inet_h=yes \
ac_cv_header_netdb_h=yes \
ac_cv_func_socket=yes \
ac_cv_func_bind=yes \
ac_cv_func_listen=yes \
ac_cv_func_accept=yes \
ac_cv_func_connect=yes \
ac_cv_func_sendto=yes \
ac_cv_func_recvfrom=yes \
ac_cv_func_setsockopt=yes \
ac_cv_func_getpeername=yes \
ac_cv_func_getsockname=yes \
ac_cv_func_inet_aton=no \
ac_cv_func_inet_ntoa=yes \
ac_cv_func_inet_pton=no
# Marker file to track if configure has been run
CONFIGURED_MARKER = .nanvix-configured
# Default target
all: build
# Configure target
configure: $(CONFIGURED_MARKER)
$(CONFIGURED_MARKER):
ifdef CONFIG_NANVIX_DOCKER
@echo "Running configure inside Docker..."
$(DOCKER_RUN) sh -c '$(CONFIGURE_ENV) ./configure $(CONFIGURE_OPTS)'
else
$(CONFIGURE_ENV) ./configure $(CONFIGURE_OPTS)
endif
touch $(CONFIGURED_MARKER)
# Build target
build: $(CONFIGURED_MARKER)
ifdef CONFIG_NANVIX_DOCKER
@echo "Building inside Docker..."
$(DOCKER_RUN) $(MAKE) -j$$(nproc) all
@if [ -f python ]; then cp -f python python$(EXE); fi
$(DOCKER_RUN) sh -c '\
if [ ! -x "$(DOCKER_TOOLCHAIN_PATH)/bin/i686-nanvix-strip" ]; then \
echo "Warning: i686-nanvix-strip not found in Docker toolchain; skipping strip."; \
elif [ ! -f "$(DOCKER_WORKSPACE_PATH)/python$(EXE)" ]; then \
echo "Warning: $(DOCKER_WORKSPACE_PATH)/python$(EXE) not found; skipping strip."; \
else \
"$(DOCKER_TOOLCHAIN_PATH)/bin/i686-nanvix-strip" --strip-all "$(DOCKER_WORKSPACE_PATH)/python$(EXE)" || { \
echo "Error: failed to strip python$(EXE) inside Docker."; exit 1; }; \
fi'
else
$(MAKE) -j$$(nproc) all
@if [ -f python ]; then cp -f python python$(EXE); fi
@if [ -x "$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-strip" ]; then \
if [ -f "python$(EXE)" ]; then \
"$(TOOLCHAIN_PREFIX)/bin/i686-nanvix-strip" --strip-all "python$(EXE)" || { \
echo "Error: failed to strip python$(EXE)."; exit 1; }; \
else \
echo "Warning: python$(EXE) not found; skipping strip."; \
fi; \
else \
echo "Warning: i686-nanvix-strip not found in $(TOOLCHAIN_PREFIX); skipping strip."; \
fi
endif
# Install target
install: build
ifdef CONFIG_NANVIX_DOCKER
$(DOCKER_RUN) $(MAKE) install DESTDIR="$(patsubst $(CURDIR)%,$(DOCKER_WORKSPACE_PATH)%,$(abspath $(DESTDIR)))"
else
$(MAKE) install DESTDIR="$(DESTDIR)"
endif
# Clean target
clean:
-$(MAKE) clean 2>/dev/null || true
rm -f $(CONFIGURED_MARKER)
rm -f python$(EXE)
# Distclean target
distclean: clean
-$(MAKE) distclean 2>/dev/null || true
git clean -fdx -e Makefile.nanvix -e NANVIX.md -e .github \
-e .nanvix/z.py -e .nanvix/_loader.py -e .nanvix/nanvix.toml -e .nanvix/.gitignore \
-e .nanvix/config.py -e .nanvix/build.py -e .nanvix/docker.py \
-e .nanvix/ramfs.py -e .nanvix/test.py -e .nanvix/package.py \
-e .nanvix/run-regrtest.py -e .nanvix/run-tests.py \
-e z -e z.sh -e z.ps1 2>/dev/null || true
.PHONY: all configure build install clean distclean