build-static.sh 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #!/bin/sh
  2. set -o errexit
  3. set -x
  4. if ! type "git" > /dev/null 2>&1; then
  5. echo "The \"git\" command must be installed."
  6. exit 1
  7. fi
  8. arch="$(uname -m)"
  9. os="$(uname -s | tr '[:upper:]' '[:lower:]')"
  10. md5binary="md5sum"
  11. if [ "${os}" = "darwin" ]; then
  12. os="mac"
  13. md5binary="md5 -q"
  14. fi
  15. if [ "${os}" = "linux" ] && ! type "cmake" > /dev/null 2>&1; then
  16. echo "The \"cmake\" command must be installed."
  17. exit 1
  18. fi
  19. if [ -z "${PHP_EXTENSIONS}" ]; then
  20. if [ -n "${EMBED}" ] && [ -f "${EMBED}/composer.json" ]; then
  21. cd "${EMBED}"
  22. PHP_EXTENSIONS="$(composer check-platform-reqs --no-dev 2>/dev/null | grep ^ext | sed -e 's/^ext-//' -e 's/ .*//' | xargs | tr ' ' ',')"
  23. export PHP_EXTENSIONS
  24. cd -
  25. else
  26. export PHP_EXTENSIONS="apcu,bcmath,bz2,calendar,ctype,curl,dba,dom,exif,fileinfo,filter,ftp,gd,gmp,gettext,iconv,igbinary,imagick,intl,ldap,mbregex,mbstring,mysqli,mysqlnd,opcache,openssl,pcntl,pdo,pdo_mysql,pdo_pgsql,pdo_sqlite,pgsql,phar,posix,protobuf,readline,redis,session,shmop,simplexml,soap,sockets,sodium,sqlite3,ssh2,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,xlswriter,xml,xmlreader,xmlwriter,zip,zlib,yaml,zstd"
  27. fi
  28. fi
  29. if [ -z "${PHP_EXTENSION_LIBS}" ]; then
  30. export PHP_EXTENSION_LIBS="bzip2,freetype,libavif,libjpeg,liblz4,libwebp,libzip"
  31. fi
  32. # The Brotli library must always be built as it is required by http://github.com/dunglas/caddy-cbrotli
  33. if ! echo "${PHP_EXTENSION_LIBS}" | grep -q "\bbrotli\b"; then
  34. export PHP_EXTENSION_LIBS="${PHP_EXTENSION_LIBS},brotli"
  35. fi
  36. if [ -z "${PHP_VERSION}" ]; then
  37. export PHP_VERSION="8.3"
  38. fi
  39. if [ -z "${FRANKENPHP_VERSION}" ]; then
  40. FRANKENPHP_VERSION="$(git rev-parse --verify HEAD)"
  41. export FRANKENPHP_VERSION
  42. elif [ -d ".git/" ]; then
  43. CURRENT_REF="$(git rev-parse --abbrev-ref HEAD)"
  44. export CURRENT_REF
  45. if echo "${FRANKENPHP_VERSION}" | grep -F -q "."; then
  46. # Tag
  47. # Trim "v" prefix if any
  48. FRANKENPHP_VERSION=${FRANKENPHP_VERSION#v}
  49. export FRANKENPHP_VERSION
  50. git checkout "v${FRANKENPHP_VERSION}"
  51. else
  52. git checkout "${FRANKENPHP_VERSION}"
  53. fi
  54. fi
  55. bin="frankenphp-${os}-${arch}"
  56. if [ -n "${CLEAN}" ]; then
  57. rm -Rf dist/
  58. go clean -cache
  59. fi
  60. # Build libphp if necessary
  61. if [ -f "dist/static-php-cli/buildroot/lib/libphp.a" ]; then
  62. cd dist/static-php-cli
  63. else
  64. mkdir -p dist/
  65. cd dist/
  66. if [ -d "static-php-cli/" ]; then
  67. cd static-php-cli/
  68. git pull
  69. else
  70. # TODO: switch back to upstream when https://github.com/crazywhalecc/static-php-cli/pull/481 will be merged
  71. #git clone --depth 1 https://github.com/crazywhalecc/static-php-cli
  72. git clone --depth 1 --branch fix/480 https://github.com/dunglas/static-php-cli
  73. cd static-php-cli/
  74. fi
  75. if type "brew" > /dev/null 2>&1; then
  76. if ! type "composer" > /dev/null; then
  77. packages="composer"
  78. fi
  79. if ! type "go" > /dev/null; then
  80. packages="${packages} go"
  81. fi
  82. if [ -n "${RELEASE}" ] && ! type "gh" > /dev/null 2>&1; then
  83. packages="${packages} gh"
  84. fi
  85. if [ -n "${packages}" ]; then
  86. # shellcheck disable=SC2086
  87. brew install --formula --quiet ${packages}
  88. fi
  89. fi
  90. composer install --no-dev -a
  91. if [ "${os}" = "linux" ]; then
  92. extraOpts="--disable-opcache-jit"
  93. fi
  94. if [ -n "${DEBUG_SYMBOLS}" ]; then
  95. extraOpts="${extraOpts} --no-strip"
  96. fi
  97. ./bin/spc doctor --auto-fix
  98. ./bin/spc download --with-php="${PHP_VERSION}" --for-extensions="${PHP_EXTENSIONS}" --for-libs="${PHP_EXTENSION_LIBS}" --ignore-cache-sources=php-src
  99. # shellcheck disable=SC2086
  100. ./bin/spc build --debug --enable-zts --build-embed ${extraOpts} "${PHP_EXTENSIONS}" --with-libs="${PHP_EXTENSION_LIBS}"
  101. fi
  102. CGO_CFLAGS="-DFRANKENPHP_VERSION=${FRANKENPHP_VERSION} -I${PWD}/buildroot/include/ $(./buildroot/bin/php-config --includes | sed s#-I/#-I"${PWD}"/buildroot/#g)"
  103. if [ -n "${DEBUG_SYMBOLS}" ]; then
  104. CGO_CFLAGS="-g ${CGO_CFLAGS}"
  105. fi
  106. export CGO_CFLAGS
  107. if [ "${os}" = "mac" ]; then
  108. export CGO_LDFLAGS="-framework CoreFoundation -framework SystemConfiguration"
  109. fi
  110. CGO_LDFLAGS="${CGO_LDFLAGS} ${PWD}/buildroot/lib/libbrotlicommon.a ${PWD}/buildroot/lib/libbrotlienc.a ${PWD}/buildroot/lib/libbrotlidec.a $(./buildroot/bin/php-config --ldflags || true) $(./buildroot/bin/php-config --libs || true)"
  111. if [ "${os}" = "linux" ]; then
  112. if echo "${PHP_EXTENSIONS}" | grep -qE "\b(intl|imagick|grpc|v8js|protobuf|mongodb|tbb)\b"; then
  113. CGO_LDFLAGS="${CGO_LDFLAGS} -lstdc++"
  114. fi
  115. fi
  116. export CGO_LDFLAGS
  117. LIBPHP_VERSION="$(./buildroot/bin/php-config --version)"
  118. export LIBPHP_VERSION
  119. cd ../
  120. if [ "${os}" = "linux" ]; then
  121. if [ -n "${MIMALLOC}" ]; then
  122. # Replace musl's mallocng by mimalloc
  123. # The default musl allocator is slow, especially when used by multi-threaded apps,
  124. # and triggers weird bugs
  125. # Adapted from https://www.tweag.io/blog/2023-08-10-rust-static-link-with-mimalloc/
  126. echo 'The USE_MIMALLOC environment variable is EXPERIMENTAL.'
  127. echo 'This option can be removed or its behavior modified at any time.'
  128. if [ ! -f "mimalloc/out/libmimalloc.a" ]; then
  129. if [ -d "mimalloc" ]; then
  130. cd mimalloc/
  131. git reset --hard
  132. git clean -xdf
  133. git fetch --tags
  134. else
  135. git clone https://github.com/microsoft/mimalloc.git
  136. cd mimalloc/
  137. fi
  138. git checkout "$(git describe --tags "$(git rev-list --tags --max-count=1 || true)" || true)"
  139. curl -f -L --retry 5 https://raw.githubusercontent.com/tweag/rust-alpine-mimalloc/b26002b49d466a295ea8b50828cb7520a71a872a/mimalloc.diff -o mimalloc.diff
  140. patch -p1 < mimalloc.diff
  141. mkdir -p out/
  142. cd out/
  143. if [ -n "${DEBUG_SYMBOLS}" ]; then
  144. cmake \
  145. -DCMAKE_BUILD_TYPE=Debug \
  146. -DMI_BUILD_SHARED=OFF \
  147. -DMI_BUILD_OBJECT=OFF \
  148. -DMI_BUILD_TESTS=OFF \
  149. ../
  150. else
  151. cmake \
  152. -DCMAKE_BUILD_TYPE=Release \
  153. -DMI_BUILD_SHARED=OFF \
  154. -DMI_BUILD_OBJECT=OFF \
  155. -DMI_BUILD_TESTS=OFF \
  156. ../
  157. fi
  158. make -j"$(nproc || true)"
  159. cd ../../
  160. fi
  161. if [ -n "${DEBUG_SYMBOLS}" ]; then
  162. libmimalloc_path=mimalloc/out/libmimalloc-debug.a
  163. else
  164. libmimalloc_path=mimalloc/out/libmimalloc.a
  165. fi
  166. # Patch musl library to use mimalloc
  167. for libc_path in "/usr/local/musl/lib/libc.a" "/usr/local/musl/$(uname -m)-linux-musl/lib/libc.a" "/usr/lib/libc.a"
  168. do
  169. if [ ! -f "${libc_path}" ] || [ -f "${libc_path}.unpatched" ]; then
  170. continue
  171. fi
  172. {
  173. echo "CREATE libc.a"
  174. echo "ADDLIB ${libc_path}"
  175. echo "DELETE aligned_alloc.lo calloc.lo donate.lo free.lo libc_calloc.lo lite_malloc.lo malloc.lo malloc_usable_size.lo memalign.lo posix_memalign.lo realloc.lo reallocarray.lo valloc.lo"
  176. echo "ADDLIB ${libmimalloc_path}"
  177. echo "SAVE"
  178. } | ar -M
  179. mv "${libc_path}" "${libc_path}.unpatched"
  180. mv libc.a "${libc_path}"
  181. done
  182. fi
  183. # Increase the default stack size to prevents issues with code including many files such as Symfony containers
  184. extraExtldflags="-Wl,-z,stack-size=0x80000"
  185. fi
  186. if [ -z "${DEBUG_SYMBOLS}" ]; then
  187. extraLdflags="-w -s"
  188. fi
  189. cd ../
  190. # Embed PHP app, if any
  191. if [ -n "${EMBED}" ] && [ -d "${EMBED}" ]; then
  192. tar -cf app.tar -C "${EMBED}" .
  193. ${md5binary} app.tar | awk '{printf $1}' > app_checksum.txt
  194. fi
  195. cd caddy/frankenphp/
  196. go env
  197. go build -buildmode=pie -tags "cgo netgo osusergo static_build" -ldflags "-linkmode=external -extldflags '-static-pie ${extraExtldflags}' ${extraLdflags} -X 'github.com/caddyserver/caddy/v2.CustomVersion=FrankenPHP ${FRANKENPHP_VERSION} PHP ${LIBPHP_VERSION} Caddy'" -o "../../dist/${bin}"
  198. cd ../..
  199. if [ -d "${EMBED}" ]; then
  200. truncate -s 0 app.tar
  201. truncate -s 0 app_checksum.txt
  202. fi
  203. if type "upx" > /dev/null 2>&1 && [ -z "${DEBUG_SYMBOLS}" ] && [ -z "${NO_COMPRESS}" ]; then
  204. upx --best "dist/${bin}"
  205. fi
  206. "dist/${bin}" version
  207. if [ -n "${RELEASE}" ]; then
  208. gh release upload "v${FRANKENPHP_VERSION}" "dist/${bin}" --repo dunglas/frankenphp --clobber
  209. fi
  210. if [ -n "${CURRENT_REF}" ]; then
  211. git checkout "${CURRENT_REF}"
  212. fi