From 5a5ad237ccf2d50566a796cd0762c6a0a6b88cbf Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Fri, 17 Oct 2025 12:22:09 -0700 Subject: [PATCH 01/12] first successful build and run in simulator using in-repo test app --- package.json | 6 +- patches/jsc_android_collator_static.patch | 30 ++++++++ patches/jsc_android_define_bun_macros.patch | 15 ++++ patches/jsc_android_jit_guards.patch | 36 ++++++++++ patches/jsc_android_jsc_wait.patch | 22 ++++++ patches/jsc_android_log_version.patch | 29 ++++++++ patches/jsc_android_silence_unused.patch | 12 ++++ patches/jsc_android_timezone_validate.patch | 48 +++++++++++++ patches/jsc_android_uv_pri64.patch | 16 +++++ patches/jsc_android_wtf_sources.patch | 45 ++++++++++++ patches/jsc_async_frame_guard.patch | 16 +++++ patches/jsc_disable_api_tests.patch | 16 +++++ patches/jsc_fix_external_string_tests.patch | 40 +++++++++++ patches/jsc_guard_error_instance.patch | 58 ++++++++++++++++ patches/jsc_stub_dfg_abstract_heap.patch | 23 +++++++ scripts/compile/common.sh | 15 ++-- scripts/compile/icu.sh | 12 +++- scripts/compile/jsc.sh | 37 +++++----- scripts/download.sh | 43 +++++++++--- scripts/info.sh | 29 +++++--- scripts/patch.sh | 63 ++++++++--------- scripts/start.sh | 76 +++++++++++++++------ 22 files changed, 590 insertions(+), 97 deletions(-) create mode 100644 patches/jsc_android_collator_static.patch create mode 100644 patches/jsc_android_define_bun_macros.patch create mode 100644 patches/jsc_android_jit_guards.patch create mode 100644 patches/jsc_android_jsc_wait.patch create mode 100644 patches/jsc_android_log_version.patch create mode 100644 patches/jsc_android_silence_unused.patch create mode 100644 patches/jsc_android_timezone_validate.patch create mode 100644 patches/jsc_android_uv_pri64.patch create mode 100644 patches/jsc_android_wtf_sources.patch create mode 100644 patches/jsc_async_frame_guard.patch create mode 100644 patches/jsc_disable_api_tests.patch create mode 100644 patches/jsc_fix_external_string_tests.patch create mode 100644 patches/jsc_guard_error_instance.patch create mode 100644 patches/jsc_stub_dfg_abstract_heap.patch diff --git a/package.json b/package.json index 3abd6d9c..1c0fbbdc 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,10 @@ "start": "./scripts/start.sh" }, "config": { - "webkitGTK": "2.26.4", - "chromiumICUCommit": "64e5d7d43a1ff205e3787ab6150bbc1a1837332b" + "bunWebKitRepo": "https://github.com/oven-sh/WebKit.git", + "bunWebKitCommit": "6d0f3aac0b817cc01a846b3754b21271adedac12", + "icuRelease": "release-74-2", + "icuArchive": "icu4c-74_2-src.tgz" }, "devDependencies": { "commander": "^12.1.0", diff --git a/patches/jsc_android_collator_static.patch b/patches/jsc_android_collator_static.patch new file mode 100644 index 00000000..215afffa --- /dev/null +++ b/patches/jsc_android_collator_static.patch @@ -0,0 +1,30 @@ +diff --git a/webkit/Source/WTF/wtf/unicode/CollatorDefault.cpp b/webkit/Source/WTF/wtf/unicode/CollatorDefault.cpp +index 9a45990fa7..b709f02052 100644 +--- a/webkit/Source/WTF/wtf/unicode/CollatorDefault.cpp ++++ b/webkit/Source/WTF/wtf/unicode/CollatorDefault.cpp +@@ -27,13 +27,15 @@ + */ + + #include "config.h" ++#include ++#include + #include + + #if UCONFIG_NO_COLLATION + + namespace WTF { + +-int Collator::collate(StringView a, StringView b) const ++int Collator::collate(StringView a, StringView b) + { + unsigned commonLength = std::min(a.length(), b.length()); + for (unsigned i = 0; i < commonLength; ++i) { +@@ -51,7 +53,7 @@ int Collator::collate(StringView a, StringView b) const + return 0; + } + +-int Collator::collate(const char8_t* a, const char8_t* b) const ++int Collator::collate(const char8_t* a, const char8_t* b) + { + return collate(String::fromUTF8(byteCast(a)), String::fromUTF8(byteCast(b))); + } diff --git a/patches/jsc_android_define_bun_macros.patch b/patches/jsc_android_define_bun_macros.patch new file mode 100644 index 00000000..bef6648b --- /dev/null +++ b/patches/jsc_android_define_bun_macros.patch @@ -0,0 +1,15 @@ +diff --git a/webkit/Source/WTF/wtf/posix/OSAllocatorPOSIX.cpp b/webkit/Source/WTF/wtf/posix/OSAllocatorPOSIX.cpp +index 483d333b83..1612e84271 100644 +--- a/webkit/Source/WTF/wtf/posix/OSAllocatorPOSIX.cpp ++++ b/webkit/Source/WTF/wtf/posix/OSAllocatorPOSIX.cpp +@@ -63,6 +63,10 @@ + + #endif + ++#ifndef BUN_MACOSX ++#define BUN_MACOSX 0 ++#endif ++ + #ifdef MADV_DONTFORK + #define BUN_MADV_DONTFORK MADV_DONTFORK + #else diff --git a/patches/jsc_android_jit_guards.patch b/patches/jsc_android_jit_guards.patch new file mode 100644 index 00000000..abe8af33 --- /dev/null +++ b/patches/jsc_android_jit_guards.patch @@ -0,0 +1,36 @@ +diff --git a/webkit/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h b/webkit/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h +index 8b89733b83..9c1d93c23f 100644 +--- a/webkit/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h ++++ b/webkit/Source/JavaScriptCore/jit/JITInlineCacheGenerator.h +@@ -53,7 +53,11 @@ enum class AccessType : int8_t; + enum class CacheType : int8_t; + enum class JITType : uint8_t; + ++#if ENABLE(DFG_JIT) + using CompileTimeStructureStubInfo = Variant; ++#else ++using CompileTimeStructureStubInfo = Variant; ++#endif + + class JITInlineCacheGenerator { + protected: +diff --git a/webkit/Source/JavaScriptCore/jit/PCToCodeOriginMap.h b/webkit/Source/JavaScriptCore/jit/PCToCodeOriginMap.h +index 196e0516a9..572f6a4444 100644 +--- a/webkit/Source/JavaScriptCore/jit/PCToCodeOriginMap.h ++++ b/webkit/Source/JavaScriptCore/jit/PCToCodeOriginMap.h +@@ -41,6 +41,7 @@ namespace B3 { + class PCToOriginMap; + } + ++#if ENABLE(WEBASSEMBLY) + namespace Wasm { + class WasmOrigin { + MAKE_VALIDATED_REINTERPRET_CAST +@@ -59,6 +60,7 @@ public: + MAKE_VALIDATED_REINTERPRET_CAST_IMPL("WasmOrigin", WasmOrigin) + + } // namespace Wasm ++#endif + + class LinkBuffer; + class PCToCodeOriginMapBuilder; diff --git a/patches/jsc_android_jsc_wait.patch b/patches/jsc_android_jsc_wait.patch new file mode 100644 index 00000000..4c667da9 --- /dev/null +++ b/patches/jsc_android_jsc_wait.patch @@ -0,0 +1,22 @@ +diff --git a/webkit/Source/JavaScriptCore/jsc.cpp b/webkit/Source/JavaScriptCore/jsc.cpp +index 2b3b231e54..6b861fd1fe 100644 +--- a/webkit/Source/JavaScriptCore/jsc.cpp ++++ b/webkit/Source/JavaScriptCore/jsc.cpp +@@ -3649,7 +3649,7 @@ int main(int argc, char** argv) + WTF::fastDisableScavenger(); + fprintf(stdout, "\njs shell waiting for input to exit\n"); + fflush(stdout); +- getc(stdin); ++ (void)getc(stdin); + } + + #if OS(UNIX) +@@ -3658,7 +3658,7 @@ int main(int argc, char** argv) + fprintf(stdout, "\njs shell waiting for `kill -USR2 [pid]` to exit\n"); + fflush(stdout); + +- waitToExit.wait(); ++ (void)waitToExit.wait(); + + fprintf(stdout, "\njs shell exiting\n"); + fflush(stdout); diff --git a/patches/jsc_android_log_version.patch b/patches/jsc_android_log_version.patch new file mode 100644 index 00000000..483a6bb0 --- /dev/null +++ b/patches/jsc_android_log_version.patch @@ -0,0 +1,29 @@ +diff --git a/webkit/Source/JavaScriptCore/API/JSBase.cpp b/webkit/Source/JavaScriptCore/API/JSBase.cpp +index f0b01ec79a..1c9869b40b 100644 +--- a/webkit/Source/JavaScriptCore/API/JSBase.cpp ++++ b/webkit/Source/JavaScriptCore/API/JSBase.cpp +@@ -37,6 +37,12 @@ + #include "OpaqueJSString.h" + #include "SourceCode.h" + ++#if OS(ANDROID) ++#include ++#ifndef JSC_VERSION ++#define JSC_VERSION "unknown" ++#endif ++#endif + #if ENABLE(REMOTE_INSPECTOR) + #include "JSGlobalObjectInspectorController.h" + #endif +@@ -220,3 +226,11 @@ JSObjectRef JSGetMemoryUsageStatistics(JSContextRef ctx) + + return toRef(object); + } ++ ++#if OS(ANDROID) ++__attribute__((constructor)) ++static void logJSCVersion() ++{ ++ __android_log_print(ANDROID_LOG_INFO, "JavaScriptCore.Version", "%s", JSC_VERSION); ++} ++#endif diff --git a/patches/jsc_android_silence_unused.patch b/patches/jsc_android_silence_unused.patch new file mode 100644 index 00000000..4aa7ec07 --- /dev/null +++ b/patches/jsc_android_silence_unused.patch @@ -0,0 +1,12 @@ +diff --git a/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp b/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp +index 09b051775b..c1599e5c9d 100644 +--- a/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp ++++ b/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp +@@ -220,6 +220,7 @@ void JSPromise::fulfillWithNonPromise(JSGlobalObject* lexicalGlobalObject, JSVal + VM& vm = lexicalGlobalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + uint32_t flags = this->flags(); ++ UNUSED_PARAM(scope); + ASSERT_WITH_MESSAGE(!value.inherits(), "Promise fulfilled with exception"); + ASSERT_WITH_MESSAGE(!value.inherits(), "Promise fulfilled with another promise"); + diff --git a/patches/jsc_android_timezone_validate.patch b/patches/jsc_android_timezone_validate.patch new file mode 100644 index 00000000..e45b2cd1 --- /dev/null +++ b/patches/jsc_android_timezone_validate.patch @@ -0,0 +1,48 @@ +diff --git a/webkit/Source/JavaScriptCore/runtime/JSDateMath.cpp b/webkit/Source/JavaScriptCore/runtime/JSDateMath.cpp +index cbd32cefe0..16df1ec5a0 100644 +--- a/webkit/Source/JavaScriptCore/runtime/JSDateMath.cpp ++++ b/webkit/Source/JavaScriptCore/runtime/JSDateMath.cpp +@@ -502,6 +502,12 @@ static std::tuple> retrieveTimeZoneInformation() + getTimeZoneOverride(timeZoneID); + String canonical; + UErrorCode status = U_ZERO_ERROR; ++#if (defined(UCONFIG_NO_FORMATTING) && UCONFIG_NO_FORMATTING) || !defined(ucal_getCanonicalTimeZoneID) ++ if (!timeZoneID.isEmpty()) ++ canonical = String(timeZoneID.span()); ++ else ++ canonical = "UTC"_s; ++#else + if (timeZoneID.isEmpty()) { + status = callBufferProducingFunction(ucal_getHostTimeZone, timeZoneID); + ASSERT_UNUSED(status, U_SUCCESS(status)); +@@ -514,6 +520,7 @@ static std::tuple> retrieveTimeZoneInformation() + } + if (canonical.isNull() || isUTCEquivalent(canonical)) + canonical = "UTC"_s; ++#endif + + globalCache.get() = std::tuple { canonical.isolatedCopy(), WTFMove(timeZoneID), currentID }; + } +diff --git a/webkit/Source/WTF/wtf/DateMath.cpp b/webkit/Source/WTF/wtf/DateMath.cpp +index d4551c78ef..37b2ef6fc2 100644 +--- a/webkit/Source/WTF/wtf/DateMath.cpp ++++ b/webkit/Source/WTF/wtf/DateMath.cpp +@@ -1008,6 +1008,10 @@ String makeRFC2822DateString(unsigned dayOfWeek, unsigned day, unsigned month, u + + static std::optional> validateTimeZone(StringView timeZone) + { ++#if defined(UCONFIG_NO_FORMATTING) && UCONFIG_NO_FORMATTING ++ UNUSED_PARAM(timeZone); ++ return std::nullopt; ++#else + auto buffer = timeZone.upconvertedCharacters(); + const char16_t* characters = buffer; + Vector canonicalBuffer; +@@ -1015,6 +1019,7 @@ static std::optional> validateTimeZone(StringView timeZone) + if (!U_SUCCESS(status)) + return std::nullopt; + return canonicalBuffer; ++#endif + } + + bool isTimeZoneValid(StringView timeZone) diff --git a/patches/jsc_android_uv_pri64.patch b/patches/jsc_android_uv_pri64.patch new file mode 100644 index 00000000..684edd13 --- /dev/null +++ b/patches/jsc_android_uv_pri64.patch @@ -0,0 +1,16 @@ +diff --git a/webkit/Source/bmalloc/bmalloc/uv_get_constrained_memory.cpp b/webkit/Source/bmalloc/bmalloc/uv_get_constrained_memory.cpp +index e39afda511..48f46d76fc 100644 +--- a/webkit/Source/bmalloc/bmalloc/uv_get_constrained_memory.cpp ++++ b/webkit/Source/bmalloc/bmalloc/uv_get_constrained_memory.cpp +@@ -21,11 +21,12 @@ + + #include "BPlatform.h" + #include ++#include + + #if BOS(LINUX) + + #ifndef PRIu64 +-#define PRIu64 "lu" ++#define PRIu64 "llu" + #endif diff --git a/patches/jsc_android_wtf_sources.patch b/patches/jsc_android_wtf_sources.patch new file mode 100644 index 00000000..0d1c18a6 --- /dev/null +++ b/patches/jsc_android_wtf_sources.patch @@ -0,0 +1,45 @@ +diff --git a/webkit/Source/WTF/wtf/PlatformJSCOnly.cmake b/webkit/Source/WTF/wtf/PlatformJSCOnly.cmake +index 0ab58fe2a2..4b94606617 100644 +--- a/webkit/Source/WTF/wtf/PlatformJSCOnly.cmake ++++ b/webkit/Source/WTF/wtf/PlatformJSCOnly.cmake +@@ -36,8 +36,16 @@ else () + text/unix/TextBreakIteratorInternalICUUnix.cpp + + unix/LanguageUnix.cpp +- unix/LoggingUnix.cpp + ) ++ if (CMAKE_SYSTEM_NAME MATCHES "Android") ++ list(APPEND WTF_SOURCES ++ android/LoggingAndroid.cpp ++ ) ++ else () ++ list(APPEND WTF_SOURCES ++ unix/LoggingUnix.cpp ++ ) ++ endif () + if (WTF_OS_FUCHSIA) + list(APPEND WTF_SOURCES + fuchsia/CPUTimeFuchsia.cpp +@@ -97,6 +105,22 @@ elseif (CMAKE_SYSTEM_NAME MATCHES "Linux") + + unix/MemoryPressureHandlerUnix.cpp + ) ++ list(APPEND WTF_PUBLIC_HEADERS ++ linux/CurrentProcessMemoryStatus.h ++ linux/ProcessMemoryFootprint.h ++ ) ++elseif (CMAKE_SYSTEM_NAME MATCHES "Android") ++ list(APPEND WTF_SOURCES ++ linux/CurrentProcessMemoryStatus.cpp ++ linux/MemoryFootprintLinux.cpp ++ linux/RealTimeThreads.cpp ++ ++ unix/MemoryPressureHandlerUnix.cpp ++ ) ++ list(APPEND WTF_PUBLIC_HEADERS ++ linux/CurrentProcessMemoryStatus.h ++ linux/ProcessMemoryFootprint.h ++ ) + elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD") + list(APPEND WTF_SOURCES + generic/MemoryFootprintGeneric.cpp diff --git a/patches/jsc_async_frame_guard.patch b/patches/jsc_async_frame_guard.patch new file mode 100644 index 00000000..20435d3c --- /dev/null +++ b/patches/jsc_async_frame_guard.patch @@ -0,0 +1,16 @@ +diff --git a/webkit/Source/JavaScriptCore/runtime/StackFrame.h b/webkit/Source/JavaScriptCore/runtime/StackFrame.h +index b689e68d58..5578051a6b 100644 +--- a/webkit/Source/JavaScriptCore/runtime/StackFrame.h ++++ b/webkit/Source/JavaScriptCore/runtime/StackFrame.h +@@ -100,7 +100,11 @@ public: + + bool isAsyncFrameWithoutCodeBlock() const + { ++#if USE(BUN_JSC_ADDITIONS) + return isAsyncFrame() && !codeBlock(); ++#else ++ return false; ++#endif + } + + #if USE(BUN_JSC_ADDITIONS) diff --git a/patches/jsc_disable_api_tests.patch b/patches/jsc_disable_api_tests.patch new file mode 100644 index 00000000..f808ea81 --- /dev/null +++ b/patches/jsc_disable_api_tests.patch @@ -0,0 +1,16 @@ +diff --git a/webkit/Source/cmake/OptionsJSCOnly.cmake b/webkit/Source/cmake/OptionsJSCOnly.cmake +index f00830dd41..f9e131cfa3 100644 +--- a/webkit/Source/cmake/OptionsJSCOnly.cmake ++++ b/webkit/Source/cmake/OptionsJSCOnly.cmake +@@ -101,10 +101,8 @@ else () + endif () + WEBKIT_OPTION_END() + +-if (WIN32) ++if (NOT DEFINED ENABLE_API_TESTS) + set(ENABLE_API_TESTS OFF) +-else () +- set(ENABLE_API_TESTS ON) + endif () + + if (WTF_CPU_ARM OR WTF_CPU_MIPS) diff --git a/patches/jsc_fix_external_string_tests.patch b/patches/jsc_fix_external_string_tests.patch new file mode 100644 index 00000000..d13d8180 --- /dev/null +++ b/patches/jsc_fix_external_string_tests.patch @@ -0,0 +1,40 @@ +diff --git a/webkit/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp b/webkit/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp +index 72339a29d7..cfaac6b233 100644 +--- a/webkit/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp ++++ b/webkit/Tools/TestWebKitAPI/Tests/WTF/StringImpl.cpp +@@ -758,7 +758,7 @@ TEST(WTF, ExternalStringImplCreate8bit) + bool freeFunctionCalled = false; + + { +- auto external = ExternalStringImpl::create({ buffer, bufferStringLength }, [&freeFunctionCalled](ExternalStringImpl* externalStringImpl, void* buffer, unsigned bufferSize) mutable { ++ auto external = ExternalStringImpl::create({ buffer, bufferStringLength }, nullptr, [&freeFunctionCalled](void*, void*, unsigned) mutable { + freeFunctionCalled = true; + }); + +@@ -780,7 +780,7 @@ TEST(WTF, ExternalStringImplCreate16bit) + bool freeFunctionCalled = false; + + { +- auto external = ExternalStringImpl::create({ buffer, bufferStringLength }, [&freeFunctionCalled](ExternalStringImpl* externalStringImpl, void* buffer, unsigned bufferSize) mutable { ++ auto external = ExternalStringImpl::create({ buffer, bufferStringLength }, nullptr, [&freeFunctionCalled](void*, void*, unsigned) mutable { + freeFunctionCalled = true; + }); + +@@ -809,7 +809,7 @@ TEST(WTF, ExternalStringAtom) + bool freeFunctionCalled = false; + + { +- auto external = ExternalStringImpl::create({ buffer, bufferStringLength }, [&freeFunctionCalled](ExternalStringImpl* externalStringImpl, void* buffer, unsigned bufferSize) mutable { ++ auto external = ExternalStringImpl::create({ buffer, bufferStringLength }, nullptr, [&freeFunctionCalled](void*, void*, unsigned) mutable { + freeFunctionCalled = true; + }); + +@@ -845,7 +845,7 @@ TEST(WTF, ExternalStringToSymbol) + bool freeFunctionCalled = false; + + { +- auto external = ExternalStringImpl::create(buffer.span8(), [&freeFunctionCalled](ExternalStringImpl* externalStringImpl, void* buffer, unsigned bufferSize) mutable { ++ auto external = ExternalStringImpl::create(buffer.span8(), nullptr, [&freeFunctionCalled](void*, void*, unsigned) mutable { + freeFunctionCalled = true; + }); + diff --git a/patches/jsc_guard_error_instance.patch b/patches/jsc_guard_error_instance.patch new file mode 100644 index 00000000..7cb15732 --- /dev/null +++ b/patches/jsc_guard_error_instance.patch @@ -0,0 +1,58 @@ +diff --git a/webkit/Source/JavaScriptCore/runtime/ErrorInstance.cpp b/webkit/Source/JavaScriptCore/runtime/ErrorInstance.cpp +index 07999a9b28..e694388e2e 100644 +--- a/webkit/Source/JavaScriptCore/runtime/ErrorInstance.cpp ++++ b/webkit/Source/JavaScriptCore/runtime/ErrorInstance.cpp +@@ -108,6 +108,7 @@ String appendSourceToErrorMessage(CodeBlock* codeBlock, BytecodeIndex bytecodeIn + return appender(message, codeBlock->source().provider()->getRange(start, stop), type, ErrorInstance::FoundApproximateSource); + } + ++#if USE(BUN_JSC_ADDITIONS) + void ErrorInstance::setStackFrames(VM& vm, WTF::Vector&& stackFrames) + { + std::unique_ptr> stackTrace = makeUnique>(WTFMove(stackFrames)); +@@ -116,6 +117,7 @@ void ErrorInstance::setStackFrames(VM& vm, WTF::Vector&& stackFrames + m_stackTrace = WTFMove(stackTrace); + vm.writeBarrier(this); + } ++#endif + + size_t ErrorInstance::estimatedSize(JSCell* cell, VM& vm) + { +@@ -370,11 +372,16 @@ void ErrorInstance::computeErrorInfo(VM& vm, bool allocationAllowed) + UNUSED_PARAM(allocationAllowed); + + if (m_stackTrace && !m_stackTrace->isEmpty()) { +- auto& fn = vm.onComputeErrorInfo(); + WTF::String stackString; +- if (fn) { +- stackString = fn(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL, this->bunErrorData()); +- } else { ++ bool handled = false; ++#if USE(BUN_JSC_ADDITIONS) ++ auto& computeErrorInfo = vm.onComputeErrorInfo(); ++ if (computeErrorInfo) { ++ stackString = computeErrorInfo(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL, this->bunErrorData()); ++ handled = true; ++ } ++#endif ++ if (!handled) { + getLineColumnAndSource(vm, m_stackTrace.get(), m_lineColumn, m_sourceURL); + stackString = Interpreter::stackTraceAsString(vm, *m_stackTrace.get()); + } +@@ -394,13 +401,12 @@ bool ErrorInstance::materializeErrorInfoIfNeeded(VM& vm) + return false; + + #if USE(BUN_JSC_ADDITIONS) +- +- auto& fn = vm.onComputeErrorInfoJSValue(); +- if (fn && m_stackTrace && !m_stackTrace->isEmpty()) { ++ auto& computeErrorInfoJSValue = vm.onComputeErrorInfoJSValue(); ++ if (computeErrorInfoJSValue && m_stackTrace && !m_stackTrace->isEmpty()) { + m_errorInfoMaterialized = true; + DeferGCForAWhile deferGC(vm); + +- JSValue stack = fn(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL, this, this->bunErrorData()); ++ JSValue stack = computeErrorInfoJSValue(vm, *m_stackTrace.get(), m_lineColumn.line, m_lineColumn.column, m_sourceURL, this, this->bunErrorData()); + + { + Locker locker { cellLock() }; diff --git a/patches/jsc_stub_dfg_abstract_heap.patch b/patches/jsc_stub_dfg_abstract_heap.patch new file mode 100644 index 00000000..d4027a2a --- /dev/null +++ b/patches/jsc_stub_dfg_abstract_heap.patch @@ -0,0 +1,23 @@ +diff --git a/webkit/Source/JavaScriptCore/domjit/DOMJITEffect.h b/webkit/Source/JavaScriptCore/domjit/DOMJITEffect.h +index 6ba85116d1..91c6f4dc26 100644 +--- a/webkit/Source/JavaScriptCore/domjit/DOMJITEffect.h ++++ b/webkit/Source/JavaScriptCore/domjit/DOMJITEffect.h +@@ -25,7 +25,18 @@ + + #pragma once + ++#if ENABLE(DFG_JIT) + #include "DFGAbstractHeap.h" ++#else ++namespace JSC { ++namespace DFG { ++enum AbstractHeapKind : unsigned { ++ Heap, ++ InvalidAbstractHeap ++}; ++} ++} ++#endif + #include "DOMJITHeapRange.h" + + WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN diff --git a/scripts/compile/common.sh b/scripts/compile/common.sh index 7eb3b3d5..4310b316 100755 --- a/scripts/compile/common.sh +++ b/scripts/compile/common.sh @@ -53,6 +53,7 @@ PLATFORM_CFLAGS_arm="" PLATFORM_LDFLAGS_arm="" JNI_ARCH_arm=armeabi-v7a +PLATFORM_CFLAGS_arm64="-mtune=cortex-a77" PLATFORM_LDFLAGS_arm64="" JNI_ARCH_arm64=arm64-v8a @@ -74,7 +75,7 @@ JNI_ARCH=${!var} # options flags # INTL -SWITCH_COMMON_CFLAGS_INTL_OFF="-DUCONFIG_NO_COLLATION=1 -DUCONFIG_NO_FORMATTING=1" +SWITCH_COMMON_CFLAGS_INTL_OFF="" SWITCH_BUILD_WEBKIT_OPTIONS_INTL_OFF="--no-intl" SWITCH_BUILD_WEBKIT_OPTIONS_INTL_ON="--intl" @@ -82,6 +83,7 @@ SWITCH_BUILD_WEBKIT_OPTIONS_INTL_ON="--intl" fix_zero_value_flag "INTL" process_switch_options "INTL" + # checks err=false if ! [[ $ANDROID_API_FOR_ABI_32 ]]; then echo "set ANDROID_API_FOR_ABI_32 to the minimum supported Android platform version for arm and x86 (e.g. 16)"; err=true; fi @@ -99,8 +101,8 @@ DEBUG_SYMBOL_LEVEL="-g2" if [[ "$BUILD_TYPE" = "Release" ]] then FRAME_POINTER_FLAG="-fomit-frame-pointer" - CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -Oz -flto=full" - ICU_CFLAGS_BUILD_TYPE="-Oz" + CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -Oz -flto=thin" + ICU_CFLAGS_BUILD_TYPE="-Oz -flto=thin" else FRAME_POINTER_FLAG="-fno-omit-frame-pointer" CFLAGS_BUILD_TYPE="" @@ -115,6 +117,7 @@ COMMON_LDFLAGS=" \ -Wl,--exclude-libs,libgcc.a \ -Wl,--no-undefined \ -Wl,-z,max-page-size=16384 \ +-flto=thin \ " COMMON_CFLAGS=" \ @@ -141,5 +144,9 @@ ICU_LDFLAGS="$COMMON_LDFLAGS \ $PLATFORM_LDFLAGS \ " -JSC_LDFLAGS="$COMMON_LDFLAGS" +JSC_LDFLAGS="$COMMON_LDFLAGS -llog" JSC_CFLAGS="$COMMON_CFLAGS -Wno-implicit-const-int-float-conversion -DU_STATIC_IMPLEMENTATION=1 -DU_SHOW_CPLUSPLUS_API=0 -DTRUE=1 -DFALSE=0" + +if [[ -n "$JSC_VERSION" ]]; then + JSC_CFLAGS="$JSC_CFLAGS -DJSC_VERSION=\\\"${JSC_VERSION}\\\"" +fi diff --git a/scripts/compile/icu.sh b/scripts/compile/icu.sh index 1696958d..1029d7cf 100755 --- a/scripts/compile/icu.sh +++ b/scripts/compile/icu.sh @@ -26,8 +26,16 @@ else BUILD_TYPE_CONFIG="--enable-debug=yes" fi -ICU_DATA_FILTER_FILE="${TARGETDIR}/icu/filters/android.json" \ -$TARGETDIR/icu/source/configure --prefix=${INSTALL_DIR} \ +ICU_FILTER_FILE="${TARGETDIR}/icu/filters/android.json" +if [[ -f "$ICU_FILTER_FILE" ]]; then + echo "Using ICU data filter: ${ICU_FILTER_FILE}" + CONFIGURE_PREFIX=(env ICU_DATA_FILTER_FILE="$ICU_FILTER_FILE") +else + echo "ICU data filter not found at ${ICU_FILTER_FILE}; building without data pruning" + CONFIGURE_PREFIX=() +fi + +"${CONFIGURE_PREFIX[@]}" $TARGETDIR/icu/source/configure --prefix=${INSTALL_DIR} \ $BUILD_TYPE_CONFIG \ --host=$CROSS_COMPILE_PLATFORM \ --enable-static=yes \ diff --git a/scripts/compile/jsc.sh b/scripts/compile/jsc.sh index d8eb4716..6f7c5ca3 100755 --- a/scripts/compile/jsc.sh +++ b/scripts/compile/jsc.sh @@ -41,25 +41,16 @@ else BUILD_TYPE_FLAGS="-DDEBUG_FISSION=OFF" fi -if [[ "$ARCH_NAME" = "i686" ]] -then - JSC_FEATURE_FLAGS=" \ - -DENABLE_JIT=OFF \ - -DENABLE_C_LOOP=ON \ - " -else - JSC_FEATURE_FLAGS=" \ - -DENABLE_JIT=ON \ - -DENABLE_C_LOOP=OFF \ - " -fi +JSC_FEATURE_FLAGS=" \ + -DENABLE_JIT=OFF \ + -DENABLE_C_LOOP=ON \ + -DENABLE_WEBASSEMBLY=OFF \ +" $TARGETDIR/webkit/Tools/Scripts/build-webkit \ --jsc-only \ $BUILD_TYPE_CONFIG \ - --jit \ "$SWITCH_BUILD_WEBKIT_OPTIONS_INTL" \ - --no-webassembly \ --no-xslt \ --no-netscape-plugin-api \ --no-tools \ @@ -67,9 +58,14 @@ $TARGETDIR/webkit/Tools/Scripts/build-webkit \ -DUSE_LD_GOLD=OFF \ -DANDROID_ABI=${JNI_ARCH} \ -DANDROID_PLATFORM=${ANDROID_API} \ + -DANDROID_TARGET_SDK_VERSION=${ANDROID_TARGET_API:-${ANDROID_API}} \ + -DENABLE_JIT=OFF \ + -DENABLE_WEBASSEMBLY=OFF \ -DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=BOTH \ -DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=BOTH \ -DICU_ROOT=${TARGETDIR}/icu/${CROSS_COMPILE_PLATFORM}-${FLAVOR}/prebuilts \ + -DICU_INCLUDE_DIR=${TARGETDIR}/icu/${CROSS_COMPILE_PLATFORM}-${FLAVOR}/prebuilts/include \ + -DENABLE_API_TESTS=OFF \ -DCMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS} $COMMON_CXXFLAGS $CMAKE_CXX_FLAGS' \ -DCMAKE_C_FLAGS='${CMAKE_C_FLAGS} $CMAKE_CXX_FLAGS' \ -DCMAKE_C_FLAGS_DEBUG='${DEBUG_SYMBOL_LEVEL}' \ @@ -89,9 +85,16 @@ $TARGETDIR/webkit/Tools/Scripts/build-webkit \ mkdir -p $INSTALL_UNSTRIPPED_DIR_I18N/$JNI_ARCH mkdir -p $INSTALL_DIR_I18N/$JNI_ARCH -cp $TARGETDIR/webkit/WebKitBuild/$BUILD_TYPE/lib/libjsc.so $INSTALL_UNSTRIPPED_DIR_I18N/$JNI_ARCH -cp $TARGETDIR/webkit/WebKitBuild/$BUILD_TYPE/lib/libjsc.so $INSTALL_DIR_I18N/$JNI_ARCH -$TOOLCHAIN_DIR/bin/llvm-strip $INSTALL_DIR_I18N/$JNI_ARCH/libjsc.so +BUILT_LIB_PATH=$TARGETDIR/webkit/WebKitBuild/JSCOnly/$BUILD_TYPE/lib/libJavaScriptCore.so +OUTPUT_LIB_NAME=libjsc.so +if [[ ! -f $BUILT_LIB_PATH ]] +then + echo "Failed to find expected JavaScriptCore shared library at $BUILT_LIB_PATH" >&2 + exit 1 +fi +cp $BUILT_LIB_PATH $INSTALL_UNSTRIPPED_DIR_I18N/$JNI_ARCH/$OUTPUT_LIB_NAME +cp $BUILT_LIB_PATH $INSTALL_DIR_I18N/$JNI_ARCH/$OUTPUT_LIB_NAME +$TOOLCHAIN_DIR/bin/llvm-strip $INSTALL_DIR_I18N/$JNI_ARCH/$OUTPUT_LIB_NAME mv $TARGETDIR/webkit/WebKitBuild $TARGETDIR/webkit/${CROSS_COMPILE_PLATFORM}-${FLAVOR} mkdir -p $INSTALL_CPPRUNTIME_DIR/$JNI_ARCH diff --git a/scripts/download.sh b/scripts/download.sh index e0b92980..d94fa9eb 100755 --- a/scripts/download.sh +++ b/scripts/download.sh @@ -1,13 +1,40 @@ #!/bin/bash -e +ROOTDIR=$PWD + # download sources -rm -rf $PWD/build -TARGET_DIR=$PWD/build/download -REPO_URL="https://github.com/WebKit/WebKit.git" +rm -rf "$ROOTDIR/build" +TARGET_DIR="$ROOTDIR/build/download" +WEBKIT_DIR="$TARGET_DIR/webkit" + +REPO_URL="${npm_package_config_bunWebKitRepo:-https://github.com/oven-sh/WebKit.git}" +WEBKIT_COMMIT="${npm_package_config_bunWebKitCommit}" +ICU_RELEASE="${npm_package_config_icuRelease}" +ICU_ARCHIVE="${npm_package_config_icuArchive}" + +if [[ -z "$WEBKIT_COMMIT" ]]; then + echo "Missing bunWebKitCommit in package.json config." >&2 + exit 1 +fi + +mkdir -p "$TARGET_DIR" +mkdir -p "$WEBKIT_DIR" -mkdir -p $TARGET_DIR -# FIXME: hardcoded branch because WebKitGTK 2.26 does not have tag -git clone $REPO_URL --branch 'webkitgtk/2.26' --depth 1 $TARGET_DIR/webkit +pushd "$WEBKIT_DIR" > /dev/null +git init +git remote add origin "$REPO_URL" +git fetch --depth 1 origin "$WEBKIT_COMMIT" +git checkout --detach FETCH_HEAD +popd > /dev/null -mkdir -p $TARGET_DIR/icu -curl "https://chromium.googlesource.com/chromium/deps/icu/+archive/${npm_package_config_chromiumICUCommit}.tar.gz" | tar xzf - -C $TARGET_DIR/icu +mkdir -p "$TARGET_DIR/icu" +if [[ -n "$ICU_RELEASE" ]]; then + if [[ -z "$ICU_ARCHIVE" ]]; then + echo "Missing icuArchive for release ${ICU_RELEASE}" >&2 + exit 1 + fi + curl -L "https://github.com/unicode-org/icu/releases/download/${ICU_RELEASE}/${ICU_ARCHIVE}" | tar xzf - --strip-components=1 -C "$TARGET_DIR/icu" +else + echo "No ICU release configured for download." >&2 + exit 1 +fi diff --git a/scripts/info.sh b/scripts/info.sh index fdf737c4..3ec6b58f 100755 --- a/scripts/info.sh +++ b/scripts/info.sh @@ -1,24 +1,31 @@ #!/bin/bash -e -WEBKITGTK_VERSION="${npm_package_config_webkitGTK}" -# URL="https://github.com/WebKit/WebKit/tree/webkitgtk-${WEBKITGTK_VERSION}" -# FIXME: hardcoded branch because WebKitGTK 2.26 does not have tag -URL="https://github.com/WebKit/WebKit/tree/webkitgtk/2.26" -RAW_URL="https://raw.githubusercontent.com/WebKit/WebKit/refs/heads/webkitgtk/2.26" ROOTDIR=$PWD +WEBKIT_REPO="${npm_package_config_bunWebKitRepo:-https://github.com/oven-sh/WebKit.git}" +WEBKIT_COMMIT="${npm_package_config_bunWebKitCommit}" + export REVISION=$(node -e "console.log(require('./package.json').version.split('.')[0])") -CONFIG=$(node -e "console.log(require('$ROOTDIR/package.json').config)") -APPLE_VERSION=$(wget -q -O - "${RAW_URL}/Source/WebCore/Configurations/Version.xcconfig" | grep 'MAJOR_VERSION\s=\|MINOR_VERSION\s=\|TINY_VERSION\s=\|MICRO_VERSION\s=\|NANO_VERSION\s=') +CONFIG=$(node -e "console.log(JSON.stringify(require('$ROOTDIR/package.json').config, null, 2))") + +if [[ -n "$WEBKIT_COMMIT" ]]; then + WEBKIT_URL="https://github.com/oven-sh/WebKit/tree/${WEBKIT_COMMIT}" + RAW_URL="https://raw.githubusercontent.com/oven-sh/WebKit/${WEBKIT_COMMIT}" + APPLE_VERSION=$(curl -fsSL "${RAW_URL}/Configurations/Version.xcconfig" | grep 'MAJOR_VERSION\s=\|MINOR_VERSION\s=\|TINY_VERSION\s=\|MICRO_VERSION\s=\|NANO_VERSION\s=' || true) +else + WEBKIT_URL="$WEBKIT_REPO" + APPLE_VERSION="unknown" +fi if [ -d "$INSTALL_DIR_I18N_false" ]; then - SIZE=$(du -ah $INSTALL_DIR_I18N_false) + SIZE=$(du -ah "$INSTALL_DIR_I18N_false") else SIZE="0" fi printf "\n\n\n\n\n\t\t\tRevision: \x1B[32m$REVISION\x1B[0m\n\n\n" -printf "WebKitGTK version:\n$WEBKITGTK_VERSION\n\n" -printf "Config:\n$CONFIG\n\n" -printf "AppleWebkit:\n$APPLE_VERSION\n\n" +printf "WebKit repository:\n%s @ %s\n\n" "$WEBKIT_REPO" "${WEBKIT_COMMIT:-unknown}" +printf "Upstream URL:\n%s\n\n" "$WEBKIT_URL" +printf "Config:\n%s\n\n" "$CONFIG" +printf "AppleWebKit version components:\n%s\n\n" "$APPLE_VERSION" printf "Size:\n$SIZE\n\n" diff --git a/scripts/patch.sh b/scripts/patch.sh index 82178529..16c1c42d 100755 --- a/scripts/patch.sh +++ b/scripts/patch.sh @@ -9,51 +9,48 @@ ICU_PATCHSET=( ) JSC_PATCHSET=( - # Basic build setup, e.g. libjsc.so output name - "jsc.patch" + # Android-specific logging for version identification + "jsc_android_log_version.patch" - # Feature toggles, e.g. disable unnecessary build or JIT settings - "jsc_features.patch" + # Ensure PRIu64 fallback matches 64-bit width on Android builds + "jsc_android_uv_pri64.patch" - # NDK does not support backtrace and execinfo.h - "jsc_fix_build_error_execinfo.patch" + # Fix Collator stub implementation for builds without ICU collation + "jsc_android_collator_static.patch" - # Fix build error which related to C++StringView - "jsc_fix_build_error_stringview.patch" + # Provide defaults for Bun-specific macros on non-Bun platforms + "jsc_android_define_bun_macros.patch" - # Integrate with Chromium ICU - "jsc_icu_integrate.patch" + # Update TestWTF to match new ExternalStringImpl signature + "jsc_fix_external_string_tests.patch" - # Support getting correct locale setting in Android system - "jsc_locale_support.patch" + # Disable building TestWebKitAPI when consuming JSC only + "jsc_disable_api_tests.patch" - # Will print current JSC version in adb log during initialization - "jsc_startup_log_version.patch" + # Provide stubs for DFG abstract heap when DFG JIT is disabled + "jsc_stub_dfg_abstract_heap.patch" - # Misc errors - "jsc_fix_build_error_miss_headers.patch" + # Guard async frame helper when Bun additions disabled + "jsc_async_frame_guard.patch" - # Workaround JIT crash on arm64, especially for Saumsung S7 Edge - "jsc_fix_arm64_jit_crash.patch" + # Guard Bun-specific error helpers when Bun additions are disabled + "jsc_guard_error_instance.patch" - # Intl default timezone with Android integration - "jsc_intl_timezone.patch" + # Silence unused parameter warnings in modules and promise helpers + "jsc_android_silence_unused.patch" - # Improve heap GC mechanism like iOS - "jsc_heap_gc_like_ios.patch" + # Include Android-specific WTF sources for logging and real-time threads + "jsc_android_wtf_sources.patch" - # GC concurrent issue potential fix - # https://trac.webkit.org/changeset/251307/webkit - "jsc_fix_concurrent_gc_issue.patch" -) + # Avoid unused-result warning in CLI wait helper + "jsc_android_jsc_wait.patch" -if [[ "$I18N" = false ]] -then - JSC_PATCHSET+=( - # Disable i18n for non-i18n build - "jsc_disable_icu.patch" - ) -fi + # Guard JIT helpers when DFG/WebAssembly are disabled + "jsc_android_jit_guards.patch" + + # Avoid ICU formatting dependencies when validating time zones + "jsc_android_timezone_validate.patch" +) ###################################################################################### # Patchset management end diff --git a/scripts/start.sh b/scripts/start.sh index f03eb65c..cebcc13f 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,9 +1,21 @@ #!/bin/bash -e -export ANDROID_API_FOR_ABI_32=24 -export ANDROID_API_FOR_ABI_64=24 +export ANDROID_API_FOR_ABI_32=29 +export ANDROID_API_FOR_ABI_64=29 +export ANDROID_TARGET_API=35 export ROOTDIR=$PWD +# Default toolchain locations when not provided. +DEFAULT_ANDROID_HOME="$HOME/Library/Android/sdk" +if [[ -z "$ANDROID_HOME" || ! -d "$ANDROID_HOME" ]]; then + export ANDROID_HOME="$DEFAULT_ANDROID_HOME" +fi + +DEFAULT_ANDROID_NDK="$ANDROID_HOME/ndk/28.2.13676358" +if [[ -z "$ANDROID_NDK" || ! -d "$ANDROID_NDK" ]]; then + export ANDROID_NDK="$DEFAULT_ANDROID_NDK" +fi + source $ROOTDIR/scripts/env.sh source $ROOTDIR/scripts/info.sh export JSC_VERSION=${npm_package_version} @@ -17,27 +29,47 @@ patchAndMakeICU() { ICU_VERSION_MAJOR="$(awk '/ICU_VERSION_MAJOR_NUM/ {print $3}' $TARGETDIR/icu/source/common/unicode/uvernum.h)" printf "ICU version: ${ICU_VERSION_MAJOR}\n" $SCRIPT_DIR/patch.sh icu - rm -rf $TARGETDIR/icu/host mkdir -p $TARGETDIR/icu/host cd $TARGETDIR/icu/host if [[ "$BUILD_TYPE" = "Release" ]] then - CFLAGS="-Os" + CFLAGS="-Os -flto=thin" + CXXFLAGS="--std=c++11 -flto=thin" + LDFLAGS="-flto=thin" else CFLAGS="-g2" + CXXFLAGS="--std=c++11" + LDFLAGS="" + fi + + ICU_FILTER_FILE="${TARGETDIR}/icu/filters/android.json" + local CONFIG_ENV=(env "CFLAGS=$CFLAGS" "CXXFLAGS=$CXXFLAGS") + if [[ -n "$LDFLAGS" ]]; then + CONFIG_ENV+=("LDFLAGS=$LDFLAGS") fi - ICU_DATA_FILTER_FILE="${TARGETDIR}/icu/filters/android.json" \ - $TARGETDIR/icu/source/runConfigureICU Linux \ - --prefix=$PWD/prebuilts \ - CFLAGS="$CFLAGS" \ - CXXFLAGS="--std=c++11" \ - --disable-tests \ - --disable-samples \ - --disable-layout \ - --disable-layoutex + if [[ -f "$ICU_FILTER_FILE" ]]; then + printf "Using ICU data filter: %s\n" "$ICU_FILTER_FILE" + ICU_DATA_FILTER_FILE="$ICU_FILTER_FILE" \ + "${CONFIG_ENV[@]}" \ + $TARGETDIR/icu/source/runConfigureICU Linux \ + --prefix=$PWD/prebuilts \ + --disable-tests \ + --disable-samples \ + --disable-layout \ + --disable-layoutex + else + printf "ICU data filter not found at %s, building without data pruning\n" "$ICU_FILTER_FILE" + "${CONFIG_ENV[@]}" \ + $TARGETDIR/icu/source/runConfigureICU Linux \ + --prefix=$PWD/prebuilts \ + --disable-tests \ + --disable-samples \ + --disable-layout \ + --disable-layoutex + fi make -j5 cd $ROOTDIR @@ -98,13 +130,17 @@ copyHeaders() { cp -Rf $TARGETDIR/webkit/Source/JavaScriptCore/API/*.h ${distDir}/include } -export I18N=false -prep -compile - -export I18N=true -prep -compile +if [[ "${SKIP_NO_INTL}" != "1" ]]; then + export I18N=false + prep + compile +fi + +if [[ "${SKIP_INTL}" != "1" ]]; then + export I18N=true + prep + compile +fi printf "\n\n\t\t===================== create stripped distributions =====================\n\n" export DISTDIR=${ROOTDIR}/dist From ea4064811b84c2b36884cd9a46a2e62d96733140 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Fri, 17 Oct 2025 20:59:55 -0700 Subject: [PATCH 02/12] adjust optimization and C++ flags. -Oz produces a 2MB smaller intl binary, but without vectorization (helped by the -O2 inlining), the interpreter hot path might not stay hot in the CPU caches. if using JSC on Android, users probably have a Very Good Reason(tm), but I'm not sure which they care about more: app download size or performance per watt efficiency. --- patches/jsc_android_silence_unused.patch | 74 ++++++++++++++++++++++-- scripts/compile/common.sh | 5 +- scripts/start.sh | 7 ++- 3 files changed, 77 insertions(+), 9 deletions(-) diff --git a/patches/jsc_android_silence_unused.patch b/patches/jsc_android_silence_unused.patch index 4aa7ec07..94799666 100644 --- a/patches/jsc_android_silence_unused.patch +++ b/patches/jsc_android_silence_unused.patch @@ -1,12 +1,78 @@ +diff --git a/webkit/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp b/webkit/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp +index 2e2203099e..9c8917096d 100644 +--- a/webkit/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp ++++ b/webkit/Source/JavaScriptCore/runtime/JSModuleNamespaceObject.cpp +@@ -45,6 +45,7 @@ void JSModuleNamespaceObject::finishCreation(JSGlobalObject* globalObject, Abstr + { + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); ++ UNUSED_PARAM(shouldPreventExtensions); + Base::finishCreation(vm); + ASSERT(inherits(info())); + +@@ -208,6 +209,9 @@ bool JSModuleNamespaceObject::put(JSCell* cell, JSGlobalObject* globalObject, Pr + { + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); ++ UNUSED_PARAM(cell); ++ UNUSED_PARAM(propertyName); ++ UNUSED_PARAM(value); + + #if USE(BUN_JSC_ADDITIONS) + auto* thisObject = jsCast(cell); +diff --git a/webkit/Source/JavaScriptCore/runtime/JSModuleRecord.cpp b/webkit/Source/JavaScriptCore/runtime/JSModuleRecord.cpp +index 24cea00a3f..41ccae3c5e 100644 +--- a/webkit/Source/JavaScriptCore/runtime/JSModuleRecord.cpp ++++ b/webkit/Source/JavaScriptCore/runtime/JSModuleRecord.cpp +@@ -63,18 +63,18 @@ JSModuleRecord::JSModuleRecord(VM& vm, Structure* structure, const Identifier& m + { + } + +-#if USE(BUN_JSC_ADDITIONS) + size_t JSModuleRecord::estimatedSize(JSCell* cell, VM& vm) + { +- const auto& thisObject = jsCast(cell); + size_t size = Base::estimatedSize(cell, vm); ++#if USE(BUN_JSC_ADDITIONS) ++ const auto& thisObject = jsCast(cell); + const SourceCode& sourceCode = thisObject->sourceCode(); + StringView view = sourceCode.provider() ? sourceCode.provider()->source() : StringView(); + size += view.length() * (view.is8Bit() ? sizeof(Latin1Character) : sizeof(UChar)); + size += sourceCode.memoryCost(); ++#endif + return size; + } +-#endif + void JSModuleRecord::destroy(JSCell* cell) + { + JSModuleRecord* thisObject = static_cast(cell); diff --git a/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp b/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp -index 09b051775b..c1599e5c9d 100644 +index 09b051775b..a0a4a6b10f 100644 --- a/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp +++ b/webkit/Source/JavaScriptCore/runtime/JSPromise.cpp -@@ -220,6 +220,7 @@ void JSPromise::fulfillWithNonPromise(JSGlobalObject* lexicalGlobalObject, JSVal +@@ -219,6 +219,7 @@ void JSPromise::fulfillWithNonPromise(JSGlobalObject* lexicalGlobalObject, JSVal + { VM& vm = lexicalGlobalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - uint32_t flags = this->flags(); + UNUSED_PARAM(scope); + uint32_t flags = this->flags(); ASSERT_WITH_MESSAGE(!value.inherits(), "Promise fulfilled with exception"); ASSERT_WITH_MESSAGE(!value.inherits(), "Promise fulfilled with another promise"); - +diff --git a/webkit/Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp b/webkit/Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp +index be5602f0b7..2f26538268 100644 +--- a/webkit/Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp ++++ b/webkit/Source/JavaScriptCore/runtime/ModuleProgramExecutable.cpp +@@ -37,10 +37,11 @@ ModuleProgramExecutable::ModuleProgramExecutable(JSGlobalObject* globalObject, c + { + SourceProviderSourceType sourceType = source.provider()->sourceType(); + ASSERT(sourceType == SourceProviderSourceType::Module +- #if USE(BUN_JSC_ADDITIONS) ++#if USE(BUN_JSC_ADDITIONS) + || sourceType == SourceProviderSourceType::BunTranspiledModule +- #endif ++#endif + ); ++ UNUSED_PARAM(sourceType); + VM& vm = globalObject->vm(); + if (vm.typeProfiler() || vm.controlFlowProfiler()) + vm.functionHasExecutedCache()->insertUnexecutedRange(sourceID(), typeProfilingStartOffset(), typeProfilingEndOffset()); diff --git a/scripts/compile/common.sh b/scripts/compile/common.sh index 4310b316..86aa63c3 100755 --- a/scripts/compile/common.sh +++ b/scripts/compile/common.sh @@ -101,8 +101,8 @@ DEBUG_SYMBOL_LEVEL="-g2" if [[ "$BUILD_TYPE" = "Release" ]] then FRAME_POINTER_FLAG="-fomit-frame-pointer" - CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -Oz -flto=thin" - ICU_CFLAGS_BUILD_TYPE="-Oz -flto=thin" + CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -O2 -flto=thin -fvectorize -fslp-vectorize -funroll-loops" + ICU_CFLAGS_BUILD_TYPE="-O2 -flto=thin -fvectorize -fslp-vectorize -funroll-loops" else FRAME_POINTER_FLAG="-fno-omit-frame-pointer" CFLAGS_BUILD_TYPE="" @@ -136,6 +136,7 @@ $CFLAGS_BUILD_TYPE \ " COMMON_CXXFLAGS=" \ +-std=c++2b \ " ICU_CFLAGS="$COMMON_CFLAGS $PLATFORM_CFLAGS $ICU_CFLAGS_BUILD_TYPE" diff --git a/scripts/start.sh b/scripts/start.sh index cebcc13f..84427215 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -35,12 +35,13 @@ patchAndMakeICU() { if [[ "$BUILD_TYPE" = "Release" ]] then - CFLAGS="-Os -flto=thin" - CXXFLAGS="--std=c++11 -flto=thin" + local OPT_FLAGS="-O2 -flto=thin -fvectorize -fslp-vectorize -funroll-loops" + CFLAGS="$OPT_FLAGS" + CXXFLAGS="-std=c++2b $OPT_FLAGS" LDFLAGS="-flto=thin" else CFLAGS="-g2" - CXXFLAGS="--std=c++11" + CXXFLAGS="-std=c++2b" LDFLAGS="" fi From 4b4310fa63bf113b96b3402d608106f7b6676ec2 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Sun, 19 Oct 2025 11:36:44 -0700 Subject: [PATCH 03/12] Update scripts/start.sh Co-authored-by: Kudo Chien --- scripts/start.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/start.sh b/scripts/start.sh index 84427215..3f8b22dc 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -1,7 +1,7 @@ #!/bin/bash -e -export ANDROID_API_FOR_ABI_32=29 -export ANDROID_API_FOR_ABI_64=29 +export ANDROID_API_FOR_ABI_32=24 +export ANDROID_API_FOR_ABI_64=24 export ANDROID_TARGET_API=35 export ROOTDIR=$PWD From bf0a760fdaa07a14e020c1b794baac4401ad7f9a Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Sun, 19 Oct 2025 12:15:21 -0700 Subject: [PATCH 04/12] clang bug in NDK 27 makes loop vectorization pass error. disable the warning (to about warnings as errors killing the build unnecessarily), but also disable loop vectorization options until NDK can be bumped. downgrade to c++20 to align with React Native constraints. --- lib/jsc-android/build.gradle | 4 ++++ scripts/compile/all.sh | 14 ++++++------ scripts/compile/common.sh | 11 +++++----- scripts/compile/jsc.sh | 42 +++++++++++++++++++----------------- scripts/start.sh | 6 +++--- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/lib/jsc-android/build.gradle b/lib/jsc-android/build.gradle index fab7dbb2..7e71b48c 100644 --- a/lib/jsc-android/build.gradle +++ b/lib/jsc-android/build.gradle @@ -28,6 +28,10 @@ android { versionCode 1 versionName "1.0" + ndk { + abiFilters 'arm64-v8a', 'x86_64' + } + externalNativeBuild { cmake { arguments '-DANDROID_STL=c++_shared', diff --git a/scripts/compile/all.sh b/scripts/compile/all.sh index 1a6e29c0..5c5bc744 100755 --- a/scripts/compile/all.sh +++ b/scripts/compile/all.sh @@ -15,12 +15,14 @@ compile_arch() { } compile() { - for arch in arm x86 - do - export ANDROID_API=$ANDROID_API_FOR_ABI_32 - export JSC_ARCH=$arch - compile_arch - done + if [[ "${INCLUDE_32_BIT_ABIS:-0}" == "1" ]]; then + for arch in arm x86 + do + export ANDROID_API=$ANDROID_API_FOR_ABI_32 + export JSC_ARCH=$arch + compile_arch + done + fi for arch in arm64 x86_64 do diff --git a/scripts/compile/common.sh b/scripts/compile/common.sh index 86aa63c3..7b7f43a6 100755 --- a/scripts/compile/common.sh +++ b/scripts/compile/common.sh @@ -86,7 +86,7 @@ process_switch_options "INTL" # checks err=false -if ! [[ $ANDROID_API_FOR_ABI_32 ]]; then echo "set ANDROID_API_FOR_ABI_32 to the minimum supported Android platform version for arm and x86 (e.g. 16)"; err=true; fi +if [[ "${INCLUDE_32_BIT_ABIS:-0}" == "1" ]] && ! [[ $ANDROID_API_FOR_ABI_32 ]]; then echo "set ANDROID_API_FOR_ABI_32 to the minimum supported Android platform version for arm and x86 (e.g. 16)"; err=true; fi if ! [[ $ANDROID_API_FOR_ABI_64 ]]; then echo "set ANDROID_API_FOR_ABI_64 to the minimum supported Android platform version for arm64 and x86_64 (e.g. 21)"; err=true; fi if ! [[ $FLAVOR ]]; then echo "set FLAVOR to the name of the flavor"; err=true; fi if ! [[ $CROSS_COMPILE_PLATFORM ]]; then echo "set JSC_ARCH to one of {arm,arm64,x86,x86_64}"; err=true; fi @@ -101,8 +101,8 @@ DEBUG_SYMBOL_LEVEL="-g2" if [[ "$BUILD_TYPE" = "Release" ]] then FRAME_POINTER_FLAG="-fomit-frame-pointer" - CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -O2 -flto=thin -fvectorize -fslp-vectorize -funroll-loops" - ICU_CFLAGS_BUILD_TYPE="-O2 -flto=thin -fvectorize -fslp-vectorize -funroll-loops" + CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -O2 -flto=thin" + ICU_CFLAGS_BUILD_TYPE="-O2 -flto=thin" else FRAME_POINTER_FLAG="-fno-omit-frame-pointer" CFLAGS_BUILD_TYPE="" @@ -132,15 +132,16 @@ $FRAME_POINTER_FLAG \ -DCUSTOMIZE_REACT_NATIVE \ $SWITCH_COMMON_CFLAGS_INTL \ $CFLAGS_BUILD_TYPE \ +-Wno-pass-failed=loop-vectorize \ -D__ANDROID_MIN_SDK_VERSION__=${ANDROID_API} \ " COMMON_CXXFLAGS=" \ --std=c++2b \ +-std=c++20 \ " ICU_CFLAGS="$COMMON_CFLAGS $PLATFORM_CFLAGS $ICU_CFLAGS_BUILD_TYPE" -ICU_CXXFLAGS="$COMMON_CXXFLAGS $ICU_CFLAGS $ICU_CFLAGS_BUILD_TYPE" +ICU_CXXFLAGS="$COMMON_CXXFLAGS $ICU_CFLAGS" ICU_LDFLAGS="$COMMON_LDFLAGS \ $PLATFORM_LDFLAGS \ " diff --git a/scripts/compile/jsc.sh b/scripts/compile/jsc.sh index 6f7c5ca3..302ac49e 100755 --- a/scripts/compile/jsc.sh +++ b/scripts/compile/jsc.sh @@ -10,20 +10,6 @@ rm -rf $TARGETDIR/webkit/$CROSS_COMPILE_PLATFORM-${FLAVOR} rm -rf $TARGETDIR/webkit/WebKitBuild cd $TARGETDIR/webkit/Tools/Scripts -CMAKE_CXX_FLAGS=" \ -$SWITCH_JSC_CFLAGS_COMPAT \ -$JSC_CFLAGS \ -$PLATFORM_CFLAGS \ -" - -CMAKE_LD_FLAGS=" \ --latomic \ --lm \ --lc++_shared \ -$JSC_LDFLAGS \ -$PLATFORM_LDFLAGS \ -" - ARCH_NAME_PLATFORM_arm="armv7-a" ARCH_NAME_PLATFORM_arm64="aarch64" ARCH_NAME_PLATFORM_x86="i686" @@ -31,6 +17,13 @@ ARCH_NAME_PLATFORM_x86_64="x86_64" var="ARCH_NAME_PLATFORM_$JSC_ARCH" export ARCH_NAME=${!var} +BASE_C_FLAGS="$COMMON_CFLAGS $PLATFORM_CFLAGS" +BASE_CXX_FLAGS="$COMMON_CXXFLAGS $BASE_C_FLAGS" +BASE_LD_FLAGS="-latomic -lm -lc++_shared $JSC_LDFLAGS $PLATFORM_LDFLAGS" + +export CFLAGS="$BASE_C_FLAGS" +export CXXFLAGS="$BASE_CXX_FLAGS" +export LDFLAGS="$BASE_LD_FLAGS" if [[ "$BUILD_TYPE" = "Release" ]] then @@ -50,6 +43,7 @@ JSC_FEATURE_FLAGS=" \ $TARGETDIR/webkit/Tools/Scripts/build-webkit \ --jsc-only \ $BUILD_TYPE_CONFIG \ + --no-fatal-warnings \ "$SWITCH_BUILD_WEBKIT_OPTIONS_INTL" \ --no-xslt \ --no-netscape-plugin-api \ @@ -66,12 +60,20 @@ $TARGETDIR/webkit/Tools/Scripts/build-webkit \ -DICU_ROOT=${TARGETDIR}/icu/${CROSS_COMPILE_PLATFORM}-${FLAVOR}/prebuilts \ -DICU_INCLUDE_DIR=${TARGETDIR}/icu/${CROSS_COMPILE_PLATFORM}-${FLAVOR}/prebuilts/include \ -DENABLE_API_TESTS=OFF \ - -DCMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS} $COMMON_CXXFLAGS $CMAKE_CXX_FLAGS' \ - -DCMAKE_C_FLAGS='${CMAKE_C_FLAGS} $CMAKE_CXX_FLAGS' \ - -DCMAKE_C_FLAGS_DEBUG='${DEBUG_SYMBOL_LEVEL}' \ - -DCMAKE_CXX_FLAGS_DEBUG='${DEBUG_SYMBOL_LEVEL}' \ - -DCMAKE_SHARED_LINKER_FLAGS='${CMAKE_SHARED_LINKER_FLAGS} $CMAKE_LD_FLAGS' \ - -DCMAKE_EXE_LINKER_FLAGS='${CMAKE_MODULE_LINKER_FLAGS} $CMAKE_LD_FLAGS' \ + -DCMAKE_C_FLAGS=\"$BASE_C_FLAGS\" \ + -DCMAKE_CXX_FLAGS=\"$BASE_CXX_FLAGS\" \ + -DCMAKE_C_FLAGS_RELEASE=\"$BASE_C_FLAGS\" \ + -DCMAKE_CXX_FLAGS_RELEASE=\"$BASE_CXX_FLAGS\" \ + -DCMAKE_C_FLAGS_RELWITHDEBINFO=\"$BASE_C_FLAGS\" \ + -DCMAKE_CXX_FLAGS_RELWITHDEBINFO=\"$BASE_CXX_FLAGS\" \ + -DCMAKE_C_FLAGS_DEBUG=\"$BASE_C_FLAGS $DEBUG_SYMBOL_LEVEL\" \ + -DCMAKE_CXX_FLAGS_DEBUG=\"$BASE_CXX_FLAGS $DEBUG_SYMBOL_LEVEL\" \ + -DCMAKE_SHARED_LINKER_FLAGS=\"$BASE_LD_FLAGS\" \ + -DCMAKE_MODULE_LINKER_FLAGS=\"$BASE_LD_FLAGS\" \ + -DCMAKE_EXE_LINKER_FLAGS=\"$BASE_LD_FLAGS\" \ + -DCMAKE_CXX_STANDARD=20 \ + -DCMAKE_CXX_STANDARD_REQUIRED=ON \ + -DCMAKE_CXX_EXTENSIONS=OFF \ -DCMAKE_VERBOSE_MAKEFILE=on \ -DENABLE_API_TESTS=OFF \ -DENABLE_SAMPLING_PROFILER=OFF \ diff --git a/scripts/start.sh b/scripts/start.sh index 3f8b22dc..61a4f74d 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -35,13 +35,13 @@ patchAndMakeICU() { if [[ "$BUILD_TYPE" = "Release" ]] then - local OPT_FLAGS="-O2 -flto=thin -fvectorize -fslp-vectorize -funroll-loops" + local OPT_FLAGS="-O2 -flto=thin -Wno-pass-failed=loop-vectorize" CFLAGS="$OPT_FLAGS" - CXXFLAGS="-std=c++2b $OPT_FLAGS" + CXXFLAGS="-std=c++20 $OPT_FLAGS" LDFLAGS="-flto=thin" else CFLAGS="-g2" - CXXFLAGS="-std=c++2b" + CXXFLAGS="-std=c++20" LDFLAGS="" fi From daf4c7e617fcb7899ec5c76477047286a5c2ca0f Mon Sep 17 00:00:00 2001 From: Kudo Chien Date: Sun, 19 Oct 2025 16:46:55 +0800 Subject: [PATCH 05/12] [ci] cleanup ubuntu runner disk (#188) --- .../cleanup-linux-disk-space/action.yml | 34 ++++++++++++++++--- .github/workflows/build_and_test.yml | 3 ++ 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/.github/actions/cleanup-linux-disk-space/action.yml b/.github/actions/cleanup-linux-disk-space/action.yml index 0e823f22..daee2402 100644 --- a/.github/actions/cleanup-linux-disk-space/action.yml +++ b/.github/actions/cleanup-linux-disk-space/action.yml @@ -13,13 +13,37 @@ runs: run: | echo 'Disk space before cleanup' df -aH - sudo apt-get remove -y --purge '^mysql-.*' '^mongodb-.*' '^mssql-.*' '^postgresql-.*' '^aspnetcore-*' '^dotnet-.*' '^php.*-.*' 'mono-complete' '^llvm-.*' 'powershell' 'google-chrome-*' 'microsoft-edge-*' 'firefox' 'nginx' 'apache2' + + # Regular package cleanup + sudo apt-get remove -y --purge '^mysql-.*' '^mongodb-.*' '^mssql-.*' '^postgresql-.*' '^aspnetcore-*' '^dotnet-.*' '^php.*-.*' 'mono-complete' '^llvm-.*' 'powershell' 'google-chrome-*' 'microsoft-edge-*' 'firefox' 'nginx' 'apache2' 'ghc' '^ghc-*' sudo apt-get autoremove -y + + # Remove unnecessary large directories sudo rm -rf /usr/share/dotnet - echo 'Showing Android SDKs' - ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --list - ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --uninstall 'ndk;24.0.8215888' 'ndk;25.2.9519653' 'ndk;26.2.11394342' + sudo rm -rf /usr/local/.ghcup /opt/ghc + + # Android SDK cleanup + echo 'Showing installed Android SDKs' + ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --list_installed + + echo 'Cleaning unnecessary Android SDK components...' + echo 'Removing old build tools...' + ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --uninstall "build-tools;31.0.0" "build-tools;32.0.0" "build-tools;33.0.0" "build-tools;33.0.1" "build-tools;33.0.2" "build-tools;33.0.3" "build-tools;34.0.0" + + echo 'Removing old platforms...' + ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --uninstall "platforms;android-31" "platforms;android-32" "platforms;android-33" "platforms;android-33-ext4" "platforms;android-33-ext5" "platforms;android-34" "platforms;android-34-ext8" "platforms;android-34-ext10" "platforms;android-34-ext11" "platforms;android-34-ext12" + + echo 'Removing NDKs...' + ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --uninstall "ndk;26.3.11579264" + + echo 'Removing extras...' + ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --uninstall "extras;android;m2repository" "extras;google;google_play_services" "extras;google;m2repository" + + # Docker cleanup + echo 'Cleaning up Docker resources' + docker system prune -af || true echo 'Removing all Docker images' - docker rmi -f $(docker images -aq) + docker rmi -f $(docker images -aq) || true + echo 'Disk space after cleanup' df -aH diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 74959699..0c4118c2 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -16,6 +16,9 @@ jobs: steps: - uses: actions/checkout@v4 + - name: 🧹 Cleanup GitHub Linux runner disk space + uses: ./.github/actions/cleanup-linux-disk-space + - name: 🔨 Use JDK 17 uses: actions/setup-java@v4 with: From 055fcd814ab9514b5e86422471472c9ea59eb0f2 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Sun, 2 Nov 2025 14:52:17 -0800 Subject: [PATCH 06/12] patches on top of bun, scripts to make things more dynamically discoverable on any build machine --- patches/jsc_android_logging_property.patch | 30 +++++++++++++++++++ patches/jsc_android_release_flags.patch | 17 +++++++++++ ...sc_android_systemheap_posix_memalign.patch | 18 +++++++++++ patches/jsc_android_thread_name_guard.patch | 17 +++++++++++ scripts/patch.sh | 12 ++++++++ scripts/start.sh | 15 ++++++++++ 6 files changed, 109 insertions(+) create mode 100644 patches/jsc_android_logging_property.patch create mode 100644 patches/jsc_android_release_flags.patch create mode 100644 patches/jsc_android_systemheap_posix_memalign.patch create mode 100644 patches/jsc_android_thread_name_guard.patch diff --git a/patches/jsc_android_logging_property.patch b/patches/jsc_android_logging_property.patch new file mode 100644 index 00000000..7e65e2fc --- /dev/null +++ b/patches/jsc_android_logging_property.patch @@ -0,0 +1,30 @@ +diff --git a/webkit/Source/WTF/wtf/android/LoggingAndroid.cpp b/webkit/Source/WTF/wtf/android/LoggingAndroid.cpp +--- a/webkit/Source/WTF/wtf/android/LoggingAndroid.cpp ++++ b/webkit/Source/WTF/wtf/android/LoggingAndroid.cpp +@@ -32,20 +32,15 @@ + + String logLevelString() + { +- const char* propertyValue = nullptr; ++ constexpr auto propertyName = "debug." LOG_CHANNEL_WEBKIT_SUBSYSTEM ".log"; ++ char propertyValue[PROP_VALUE_MAX] { }; ++ int length = __system_property_get(propertyName, propertyValue); + +- if (const auto* propertyInfo = __system_property_find("debug." LOG_CHANNEL_WEBKIT_SUBSYSTEM ".log")) { +- __system_property_read_callback(propertyInfo, [](void *userData, const char*, const char* value, unsigned) { +- auto **propertyValue = static_cast(userData); +- *propertyValue = value; +- }, &propertyValue); +- } +- + // Disable all log channels if the property is unset or empty. +- if (!propertyValue || !*propertyValue) +- return makeString("-all"_s); ++ if (length <= 0) ++ return "-all"_s; + +- return String::fromLatin1(propertyValue); ++ return String::fromLatin1(propertyValue); + } + + } // namespace WTF diff --git a/patches/jsc_android_release_flags.patch b/patches/jsc_android_release_flags.patch new file mode 100644 index 00000000..50a84027 --- /dev/null +++ b/patches/jsc_android_release_flags.patch @@ -0,0 +1,17 @@ +diff --git a/webkit/Source/cmake/OptionsCommon.cmake b/webkit/Source/cmake/OptionsCommon.cmake +index ea7d508967d6..b4de9ef50bbf 100644 +--- a/webkit/Source/cmake/OptionsCommon.cmake ++++ b/webkit/Source/cmake/OptionsCommon.cmake +@@ -1,7 +1,11 @@ +-set(CMAKE_CXX_STANDARD 23) ++set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + set(CMAKE_CXX_EXTENSIONS OFF) + set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP OFF) ++ ++# Android builds rely on downstream flags, so keep CMake from adding -O3 automatically. ++set(CMAKE_C_COMPILE_OPTIONS_RELEASE -DNDEBUG CACHE STRING "" FORCE) ++set(CMAKE_CXX_COMPILE_OPTIONS_RELEASE -DNDEBUG CACHE STRING "" FORCE) + + add_definitions(-DBUILDING_WITH_CMAKE=1) + add_definitions(-DBUILDING_WEBKIT=1) diff --git a/patches/jsc_android_systemheap_posix_memalign.patch b/patches/jsc_android_systemheap_posix_memalign.patch new file mode 100644 index 00000000..a02e93e6 --- /dev/null +++ b/patches/jsc_android_systemheap_posix_memalign.patch @@ -0,0 +1,18 @@ +diff --git a/webkit/Source/bmalloc/bmalloc/SystemHeap.cpp b/webkit/Source/bmalloc/bmalloc/SystemHeap.cpp +--- a/webkit/Source/bmalloc/bmalloc/SystemHeap.cpp ++++ b/webkit/Source/bmalloc/bmalloc/SystemHeap.cpp +@@ -210,7 +210,13 @@ void* SystemHeap::malloc(size_t size, FailureAction action) + + void* SystemHeap::memalign(size_t alignment, size_t size, FailureAction action) + { +- void* result = ::aligned_alloc(alignment, size); ++ void* result = nullptr; ++#if PAS_OS(ANDROID) ++ if (::posix_memalign(&result, alignment, size)) ++ result = nullptr; ++#else ++ result = ::aligned_alloc(alignment, size); ++#endif + RELEASE_BASSERT(action == FailureAction::ReturnNull || result); + return result; + } diff --git a/patches/jsc_android_thread_name_guard.patch b/patches/jsc_android_thread_name_guard.patch new file mode 100644 index 00000000..9689e62b --- /dev/null +++ b/patches/jsc_android_thread_name_guard.patch @@ -0,0 +1,17 @@ +diff --git a/webkit/Source/bmalloc/libpas/src/libpas/pas_thread_local_cache.c b/webkit/Source/bmalloc/libpas/src/libpas/pas_thread_local_cache.c +--- a/webkit/Source/bmalloc/libpas/src/libpas/pas_thread_local_cache.c ++++ b/webkit/Source/bmalloc/libpas/src/libpas/pas_thread_local_cache.c +@@ -221,9 +221,13 @@ static void dump_thread_diagnostics(pthread_t thread) + #endif + #if PAS_PLATFORM(PLAYSTATION) + getname_result = pthread_get_name_np(thread, thread_name); ++#else ++#if PAS_OS(ANDROID) ++ getname_result = -1; + #else + getname_result = pthread_getname_np(thread, thread_name, sizeof(thread_name)); + #endif ++#endif + if (!getname_result) + pas_log("[%d] thread %p has name %s\n", getpid(), (void*)thread, thread_name); + else diff --git a/scripts/patch.sh b/scripts/patch.sh index 16c1c42d..9452ca5d 100755 --- a/scripts/patch.sh +++ b/scripts/patch.sh @@ -50,6 +50,18 @@ JSC_PATCHSET=( # Avoid ICU formatting dependencies when validating time zones "jsc_android_timezone_validate.patch" + + # Force Android builds to use C++20 and drop toolchain -O3 defaults + "jsc_android_release_flags.patch" + + # Avoid pthread_getname_np usage on Android where it's unavailable + "jsc_android_thread_name_guard.patch" + + # Use posix_memalign when aligned_alloc is missing on older Android + "jsc_android_systemheap_posix_memalign.patch" + + # Use legacy system property getter on older Android + "jsc_android_logging_property.patch" ) ###################################################################################### diff --git a/scripts/start.sh b/scripts/start.sh index 61a4f74d..31471f95 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -16,6 +16,21 @@ if [[ -z "$ANDROID_NDK" || ! -d "$ANDROID_NDK" ]]; then export ANDROID_NDK="$DEFAULT_ANDROID_NDK" fi +if [[ -z "$JAVA_HOME" || ! -x "$JAVA_HOME/bin/java" ]]; then + if [[ "$(uname -s)" == "Darwin" ]]; then + if command -v brew >/dev/null 2>&1; then + HOMEBREW_JAVA_ROOT="$(brew --prefix openjdk@17 2>/dev/null)/libexec/openjdk.jdk/Contents/Home" + if [[ -x "$HOMEBREW_JAVA_ROOT/bin/java" ]]; then + export JAVA_HOME="$HOMEBREW_JAVA_ROOT" + fi + fi + fi +fi + +if [[ -n "$JAVA_HOME" && -x "$JAVA_HOME/bin/java" ]]; then + export PATH="$JAVA_HOME/bin:$PATH" +fi + source $ROOTDIR/scripts/env.sh source $ROOTDIR/scripts/info.sh export JSC_VERSION=${npm_package_version} From b2e424083b8afdd2d788d80e137f27157885ae18 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Mon, 3 Nov 2025 12:42:26 -0800 Subject: [PATCH 07/12] lto=thin is clang-only, even in Ubuntu 24.x, and we only really care about LTO for the binary bits that end up in the app package itself anyway. gradefully degrade to generic LTO when clang isn't detected on the system. when clang is detected, make sure we use it for generating the ICU host-run codegen --- scripts/start.sh | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/scripts/start.sh b/scripts/start.sh index 31471f95..a8dacdab 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -48,12 +48,30 @@ patchAndMakeICU() { mkdir -p $TARGETDIR/icu/host cd $TARGETDIR/icu/host + local HAS_CLANG=0 + if command -v clang >/dev/null 2>&1 && command -v clang++ >/dev/null 2>&1; then + HAS_CLANG=1 + fi + if [[ "$BUILD_TYPE" = "Release" ]] then - local OPT_FLAGS="-O2 -flto=thin -Wno-pass-failed=loop-vectorize" + local LTO_FLAG + local EXTRA_FLAGS="" + if [[ $HAS_CLANG -eq 1 ]]; then + LTO_FLAG="-flto=thin" + EXTRA_FLAGS="-Wno-pass-failed=loop-vectorize" + else + LTO_FLAG="-flto" + fi + + local OPT_FLAGS="-O2 $LTO_FLAG" + if [[ -n "$EXTRA_FLAGS" ]]; then + OPT_FLAGS="$OPT_FLAGS $EXTRA_FLAGS" + fi + CFLAGS="$OPT_FLAGS" CXXFLAGS="-std=c++20 $OPT_FLAGS" - LDFLAGS="-flto=thin" + LDFLAGS="$LTO_FLAG" else CFLAGS="-g2" CXXFLAGS="-std=c++20" @@ -65,6 +83,9 @@ patchAndMakeICU() { if [[ -n "$LDFLAGS" ]]; then CONFIG_ENV+=("LDFLAGS=$LDFLAGS") fi + if [[ $HAS_CLANG -eq 1 ]]; then + CONFIG_ENV+=("CC=clang" "CXX=clang++") + fi if [[ -f "$ICU_FILTER_FILE" ]]; then printf "Using ICU data filter: %s\n" "$ICU_FILTER_FILE" From 79cf23878dced5662cf3ffac72594a774f20c3cf Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Mon, 3 Nov 2025 15:58:53 -0800 Subject: [PATCH 08/12] be more flexible with suboptimal ABI requests, but still favor armv8a (pixel 2 and above) for the superior vectorization codegen that's possible --- test/plugins/withJscAndroid.js | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/plugins/withJscAndroid.js b/test/plugins/withJscAndroid.js index 63c864fa..3c611826 100644 --- a/test/plugins/withJscAndroid.js +++ b/test/plugins/withJscAndroid.js @@ -1,6 +1,7 @@ const assert = require('assert'); const { withAppBuildGradle, + withGradleProperties, withProjectBuildGradle, } = require('expo/config-plugins'); @@ -47,6 +48,7 @@ afterEvaluate { `; config.modResults.contents += code; } + return config; }); }; @@ -73,6 +75,34 @@ const withJscAndroidProjectBuildGradle = (config) => { const withJscAndroid = (config) => { config = withJscAndroidAppBuildGradle(config); config = withJscAndroidProjectBuildGradle(config); + config = withGradleProperties(config, (config) => { + const propertyName = 'reactNativeArchitectures'; + const desiredValue = 'arm64-v8a'; + const existingProp = config.modResults.find( + (item) => item.type === 'property' && item.key === propertyName + ); + const cleanValue = (value) => { + if (!value) return desiredValue; + const archs = value + .split(',') + .map((arch) => arch.trim()) + .filter(Boolean); + if (!archs.length) return desiredValue; + const result = archs.filter((arch) => arch !== 'armeabi-v7a'); + if (!result.length) return desiredValue; + return Array.from(new Set(result)).join(','); + }; + if (existingProp) { + existingProp.value = cleanValue(existingProp.value); + } else { + config.modResults.push({ + type: 'property', + key: propertyName, + value: desiredValue, + }); + } + return config; + }); return config; }; From 509e65bde3f3ec2fbb6673e78e7dd7846492098a Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Tue, 4 Nov 2025 11:44:03 -0800 Subject: [PATCH 09/12] filter 32bit platforms out --- test/plugins/withJscAndroid.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/test/plugins/withJscAndroid.js b/test/plugins/withJscAndroid.js index 3c611826..3143e6f1 100644 --- a/test/plugins/withJscAndroid.js +++ b/test/plugins/withJscAndroid.js @@ -76,8 +76,10 @@ const withJscAndroid = (config) => { config = withJscAndroidAppBuildGradle(config); config = withJscAndroidProjectBuildGradle(config); config = withGradleProperties(config, (config) => { + const DEFAULT_ARCHS = ['arm64-v8a', 'x86_64']; + const ARCHS_TO_REMOVE = new Set(['armeabi-v7a', 'x86']); const propertyName = 'reactNativeArchitectures'; - const desiredValue = 'arm64-v8a'; + const desiredValue = DEFAULT_ARCHS.join(','); const existingProp = config.modResults.find( (item) => item.type === 'property' && item.key === propertyName ); @@ -88,9 +90,14 @@ const withJscAndroid = (config) => { .map((arch) => arch.trim()) .filter(Boolean); if (!archs.length) return desiredValue; - const result = archs.filter((arch) => arch !== 'armeabi-v7a'); + const result = []; + archs.forEach((arch) => { + if (!ARCHS_TO_REMOVE.has(arch) && !result.includes(arch)) { + result.push(arch); + } + }); if (!result.length) return desiredValue; - return Array.from(new Set(result)).join(','); + return result.join(','); }; if (existingProp) { existingProp.value = cleanValue(existingProp.value); From abbf7324575e70176a7b52c062c89106a79eecab Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Wed, 5 Nov 2025 09:59:08 -0800 Subject: [PATCH 10/12] upstream update fixes a problem where Promise reactions weren't first-class GC collectible, which can cause OOM on a specific synthetic stress test on mobile/wearable devices --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c0fbbdc..8de68b87 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ }, "config": { "bunWebKitRepo": "https://github.com/oven-sh/WebKit.git", - "bunWebKitCommit": "6d0f3aac0b817cc01a846b3754b21271adedac12", + "bunWebKitCommit": "26e6460697dab3e8681489ce67857434b2180e6f", "icuRelease": "release-74-2", "icuArchive": "icu4c-74_2-src.tgz" }, From d7dbeb7d8191424e7736de6000f0ae2b751796c0 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Wed, 5 Nov 2025 15:08:29 -0800 Subject: [PATCH 11/12] build and publish separate artifacts for NDK 27, 28, and 29 so decouple React Native's lagging toolchain from other downstream users like BabylonNative --- package.json | 33 ++++++++- scripts/compile/common.sh | 19 ++++-- scripts/env.sh | 13 ++-- scripts/info.sh | 10 +++ scripts/publish.js | 120 ++++++++++++++++++++++++--------- scripts/start.sh | 50 +++++++++----- test/plugins/withJscAndroid.js | 6 +- 7 files changed, 195 insertions(+), 56 deletions(-) diff --git a/package.json b/package.json index 8de68b87..6df36250 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,38 @@ "bunWebKitRepo": "https://github.com/oven-sh/WebKit.git", "bunWebKitCommit": "26e6460697dab3e8681489ce67857434b2180e6f", "icuRelease": "release-74-2", - "icuArchive": "icu4c-74_2-src.tgz" + "icuArchive": "icu4c-74_2-src.tgz", + "ndkVariants": [ + { + "id": "ndk27", + "label": "Android NDK r27", + "npmPackage": "jsc-android-ndk27", + "pkgRevisionPrefixes": ["27."], + "buildSuffix": "ndk27", + "distDir": "dist-ndk27", + "distUnstrippedDir": "dist-ndk27.unstripped", + "disableLoopVectorization": true + }, + { + "id": "ndk28c", + "label": "Android NDK r28c", + "npmPackage": "jsc-android", + "pkgRevisionPrefixes": ["28.2."], + "buildSuffix": "ndk28", + "distDir": "dist-ndk28", + "distUnstrippedDir": "dist-ndk28.unstripped", + "default": true + }, + { + "id": "ndk29", + "label": "Android NDK r29", + "npmPackage": "jsc-android-ndk29", + "pkgRevisionPrefixes": ["29."], + "buildSuffix": "ndk29", + "distDir": "dist-ndk29", + "distUnstrippedDir": "dist-ndk29.unstripped" + } + ] }, "devDependencies": { "commander": "^12.1.0", diff --git a/scripts/compile/common.sh b/scripts/compile/common.sh index 7b7f43a6..46e9c1eb 100755 --- a/scripts/compile/common.sh +++ b/scripts/compile/common.sh @@ -26,6 +26,7 @@ process_switch_options() { } if ! [[ $ROOTDIR ]]; then ROOTDIR=`pwd`; fi +source $ROOTDIR/scripts/toolchain.sh ARCH=$JSC_ARCH TARGETDIR=$ROOTDIR/build/target @@ -101,8 +102,16 @@ DEBUG_SYMBOL_LEVEL="-g2" if [[ "$BUILD_TYPE" = "Release" ]] then FRAME_POINTER_FLAG="-fomit-frame-pointer" - CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -O2 -flto=thin" - ICU_CFLAGS_BUILD_TYPE="-O2 -flto=thin" + CFLAGS_BUILD_TYPE="-DNDEBUG -g0 -O2" + ICU_CFLAGS_BUILD_TYPE="-O2" + if [[ -n "$JSC_TOOLCHAIN_LTO_FLAG" ]]; then + CFLAGS_BUILD_TYPE="$CFLAGS_BUILD_TYPE $JSC_TOOLCHAIN_LTO_FLAG" + ICU_CFLAGS_BUILD_TYPE="$ICU_CFLAGS_BUILD_TYPE $JSC_TOOLCHAIN_LTO_FLAG" + fi + if [[ -n "$JSC_TOOLCHAIN_RELEASE_CFLAGS" ]]; then + CFLAGS_BUILD_TYPE="$CFLAGS_BUILD_TYPE $JSC_TOOLCHAIN_RELEASE_CFLAGS" + ICU_CFLAGS_BUILD_TYPE="$ICU_CFLAGS_BUILD_TYPE $JSC_TOOLCHAIN_RELEASE_CFLAGS" + fi else FRAME_POINTER_FLAG="-fno-omit-frame-pointer" CFLAGS_BUILD_TYPE="" @@ -117,9 +126,12 @@ COMMON_LDFLAGS=" \ -Wl,--exclude-libs,libgcc.a \ -Wl,--no-undefined \ -Wl,-z,max-page-size=16384 \ --flto=thin \ " +if [[ "$BUILD_TYPE" = "Release" && -n "$JSC_TOOLCHAIN_RELEASE_LDFLAGS" ]]; then + COMMON_LDFLAGS="$COMMON_LDFLAGS $JSC_TOOLCHAIN_RELEASE_LDFLAGS" +fi + COMMON_CFLAGS=" \ -fstack-protector \ -ffunction-sections \ @@ -132,7 +144,6 @@ $FRAME_POINTER_FLAG \ -DCUSTOMIZE_REACT_NATIVE \ $SWITCH_COMMON_CFLAGS_INTL \ $CFLAGS_BUILD_TYPE \ --Wno-pass-failed=loop-vectorize \ -D__ANDROID_MIN_SDK_VERSION__=${ANDROID_API} \ " diff --git a/scripts/env.sh b/scripts/env.sh index b5779e7c..ee88d028 100644 --- a/scripts/env.sh +++ b/scripts/env.sh @@ -2,17 +2,22 @@ export ROOTDIR=$PWD +BUILD_VARIANT_SUFFIX="" +if [[ -n "$JSC_BUILD_VARIANT" ]]; then + BUILD_VARIANT_SUFFIX="-$JSC_BUILD_VARIANT" +fi + # Intermediated build target dir -export TARGETDIR=$ROOTDIR/build/target +export TARGETDIR=${TARGETDIR:-$ROOTDIR/build/target${BUILD_VARIANT_SUFFIX}} # JSC shared library install dir -export INSTALL_DIR=$ROOTDIR/build/compiled +export INSTALL_DIR=${INSTALL_DIR:-$ROOTDIR/build/compiled${BUILD_VARIANT_SUFFIX}} # JSC unstripped shared library install dir -export INSTALL_UNSTRIPPED_DIR=$ROOTDIR/build/compiled.unstripped +export INSTALL_UNSTRIPPED_DIR=${INSTALL_UNSTRIPPED_DIR:-$ROOTDIR/build/compiled.unstripped${BUILD_VARIANT_SUFFIX}} # CPP runtime shared library install dir -export INSTALL_CPPRUNTIME_DIR=$ROOTDIR/build/cppruntime +export INSTALL_CPPRUNTIME_DIR=${INSTALL_CPPRUNTIME_DIR:-$ROOTDIR/build/cppruntime${BUILD_VARIANT_SUFFIX}} # Install dir for i18n build variants export INSTALL_DIR_I18N_true=$INSTALL_DIR/intl diff --git a/scripts/info.sh b/scripts/info.sh index 3ec6b58f..2bd19efd 100755 --- a/scripts/info.sh +++ b/scripts/info.sh @@ -5,6 +5,10 @@ ROOTDIR=$PWD WEBKIT_REPO="${npm_package_config_bunWebKitRepo:-https://github.com/oven-sh/WebKit.git}" WEBKIT_COMMIT="${npm_package_config_bunWebKitCommit}" +export JSC_TOOLCHAIN_SUPPRESS_LOG=1 +source $ROOTDIR/scripts/toolchain.sh +unset JSC_TOOLCHAIN_SUPPRESS_LOG + export REVISION=$(node -e "console.log(require('./package.json').version.split('.')[0])") CONFIG=$(node -e "console.log(JSON.stringify(require('$ROOTDIR/package.json').config, null, 2))") @@ -27,5 +31,11 @@ printf "\n\n\n\n\n\t\t\tRevision: \x1B[32m$REVISION\x1B[0m\n\n\n" printf "WebKit repository:\n%s @ %s\n\n" "$WEBKIT_REPO" "${WEBKIT_COMMIT:-unknown}" printf "Upstream URL:\n%s\n\n" "$WEBKIT_URL" printf "Config:\n%s\n\n" "$CONFIG" +printf "NDK variant: %s\n" "${JSC_TOOLCHAIN_VARIANT:-unknown}" +if [[ -n "$JSC_TOOLCHAIN_NDK_REVISION" ]]; then + printf "NDK revision: %s\n\n" "$JSC_TOOLCHAIN_NDK_REVISION" +else + printf "\n" +fi printf "AppleWebKit version components:\n%s\n\n" "$APPLE_VERSION" printf "Size:\n$SIZE\n\n" diff --git a/scripts/publish.js b/scripts/publish.js index b8f23a7c..bcc1fbcb 100755 --- a/scripts/publish.js +++ b/scripts/publish.js @@ -10,8 +10,8 @@ const path = require('path'); const rimraf = require('rimraf'); const semver = require('semver'); -if (!semver.satisfies(process.versions.node, '>= 10.12.0')) { - console.log('Please execute this script with node version >= 10.12.0'); +if (!semver.satisfies(process.versions.node, '>= 16.7.0')) { + console.log('Please execute this script with node version >= 16.7.0'); process.exit(1); } @@ -23,8 +23,11 @@ commander const artifactZipFile = verifyFile(commander.args[0], ''); const rootDir = path.dirname(__dirname); +const pkgJsonPath = path.join(rootDir, 'package.json'); +const packageTemplate = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); const workDir = path.join(rootDir, 'build', 'publish'); const distDir = path.join(rootDir, 'dist'); + if (fs.existsSync(workDir)) { rimraf.sync(workDir); } @@ -32,40 +35,82 @@ fs.mkdirSync(workDir, {recursive: true}); child_process.execFileSync('unzip', [artifactZipFile, '-d', workDir]); -// Publish standard package -console.log('\n\n========== Publish standard package =========='); -createPatchedContext(rootDir, '', () => { - if (fs.existsSync(distDir)) { - rimraf.sync(distDir); - } - fs.renameSync(path.join(workDir, 'dist'), distDir); - const publishArgs = ['publish', '--tag', commander.tag]; - if (commander.dryRun) { - publishArgs.push('--dry-run'); - } - child_process.execFileSync('npm', publishArgs); -}); +const variantList = Array.isArray(packageTemplate.config?.ndkVariants) + ? packageTemplate.config.ndkVariants + : []; -// Publish unstripped package -// 1. Add suffix in version, e.g. 245459.0.0-unstripped -// 2. Add suffix in tag, e.g. latest-unstripped -// 3. Get unstripped distribution from dist.unstripped/ in CI archive.zip -console.log('\n\n========== Publish unstripped package =========='); -createPatchedContext(rootDir, 'unstripped', () => { - if (fs.existsSync(distDir)) { - rimraf.sync(distDir); - } - fs.renameSync(path.join(workDir, 'dist.unstripped'), distDir); - const publishArgs = ['publish', '--tag', `${commander.tag}-unstripped`]; - if (commander.dryRun) { - publishArgs.push('--dry-run'); - } - child_process.execFileSync('npm', publishArgs); +const variants = + variantList.length > 0 + ? [...variantList].sort((a, b) => { + const aDefault = a && a.default ? 1 : 0; + const bDefault = b && b.default ? 1 : 0; + return bDefault - aDefault; + }) + : [ + { + id: 'default', + npmPackage: packageTemplate.name, + distDir: 'dist', + distUnstrippedDir: 'dist.unstripped', + }, + ]; + +variants.forEach((variant) => { + publishVariant(variant); }); // --------------------------------------------------------------------------- // Helper functions // --------------------------------------------------------------------------- +function publishVariant(variant) { + const displayName = variant?.id || 'default'; + console.log(`\n\n========== Publish ${displayName} package ==========`); // eslint-disable-line no-console + + publishVariantFlavor(variant, { + sourceDirName: variant.distDir || 'dist', + versionSuffix: '', + tagSuffix: '', + }); + + publishVariantFlavor(variant, { + sourceDirName: variant.distUnstrippedDir || 'dist.unstripped', + versionSuffix: 'unstripped', + tagSuffix: '-unstripped', + }); +} + +function publishVariantFlavor(variant, {sourceDirName, versionSuffix, tagSuffix}) { + if (!sourceDirName) { + return; + } + + const sourceDir = path.join(workDir, sourceDirName); + if (!fs.existsSync(sourceDir)) { + console.warn( + `Skipping ${variant?.id || 'default'}${tagSuffix ? ` (${tagSuffix.replace('-', '')})` : ''} - missing directory ${sourceDirName}`, + ); + return; + } + + createPatchedContext(rootDir, {variant, versionSuffix}, () => { + if (fs.existsSync(distDir)) { + rimraf.sync(distDir); + } + copyDir(sourceDir, distDir); + const publishTag = tagSuffix ? `${commander.tag}${tagSuffix}` : commander.tag; + const publishArgs = ['publish', '--tag', publishTag]; + if (commander.dryRun) { + publishArgs.push('--dry-run'); + } + child_process.execFileSync('npm', publishArgs, {stdio: 'inherit'}); + }); +} + +function copyDir(source, destination) { + fs.mkdirSync(path.dirname(destination), {recursive: true}); + fs.cpSync(source, destination, {recursive: true, force: true}); +} + function verifyFile(filePath, argName) { if (filePath == null) { console.error(`Error: ${argName} is required`); @@ -88,12 +133,25 @@ function verifyFile(filePath, argName) { return filePath; } -function createPatchedContext(rootDir, versionSuffix, wrappedRunner) { +function createPatchedContext(rootDir, options, wrappedRunner) { + const {versionSuffix, variant} = options || {}; const configPath = path.join(rootDir, 'package.json'); const origConfig = fs.readFileSync(configPath); function enter() { const patchedConfig = JSON.parse(origConfig); + if (variant) { + if (variant.npmPackage) { + patchedConfig.name = variant.npmPackage; + } + patchedConfig.config = patchedConfig.config || {}; + if (variant.id) { + patchedConfig.config.selectedNdkVariant = variant.id; + } + if (variant.npmPackage) { + patchedConfig.config.selectedNdkPackage = variant.npmPackage; + } + } if (versionSuffix) { patchedConfig.version += '-' + versionSuffix; } diff --git a/scripts/start.sh b/scripts/start.sh index a8dacdab..d2e80f78 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -16,6 +16,10 @@ if [[ -z "$ANDROID_NDK" || ! -d "$ANDROID_NDK" ]]; then export ANDROID_NDK="$DEFAULT_ANDROID_NDK" fi +export JSC_TOOLCHAIN_SUPPRESS_LOG=1 +source $ROOTDIR/scripts/toolchain.sh +unset JSC_TOOLCHAIN_SUPPRESS_LOG + if [[ -z "$JAVA_HOME" || ! -x "$JAVA_HOME/bin/java" ]]; then if [[ "$(uname -s)" == "Darwin" ]]; then if command -v brew >/dev/null 2>&1; then @@ -37,6 +41,17 @@ export JSC_VERSION=${npm_package_version} export BUILD_TYPE=Release # export BUILD_TYPE=Debug +STRIPPED_DIST_DIR=${JSC_DIST_DIR:-${ROOTDIR}/dist-ndk28} +UNSTRIPPED_DIST_DIR=${JSC_DIST_UNSTRIPPED_DIR:-${ROOTDIR}/dist-ndk28.unstripped} + +printf "Building with Android NDK variant: %s\n" "${JSC_TOOLCHAIN_VARIANT}" +if [[ -n "$JSC_TOOLCHAIN_NDK_REVISION" ]]; then + printf "Detected Android NDK revision: %s\n" "${JSC_TOOLCHAIN_NDK_REVISION}" +fi +printf "Using distribution directories:\n" +printf " stripped : %s\n" "$STRIPPED_DIST_DIR" +printf " unstripped : %s\n" "$UNSTRIPPED_DIST_DIR" + SCRIPT_DIR=$(cd `dirname $0`; pwd) patchAndMakeICU() { @@ -55,23 +70,28 @@ patchAndMakeICU() { if [[ "$BUILD_TYPE" = "Release" ]] then - local LTO_FLAG - local EXTRA_FLAGS="" + local opt_flags="-O2" + local lto_flag="" if [[ $HAS_CLANG -eq 1 ]]; then - LTO_FLAG="-flto=thin" - EXTRA_FLAGS="-Wno-pass-failed=loop-vectorize" - else - LTO_FLAG="-flto" + lto_flag="$JSC_TOOLCHAIN_LTO_FLAG" + elif [[ -n "$JSC_TOOLCHAIN_LTO_FLAG" ]]; then + lto_flag="-flto" fi - - local OPT_FLAGS="-O2 $LTO_FLAG" - if [[ -n "$EXTRA_FLAGS" ]]; then - OPT_FLAGS="$OPT_FLAGS $EXTRA_FLAGS" + if [[ -n "$lto_flag" ]]; then + opt_flags="$opt_flags $lto_flag" + fi + if [[ -n "$JSC_TOOLCHAIN_RELEASE_CFLAGS" ]]; then + opt_flags="$opt_flags $JSC_TOOLCHAIN_RELEASE_CFLAGS" fi - CFLAGS="$OPT_FLAGS" - CXXFLAGS="-std=c++20 $OPT_FLAGS" - LDFLAGS="$LTO_FLAG" + CFLAGS="$opt_flags" + CXXFLAGS="-std=c++20 $opt_flags" + + local ldflags="$JSC_TOOLCHAIN_RELEASE_LDFLAGS" + if [[ $HAS_CLANG -eq 0 && "$ldflags" == "-flto=thin" ]]; then + ldflags="-flto" + fi + LDFLAGS="$ldflags" else CFLAGS="-g2" CXXFLAGS="-std=c++20" @@ -180,14 +200,14 @@ if [[ "${SKIP_INTL}" != "1" ]]; then fi printf "\n\n\t\t===================== create stripped distributions =====================\n\n" -export DISTDIR=${ROOTDIR}/dist +export DISTDIR=${STRIPPED_DIST_DIR} copyHeaders ${DISTDIR} createAAR "jsc-android" ${DISTDIR} ${INSTALL_DIR_I18N_false} "false" createAAR "jsc-android" ${DISTDIR} ${INSTALL_DIR_I18N_true} "true" createAAR "cppruntime" ${DISTDIR} ${INSTALL_CPPRUNTIME_DIR} "false" printf "\n\n\t\t===================== create unstripped distributions =====================\n\n" -export DISTDIR=${ROOTDIR}/dist.unstripped +export DISTDIR=${UNSTRIPPED_DIST_DIR} copyHeaders ${DISTDIR} createAAR "jsc-android" ${DISTDIR} ${INSTALL_UNSTRIPPED_DIR_I18N_false} "false" createAAR "jsc-android" ${DISTDIR} ${INSTALL_UNSTRIPPED_DIR_I18N_true} "true" diff --git a/test/plugins/withJscAndroid.js b/test/plugins/withJscAndroid.js index 3143e6f1..37b6c132 100644 --- a/test/plugins/withJscAndroid.js +++ b/test/plugins/withJscAndroid.js @@ -5,6 +5,10 @@ const { withProjectBuildGradle, } = require('expo/config-plugins'); +const DEFAULT_RELATIVE_DIST_PATH = '../../dist-ndk28'; +const GRADLE_DIST_PATH = + process.env.JSC_GRADLE_DIST_PATH || DEFAULT_RELATIVE_DIST_PATH; + const withJscAndroidAppBuildGradle = (config) => { return withAppBuildGradle(config, (config) => { assert(config.modResults.language === 'groovy'); @@ -25,7 +29,7 @@ afterEvaluate { repositories { maven { // Local dist maven repo - url("\${rootDir}/../../dist") + url("\${rootDir}/${GRADLE_DIST_PATH}") } mavenCentral { From 940c07175c35f1c50535c1ce51006c81b2016860 Mon Sep 17 00:00:00 2001 From: Matt Hargett Date: Wed, 5 Nov 2025 15:57:08 -0800 Subject: [PATCH 12/12] cache the NDKs instead of re-downloading and re-unpacking each time. for that matter, use cccache so that incremental updates from upstream bun-sh/webkit don't trigger the 90+ minute (time three, for each NDK) build. --- .github/workflows/build_and_test.yml | 197 ++++++++++++++++++++++++--- package.json | 2 +- scripts/build-hash.js | 75 ++++++++++ scripts/compile/icu.sh | 11 +- scripts/compile/jsc.sh | 6 + scripts/publish.js | 24 ++-- scripts/start.sh | 8 +- scripts/toolchain.sh | 193 ++++++++++++++++++++++++++ 8 files changed, 486 insertions(+), 30 deletions(-) create mode 100644 scripts/build-hash.js create mode 100644 scripts/toolchain.sh diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 0c4118c2..72214ce2 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,7 +1,20 @@ name: Build jsc-android and test on: - workflow_dispatch: {} + workflow_dispatch: + inputs: + publish: + description: 'Publish npm packages after build/test succeed' + type: boolean + default: false + npm-tag: + description: 'npm dist-tag' + required: false + default: latest + dry-run: + description: 'Run npm publish in dry-run mode' + type: boolean + default: true push: branches: [main] pull_request: @@ -12,6 +25,9 @@ jobs: env: ORG_GRADLE_PROJECT_signingKey: ${{ secrets.GPG_SIGNING_KEY }} ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.GPG_SIGNING_PASSWORD }} + NDK_VERSION_27: '27.1.12297006' + NDK_VERSION_28: '28.2.13676358' + BUILD_CACHE_VERSION: v1 steps: - uses: actions/checkout@v4 @@ -30,39 +46,126 @@ jobs: with: node-version: 22 + - name: Compute build cache key + id: build-hash + run: | + HASH=$(node scripts/build-hash.js) + echo "hash=$HASH" >> "$GITHUB_OUTPUT" + + - name: Restore JSC build artifacts + id: cache-dist + uses: actions/cache@v4 + with: + path: | + dist-ndk27 + dist-ndk27.unstripped + dist-ndk28 + dist-ndk28.unstripped + dist-ndk29 + dist-ndk29.unstripped + key: jsc-dist-${{ env.BUILD_CACHE_VERSION }}-${{ steps.build-hash.outputs.hash }} + + - name: Restore WebKit sources + id: cache-download + uses: actions/cache@v4 + with: + path: build/download + key: jsc-download-${{ env.BUILD_CACHE_VERSION }}-${{ steps.build-hash.outputs.hash }} + restore-keys: | + jsc-download-${{ env.BUILD_CACHE_VERSION }}- + - name: Install packages run: | sudo apt-get update - sudo apt-get install coreutils curl git wget python3 ruby gperf -y + sudo apt-get install coreutils curl git wget python3 ruby gperf ccache -y + shell: bash + + - name: Restore ccache + id: cache-ccache + uses: actions/cache@v4 + with: + path: ~/.cache/ccache + key: ccache-${{ env.BUILD_CACHE_VERSION }}-${{ runner.os }}-${{ env.NDK_VERSION_27 }}-${{ steps.build-hash.outputs.hash }} + restore-keys: | + ccache-${{ env.BUILD_CACHE_VERSION }}-${{ runner.os }}-${{ env.NDK_VERSION_27 }}- + + - name: Configure ccache + run: | + mkdir -p ~/.cache/ccache + if command -v ccache >/dev/null 2>&1; then + ccache --max-size=5G + ccache --zero-stats || true + fi shell: bash + - name: Cache Android NDK r27 + id: cache-ndk-27 + uses: actions/cache@v4 + with: + path: ${{ env.ANDROID_HOME }}/ndk/${{ env.NDK_VERSION_27 }} + key: android-ndk-${{ runner.os }}-${{ env.NDK_VERSION_27 }} + - name: Install Android packages run: | export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools yes | sdkmanager --licenses || true - sdkmanager \ - "cmake;3.22.1" \ - "ndk;27.1.12297006" - # move out builtin icu headers from ndk and prevent icu build errors - mv "${ANDROID_HOME}/ndk/27.1.12297006/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/unicode" "${ANDROID_HOME}/ndk/27.1.12297006/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/unicode2" - - echo "ANDROID_NDK=$ANDROID_HOME/ndk/27.1.12297006" >> $GITHUB_ENV - echo "PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools" >> $GITHUB_ENV + sdkmanager "cmake;3.22.1" + if [[ ! -d "${ANDROID_HOME}/ndk/${NDK_VERSION_27}" ]]; then + sdkmanager "ndk;${NDK_VERSION_27}" + fi + UNICODE_DIR="${ANDROID_HOME}/ndk/${NDK_VERSION_27}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/include/unicode" + if [[ -d "${UNICODE_DIR}" ]]; then + mv "${UNICODE_DIR}" "${UNICODE_DIR}2" + fi + echo "ANDROID_NDK=$ANDROID_HOME/ndk/${NDK_VERSION_27}" >> $GITHUB_ENV + echo "PATH=$PATH" >> $GITHUB_ENV + shell: bash + + - name: Install dependencies + run: yarn install --frozen-lockfile + shell: bash + + - name: Clean previous build outputs + if: steps.cache-dist.outputs.cache-hit != 'true' + run: | + rm -rf dist dist.unstripped dist-ndk* build/target* build/compiled* build/cppruntime* + shell: bash + + - name: Download sources + if: steps.cache-download.outputs.cache-hit != 'true' + run: yarn download shell: bash - name: Build + if: steps.cache-dist.outputs.cache-hit != 'true' + run: yarn start + shell: bash + + - name: Show ccache stats + if: steps.cache-dist.outputs.cache-hit != 'true' run: | - yarn install --frozen-lockfile - yarn clean - yarn download - yarn start + if command -v ccache >/dev/null 2>&1; then + ccache --show-stats + fi shell: bash - name: Archive run: | + rm -rf archive mkdir -p archive - mv dist archive/ - mv dist.unstripped archive/ + shopt -s nullglob + found=0 + for dir in dist-ndk*; do + if [[ -d "$dir" ]]; then + cp -R "$dir" archive/ + found=1 + fi + done + shopt -u nullglob + if [[ $found -eq 0 ]]; then + echo "No distribution directories were produced." >&2 + exit 1 + fi shell: bash - uses: actions/upload-artifact@v4 @@ -99,8 +202,12 @@ jobs: - name: Extract archive run: | - mv archive/dist dist - mv archive/dist.unstripped dist.unstripped + shopt -s nullglob + for dir in archive/dist-ndk*; do + dest=$(basename "$dir") + mv "$dir" "$dest" + done + shopt -u nullglob rmdir archive shell: bash @@ -128,6 +235,7 @@ jobs: target: google_apis working-directory: test script: | + export JSC_GRADLE_DIST_PATH=../../dist-ndk27 npx expo run:android --variant release --no-bundler adb logcat -c set +e @@ -145,3 +253,56 @@ jobs: $HOME/.maestro/tests/**/* test/android/app/build/outputs/apk/release/app-release.apk test/adb.log + + publish: + if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true' + needs: + - build + - test + runs-on: ubuntu-latest + environment: + name: npm-publish + url: https://www.npmjs.com/package/jsc-android + permissions: + contents: read + steps: + - uses: actions/checkout@v4 + + - name: ⬢ Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - uses: actions/download-artifact@v4 + with: + name: archive + path: archive + + - name: Install dependencies + run: yarn install --frozen-lockfile + shell: bash + + - name: Verify npm token availability + if: github.event.inputs.dry-run != 'true' + run: | + if [[ -z "${NPM_TOKEN:-}" ]]; then + echo "NPM_TOKEN secret is required for publishing." >&2 + exit 1 + fi + env: + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + shell: bash + + - name: Publish packages + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + NPM_CONFIG_PROVENANCE: 'true' + run: | + TAG="${{ github.event.inputs.npm-tag }}" + DRY_RUN="${{ github.event.inputs.dry-run }}" + PUBLISH_ARGS=("-T" "$TAG") + if [[ "$DRY_RUN" == 'true' ]]; then + PUBLISH_ARGS+=("--dry-run") + fi + node scripts/publish.js "${PUBLISH_ARGS[@]}" archive + shell: bash diff --git a/package.json b/package.json index 6df36250..c5ea81e0 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "/dist" ], "scripts": { - "clean": "rm -rf dist; rm -rf build", + "clean": "rm -rf dist dist.unstripped dist-ndk* dist-ndk*.unstripped; rm -rf build", "info": "./scripts/info.sh", "download": "./scripts/download.sh", "start": "./scripts/start.sh" diff --git a/scripts/build-hash.js b/scripts/build-hash.js new file mode 100644 index 00000000..83aa879d --- /dev/null +++ b/scripts/build-hash.js @@ -0,0 +1,75 @@ +#!/usr/bin/env node + +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); + +const rootDir = process.cwd(); + +const includePaths = [ + 'package.json', + 'yarn.lock', + 'patches', + 'scripts', + 'lib', +]; + +const excludePatterns = [/^scripts\/publish\.js$/]; + +const hash = crypto.createHash('sha256'); + +function shouldInclude(relPath) { + if (relPath.includes('node_modules')) { + return false; + } + if (relPath.startsWith('.git')) { + return false; + } + return true; +} + +function shouldExclude(relPath) { + return excludePatterns.some((pattern) => pattern.test(relPath)); +} + +function addFile(filePath, relPath) { + const content = fs.readFileSync(filePath); + hash.update(relPath); + hash.update('\0'); + hash.update(content); +} + +function walk(currentPath, basePath) { + const entries = fs.readdirSync(currentPath).sort(); + entries.forEach((entry) => { + const absPath = path.join(currentPath, entry); + const relPath = path.relative(basePath, absPath).replace(/\\/g, '/'); + + if (!shouldInclude(relPath) || shouldExclude(relPath)) { + return; + } + + const stat = fs.statSync(absPath); + if (stat.isDirectory()) { + walk(absPath, basePath); + } else if (stat.isFile()) { + addFile(absPath, relPath); + } + }); +} + +includePaths.forEach((relativePath) => { + const relative = relativePath.replace(/\\/g, '/'); + const abs = path.join(rootDir, relative); + if (!fs.existsSync(abs)) { + return; + } + const stat = fs.statSync(abs); + if (stat.isDirectory()) { + walk(abs, rootDir); + } else if (stat.isFile()) { + addFile(abs, path.relative(rootDir, abs).replace(/\\/g, '/')); + } +}); + +process.stdout.write(hash.digest('hex')); diff --git a/scripts/compile/icu.sh b/scripts/compile/icu.sh index 1029d7cf..d96a15ce 100755 --- a/scripts/compile/icu.sh +++ b/scripts/compile/icu.sh @@ -35,6 +35,13 @@ else CONFIGURE_PREFIX=() fi +CC_BIN=$CROSS_COMPILE_PLATFORM_CC-clang +CXX_BIN=$CROSS_COMPILE_PLATFORM_CC-clang++ +if [[ -n "$JSC_CCACHE_BIN" ]]; then + CC_BIN="$JSC_CCACHE_BIN $CC_BIN" + CXX_BIN="$JSC_CCACHE_BIN $CXX_BIN" +fi + "${CONFIGURE_PREFIX[@]}" $TARGETDIR/icu/source/configure --prefix=${INSTALL_DIR} \ $BUILD_TYPE_CONFIG \ --host=$CROSS_COMPILE_PLATFORM \ @@ -52,8 +59,8 @@ fi CFLAGS="$ICU_CFLAGS" \ CXXFLAGS="$ICU_CXXFLAGS" \ LDFLAGS="$ICU_LDFLAGS" \ - CC=$CROSS_COMPILE_PLATFORM_CC-clang \ - CXX=$CROSS_COMPILE_PLATFORM_CC-clang++ \ + CC="$CC_BIN" \ + CXX="$CXX_BIN" \ AR=$TOOLCHAIN_DIR/bin/llvm-ar \ LD=$TOOLCHAIN_DIR/bin/ld \ RANLIB=$TOOLCHAIN_DIR/bin/llvm-ranlib \ diff --git a/scripts/compile/jsc.sh b/scripts/compile/jsc.sh index 302ac49e..8abcd62a 100755 --- a/scripts/compile/jsc.sh +++ b/scripts/compile/jsc.sh @@ -6,6 +6,11 @@ source $SCRIPT_DIR/common.sh CMAKE_FOLDER=$(cd $ANDROID_HOME/cmake && ls -1 | sort -r | head -1) PATH=$TOOLCHAIN_DIR/bin:$ANDROID_HOME/cmake/$CMAKE_FOLDER/bin/:$PATH +CCACHE_CMAKE_ARGS="" +if [[ -n "$JSC_CCACHE_BIN" ]]; then + CCACHE_CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=${JSC_CCACHE_BIN} -DCMAKE_CXX_COMPILER_LAUNCHER=${JSC_CCACHE_BIN}" +fi + rm -rf $TARGETDIR/webkit/$CROSS_COMPILE_PLATFORM-${FLAVOR} rm -rf $TARGETDIR/webkit/WebKitBuild cd $TARGETDIR/webkit/Tools/Scripts @@ -75,6 +80,7 @@ $TARGETDIR/webkit/Tools/Scripts/build-webkit \ -DCMAKE_CXX_STANDARD_REQUIRED=ON \ -DCMAKE_CXX_EXTENSIONS=OFF \ -DCMAKE_VERBOSE_MAKEFILE=on \ + $CCACHE_CMAKE_ARGS \ -DENABLE_API_TESTS=OFF \ -DENABLE_SAMPLING_PROFILER=OFF \ -DENABLE_DFG_JIT=OFF \ diff --git a/scripts/publish.js b/scripts/publish.js index bcc1fbcb..9a51d269 100755 --- a/scripts/publish.js +++ b/scripts/publish.js @@ -21,7 +21,7 @@ commander .option('--dry-run', 'Dry run mode for npm publish') .parse(process.argv); -const artifactZipFile = verifyFile(commander.args[0], ''); +const artifactInput = resolveArtifactPath(commander.args[0], ''); const rootDir = path.dirname(__dirname); const pkgJsonPath = path.join(rootDir, 'package.json'); const packageTemplate = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8')); @@ -33,7 +33,11 @@ if (fs.existsSync(workDir)) { } fs.mkdirSync(workDir, {recursive: true}); -child_process.execFileSync('unzip', [artifactZipFile, '-d', workDir]); +if (artifactInput.isDirectory) { + fs.cpSync(artifactInput.path, workDir, {recursive: true}); +} else { + child_process.execFileSync('unzip', [artifactInput.path, '-d', workDir]); +} const variantList = Array.isArray(packageTemplate.config?.ndkVariants) ? packageTemplate.config.ndkVariants @@ -111,26 +115,27 @@ function copyDir(source, destination) { fs.cpSync(source, destination, {recursive: true, force: true}); } -function verifyFile(filePath, argName) { - if (filePath == null) { +function resolveArtifactPath(inputPath, argName) { + if (inputPath == null) { console.error(`Error: ${argName} is required`); process.exit(1); } + const resolvedPath = path.resolve(process.cwd(), inputPath); let stat; try { - stat = fs.lstatSync(filePath); + stat = fs.lstatSync(resolvedPath); } catch (error) { console.error(error.toString()); process.exit(1); } - if (!stat.isFile()) { - console.error(`Error: ${argName} is not a regular file`); + if (!stat.isDirectory() && !stat.isFile()) { + console.error(`Error: ${argName} must be a directory or zip file`); process.exit(1); } - return filePath; + return {path: resolvedPath, isDirectory: stat.isDirectory()}; } function createPatchedContext(rootDir, options, wrappedRunner) { @@ -151,6 +156,9 @@ function createPatchedContext(rootDir, options, wrappedRunner) { if (variant.npmPackage) { patchedConfig.config.selectedNdkPackage = variant.npmPackage; } + if (variant.distDir) { + patchedConfig.config.selectedNdkDistDir = variant.distDir; + } } if (versionSuffix) { patchedConfig.version += '-' + versionSuffix; diff --git a/scripts/start.sh b/scripts/start.sh index d2e80f78..70fcc4cb 100755 --- a/scripts/start.sh +++ b/scripts/start.sh @@ -104,7 +104,13 @@ patchAndMakeICU() { CONFIG_ENV+=("LDFLAGS=$LDFLAGS") fi if [[ $HAS_CLANG -eq 1 ]]; then - CONFIG_ENV+=("CC=clang" "CXX=clang++") + local cc_cmd="clang" + local cxx_cmd="clang++" + if [[ -n "$JSC_CCACHE_BIN" ]]; then + cc_cmd="$JSC_CCACHE_BIN clang" + cxx_cmd="$JSC_CCACHE_BIN clang++" + fi + CONFIG_ENV+=("CC=$cc_cmd" "CXX=$cxx_cmd") fi if [[ -f "$ICU_FILTER_FILE" ]]; then diff --git a/scripts/toolchain.sh b/scripts/toolchain.sh new file mode 100644 index 00000000..e4571631 --- /dev/null +++ b/scripts/toolchain.sh @@ -0,0 +1,193 @@ +#!/bin/bash + +if [[ "${JSC_TOOLCHAIN_CONFIGURED:-0}" == "1" ]]; then + return +fi +export JSC_TOOLCHAIN_CONFIGURED=1 + +if ! [[ $ROOTDIR ]]; then + ROOTDIR=$(pwd) +fi + +ANDROID_NDK_PATH="${ANDROID_NDK:-}" +SOURCE_PROPERTIES_PATH="" +if [[ -n "$ANDROID_NDK_PATH" && -f "$ANDROID_NDK_PATH/source.properties" ]]; then + SOURCE_PROPERTIES_PATH="$ANDROID_NDK_PATH/source.properties" +fi + +VARIANT_OVERRIDE="${JSC_NDK_VARIANT:-}" + +variant_output=$(node /dev/stdin "$ROOTDIR" "$VARIANT_OVERRIDE" "$SOURCE_PROPERTIES_PATH" <<'NODE' +const fs = require('fs'); +const path = require('path'); + +const rootDir = process.argv[2]; +const variantOverride = process.argv[3] || ''; +const sourcePropertiesPath = process.argv[4]; + +const pkg = require(path.join(rootDir, 'package.json')); +const variants = Array.isArray(pkg.config?.ndkVariants) + ? pkg.config.ndkVariants + : []; + +const defaultVariant = + variants.find((variant) => variant && variant.default) || + variants[0] || + null; + +let revision = ''; +if (sourcePropertiesPath && fs.existsSync(sourcePropertiesPath)) { + const contents = fs.readFileSync(sourcePropertiesPath, 'utf8'); + const match = contents.match(/Pkg\.Revision\s*=\s*([^\s]+)/); + if (match) { + revision = match[1].trim(); + } +} + +const matchRevision = (variant) => { + if (!revision || !variant) { + return false; + } + const prefixes = Array.isArray(variant.pkgRevisionPrefixes) + ? variant.pkgRevisionPrefixes + : []; + return prefixes.some((prefix) => revision.startsWith(prefix)); +}; + +let variant = + (variantOverride && variants.find((variant) => variant.id === variantOverride)) || + variants.find(matchRevision) || + defaultVariant || + null; + +const emit = (line) => { + if (line !== undefined && line !== null) { + console.log(line); + } +}; + +if (revision) { + emit(`JSC_TOOLCHAIN_NDK_REVISION=${revision}`); +} + +if (defaultVariant) { + emit(`JSC_TOOLCHAIN_DEFAULT_VARIANT=${defaultVariant.id}`); + if (defaultVariant.npmPackage) { + emit(`JSC_TOOLCHAIN_DEFAULT_NPM_PACKAGE=${defaultVariant.npmPackage}`); + } + if (defaultVariant.distDir) { + emit(`JSC_TOOLCHAIN_DEFAULT_DIST_DIR=${defaultVariant.distDir}`); + } + if (defaultVariant.distUnstrippedDir) { + emit(`JSC_TOOLCHAIN_DEFAULT_DIST_UNSTRIPPED_DIR=${defaultVariant.distUnstrippedDir}`); + } +} + +if (variant) { + emit(`JSC_TOOLCHAIN_VARIANT=${variant.id}`); + if (variant.npmPackage) { + emit(`JSC_TOOLCHAIN_VARIANT_PACKAGE=${variant.npmPackage}`); + } + const buildSuffix = + variant.hasOwnProperty('buildSuffix') + ? String(variant.buildSuffix ?? '') + : defaultVariant && variant.id === defaultVariant.id + ? '' + : variant.id; + emit(`JSC_TOOLCHAIN_VARIANT_BUILD_SUFFIX=${buildSuffix}`); + if (variant.distDir) { + emit(`JSC_TOOLCHAIN_VARIANT_DIST_DIR=${variant.distDir}`); + } + if (variant.distUnstrippedDir) { + emit(`JSC_TOOLCHAIN_VARIANT_DIST_UNSTRIPPED_DIR=${variant.distUnstrippedDir}`); + } + emit( + `JSC_TOOLCHAIN_VARIANT_DISABLE_LOOP_VECTORIZATION=${variant.disableLoopVectorization ? 1 : 0}`, + ); + emit( + `JSC_TOOLCHAIN_VARIANT_ENABLE_THIN_LTO=${variant.enableThinLTO === false ? 0 : 1}`, + ); +} else { + emit('JSC_TOOLCHAIN_VARIANT='); + emit('JSC_TOOLCHAIN_VARIANT_BUILD_SUFFIX='); + emit('JSC_TOOLCHAIN_VARIANT_DIST_DIR='); + emit('JSC_TOOLCHAIN_VARIANT_DIST_UNSTRIPPED_DIR='); + emit('JSC_TOOLCHAIN_VARIANT_DISABLE_LOOP_VECTORIZATION=0'); + emit('JSC_TOOLCHAIN_VARIANT_ENABLE_THIN_LTO=1'); +} +NODE +) +ret=$? +if [[ $ret -ne 0 ]]; then + echo "Failed to read NDK variant configuration" 1>&2 + exit $ret +fi + +eval "$variant_output" + +if [[ -z "$JSC_TOOLCHAIN_VARIANT" ]]; then + if [[ -n "$JSC_TOOLCHAIN_DEFAULT_VARIANT" ]]; then + export JSC_TOOLCHAIN_VARIANT="$JSC_TOOLCHAIN_DEFAULT_VARIANT" + else + export JSC_TOOLCHAIN_VARIANT=unknown + fi +fi + +if [[ -n "$JSC_TOOLCHAIN_VARIANT_BUILD_SUFFIX" && -z "$JSC_BUILD_VARIANT" ]]; then + if [[ -n "$JSC_TOOLCHAIN_VARIANT_BUILD_SUFFIX" ]]; then + export JSC_BUILD_VARIANT="$JSC_TOOLCHAIN_VARIANT_BUILD_SUFFIX" + fi +fi + +if [[ -n "$JSC_TOOLCHAIN_VARIANT_DIST_DIR" && -z "$JSC_DIST_DIR" ]]; then + if [[ "$JSC_TOOLCHAIN_VARIANT_DIST_DIR" = /* ]]; then + export JSC_DIST_DIR="$JSC_TOOLCHAIN_VARIANT_DIST_DIR" + else + export JSC_DIST_DIR="$ROOTDIR/$JSC_TOOLCHAIN_VARIANT_DIST_DIR" + fi +fi + +if [[ -n "$JSC_TOOLCHAIN_VARIANT_DIST_UNSTRIPPED_DIR" && -z "$JSC_DIST_UNSTRIPPED_DIR" ]]; then + if [[ "$JSC_TOOLCHAIN_VARIANT_DIST_UNSTRIPPED_DIR" = /* ]]; then + export JSC_DIST_UNSTRIPPED_DIR="$JSC_TOOLCHAIN_VARIANT_DIST_UNSTRIPPED_DIR" + else + export JSC_DIST_UNSTRIPPED_DIR="$ROOTDIR/$JSC_TOOLCHAIN_VARIANT_DIST_UNSTRIPPED_DIR" + fi +fi + +if [[ -z "$JSC_CCACHE_BIN" && "${JSC_TOOLCHAIN_DISABLE_CCACHE:-0}" != "1" ]]; then + if command -v ccache >/dev/null 2>&1; then + export JSC_CCACHE_BIN=$(command -v ccache) + export CCACHE_DIR=${CCACHE_DIR:-$HOME/.cache/ccache} + export CCACHE_BASEDIR=${CCACHE_BASEDIR:-$ROOTDIR} + export CCACHE_COMPRESS=${CCACHE_COMPRESS:-1} + export CCACHE_CPP2=${CCACHE_CPP2:-yes} + export ANDROID_NDK_CCACHE="$JSC_CCACHE_BIN" + fi +fi + +if [[ "${JSC_TOOLCHAIN_VARIANT_ENABLE_THIN_LTO:-1}" == "1" ]]; then + export JSC_TOOLCHAIN_LTO_FLAG="-flto=thin" +else + export JSC_TOOLCHAIN_LTO_FLAG="" +fi + +if [[ "${JSC_TOOLCHAIN_VARIANT_DISABLE_LOOP_VECTORIZATION:-0}" == "1" ]]; then + export JSC_TOOLCHAIN_RELEASE_CFLAGS="-fno-vectorize -fno-slp-vectorize" +else + export JSC_TOOLCHAIN_RELEASE_CFLAGS="-Wno-pass-failed=loop-vectorize" +fi + +if [[ -n "$JSC_TOOLCHAIN_LTO_FLAG" ]]; then + export JSC_TOOLCHAIN_RELEASE_LDFLAGS="$JSC_TOOLCHAIN_LTO_FLAG" +else + export JSC_TOOLCHAIN_RELEASE_LDFLAGS="" +fi + +if [[ "${JSC_TOOLCHAIN_SUPPRESS_LOG:-0}" != "1" ]]; then + if [[ -n "$JSC_TOOLCHAIN_NDK_REVISION" ]]; then + echo "Detected Android NDK revision ${JSC_TOOLCHAIN_NDK_REVISION} (variant ${JSC_TOOLCHAIN_VARIANT})" + else + echo "Using Android NDK variant ${JSC_TOOLCHAIN_VARIANT}" + fi +fi