369 lines
16 KiB
Docker
369 lines
16 KiB
Docker
FROM ubuntu:22.04
|
|
|
|
LABEL org.opencontainers.image.description="Dusk Engine — Sega Saturn build environment (sh2eb-elf cross-compiler + Yaul SDK)"
|
|
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
|
ENV YAUL_INSTALL_ROOT=/opt/yaul
|
|
|
|
# All variables required by Yaul's env.mk
|
|
ENV YAUL_ARCH_SH_PREFIX=sh2eb-elf
|
|
ENV YAUL_PROG_SH_PREFIX=sh2eb-elf
|
|
ENV YAUL_ARCH_M68K_PREFIX=m68keb-elf
|
|
ENV YAUL_BUILD_ROOT=/tmp/yaul-build
|
|
ENV YAUL_BUILD=build
|
|
ENV YAUL_OPTION_MALLOC_IMPL=tlsf
|
|
|
|
ENV PATH="${YAUL_INSTALL_ROOT}/bin:${PATH}"
|
|
|
|
# Toolchain source versions
|
|
ARG BINUTILS_VER=2.40
|
|
ARG GCC_VER=12.3.0
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 1. Host build tools
|
|
# ---------------------------------------------------------------------------
|
|
RUN apt-get update && apt-get install -y \
|
|
build-essential \
|
|
cmake \
|
|
git \
|
|
wget \
|
|
curl \
|
|
xz-utils \
|
|
python3 \
|
|
python3-pip \
|
|
python3-polib \
|
|
python3-pil \
|
|
python3-dotenv \
|
|
texinfo \
|
|
bison \
|
|
flex \
|
|
libgmp-dev \
|
|
libmpfr-dev \
|
|
libmpc-dev \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
RUN mkdir -p "${YAUL_INSTALL_ROOT}"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 2. Download cross-compiler sources
|
|
# ---------------------------------------------------------------------------
|
|
RUN cd /tmp && \
|
|
wget -q "https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER}.tar.xz" && \
|
|
wget -q "https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER}/gcc-${GCC_VER}.tar.xz" && \
|
|
tar xf "binutils-${BINUTILS_VER}.tar.xz" && \
|
|
tar xf "gcc-${GCC_VER}.tar.xz" && \
|
|
rm "binutils-${BINUTILS_VER}.tar.xz" "gcc-${GCC_VER}.tar.xz"
|
|
|
|
# Download GCC prerequisites (gmp, mpfr, mpc if not packaged)
|
|
RUN cd /tmp/gcc-${GCC_VER} && contrib/download_prerequisites
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 3. sh2eb-elf binutils (SH-2 big-endian)
|
|
# ---------------------------------------------------------------------------
|
|
RUN mkdir -p /tmp/build-sh-binutils && cd /tmp/build-sh-binutils && \
|
|
/tmp/binutils-${BINUTILS_VER}/configure \
|
|
--target=sh2eb-elf \
|
|
--prefix="${YAUL_INSTALL_ROOT}" \
|
|
--disable-nls \
|
|
--disable-multilib \
|
|
--disable-werror \
|
|
&& make -j"$(nproc)" && make install && \
|
|
rm -rf /tmp/build-sh-binutils
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 4. sh2eb-elf GCC stage 1 (compiler only, no C library yet)
|
|
# ---------------------------------------------------------------------------
|
|
RUN mkdir -p /tmp/build-sh-gcc1 && cd /tmp/build-sh-gcc1 && \
|
|
/tmp/gcc-${GCC_VER}/configure \
|
|
--target=sh2eb-elf \
|
|
--prefix="${YAUL_INSTALL_ROOT}" \
|
|
--enable-languages=c,c++ \
|
|
--without-headers \
|
|
--with-newlib \
|
|
--disable-nls \
|
|
--disable-shared \
|
|
--disable-multilib \
|
|
--disable-decimal-float \
|
|
--disable-threads \
|
|
--disable-libatomic \
|
|
--disable-libgomp \
|
|
--disable-libquadmath \
|
|
--disable-libssp \
|
|
--disable-libvtv \
|
|
--disable-libstdcxx \
|
|
&& make -j"$(nproc)" all-gcc all-target-libgcc \
|
|
&& make install-gcc install-target-libgcc && \
|
|
rm -rf /tmp/build-sh-gcc1
|
|
|
|
# Newlib does not recognise the sh2eb CPU name, and Yaul ships its own C
|
|
# runtime in libyaul/libc/ anyway. Stage 1 (compiler + libgcc) is all
|
|
# we need; Yaul's specs file overrides *startfile:/*endfile:/*lib: to empty
|
|
# so nothing from a host C library is linked in.
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 7. m68k-elf binutils (Saturn 68EC000 sound CPU)
|
|
# ---------------------------------------------------------------------------
|
|
RUN mkdir -p /tmp/build-m68k-binutils && cd /tmp/build-m68k-binutils && \
|
|
/tmp/binutils-${BINUTILS_VER}/configure \
|
|
--target=m68k-elf \
|
|
--prefix="${YAUL_INSTALL_ROOT}" \
|
|
--disable-nls \
|
|
--disable-multilib \
|
|
--disable-werror \
|
|
&& make -j"$(nproc)" && make install && \
|
|
rm -rf /tmp/build-m68k-binutils
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 8. m68k-elf GCC (compiler only; Yaul provides its own sound startup)
|
|
# ---------------------------------------------------------------------------
|
|
RUN mkdir -p /tmp/build-m68k-gcc && cd /tmp/build-m68k-gcc && \
|
|
/tmp/gcc-${GCC_VER}/configure \
|
|
--target=m68k-elf \
|
|
--prefix="${YAUL_INSTALL_ROOT}" \
|
|
--enable-languages=c \
|
|
--without-headers \
|
|
--with-newlib \
|
|
--disable-nls \
|
|
--disable-shared \
|
|
--disable-multilib \
|
|
--disable-libssp \
|
|
&& make -j"$(nproc)" all-gcc && make install-gcc && \
|
|
rm -rf /tmp/build-m68k-gcc
|
|
|
|
# Clean up source tarballs/trees
|
|
RUN rm -rf /tmp/binutils-${BINUTILS_VER} /tmp/gcc-${GCC_VER}
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 9. Create m68keb-elf symlinks
|
|
# Yaul expects YAUL_ARCH_M68K_PREFIX=m68keb-elf but we built m68k-elf.
|
|
# m68k is always big-endian, so m68k-elf == m68keb-elf semantically.
|
|
# ---------------------------------------------------------------------------
|
|
RUN for tool in "${YAUL_INSTALL_ROOT}/bin/m68k-elf-"*; do \
|
|
base="$(basename "$tool")"; \
|
|
newname="${YAUL_INSTALL_ROOT}/bin/m68keb-elf-${base#m68k-elf-}"; \
|
|
ln -sf "$tool" "$newname"; \
|
|
done
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 10. Clone and install libyaul
|
|
# ---------------------------------------------------------------------------
|
|
RUN git clone --depth 1 --recurse-submodules \
|
|
https://github.com/yaul-org/libyaul.git /tmp/yaul && \
|
|
mkdir -p /tmp/yaul-build && \
|
|
cd /tmp/yaul && \
|
|
YAUL_INSTALL_ROOT="${YAUL_INSTALL_ROOT}" \
|
|
YAUL_ARCH_SH_PREFIX=sh2eb-elf \
|
|
YAUL_PROG_SH_PREFIX=sh2eb-elf \
|
|
YAUL_ARCH_M68K_PREFIX=m68keb-elf \
|
|
YAUL_BUILD_ROOT=/tmp/yaul-build \
|
|
YAUL_BUILD=build \
|
|
YAUL_OPTION_MALLOC_IMPL=tlsf \
|
|
make install && \
|
|
rm -rf /tmp/yaul /tmp/yaul-build
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 11. Provide a freestanding stdint.h in the Yaul sysroot.
|
|
# GCC 12 --without-headers installs a stdint.h wrapper that emits
|
|
# `#include_next <stdint.h>`, but Yaul's sysroot has no system stdint.h
|
|
# to satisfy that lookup. Yaul's own errno.h (and anything it pulls in)
|
|
# will fail to compile in any external cmake project without this stub.
|
|
# ---------------------------------------------------------------------------
|
|
RUN test -f "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/stdint.h" || \
|
|
cp /opt/yaul/lib/gcc/sh2eb-elf/12.3.0/include/stdint-gcc.h \
|
|
"${YAUL_INSTALL_ROOT}/sh2eb-elf/include/stdint.h" 2>/dev/null || \
|
|
printf '%s\n' \
|
|
'#pragma once' \
|
|
'#ifndef __STDINT_H' \
|
|
'#define __STDINT_H' \
|
|
'typedef signed char int8_t;' \
|
|
'typedef signed short int16_t;' \
|
|
'typedef signed int int32_t;' \
|
|
'typedef signed long long int64_t;' \
|
|
'typedef unsigned char uint8_t;' \
|
|
'typedef unsigned short uint16_t;' \
|
|
'typedef unsigned int uint32_t;' \
|
|
'typedef unsigned long long uint64_t;' \
|
|
'typedef int8_t int_least8_t;' \
|
|
'typedef int16_t int_least16_t;' \
|
|
'typedef int32_t int_least32_t;' \
|
|
'typedef int64_t int_least64_t;' \
|
|
'typedef uint8_t uint_least8_t;' \
|
|
'typedef uint16_t uint_least16_t;' \
|
|
'typedef uint32_t uint_least32_t;' \
|
|
'typedef uint64_t uint_least64_t;' \
|
|
'typedef int32_t int_fast8_t;' \
|
|
'typedef int32_t int_fast16_t;' \
|
|
'typedef int32_t int_fast32_t;' \
|
|
'typedef int64_t int_fast64_t;' \
|
|
'typedef uint32_t uint_fast8_t;' \
|
|
'typedef uint32_t uint_fast16_t;' \
|
|
'typedef uint32_t uint_fast32_t;' \
|
|
'typedef uint64_t uint_fast64_t;' \
|
|
'typedef int32_t intptr_t;' \
|
|
'typedef uint32_t uintptr_t;' \
|
|
'typedef int64_t intmax_t;' \
|
|
'typedef uint64_t uintmax_t;' \
|
|
'#define INT8_MIN (-128)' \
|
|
'#define INT16_MIN (-32768)' \
|
|
'#define INT32_MIN (-2147483647-1)' \
|
|
'#define INT64_MIN (-9223372036854775807LL-1)' \
|
|
'#define INT8_MAX (127)' \
|
|
'#define INT16_MAX (32767)' \
|
|
'#define INT32_MAX (2147483647)' \
|
|
'#define INT64_MAX (9223372036854775807LL)' \
|
|
'#define UINT8_MAX (255U)' \
|
|
'#define UINT16_MAX (65535U)' \
|
|
'#define UINT32_MAX (4294967295U)' \
|
|
'#define UINT64_MAX (18446744073709551615ULL)' \
|
|
'#define INTPTR_MIN INT32_MIN' \
|
|
'#define INTPTR_MAX INT32_MAX' \
|
|
'#define UINTPTR_MAX UINT32_MAX' \
|
|
'#define INTMAX_MIN INT64_MIN' \
|
|
'#define INTMAX_MAX INT64_MAX' \
|
|
'#define UINTMAX_MAX UINT64_MAX' \
|
|
'#define SIZE_MAX UINT32_MAX' \
|
|
'#define INT8_C(c) (c)' \
|
|
'#define INT16_C(c) (c)' \
|
|
'#define INT32_C(c) (c)' \
|
|
'#define INT64_C(c) (c ## LL)' \
|
|
'#define UINT8_C(c) (c ## U)' \
|
|
'#define UINT16_C(c) (c ## U)' \
|
|
'#define UINT32_C(c) (c ## U)' \
|
|
'#define UINT64_C(c) (c ## ULL)' \
|
|
'#define INTMAX_C(c) (c ## LL)' \
|
|
'#define UINTMAX_C(c)(c ## ULL)' \
|
|
'#endif' \
|
|
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/stdint.h"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 11b. Stubs for non-standard / bare-metal-missing headers.
|
|
# Yaul's sysroot only ships a minimal libc; third-party libraries such as
|
|
# libzip need these POSIX/GNU headers to compile.
|
|
# ---------------------------------------------------------------------------
|
|
RUN printf '#pragma once\n#include <stdlib.h>\n' \
|
|
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/malloc.h" && \
|
|
printf '#pragma once\n#include <string.h>\n' \
|
|
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/memory.h" && \
|
|
printf '%s\n' \
|
|
'#pragma once' \
|
|
'#ifndef _TIME_H' \
|
|
'#define _TIME_H' \
|
|
'typedef long time_t;' \
|
|
'typedef long clock_t;' \
|
|
'#define CLOCKS_PER_SEC 1000L' \
|
|
'struct tm {' \
|
|
' int tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year;' \
|
|
' int tm_wday, tm_yday, tm_isdst;' \
|
|
'};' \
|
|
'static inline time_t time(time_t *t) { if(t) *t=0; return 0; }' \
|
|
'static inline time_t mktime(struct tm *t) { (void)t; return 0; }' \
|
|
'static inline struct tm *localtime(const time_t *t) { (void)t; return 0; }' \
|
|
'static inline struct tm *gmtime(const time_t *t) { (void)t; return 0; }' \
|
|
'#endif' \
|
|
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/time.h" && \
|
|
printf '%s\n' \
|
|
'#pragma once' \
|
|
'#ifndef _SYS_STAT_H' \
|
|
'#define _SYS_STAT_H' \
|
|
'#include <sys/types.h>' \
|
|
'#include <time.h>' \
|
|
'typedef unsigned int dev_t;' \
|
|
'typedef unsigned int ino_t;' \
|
|
'typedef unsigned short nlink_t;' \
|
|
'typedef long blksize_t;' \
|
|
'typedef long blkcnt_t;' \
|
|
'struct stat {' \
|
|
' dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink;' \
|
|
' uid_t st_uid; gid_t st_gid; dev_t st_rdev; off_t st_size;' \
|
|
' blksize_t st_blksize; blkcnt_t st_blocks;' \
|
|
' time_t st_atime, st_mtime, st_ctime;' \
|
|
'};' \
|
|
'static inline int stat(const char *p, struct stat *b){(void)p;(void)b;return -1;}' \
|
|
'static inline int fstat(int f, struct stat *b){(void)f;(void)b;return -1;}' \
|
|
'#define S_ISREG(m) (((m)&0170000)==0100000)' \
|
|
'#define S_ISDIR(m) (((m)&0170000)==0040000)' \
|
|
'#define S_IRUSR 0400' '#define S_IWUSR 0200' '#define S_IXUSR 0100' \
|
|
'#endif' \
|
|
> "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/sys/stat.h"
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 12. sat-xc.cmake — shared CMake toolchain for zlib and libzip.
|
|
# CMAKE_SYSROOT tells cmake to pass --sysroot to the compiler so that
|
|
# GCC's #include_next wrappers resolve against the Yaul sysroot headers.
|
|
# ---------------------------------------------------------------------------
|
|
RUN printf '%s\n' \
|
|
'set(CMAKE_SYSTEM_NAME Generic)' \
|
|
'set(CMAKE_SYSTEM_PROCESSOR sh2)' \
|
|
"set(CMAKE_C_COMPILER \"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-gcc\")" \
|
|
"set(CMAKE_AR \"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ar\")" \
|
|
"set(CMAKE_RANLIB \"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ranlib\")" \
|
|
"set(CMAKE_SYSROOT \"${YAUL_INSTALL_ROOT}/sh2eb-elf\")" \
|
|
'set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)' \
|
|
'set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)' \
|
|
'set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)' \
|
|
'set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)' \
|
|
> /tmp/sat-xc.cmake
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 13. Cross-compile zlib for sh2eb-elf (inflate/deflate core only).
|
|
# We compile the nine core source files directly rather than via cmake
|
|
# to avoid the gz* files whose POSIX gzip-streaming API is both unused
|
|
# (libzip only needs inflate/deflate) and cannot compile on bare metal
|
|
# (gzguts.h -> errno.h -> stdint.h chain, and zutil.h includes gzguts.h
|
|
# unless NO_GZIP is defined).
|
|
# ---------------------------------------------------------------------------
|
|
RUN wget -q https://github.com/madler/zlib/releases/download/v1.3.1/zlib-1.3.1.tar.gz -O /tmp/zlib.tar.gz && \
|
|
tar xf /tmp/zlib.tar.gz -C /tmp && \
|
|
cd /tmp/zlib-1.3.1 && \
|
|
for f in adler32.c compress.c crc32.c deflate.c infback.c \
|
|
inffast.c inflate.c inftrees.c trees.c uncompr.c zutil.c; do \
|
|
"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-gcc" \
|
|
-m2 -mb -fno-builtin -O2 -DNO_GZIP -DSTDC -I. -c "$f" -o "${f%.c}.o"; \
|
|
done && \
|
|
"${YAUL_INSTALL_ROOT}/bin/sh2eb-elf-ar" rcs \
|
|
"${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/libz.a" \
|
|
adler32.o compress.o crc32.o deflate.o infback.o inffast.o \
|
|
inflate.o inftrees.o trees.o uncompr.o zutil.o && \
|
|
install -m644 zlib.h zconf.h "${YAUL_INSTALL_ROOT}/sh2eb-elf/include/" && \
|
|
rm -rf /tmp/zlib-1.3.1 /tmp/zlib.tar.gz
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 14. Cross-compile libzip for sh2eb-elf (reuses /tmp/sat-xc.cmake above)
|
|
# ---------------------------------------------------------------------------
|
|
RUN wget -q https://github.com/nih-at/libzip/releases/download/v1.10.1/libzip-1.10.1.tar.gz -O /tmp/libzip.tar.gz && \
|
|
tar xf /tmp/libzip.tar.gz -C /tmp && \
|
|
cmake -S /tmp/libzip-1.10.1 -B /tmp/libzip-build \
|
|
-DCMAKE_TOOLCHAIN_FILE=/tmp/sat-xc.cmake \
|
|
-DCMAKE_INSTALL_PREFIX="${YAUL_INSTALL_ROOT}/sh2eb-elf" \
|
|
-DCMAKE_FIND_ROOT_PATH="${YAUL_INSTALL_ROOT}/sh2eb-elf" \
|
|
-DCMAKE_C_FLAGS="-m2 -mb -fno-builtin -O2" \
|
|
-DZLIB_LIBRARY="${YAUL_INSTALL_ROOT}/sh2eb-elf/lib/libz.a" \
|
|
-DZLIB_INCLUDE_DIR="${YAUL_INSTALL_ROOT}/sh2eb-elf/include" \
|
|
-DENABLE_BZIP2=OFF \
|
|
-DENABLE_LZMA=OFF \
|
|
-DENABLE_ZSTD=OFF \
|
|
-DENABLE_OPENSSL=OFF \
|
|
-DENABLE_GNUTLS=OFF \
|
|
-DBUILD_SHARED_LIBS=OFF \
|
|
-DBUILD_EXAMPLES=OFF \
|
|
-DBUILD_DOCUMENTATION=OFF \
|
|
-DBUILD_REGRESS=OFF \
|
|
-DBUILD_TOOLS=OFF \
|
|
-DHAVE_CLONEFILE=0 \
|
|
-DHAVE_ARC4RANDOM=0 \
|
|
-DHAVE_GETPROGNAME=0 \
|
|
&& cmake --build /tmp/libzip-build -- -j"$(nproc)" \
|
|
&& cmake --install /tmp/libzip-build \
|
|
&& rm -rf /tmp/libzip-1.10.1 /tmp/libzip-build /tmp/sat-xc.cmake \
|
|
; rm -f /tmp/libzip.tar.gz /tmp/zlib.tar.gz 2>/dev/null ; true
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# 15. Disc-image tools (separate layer so it does not invalidate the
|
|
# expensive cross-compiler cache layers above on changes).
|
|
# xorriso provides xorrisofs, needed by Yaul's make-iso script.
|
|
# ---------------------------------------------------------------------------
|
|
RUN apt-get update && apt-get install -y xorriso && rm -rf /var/lib/apt/lists/*
|
|
|
|
WORKDIR /workdir
|
|
VOLUME ["/workdir"]
|