diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ef1131c..33cf4401 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,6 +32,36 @@ jobs: run: |- POD_VERSION=0.0.1-for-ci-test PRIMJS_COMMIT_ID=${{ github.event.pull_request.head.sha }} bundle exec pod spec lint PrimJS.podspec --verbose --skip-import-validation --allow-warnings + swift-package: + runs-on: macos-latest + steps: + - name: Download Source + uses: actions/checkout@v4.2.2 + - name: Check generated SwiftPM headers + run: python3 tools/ios_tools/generate_spm_headers.py --check + - name: Build and test (macOS) + run: |- + swift build + swift test + - name: Build and test with Debugger trait (macOS) + run: |- + swift build --traits Debugger + swift test --traits Debugger + - name: Build and test (iOS Simulator) + run: |- + UDID=$(xcrun simctl list devices available -j | python3 -c ' + import json, sys + devices = json.load(sys.stdin)["devices"] + runtimes = sorted((r for r in devices if "iOS" in r), reverse=True) + print(next(d["udid"] for r in runtimes for d in devices[r] if d["name"].startswith("iPhone")))') + xcodebuild test -scheme PrimJS-Package \ + -destination "platform=iOS Simulator,id=$UDID" \ + -derivedDataPath .build/DerivedData -quiet + - name: Build (iOS device) + run: |- + xcodebuild build -scheme PrimJS-Package -destination 'generic/platform=iOS' \ + -derivedDataPath .build/DerivedData -quiet + check-unittests-linux: runs-on: lynx-ubuntu-22.04-medium steps: diff --git a/.gitignore b/.gitignore index 466e2480..5f7cc2e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ -out/ \ No newline at end of file +out/ +.DS_Store + +# Swift Package Manager +.build/ +.swiftpm/ +Package.resolved +DerivedData/ diff --git a/Package.swift b/Package.swift new file mode 100644 index 00000000..e8152a99 --- /dev/null +++ b/Package.swift @@ -0,0 +1,183 @@ +// swift-tools-version: 6.1 +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. +// +// Mirrors PrimJS.podspec; keep the two in sync. +// +// PrimJS/quickjs + PrimJS/log -> PrimJS +// PrimJS/quickjs_debugger -> `Debugger` trait (PrimJSInspector) +// PrimJS/napi/{core,env,quickjs} -> PrimJSNAPI +// PrimJS/napi/jsc -> PrimJSNAPIJSC +// PrimJS/napi/adapter -> PrimJSNAPIAdapter +// +// Public headers live in swiftpm/headers//, generated by +// tools/ios_tools/generate_spm_headers.py with the CocoaPods layout so include +// paths are the same for both package managers. arm64 only, like the podspec. + +import PackageDescription + +// GCC_PREPROCESSOR_DEFINITIONS of the pod target and the `quickjs` subspec. +// They change struct layouts in quickjs-inner.h, so every target uses the +// same list. +let podDefines: [CXXSetting] = [ + .define("OS_IOS", to: "1"), + .define("ENABLE_CODECACHE"), + .define("ENABLE_VIRTUAL_STACK", to: "1"), + .define("ENABLE_PRIMJS_SNAPSHOT"), + .define("ENABLE_COMPATIBLE_MM"), + .define("ENABLE_LEPUSNG"), + .define("LYNX_SIMPLIFY", to: "0"), + // Like the quickjs_debugger subspec, this switches the whole engine. + .define("ENABLE_QUICKJS_DEBUGGER", .when(traits: ["Debugger"])), +] + +let podHeaderSearchPaths: [CXXSetting] = [ + .headerSearchPath("src"), + .headerSearchPath("src/interpreter"), +] + +// The napi sources include their headers by bare name. +let napiHeaderSearchPaths: [CXXSetting] = [ + .headerSearchPath("src/napi"), + .headerSearchPath("src/napi/common"), + .headerSearchPath("src/napi/env"), + .headerSearchPath("src/napi/quickjs"), + .headerSearchPath("src/napi/adapter"), +] + +let package = Package( + name: "PrimJS", + platforms: [ + .iOS(.v12), + // Same configuration as iOS; used for `swift build` / `swift test`. + .macOS(.v10_15), + ], + products: [ + .library(name: "PrimJS", targets: ["PrimJS"]), + .library(name: "PrimJSNAPI", targets: ["PrimJSNAPI"]), + .library(name: "PrimJSNAPIJSC", targets: ["PrimJSNAPIJSC"]), + .library(name: "PrimJSNAPIAdapter", targets: ["PrimJSNAPIAdapter"]), + ], + traits: [ + .trait( + name: "Debugger", + description: + "Build the engine with the Chrome DevTools Protocol debugger " + + "(PrimJS/quickjs_debugger). Changes the engine ABI."), + ], + targets: [ + .target( + name: "PrimJS", + dependencies: [ + .target(name: "PrimJSInspector", condition: .when(traits: ["Debugger"])) + ], + path: ".", + exclude: [ + "src/gc/BUILD.gn", + "src/interpreter/quickjs/BUILD.gn", + ], + sources: [ + "src/gc", + "src/interpreter/quickjs", + // embedded.S / embedded-inspector.S guard on ENABLE_QUICKJS_DEBUGGER. + "src/interpreter/primjs/ios", + "src/basic/log", + ], + publicHeadersPath: "swiftpm/headers/PrimJS", + cxxSettings: podDefines + podHeaderSearchPaths + ), + + // Always built with the debugger ABI; linked only under the trait. The + // engine and the inspector reference each other; the linker resolves it. + .target( + name: "PrimJSInspector", + path: ".", + exclude: [ + "src/inspector/BUILD.gn" + ], + sources: [ + "src/inspector" + ], + publicHeadersPath: "swiftpm/headers/PrimJSInspector", + cxxSettings: podDefines + podHeaderSearchPaths + [ + .define("ENABLE_QUICKJS_DEBUGGER") + ] + ), + + .target( + name: "PrimJSNAPI", + dependencies: ["PrimJS"], + path: ".", + exclude: [ + "src/napi/common/BUILD.gn", + "src/napi/env/BUILD.gn", + "src/napi/quickjs/BUILD.gn", + ], + sources: [ + // The podspec glob src/napi/*.{h,cc} is non-recursive. + "src/napi/napi.cc", + "src/napi/napi_module.cc", + "src/napi/common", + "src/napi/env", + "src/napi/quickjs", + ], + publicHeadersPath: "swiftpm/headers/PrimJSNAPI", + cxxSettings: podDefines + podHeaderSearchPaths + napiHeaderSearchPaths + ), + + .target( + name: "PrimJSNAPIJSC", + dependencies: ["PrimJSNAPI", "PrimJS"], + path: ".", + exclude: [ + "src/napi/jsc/BUILD.gn" + ], + sources: [ + "src/napi/jsc" + ], + publicHeadersPath: "swiftpm/headers/PrimJSNAPIJSC", + cxxSettings: podDefines + podHeaderSearchPaths + napiHeaderSearchPaths + [ + .define("JSC_OBJC_API_ENABLED", to: "0") + ], + linkerSettings: [ + .linkedFramework("JavaScriptCore") + ] + ), + + .target( + name: "PrimJSNAPIAdapter", + dependencies: ["PrimJSNAPI"], + path: ".", + sources: [ + // weak_napi_host_injector.cc belongs to the LynxWeakNodeAPI pod. + "src/napi/adapter/js_native_api_adapter.cc", + "src/napi/adapter/weak_napi_host_generator.cc", + ], + publicHeadersPath: "swiftpm/headers/PrimJSNAPIAdapter", + cxxSettings: podDefines + podHeaderSearchPaths + napiHeaderSearchPaths + ), + + .testTarget( + name: "PrimJSTests", + dependencies: ["PrimJS", "PrimJSNAPI"], + path: "swiftpm/Tests/PrimJSTests", + cxxSettings: [ + // Users of the Node-API headers must define this themselves. + .define("ENABLE_CODECACHE") + ], + linkerSettings: [ + // Only linked implicitly for Swift test targets. + .linkedFramework("Foundation"), + .linkedFramework("XCTest"), + ] + ), + + .testTarget( + name: "PrimJSSwiftTests", + dependencies: ["PrimJS"], + path: "swiftpm/Tests/PrimJSSwiftTests" + ), + ], + cxxLanguageStandard: .gnucxx17 +) diff --git a/swiftpm/Tests/PrimJSSwiftTests/PrimJSSwiftTests.swift b/swiftpm/Tests/PrimJSSwiftTests/PrimJSSwiftTests.swift new file mode 100644 index 00000000..8ecd5eb8 --- /dev/null +++ b/swiftpm/Tests/PrimJSSwiftTests/PrimJSSwiftTests.swift @@ -0,0 +1,26 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +// Swift consumer: `import PrimJS` goes through the generated Clang module +// (swiftpm/headers/PrimJS/module.modulemap), which exposes the C API only. + +import PrimJS +import XCTest + +final class PrimJSSwiftTests: XCTestCase { + func testEvaluatesScript() throws { + let runtime = try XCTUnwrap(LEPUS_NewRuntime()) + defer { LEPUS_FreeRuntime(runtime) } + let context = try XCTUnwrap(LEPUS_NewContext(runtime)) + defer { LEPUS_FreeContext(context) } + + let script = "const add = (a, b) => a + b; add(40, 2)" + let value = LEPUS_Eval(context, script, script.utf8.count, "", 0) + XCTAssertEqual(LEPUS_IsException(value), 0) + + var result: Int32 = 0 + XCTAssertEqual(LEPUS_ToInt32(context, &result, value), 0) + XCTAssertEqual(result, 42) + } +} diff --git a/swiftpm/Tests/PrimJSTests/PrimJSTests.mm b/swiftpm/Tests/PrimJSTests/PrimJSTests.mm new file mode 100644 index 00000000..3dfbf081 --- /dev/null +++ b/swiftpm/Tests/PrimJSTests/PrimJSTests.mm @@ -0,0 +1,65 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +// Smoke test for the Swift Package Manager build. `swift build` only archives +// the library targets, so this is what links the pre-generated snapshot +// (embedded.S) and runs the template interpreter. Includes use the CocoaPods +// paths reproduced by swiftpm/headers. + +#import + +#include "napi.h" +#include "napi_env.h" +#include "napi_env_quickjs.h" +#include "quickjs/include/quickjs.h" +#include "quickjs/include/trace-gc.h" + +@interface PrimJSTests : XCTestCase +@end + +@implementation PrimJSTests { + LEPUSRuntime *_runtime; + LEPUSContext *_context; +} + +- (void)setUp { + _runtime = LEPUS_NewRuntime(); + _context = LEPUS_NewContext(_runtime); +} + +- (void)tearDown { + LEPUS_FreeContext(_context); + LEPUS_FreeRuntime(_runtime); +} + +- (void)testEvaluatesScript { + // ENABLE_COMPATIBLE_MM (from the podspec) means tracing GC plus the + // template interpreter, i.e. the embedded snapshot is exercised here. + XCTAssertTrue(LEPUS_IsGCMode(_context)); + + const char *script = "let sum = 0; for (let i = 0; i < 1000; i++) sum += i; sum"; + HandleScope scope(_context); + LEPUSValue value = LEPUS_Eval(_context, script, strlen(script), "", + LEPUS_EVAL_TYPE_GLOBAL); + scope.PushHandle(&value, HANDLE_TYPE_LEPUS_VALUE); + XCTAssertFalse(LEPUS_IsException(value)); + + int32_t result = 0; + XCTAssertEqual(LEPUS_ToInt32(_context, &result, value), 0); + XCTAssertEqual(result, 499500); +} + +- (void)testNodeAPIOnQuickJS { + napi_env env = napi_new_env(); + napi_attach_quickjs(env, _context); + { + Napi::HandleScope scope(env); + Napi::Value result = Napi::Env(env).RunScript("6 * 7"); + XCTAssertEqual(result.As().Int32Value(), 42); + } + napi_detach_quickjs(env); + napi_free_env(env); +} + +@end diff --git a/swiftpm/headers/PrimJS/PrimJS.h b/swiftpm/headers/PrimJS/PrimJS.h new file mode 100644 index 00000000..b3056511 --- /dev/null +++ b/swiftpm/headers/PrimJS/PrimJS.h @@ -0,0 +1,18 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +// Umbrella header of the `PrimJS` Clang module (see module.modulemap). It +// lists the C API that is importable from Swift and Objective-C; the C++ +// headers next to it stay textual includes. + +#ifndef SWIFTPM_HEADERS_PRIMJS_PRIMJS_H_ +#define SWIFTPM_HEADERS_PRIMJS_PRIMJS_H_ + +// quickjs.h is written for C++, where `bool` is built in. When the module is +// built as C (Swift importer, Objective-C) it comes from here. +#include + +#include "quickjs/include/quickjs.h" + +#endif // SWIFTPM_HEADERS_PRIMJS_PRIMJS_H_ diff --git a/swiftpm/headers/PrimJS/basic/log/logging.h b/swiftpm/headers/PrimJS/basic/log/logging.h new file mode 120000 index 00000000..aa6e5d64 --- /dev/null +++ b/swiftpm/headers/PrimJS/basic/log/logging.h @@ -0,0 +1 @@ +../../../../../src/basic/log/logging.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/interpreter/quickjs/include/base_export.h b/swiftpm/headers/PrimJS/interpreter/quickjs/include/base_export.h new file mode 120000 index 00000000..40796ad1 --- /dev/null +++ b/swiftpm/headers/PrimJS/interpreter/quickjs/include/base_export.h @@ -0,0 +1 @@ +../../../../../../src/interpreter/quickjs/include/base_export.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/module.modulemap b/swiftpm/headers/PrimJS/module.modulemap new file mode 100644 index 00000000..41560d34 --- /dev/null +++ b/swiftpm/headers/PrimJS/module.modulemap @@ -0,0 +1,10 @@ +// Copyright 2024 The Lynx Authors. All rights reserved. +// Licensed under the Apache License Version 2.0 that can be found in the +// LICENSE file in the root directory of this source tree. + +// Without this file SwiftPM would treat the whole directory, C++ headers +// included, as the module and `import PrimJS` would fail. +module PrimJS { + umbrella header "PrimJS.h" + export * +} diff --git a/swiftpm/headers/PrimJS/quickjs/include/alloc_config.h b/swiftpm/headers/PrimJS/quickjs/include/alloc_config.h new file mode 120000 index 00000000..33bff574 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/alloc_config.h @@ -0,0 +1 @@ +../../../../../src/gc/alloc_config.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/alloc_utils.h b/swiftpm/headers/PrimJS/quickjs/include/alloc_utils.h new file mode 120000 index 00000000..14630dd1 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/alloc_utils.h @@ -0,0 +1 @@ +../../../../../src/gc/alloc_utils.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/allocator.h b/swiftpm/headers/PrimJS/quickjs/include/allocator.h new file mode 120000 index 00000000..606429bf --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/allocator.h @@ -0,0 +1 @@ +../../../../../src/gc/allocator.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/base-global-handles.h b/swiftpm/headers/PrimJS/quickjs/include/base-global-handles.h new file mode 120000 index 00000000..a89805a6 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/base-global-handles.h @@ -0,0 +1 @@ +../../../../../src/gc/base-global-handles.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/base_export.h b/swiftpm/headers/PrimJS/quickjs/include/base_export.h new file mode 120000 index 00000000..2edfdb09 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/base_export.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/base_export.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/bignum.h b/swiftpm/headers/PrimJS/quickjs/include/bignum.h new file mode 120000 index 00000000..553ddd95 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/bignum.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/bignum.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/cartesian_tree.h b/swiftpm/headers/PrimJS/quickjs/include/cartesian_tree.h new file mode 120000 index 00000000..e6c7b5de --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/cartesian_tree.h @@ -0,0 +1 @@ +../../../../../src/gc/cartesian_tree.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/collector.h b/swiftpm/headers/PrimJS/quickjs/include/collector.h new file mode 120000 index 00000000..b902b08d --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/collector.h @@ -0,0 +1 @@ +../../../../../src/gc/collector.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/collector_ms.h b/swiftpm/headers/PrimJS/quickjs/include/collector_ms.h new file mode 120000 index 00000000..cefe60a1 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/collector_ms.h @@ -0,0 +1 @@ +../../../../../src/gc/collector_ms.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/cutils.h b/swiftpm/headers/PrimJS/quickjs/include/cutils.h new file mode 120000 index 00000000..d5b669b6 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/cutils.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/cutils.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/deque.h b/swiftpm/headers/PrimJS/quickjs/include/deque.h new file mode 120000 index 00000000..e3705240 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/deque.h @@ -0,0 +1 @@ +../../../../../src/gc/deque.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/dtoa.h b/swiftpm/headers/PrimJS/quickjs/include/dtoa.h new file mode 120000 index 00000000..9bddb930 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/dtoa.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/dtoa.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/gc_marking.h b/swiftpm/headers/PrimJS/quickjs/include/gc_marking.h new file mode 120000 index 00000000..8edf207c --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/gc_marking.h @@ -0,0 +1 @@ +../../../../../src/gc/gc_marking.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/gc_safe_list.h b/swiftpm/headers/PrimJS/quickjs/include/gc_safe_list.h new file mode 120000 index 00000000..a6e9e76c --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/gc_safe_list.h @@ -0,0 +1 @@ +../../../../../src/gc/gc_safe_list.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/gc_tracer.h b/swiftpm/headers/PrimJS/quickjs/include/gc_tracer.h new file mode 120000 index 00000000..4904e082 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/gc_tracer.h @@ -0,0 +1 @@ +../../../../../src/gc/gc_tracer.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/gc_work_stack.h b/swiftpm/headers/PrimJS/quickjs/include/gc_work_stack.h new file mode 120000 index 00000000..96caf675 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/gc_work_stack.h @@ -0,0 +1 @@ +../../../../../src/gc/gc_work_stack.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/global-handles.h b/swiftpm/headers/PrimJS/quickjs/include/global-handles.h new file mode 120000 index 00000000..d09c30be --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/global-handles.h @@ -0,0 +1 @@ +../../../../../src/gc/global-handles.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/libregexp-opcode.h b/swiftpm/headers/PrimJS/quickjs/include/libregexp-opcode.h new file mode 120000 index 00000000..4ec363db --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/libregexp-opcode.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/libregexp-opcode.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/libregexp.h b/swiftpm/headers/PrimJS/quickjs/include/libregexp.h new file mode 120000 index 00000000..13cb1f75 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/libregexp.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/libregexp.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/libunicode-table.h b/swiftpm/headers/PrimJS/quickjs/include/libunicode-table.h new file mode 120000 index 00000000..b5e2afb4 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/libunicode-table.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/libunicode-table.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/libunicode.h b/swiftpm/headers/PrimJS/quickjs/include/libunicode.h new file mode 120000 index 00000000..30aa05a0 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/libunicode.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/libunicode.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/list.h b/swiftpm/headers/PrimJS/quickjs/include/list.h new file mode 120000 index 00000000..c7916517 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/list.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/list.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/logger.h b/swiftpm/headers/PrimJS/quickjs/include/logger.h new file mode 120000 index 00000000..af74616b --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/logger.h @@ -0,0 +1 @@ +../../../../../src/gc/logger.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/mark_stack.h b/swiftpm/headers/PrimJS/quickjs/include/mark_stack.h new file mode 120000 index 00000000..1df357b8 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/mark_stack.h @@ -0,0 +1 @@ +../../../../../src/gc/mark_stack.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/mem_map.h b/swiftpm/headers/PrimJS/quickjs/include/mem_map.h new file mode 120000 index 00000000..85b5b460 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/mem_map.h @@ -0,0 +1 @@ +../../../../../src/gc/mem_map.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/mrt_bitmap.h b/swiftpm/headers/PrimJS/quickjs/include/mrt_bitmap.h new file mode 120000 index 00000000..9f50fab6 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/mrt_bitmap.h @@ -0,0 +1 @@ +../../../../../src/gc/mrt_bitmap.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/page_group.h b/swiftpm/headers/PrimJS/quickjs/include/page_group.h new file mode 120000 index 00000000..15916c71 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/page_group.h @@ -0,0 +1 @@ +../../../../../src/gc/page_group.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/page_map.h b/swiftpm/headers/PrimJS/quickjs/include/page_map.h new file mode 120000 index 00000000..53e047d5 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/page_map.h @@ -0,0 +1 @@ +../../../../../src/gc/page_map.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/persistent-handle.h b/swiftpm/headers/PrimJS/quickjs/include/persistent-handle.h new file mode 120000 index 00000000..1af7639a --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/persistent-handle.h @@ -0,0 +1 @@ +../../../../../src/gc/persistent-handle.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/primjs_monitor.h b/swiftpm/headers/PrimJS/quickjs/include/primjs_monitor.h new file mode 120000 index 00000000..02375bfe --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/primjs_monitor.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/primjs_monitor.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/qjsvaluevalue-space.h b/swiftpm/headers/PrimJS/quickjs/include/qjsvaluevalue-space.h new file mode 120000 index 00000000..b1f61762 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/qjsvaluevalue-space.h @@ -0,0 +1 @@ +../../../../../src/gc/qjsvaluevalue-space.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs-atom.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs-atom.h new file mode 120000 index 00000000..88bc9d41 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs-atom.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs-atom.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs-inner.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs-inner.h new file mode 120000 index 00000000..b9b01809 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs-inner.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs-inner.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs-libc.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs-libc.h new file mode 120000 index 00000000..40670e48 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs-libc.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs-libc.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs-opcode.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs-opcode.h new file mode 120000 index 00000000..d7d36357 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs-opcode.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs-opcode.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs-opt-bytecode.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs-opt-bytecode.h new file mode 120000 index 00000000..a4898800 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs-opt-bytecode.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs-opt-bytecode.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs-tag.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs-tag.h new file mode 120000 index 00000000..6e5bedea --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs-tag.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs-tag.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs.h new file mode 120000 index 00000000..3af65731 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/quickjs_version.h b/swiftpm/headers/PrimJS/quickjs/include/quickjs_version.h new file mode 120000 index 00000000..05984aab --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/quickjs_version.h @@ -0,0 +1 @@ +../../../../../src/interpreter/quickjs/include/quickjs_version.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/ros_alloc_run.h b/swiftpm/headers/PrimJS/quickjs/include/ros_alloc_run.h new file mode 120000 index 00000000..3ef7e3b1 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/ros_alloc_run.h @@ -0,0 +1 @@ +../../../../../src/gc/ros_alloc_run.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/ros_allocator.h b/swiftpm/headers/PrimJS/quickjs/include/ros_allocator.h new file mode 120000 index 00000000..3b5609e8 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/ros_allocator.h @@ -0,0 +1 @@ +../../../../../src/gc/ros_allocator.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/ros_allocator_inlined.h b/swiftpm/headers/PrimJS/quickjs/include/ros_allocator_inlined.h new file mode 120000 index 00000000..938a5ab7 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/ros_allocator_inlined.h @@ -0,0 +1 @@ +../../../../../src/gc/ros_allocator_inlined.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/sizes.h b/swiftpm/headers/PrimJS/quickjs/include/sizes.h new file mode 120000 index 00000000..f8cf198c --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/sizes.h @@ -0,0 +1 @@ +../../../../../src/gc/sizes.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/space.h b/swiftpm/headers/PrimJS/quickjs/include/space.h new file mode 120000 index 00000000..79eaa889 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/space.h @@ -0,0 +1 @@ +../../../../../src/gc/space.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/structs.h b/swiftpm/headers/PrimJS/quickjs/include/structs.h new file mode 120000 index 00000000..323271c2 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/structs.h @@ -0,0 +1 @@ +../../../../../src/gc/structs.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/thread_pool.h b/swiftpm/headers/PrimJS/quickjs/include/thread_pool.h new file mode 120000 index 00000000..2ea9d5d5 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/thread_pool.h @@ -0,0 +1 @@ +../../../../../src/gc/thread_pool.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJS/quickjs/include/trace-gc.h b/swiftpm/headers/PrimJS/quickjs/include/trace-gc.h new file mode 120000 index 00000000..348efde3 --- /dev/null +++ b/swiftpm/headers/PrimJS/quickjs/include/trace-gc.h @@ -0,0 +1 @@ +../../../../../src/gc/trace-gc.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSInspector/devtool/quickjs/debugger_inner.h b/swiftpm/headers/PrimJSInspector/devtool/quickjs/debugger_inner.h new file mode 120000 index 00000000..ba1e251b --- /dev/null +++ b/swiftpm/headers/PrimJSInspector/devtool/quickjs/debugger_inner.h @@ -0,0 +1 @@ +../../../../../src/inspector/debugger_inner.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSInspector/devtool/quickjs/debugger_struct.h b/swiftpm/headers/PrimJSInspector/devtool/quickjs/debugger_struct.h new file mode 120000 index 00000000..eb0750b6 --- /dev/null +++ b/swiftpm/headers/PrimJSInspector/devtool/quickjs/debugger_struct.h @@ -0,0 +1 @@ +../../../../../src/inspector/debugger_struct.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSInspector/devtool/quickjs/protocols.h b/swiftpm/headers/PrimJSInspector/devtool/quickjs/protocols.h new file mode 120000 index 00000000..10de1da6 --- /dev/null +++ b/swiftpm/headers/PrimJSInspector/devtool/quickjs/protocols.h @@ -0,0 +1 @@ +../../../../../src/inspector/protocols.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/code_cache.h b/swiftpm/headers/PrimJSNAPI/code_cache.h new file mode 120000 index 00000000..22ef5f9d --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/code_cache.h @@ -0,0 +1 @@ +../../../src/napi/common/code_cache.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/js_native_api.h b/swiftpm/headers/PrimJSNAPI/js_native_api.h new file mode 120000 index 00000000..1a537314 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/js_native_api.h @@ -0,0 +1 @@ +../../../src/napi/js_native_api.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/js_native_api_types.h b/swiftpm/headers/PrimJSNAPI/js_native_api_types.h new file mode 120000 index 00000000..ef32ace9 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/js_native_api_types.h @@ -0,0 +1 @@ +../../../src/napi/js_native_api_types.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/napi.h b/swiftpm/headers/PrimJSNAPI/napi.h new file mode 120000 index 00000000..29fb6996 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/napi.h @@ -0,0 +1 @@ +../../../src/napi/napi.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/napi_env.h b/swiftpm/headers/PrimJSNAPI/napi_env.h new file mode 120000 index 00000000..977c30e8 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/napi_env.h @@ -0,0 +1 @@ +../../../src/napi/env/napi_env.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/napi_env_quickjs.h b/swiftpm/headers/PrimJSNAPI/napi_env_quickjs.h new file mode 120000 index 00000000..a59acd00 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/napi_env_quickjs.h @@ -0,0 +1 @@ +../../../src/napi/quickjs/napi_env_quickjs.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/napi_module.h b/swiftpm/headers/PrimJSNAPI/napi_module.h new file mode 120000 index 00000000..f0369e39 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/napi_module.h @@ -0,0 +1 @@ +../../../src/napi/napi_module.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/napi_runtime.h b/swiftpm/headers/PrimJSNAPI/napi_runtime.h new file mode 120000 index 00000000..b49aa5ae --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/napi_runtime.h @@ -0,0 +1 @@ +../../../src/napi/env/napi_runtime.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/napi_state.h b/swiftpm/headers/PrimJSNAPI/napi_state.h new file mode 120000 index 00000000..4d327afa --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/napi_state.h @@ -0,0 +1 @@ +../../../src/napi/common/napi_state.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPI/shim_js_native_api.h b/swiftpm/headers/PrimJSNAPI/shim_js_native_api.h new file mode 120000 index 00000000..051a2eec --- /dev/null +++ b/swiftpm/headers/PrimJSNAPI/shim_js_native_api.h @@ -0,0 +1 @@ +../../../src/napi/shim_js_native_api.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPIAdapter/js_native_api_adapter.h b/swiftpm/headers/PrimJSNAPIAdapter/js_native_api_adapter.h new file mode 120000 index 00000000..5cad6ef6 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPIAdapter/js_native_api_adapter.h @@ -0,0 +1 @@ +../../../src/napi/adapter/js_native_api_adapter.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPIAdapter/primjs_weak_node_api_provider.h b/swiftpm/headers/PrimJSNAPIAdapter/primjs_weak_node_api_provider.h new file mode 120000 index 00000000..481515dd --- /dev/null +++ b/swiftpm/headers/PrimJSNAPIAdapter/primjs_weak_node_api_provider.h @@ -0,0 +1 @@ +../../../src/napi/adapter/primjs_weak_node_api_provider.h \ No newline at end of file diff --git a/swiftpm/headers/PrimJSNAPIJSC/napi_env_jsc.h b/swiftpm/headers/PrimJSNAPIJSC/napi_env_jsc.h new file mode 120000 index 00000000..aa65e5a1 --- /dev/null +++ b/swiftpm/headers/PrimJSNAPIJSC/napi_env_jsc.h @@ -0,0 +1 @@ +../../../src/napi/jsc/napi_env_jsc.h \ No newline at end of file diff --git a/tools/ios_tools/generate_spm_headers.py b/tools/ios_tools/generate_spm_headers.py new file mode 100644 index 00000000..8897302e --- /dev/null +++ b/tools/ios_tools/generate_spm_headers.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 +# Copyright 2024 The Lynx Authors. All rights reserved. +# Licensed under the Apache License Version 2.0 that can be found in the +# LICENSE file in the root directory of this source tree. +"""Generates the public header directories used by Package.swift. + +Swift Package Manager allows a single public header directory per target and +cannot rename or re-root headers, so each target gets a directory of relative +symlinks under swiftpm/headers//. The layout follows the CocoaPods +rules for `public_header_files`, `header_dir` and `header_mappings_dir` +(Headers/Public/PrimJS//), which keeps the include paths +identical to the ones CocoaPods users have always used, e.g. +`quickjs/include/quickjs.h`, `quickjs/include/trace-gc.h`, `napi_env_quickjs.h`. + +TARGETS below is the source of truth for what each SwiftPM target publishes. +When PrimJS.podspec changes its public headers, mirror the change here. + +The script owns only the symlinks. Regular files in swiftpm/headers (the +hand-written umbrella header and module map that make `import PrimJS` work +from Swift) are left alone. + +Usage: + python3 tools/ios_tools/generate_spm_headers.py # regenerate + python3 tools/ios_tools/generate_spm_headers.py --check # verify (CI) +""" + +import argparse +import os +import sys +from dataclasses import dataclass +from pathlib import Path, PurePosixPath + +ROOT = Path(__file__).resolve().parents[2] +OUTPUT = ROOT / "swiftpm" / "headers" + + +@dataclass(frozen=True) +class Headers: + """A group of public headers, mapped the way CocoaPods maps a subspec. + + patterns: globs relative to the repository root. + exclude: globs removed from the result. + header_dir: directory the headers are published under (flattened). + mappings_dir: if set, the directory structure below it is preserved + instead of flattening. + """ + + patterns: tuple + exclude: tuple = () + header_dir: str = None + mappings_dir: str = None + + +# SwiftPM target -> public headers. Mirrors PrimJS.podspec: +# PrimJS = subspecs quickjs + log +# PrimJSInspector = subspec quickjs_debugger +# PrimJSNAPI = subspecs napi/core + napi/env + napi/quickjs +# PrimJSNAPIJSC = subspec napi/jsc +# PrimJSNAPIAdapter = subspec napi/adapter +TARGETS = { + "PrimJS": ( + Headers( + patterns=("src/interpreter/quickjs/include/*.h", "src/gc/*.h"), + header_dir="quickjs/include", + ), + Headers( + patterns=( + "src/basic/log/logging.h", + "src/interpreter/quickjs/include/base_export.h", + ), + mappings_dir="src", + ), + ), + "PrimJSInspector": ( + Headers( + patterns=("src/inspector/*.h",), + exclude=("src/inspector/interface.h",), + header_dir="devtool/quickjs", + ), + ), + "PrimJSNAPI": ( + Headers( + patterns=( + "src/napi/*.h", + "src/napi/common/*.h", + "src/napi/env/*.h", + "src/napi/quickjs/napi_env_quickjs.h", + ), + ), + ), + "PrimJSNAPIJSC": (Headers(patterns=("src/napi/jsc/napi_env_jsc.h",)),), + "PrimJSNAPIAdapter": ( + Headers( + patterns=( + "src/napi/adapter/js_native_api_adapter.h", + "src/napi/adapter/primjs_weak_node_api_provider.h", + ), + ), + ), +} + + +def expand(patterns): + files = set() + for pattern in patterns: + matches = sorted(ROOT.glob(pattern)) + if not matches: + sys.exit(f"error: {pattern!r} matches no files") + files.update(p for p in matches if p.is_file()) + return files + + +def mapped_path(group, header): + """Same rule as CocoaPods' PodTargetInstaller#header_mappings.""" + dest = PurePosixPath(group.header_dir) if group.header_dir else PurePosixPath() + if group.mappings_dir: + dest /= header.relative_to(ROOT / group.mappings_dir).parent.as_posix() + return dest / header.name + + +def collect_mappings(): + """Returns {"/": Path(real header)}.""" + mappings = {} + for target, groups in TARGETS.items(): + for group in groups: + for header in sorted(expand(group.patterns) - expand(group.exclude)): + key = (PurePosixPath(target) / mapped_path(group, header)).as_posix() + if key in mappings and mappings[key] != header: + sys.exit(f"error: {key} is produced by both {mappings[key]} and {header}") + mappings[key] = header + return mappings + + +def link_target(key, header): + return os.path.relpath(header, OUTPUT / PurePosixPath(key).parent) + + +def read_links(): + """Symlinks currently in swiftpm/headers: {"/": link target}.""" + if not OUTPUT.is_dir(): + return {} + return {p.relative_to(OUTPUT).as_posix(): os.readlink(p) + for p in OUTPUT.rglob("*") if p.is_symlink()} + + +def remove_links(): + for key in read_links(): + (OUTPUT / key).unlink() + # Drop directories left empty, deepest first. + for directory in sorted(OUTPUT.rglob("*"), reverse=True): + if directory.is_dir() and not any(directory.iterdir()): + directory.rmdir() + + +def main(): + parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) + parser.add_argument("--check", action="store_true", + help="exit non-zero when swiftpm/headers is out of date") + args = parser.parse_args() + + mappings = collect_mappings() + expected = {key: link_target(key, header) for key, header in mappings.items()} + rel_output = OUTPUT.relative_to(ROOT).as_posix() + + if args.check: + actual = read_links() + problems = [] + problems += [f"missing: {rel_output}/{k}" for k in sorted(expected.keys() - actual.keys())] + problems += [f"stale: {rel_output}/{k}" for k in sorted(actual.keys() - expected.keys())] + problems += [f"changed: {rel_output}/{k}" for k in sorted(expected.keys() & actual.keys()) + if expected[k] != actual[k]] + if problems: + print("\n".join(problems), file=sys.stderr) + script = Path(__file__).resolve().relative_to(ROOT).as_posix() + sys.exit(f"{rel_output} is out of date; run `python3 {script}`") + print(f"{rel_output} is up to date ({len(mappings)} headers)") + return + + remove_links() + for key, target in expected.items(): + link = OUTPUT / key + link.parent.mkdir(parents=True, exist_ok=True) + link.symlink_to(target) + for target in TARGETS: + count = sum(1 for k in expected if k.startswith(f"{target}/")) + print(f"{target:<20} {count} headers") + + +if __name__ == "__main__": + main()