Compare commits

..

53 Commits

Author SHA1 Message Date
Ashcon Partovi
88063eaad5 Fix? 2024-09-24 17:39:37 -07:00
Ashcon Partovi
da2d1e12ed Testing 2024-09-24 17:20:21 -07:00
Ashcon Partovi
6b54dbfa07 Test old 2024-09-24 16:45:04 -07:00
Ashcon Partovi
1dfa557ca4 Test changes 2024-09-24 16:18:23 -07:00
Ashcon Partovi
29d1b4db74 Fix linking 2024-09-24 16:13:37 -07:00
Ashcon Partovi
85edb39a2f Fix linking 2024-09-24 15:58:50 -07:00
Ashcon Partovi
f0384f39b7 Fix command 2024-09-24 15:50:16 -07:00
Ashcon Partovi
2e1b19b9fd Fix option 2024-09-24 15:47:57 -07:00
Ashcon Partovi
53ce1ff6c1 Fix 2024-09-24 15:23:03 -07:00
Ashcon Partovi
eee9b51df3 Fix 2024-09-24 15:14:25 -07:00
Ashcon Partovi
f0036cc6c1 Fix target names 2024-09-24 15:05:48 -07:00
Ashcon Partovi
77a25c0188 Fix target names 2024-09-24 15:03:24 -07:00
Ashcon Partovi
a3777b8c22 Fix alias 2024-09-24 15:01:34 -07:00
Ashcon Partovi
0d9d7a3436 Fix 2024-09-24 14:58:54 -07:00
Ashcon Partovi
d5e7846eaf Fix 2024-09-24 14:49:18 -07:00
Ashcon Partovi
582a53e26e Random ID 2024-09-23 16:24:57 -07:00
Ashcon Partovi
57d9cfc24d Fixes 2024-09-23 16:19:34 -07:00
Ashcon Partovi
31dc58fa79 Typo 2024-09-23 13:34:33 -07:00
Ashcon Partovi
03bb9fd9b5 Fix 2024-09-23 13:31:12 -07:00
Ashcon Partovi
f270f235c8 Fix 2024-09-23 13:30:05 -07:00
Ashcon Partovi
8d1b5856d3 Fixes 2024-09-23 13:22:34 -07:00
Ashcon Partovi
bf488d0b19 Fixes 2024-09-23 13:21:13 -07:00
Ashcon Partovi
9dc0672840 Fixes 2024-09-23 12:59:10 -07:00
Ashcon Partovi
b1d0cb0ea3 Maybe fix 2024-09-23 12:44:54 -07:00
Ashcon Partovi
155ba7ebdf Debug logs 2024-09-23 12:41:46 -07:00
Ashcon Partovi
3757a3e30f Maybe fix 2024-09-23 12:00:57 -07:00
Ashcon Partovi
4b443db0d8 Maybe fix 2024-09-23 11:59:20 -07:00
Ashcon Partovi
d74ae08946 Test 2024-09-23 11:54:56 -07:00
Ashcon Partovi
a7a5ed2fba Update ci 2024-09-20 18:58:02 -07:00
Ashcon Partovi
7d7c9877a4 WIP 2024-09-20 18:58:02 -07:00
Ashcon Partovi
1cd43f5bae WIP 2024-09-20 18:58:02 -07:00
Ashcon Partovi
56b6ed6d97 WIP 2024-09-20 18:58:02 -07:00
Ashcon Partovi
85dd8ef0d4 WIP 2024-09-20 18:58:02 -07:00
Ashcon Partovi
6cb0de3921 WIP 2024-09-20 18:58:02 -07:00
Ashcon Partovi
2c1dcb5a1d Extra if check 2024-09-20 18:58:02 -07:00
Ashcon Partovi
d3f3900da7 Maybe fix webkit 2024-09-20 18:58:02 -07:00
Ashcon Partovi
ef18731db1 Fix format 2024-09-20 18:58:02 -07:00
Ashcon Partovi
7fb15e5a03 Ensure fresh 2024-09-20 18:58:02 -07:00
Ashcon Partovi
2b017cd0ad Fix 2024-09-20 18:58:02 -07:00
Ashcon Partovi
9f857fa418 Run clang-format 2024-09-20 18:58:02 -07:00
Ashcon Partovi
1cde69d0f1 Better webkit download 2024-09-20 18:58:02 -07:00
Ashcon Partovi
800e378b6e Run scripts 2024-09-20 18:58:02 -07:00
Ashcon Partovi
dfe8fb50d9 Build 2024-09-20 18:58:02 -07:00
Ashcon Partovi
0f6cfc0b16 Changes 2024-09-20 18:58:02 -07:00
Ashcon Partovi
019cfb7927 Changes 2024-09-20 18:58:02 -07:00
Ashcon Partovi
7887689fd0 Fixes 2024-09-20 18:58:01 -07:00
Ashcon Partovi
888d8301ca Fix dependency 2024-09-20 18:58:01 -07:00
Ashcon Partovi
704c169e04 changes 2024-09-20 18:58:01 -07:00
Ashcon Partovi
c16e34078a Script changes 2024-09-20 18:58:01 -07:00
Ashcon Partovi
bc9e022c86 Changes 2024-09-20 18:58:00 -07:00
Ashcon Partovi
a88b8130b9 Fix 2024-09-20 18:57:38 -07:00
Ashcon Partovi
2139e8442a Fix 2024-09-20 18:57:38 -07:00
Ashcon Partovi
f50a80a7c1 Linting in buildkite 2024-09-20 18:57:38 -07:00
273 changed files with 10593 additions and 10535 deletions

View File

@@ -12,14 +12,14 @@ steps:
- key: "darwin-aarch64"
group: ":darwin: aarch64"
steps:
- key: "darwin-aarch64-build-deps"
label: ":darwin: aarch64 - build-deps"
- key: "darwin-aarch64-build-vendor"
label: ":darwin: aarch64 - build-vendor"
agents:
queue: "build-darwin"
os: "darwin"
arch: "aarch64"
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "darwin-aarch64-build-cpp"
label: ":darwin: aarch64 - build-cpp"
@@ -46,7 +46,7 @@ steps:
os: "darwin"
arch: "aarch64"
depends_on:
- "darwin-aarch64-build-deps"
- "darwin-aarch64-build-vendor"
- "darwin-aarch64-build-cpp"
- "darwin-aarch64-build-zig"
env:
@@ -114,14 +114,14 @@ steps:
- key: "darwin-x64"
group: ":darwin: x64"
steps:
- key: "darwin-x64-build-deps"
label: ":darwin: x64 - build-deps"
- key: "darwin-x64-build-vendor"
label: ":darwin: x64 - build-vendor"
agents:
queue: "build-darwin"
os: "darwin"
arch: "x64"
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "darwin-x64-build-cpp"
label: ":darwin: x64 - build-cpp"
@@ -148,7 +148,7 @@ steps:
os: "darwin"
arch: "x64"
depends_on:
- "darwin-x64-build-deps"
- "darwin-x64-build-vendor"
- "darwin-x64-build-cpp"
- "darwin-x64-build-zig"
env:
@@ -216,14 +216,14 @@ steps:
- key: "linux-x64"
group: ":linux: x64"
steps:
- key: "linux-x64-build-deps"
label: ":linux: x64 - build-deps"
- key: "linux-x64-build-vendor"
label: ":linux: x64 - build-vendor"
agents:
queue: "build-linux"
os: "linux"
arch: "x64"
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "linux-x64-build-cpp"
label: ":linux: x64 - build-cpp"
@@ -250,7 +250,7 @@ steps:
os: "linux"
arch: "x64"
depends_on:
- "linux-x64-build-deps"
- "linux-x64-build-vendor"
- "linux-x64-build-cpp"
- "linux-x64-build-zig"
env:
@@ -349,8 +349,8 @@ steps:
- key: "linux-x64-baseline"
group: ":linux: x64-baseline"
steps:
- key: "linux-x64-baseline-build-deps"
label: ":linux: x64-baseline - build-deps"
- key: "linux-x64-baseline-build-vendor"
label: ":linux: x64-baseline - build-vendor"
agents:
queue: "build-linux"
os: "linux"
@@ -358,7 +358,7 @@ steps:
env:
ENABLE_BASELINE: "ON"
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "linux-x64-baseline-build-cpp"
label: ":linux: x64-baseline - build-cpp"
@@ -388,7 +388,7 @@ steps:
os: "linux"
arch: "x64"
depends_on:
- "linux-x64-baseline-build-deps"
- "linux-x64-baseline-build-vendor"
- "linux-x64-baseline-build-cpp"
- "linux-x64-baseline-build-zig"
env:
@@ -488,14 +488,14 @@ steps:
- key: "linux-aarch64"
group: ":linux: aarch64"
steps:
- key: "linux-aarch64-build-deps"
label: ":linux: aarch64 - build-deps"
- key: "linux-aarch64-build-vendor"
label: ":linux: aarch64 - build-vendor"
agents:
queue: "build-linux"
os: "linux"
arch: "aarch64"
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "linux-aarch64-build-cpp"
label: ":linux: aarch64 - build-cpp"
@@ -522,7 +522,7 @@ steps:
os: "linux"
arch: "aarch64"
depends_on:
- "linux-aarch64-build-deps"
- "linux-aarch64-build-vendor"
- "linux-aarch64-build-cpp"
- "linux-aarch64-build-zig"
env:
@@ -621,18 +621,14 @@ steps:
- key: "windows-x64"
group: ":windows: x64"
steps:
- key: "windows-x64-build-deps"
label: ":windows: x64 - build-deps"
- key: "windows-x64-build-vendor"
label: ":windows: x64 - build-vendor"
agents:
queue: "build-windows"
os: "windows"
arch: "x64"
retry:
automatic:
- exit_status: 255
limit: 5
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "windows-x64-build-cpp"
label: ":windows: x64 - build-cpp"
@@ -640,10 +636,6 @@ steps:
queue: "build-windows"
os: "windows"
arch: "x64"
retry:
automatic:
- exit_status: 255
limit: 5
env:
BUN_CPP_ONLY: "ON"
command:
@@ -663,13 +655,9 @@ steps:
os: "windows"
arch: "x64"
depends_on:
- "windows-x64-build-deps"
- "windows-x64-build-vendor"
- "windows-x64-build-cpp"
- "windows-x64-build-zig"
retry:
automatic:
- exit_status: 255
limit: 5
env:
BUN_LINK_ONLY: "ON"
command:
@@ -704,20 +692,16 @@ steps:
- key: "windows-x64-baseline"
group: ":windows: x64-baseline"
steps:
- key: "windows-x64-baseline-build-deps"
label: ":windows: x64-baseline - build-deps"
- key: "windows-x64-baseline-build-vendor"
label: ":windows: x64-baseline - build-vendor"
agents:
queue: "build-windows"
os: "windows"
arch: "x64"
retry:
automatic:
- exit_status: 255
limit: 5
env:
ENABLE_BASELINE: "ON"
command:
- "bun run build:ci --target dependencies"
- "bun run build:ci --target vendor"
- key: "windows-x64-baseline-build-cpp"
label: ":windows: x64-baseline - build-cpp"
@@ -725,10 +709,6 @@ steps:
queue: "build-windows"
os: "windows"
arch: "x64"
retry:
automatic:
- exit_status: 255
limit: 5
env:
ENABLE_BASELINE: "ON"
BUN_CPP_ONLY: "ON"
@@ -751,13 +731,9 @@ steps:
os: "windows"
arch: "x64"
depends_on:
- "windows-x64-baseline-build-deps"
- "windows-x64-baseline-build-vendor"
- "windows-x64-baseline-build-cpp"
- "windows-x64-baseline-build-zig"
retry:
automatic:
- exit_status: 255
limit: 5
env:
ENABLE_BASELINE: "ON"
BUN_LINK_ONLY: "ON"

1539
.docker/chrome.json Normal file

File diff suppressed because it is too large Load Diff

14
.docker/chromium.pref Normal file
View File

@@ -0,0 +1,14 @@
# Note: 2 blank lines are required between entries
Package: *
Pin: release a=eoan
Pin-Priority: 500
Package: *
Pin: origin "ftp.debian.org"
Pin-Priority: 300
# Pattern includes 'chromium', 'chromium-browser' and similarly
# named dependencies:
Package: chromium*
Pin: origin "ftp.debian.org"
Pin-Priority: 700

View File

@@ -0,0 +1,8 @@
#!/bin/bash
set -euxo pipefail
name=$(openssl rand -hex 12)
id=$(docker create --name=bun-binary-$name $CONTAINER_TAG)
docker container cp bun-binary-$name:$BUN_RELEASE_DIR bun-binary
echo -e "bun-binary-$name"

3
.docker/debian.list Normal file
View File

@@ -0,0 +1,3 @@
deb http://deb.debian.org/debian buster main
deb http://deb.debian.org/debian buster-updates main
deb http://deb.debian.org/debian-security buster/updates main

View File

@@ -0,0 +1,34 @@
export DOCKER_BUILDKIT=1
export BUILDKIT_ARCH=$(uname -m)
export ARCH=${BUILDKIT_ARCH}
if [ "$BUILDKIT_ARCH" == "amd64" ]; then
export BUILDKIT_ARCH="amd64"
export ARCH=x64
fi
if [ "$BUILDKIT_ARCH" == "x86_64" ]; then
export BUILDKIT_ARCH="amd64"
export ARCH=x64
fi
if [ "$BUILDKIT_ARCH" == "arm64" ]; then
export BUILDKIT_ARCH="arm64"
export ARCH=aarch64
fi
if [ "$BUILDKIT_ARCH" == "aarch64" ]; then
export BUILDKIT_ARCH="arm64"
export ARCH=aarch64
fi
if [ "$BUILDKIT_ARCH" == "armv7l" ]; then
echo "Unsupported platform: $BUILDKIT_ARCH"
exit 1
fi
export BUILD_ID=$(cat build-id)
export CONTAINER_NAME=bun-linux-$ARCH
export DEBUG_CONTAINER_NAME=debug-bun-linux-$ARCH
export TEMP=/tmp/bun-0.0.$BUILD_ID

11
.docker/pull.sh Normal file
View File

@@ -0,0 +1,11 @@
#!/bin/bash
set -euxo pipefail
docker pull bunbunbunbun/bun-test-base:latest --platform=linux/amd64
docker pull bunbunbunbun/bun-base:latest --platform=linux/amd64
docker pull bunbunbunbun/bun-base-with-zig-and-webkit:latest --platform=linux/amd64
docker tag bunbunbunbun/bun-test-base:latest bun-base:latest
docker tag bunbunbunbun/bun-base:latest bun-base:latest
docker tag bunbunbunbun/bun-base-with-zig-and-webkit:latest bun-base-with-zig-and-webkit:latest

47
.docker/run-dockerfile.sh Normal file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
source "dockerfile-common.sh"
export $CONTAINER_NAME=$CONTAINER_NAME-local
rm -rf $TEMP
mkdir -p $TEMP
docker build . --target release --progress=plain -t $CONTAINER_NAME:latest --build-arg BUILDKIT_INLINE_CACHE=1 --platform=linux/$BUILDKIT_ARCH --cache-from $CONTAINER_NAME:latest
if (($?)); then
echo "Failed to build container"
exit 1
fi
id=$(docker create $CONTAINER_NAME:latest)
docker cp $id:/home/ubuntu/bun-release $TEMP/$CONTAINER_NAME
if (($?)); then
echo "Failed to cp container"
exit 1
fi
cd $TEMP
mkdir -p $TEMP/$CONTAINER_NAME $TEMP/$DEBUG_CONTAINER_NAME
mv $CONTAINER_NAME/bun-profile $DEBUG_CONTAINER_NAME/bun
zip -r $CONTAINER_NAME.zip $CONTAINER_NAME
zip -r $DEBUG_CONTAINER_NAME.zip $DEBUG_CONTAINER_NAME
docker rm -v $id
abs=$(realpath $TEMP/$CONTAINER_NAME.zip)
debug_abs=$(realpath $TEMP/$DEBUG_CONTAINER_NAME.zip)
case $(uname -s) in
"Linux") target="linux" ;;
*) target="other" ;;
esac
if [ "$target" = "linux" ]; then
if command -v bun --version >/dev/null; then
cp $TEMP/$CONTAINER_NAME/bun $(which bun)
cp $TEMP/$DEBUG_CONTAINER_NAME/bun $(which bun-profile)
fi
fi
echo "Saved to:"
echo $debug_abs
echo $abs

9
.docker/run-test.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/bin/bash
set -euxo pipefail
bun install
bun install --cwd ./test/snippets
bun install --cwd ./test/scripts
make $BUN_TEST_NAME

5
.docker/runner.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -euxo pipefail
docker container run --security-opt seccomp=.docker/chrome.json --env GITHUB_WORKSPACE=$GITHUB_WORKSPACE --env BUN_TEST_NAME=$BUN_TEST_NAME --ulimit memlock=-1:-1 --init --rm bun-test:latest

5
.docker/unit-tests.sh Normal file
View File

@@ -0,0 +1,5 @@
#!/bin/bash
set -euxo pipefail
docker container run --security-opt seccomp=.docker/chrome.json --env GITHUB_WORKSPACE=$GITHUB_WORKSPACE --ulimit memlock=-1:-1 --init --rm bun-unit-tests:latest

View File

@@ -47,5 +47,4 @@ runs:
mkdir -p ${{ runner.temp }}/.bun/bin
mv ${target}/bun* ${{ runner.temp }}/.bun/bin/
chmod +x ${{ runner.temp }}/.bun/bin/*
ln -fs ${{ runner.temp }}/.bun/bin/bun ${{ runner.temp }}/.bun/bin/bunx
echo "${{ runner.temp }}/.bun/bin" >> ${GITHUB_PATH}

View File

@@ -1,60 +0,0 @@
name: clang-format
permissions:
contents: write
on:
workflow_call:
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/clang-format.yml"
- ".clang-format"
- "package.json"
- "scripts/**"
- "cmake/**"
- "src/**/*.c"
- "src/**/*.cpp"
- "src/**/*.h"
- "packages/**/*.c"
- "packages/**/*.cpp"
- "packages/**/*.h"
env:
BUN_VERSION: "1.1.27"
LLVM_VERSION: "18.1.8"
LLVM_VERSION_MAJOR: "18"
jobs:
clang-format:
name: clang-format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.github
.clang-format
package.json
scripts
cmake
src
packages
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install LLVM
run: |
curl -fsSL https://apt.llvm.org/llvm.sh | sudo bash -s -- ${{ env.LLVM_VERSION_MAJOR }} all
- name: Clang Format
env:
ENABLE_CCACHE: OFF
LLVM_VERSION: ${{ env.LLVM_VERSION }}
run: |
bun run clang-format:diff
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "`bun run clang-format`"

View File

@@ -1,60 +0,0 @@
name: clang-tidy
permissions:
contents: write
on:
workflow_call:
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/clang-tidy.yml"
- ".clang-tidy"
- "package.json"
- "scripts/**"
- "cmake/**"
- "src/**/*.c"
- "src/**/*.cpp"
- "src/**/*.h"
- "packages/**/*.c"
- "packages/**/*.cpp"
- "packages/**/*.h"
env:
BUN_VERSION: "1.1.27"
LLVM_VERSION: "18.1.8"
LLVM_VERSION_MAJOR: "18"
jobs:
clang-tidy:
name: clang-tidy
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.github
.clang-tidy
package.json
scripts
cmake
src
packages
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Install LLVM
run: |
curl -fsSL https://apt.llvm.org/llvm.sh | sudo bash -s -- ${{ env.LLVM_VERSION_MAJOR }} all
- name: Clang Tidy
env:
ENABLE_CCACHE: OFF
LLVM_VERSION: ${{ env.LLVM_VERSION }}
run: |
bun run clang-tidy:diff
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "`bun run clang-tidy`"

61
.github/workflows/format.yml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: Format
permissions:
contents: write
concurrency:
group: format-${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && inputs.run-id || github.ref }}
cancel-in-progress: true
on:
workflow_dispatch:
inputs:
run-id:
type: string
description: The workflow ID to download artifacts (skips the build step)
pull_request:
jobs:
format:
name: Format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.prettierrc-ci
.github
.vscode
src
scripts
packages
test
bench
package.json
bun.lockb
.clang-format
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: "1.1.25"
- name: Setup Zig
uses: mlugg/setup-zig@v1
with:
version: 0.13.0
- name: Install Dependencies
run: |
bun install
- name: Format
run: |
bun fmt
- name: Format Zig
run: |
bun fmt:zig
- name: Format Cpp
run: |
bun fmt:cpp
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Apply formatting changes

View File

@@ -1,49 +0,0 @@
name: prettier-format
permissions:
contents: write
on:
workflow_call:
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/prettier-format.yml"
- "package.json"
- "scripts/**"
- "**.yml"
- "**.json"
- "**.js"
- "**.jsx"
- "**.ts"
- "**.tsx"
- "**.mjs"
- "**.cjs"
env:
BUN_VERSION: "1.1.27"
jobs:
prettier-format:
name: prettier-format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Setup Dependencies
run: |
bun install
- name: Prettier Format
env:
ENABLE_CCACHE: OFF
SKIP_LLVM: ON
run: |
bun run prettier:diff
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "`bun run prettier:extra`"

56
.github/workflows/run-format.yml vendored Normal file
View File

@@ -0,0 +1,56 @@
name: Format
permissions:
contents: write
on:
workflow_call:
inputs:
zig-version:
type: string
required: true
jobs:
format:
name: Format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.prettierrc-ci
.github
.vscode
src
scripts
packages
test
bench
package.json
bun.lockb
.clang-format
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: "1.1.25"
- name: Setup Zig
uses: mlugg/setup-zig@v1
with:
version: ${{ inputs.zig-version }}
- name: Install Dependencies
run: |
bun install
- name: Format
run: |
bun fmt
- name: Format Zig
run: |
bun fmt:zig
- name: Format Cpp
run: |
bun fmt:cpp
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: Apply formatting changes

View File

@@ -1,48 +0,0 @@
name: zig-format
permissions:
contents: write
on:
workflow_call:
workflow_dispatch:
pull_request:
paths:
- ".github/workflows/zig-format.yml"
- "package.json"
- "scripts/**"
- "cmake/**"
- "src/**/*.zig"
env:
BUN_VERSION: "1.1.27"
jobs:
zig-format:
name: zig-format
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
sparse-checkout: |
.github
package.json
scripts
cmake
src
packages
- name: Setup Bun
uses: ./.github/actions/setup-bun
with:
bun-version: ${{ env.BUN_VERSION }}
- name: Zig Format
env:
ENABLE_CCACHE: OFF
SKIP_LLVM: ON
run: |
bun run zig-format:diff
- name: Commit
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: "`bun run zig-format`"

3
.gitignore vendored
View File

@@ -142,6 +142,9 @@ test/node.js/upstream
scripts/env.local
*.generated.ts
# Temporary files
/tmp
# Dependencies
/vendor

31
.prettierrc-ci Normal file
View File

@@ -0,0 +1,31 @@
{
"arrowParens": "avoid",
"printWidth": 120,
"trailingComma": "all",
"useTabs": false,
"quoteProps": "preserve",
"plugins": [
"prettier-plugin-organize-imports"
],
"overrides": [
{
"files": [
".vscode/*.json"
],
"options": {
"parser": "jsonc",
"quoteProps": "preserve",
"singleQuote": false,
"trailingComma": "all"
}
},
{
"files": [
"*.md"
],
"options": {
"printWidth": 80
}
}
]
}

View File

@@ -52,15 +52,12 @@
"cmake.configureOnOpen": false,
"C_Cpp.errorSquiggles": "enabled",
"[cpp]": {
"editor.tabSize": 4,
"editor.defaultFormatter": "xaver.clang-format",
},
"[c]": {
"editor.tabSize": 4,
"editor.defaultFormatter": "xaver.clang-format",
},
"[h]": {
"editor.tabSize": 4,
"editor.defaultFormatter": "xaver.clang-format",
},
"clangd.arguments": ["-header-insertion=never"],

52
.vscode/tasks.json vendored
View File

@@ -1,52 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "process",
"label": "Install Dependencies",
"command": "scripts/all-dependencies.sh",
"windows": {
"command": "scripts/all-dependencies.ps1",
},
"icon": {
"id": "arrow-down",
},
"options": {
"cwd": "${workspaceFolder}",
},
},
{
"type": "process",
"label": "Setup Environment",
"dependsOn": ["Install Dependencies"],
"command": "scripts/setup.sh",
"windows": {
"command": "scripts/setup.ps1",
},
"icon": {
"id": "check",
},
"options": {
"cwd": "${workspaceFolder}",
},
},
{
"type": "process",
"label": "Build Bun",
"dependsOn": ["Setup Environment"],
"command": "bun",
"args": ["run", "build"],
"icon": {
"id": "gear",
},
"options": {
"cwd": "${workspaceFolder}",
},
"isBuildCommand": true,
"runOptions": {
"instanceLimit": 1,
"reevaluateOnRerun": true,
},
},
],
}

View File

@@ -30,7 +30,6 @@ include(CompilerFlags)
# --- Tools ---
include(SetupGit)
include(SetupBuildkite)
include(SetupBun)
include(SetupEsbuild)
@@ -39,6 +38,21 @@ include(SetupRust)
# --- Targets ---
include(BuildBoringSSL)
include(BuildBrotli)
include(BuildCares)
include(BuildLibDeflate)
include(BuildLibuv)
include(BuildLolHtml)
include(BuildLshpack)
include(BuildMimalloc)
include(BuildPicoHTTPParser)
include(BuildTinyCC)
include(BuildSQLite)
include(BuildWebKit)
include(BuildZlib)
include(BuildLibArchive) # must be loaded after zlib
include(BuildZstd)
include(BuildBun)
# --- Analysis ---
@@ -46,4 +60,3 @@ include(BuildBun)
include(RunClangFormat)
include(RunClangTidy)
include(RunZigFormat)
include(RunPrettier)

2
LATEST
View File

@@ -1 +1 @@
1.1.29
1.1.27

View File

@@ -1,24 +0,0 @@
import { bench, run } from "mitata";
const crypto = require("node:crypto");
const keyPair = crypto.generateKeyPairSync("rsa", {
modulusLength: 2048,
publicKeyEncoding: {
type: "spki",
format: "pem",
},
privateKeyEncoding: {
type: "pkcs8",
format: "pem",
},
});
// Max message size for 2048-bit RSA keys
const plaintext = crypto.getRandomValues(Buffer.alloc(245));
bench("RSA sign RSA_PKCS1_PADDING round-trip", () => {
const sig = crypto.privateEncrypt(keyPair.privateKey, plaintext);
crypto.publicDecrypt(keyPair.publicKey, sig);
});
await run();

View File

@@ -1,8 +1,6 @@
# clang: https://clang.llvm.org/docs/CommandGuide/clang.html
# clang-cl: https://clang.llvm.org/docs/UsersManual.html#id11
# --- Macros ---
macro(setb variable)
if(${variable})
set(${variable} ON)
@@ -11,20 +9,20 @@ macro(setb variable)
endif()
endmacro()
set(targets WIN32 APPLE UNIX LINUX)
foreach(target ${targets})
setb(${target})
set(bvariables WIN32 APPLE UNIX LINUX)
foreach(bvariable ${bvariables})
setb(${bvariable})
endforeach()
# --- CPU target ---
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm|ARM|arm64|ARM64|aarch64|AARCH64")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
if(APPLE)
register_compiler_flags(-mcpu=apple-m1)
else()
register_compiler_flags(-march=armv8-a+crc -mtune=ampere1)
endif()
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|AMD64|x86_64|X86_64|x64|X64")
if(ENABLE_BASELINE)
register_compiler_flags(-march=nehalem)
else()
@@ -35,6 +33,7 @@ else()
endif()
# --- MSVC runtime ---
if(WIN32)
register_compiler_flags(
DESCRIPTION "Use static MSVC runtime"
@@ -45,6 +44,7 @@ if(WIN32)
endif()
# --- Optimization level ---
if(DEBUG)
register_compiler_flags(
DESCRIPTION "Disable optimization"
@@ -66,6 +66,7 @@ else()
endif()
# --- Debug level ---
if(WIN32)
register_compiler_flags(
DESCRIPTION "Enable debug symbols (.pdb)"
@@ -97,6 +98,7 @@ endif()
# -fno-eliminate-unused-debug-types # Don't eliminate unused debug symbols
# --- C/C++ flags ---
register_compiler_flags(
DESCRIPTION "Disable C/C++ exceptions"
-fno-exceptions ${UNIX}
@@ -104,8 +106,8 @@ register_compiler_flags(
)
register_compiler_flags(
LANGUAGE CXX
DESCRIPTION "Disable C++ static destructors"
LANGUAGES CXX
-Xclang ${WIN32}
-fno-c++-static-destructors
)
@@ -149,9 +151,9 @@ register_compiler_flags(
/Gw ${WIN32}
)
# having this enabled in debug mode on macOS >=14 causes libarchive to fail to configure with the error:
# This causes libarchive to fail on macOS, with the error:
# > pid_t doesn't exist on this platform?
if((DEBUG AND LINUX) OR((NOT DEBUG) AND UNIX))
if((DEBUG AND LINUX) OR ((NOT DEBUG) AND UNIX))
register_compiler_flags(
DESCRIPTION "Emit an address-significance table"
-faddrsig
@@ -171,6 +173,7 @@ if(WIN32)
endif()
# --- Linker flags ---
if(LINUX)
register_linker_flags(
DESCRIPTION "Disable relocation read-only (RELRO)"
@@ -182,6 +185,7 @@ endif()
# Note: This is a helpful guide about assertions:
# https://best.openssf.org/Compiler-Hardening-Guides/Compiler-Options-Hardening-Guide-for-C-and-C++
if(ENABLE_ASSERTIONS)
register_compiler_flags(
DESCRIPTION "Do not eliminate null-pointer checks"
@@ -227,6 +231,7 @@ else()
endif()
# --- Diagnostics ---
if(UNIX)
register_compiler_flags(
DESCRIPTION "Enable color diagnostics"
@@ -240,6 +245,7 @@ register_compiler_flags(
)
# --- LTO ---
if(ENABLE_LTO)
register_compiler_flags(
DESCRIPTION "Enable link-time optimization (LTO)"
@@ -249,8 +255,8 @@ if(ENABLE_LTO)
if(UNIX)
register_compiler_flags(
LANGUAGE CXX
DESCRIPTION "Enable virtual tables"
LANGUAGES CXX
-fforce-emit-vtables
-fwhole-program-vtables
)
@@ -265,6 +271,7 @@ if(ENABLE_LTO)
endif()
# --- Remapping ---
if(UNIX)
register_compiler_flags(
DESCRIPTION "Remap source files"
@@ -282,6 +289,12 @@ if(ENABLE_VALGRIND AND ARCH STREQUAL "x64")
register_compiler_definitions(__SSE4_2__=0)
endif()
if(APPLE)
# The $NOCANCEL variants of various system calls are activated by compiling
# with __DARWIN_NON_CANCELABLE, which prevents them from being pthread cancellation points.
register_compiler_definitions(__DARWIN_NON_CANCELABLE=1)
endif()
# --- Other ---
# Workaround for CMake and clang-cl bug.

View File

@@ -124,19 +124,12 @@ optionx(CACHE_STRATEGY "read-write|read-only|write-only|none" "The strategy to u
optionx(CI BOOL "If CI is enabled" DEFAULT OFF)
if(CI)
set(WARNING FATAL_ERROR)
set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor)
else()
set(WARNING WARNING)
set(DEFAULT_VENDOR_PATH ${CWD}/vendor)
endif()
# TODO: This causes flaky zig builds in CI, so temporarily disable it.
# if(CI)
# set(DEFAULT_VENDOR_PATH ${CACHE_PATH}/vendor)
# else()
# set(DEFAULT_VENDOR_PATH ${CWD}/vendor)
# endif()
optionx(VENDOR_PATH FILEPATH "The path to the vendor directory" DEFAULT ${CWD}/vendor)
optionx(VENDOR_PATH FILEPATH "The path to the vendor directory" DEFAULT ${DEFAULT_VENDOR_PATH})
optionx(TMP_PATH FILEPATH "The path to the temporary directory" DEFAULT ${BUILD_PATH}/tmp)
optionx(FRESH BOOL "Set when --fresh is used" DEFAULT OFF)
@@ -305,19 +298,18 @@ endfunction()
# SOURCES string[] - The files that this command depends on
# OUTPUTS string[] - The files that this command produces
# ARTIFACTS string[] - The files that this command produces, and uploads as an artifact in CI
# ALWAYS_RUN bool - If true, the command will always run
# TARGET string - The target to register the command with
# TARGET_PHASE string - The target phase to register the command with (e.g. PRE_BUILD, PRE_LINK, POST_BUILD)
# GROUP string - The group to register the command with (e.g. similar to JOB_POOL)
function(register_command)
set(options ALWAYS_RUN)
set(args COMMENT CWD TARGET TARGET_PHASE GROUP)
set(multiArgs COMMAND ENVIRONMENT TARGETS SOURCES OUTPUTS ARTIFACTS)
cmake_parse_arguments(CMD "${options}" "${args}" "${multiArgs}" ${ARGN})
cmake_parse_arguments(CMD "" "${args}" "${multiArgs}" ${ARGN})
if(NOT CMD_COMMAND)
message(FATAL_ERROR "register_command: COMMAND is required")
endif()
parse_list(CMD_COMMAND CMD_COMMAND)
if(NOT CMD_CWD)
set(CMD_CWD ${CWD})
@@ -356,10 +348,6 @@ function(register_command)
list(APPEND CMD_EFFECTIVE_DEPENDS ${source})
endforeach()
if(NOT CMD_EFFECTIVE_DEPENDS AND NOT CMD_ALWAYS_RUN)
message(FATAL_ERROR "register_command: TARGETS or SOURCES is required")
endif()
set(CMD_EFFECTIVE_OUTPUTS)
foreach(output ${CMD_OUTPUTS})
@@ -383,12 +371,13 @@ function(register_command)
foreach(output ${CMD_EFFECTIVE_OUTPUTS})
get_source_file_property(generated ${output} GENERATED)
if(generated)
list(REMOVE_ITEM CMD_EFFECTIVE_OUTPUTS ${output})
list(APPEND CMD_EFFECTIVE_OUTPUTS ${output}.always_run_${CMD_TARGET})
add_custom_target(${CMD_TARGET})
message(WARNING "Skipping ${CMD_TARGET}, since ${output} is generated by another target")
return()
endif()
endforeach()
if(CMD_ALWAYS_RUN)
if(NOT CMD_EFFECTIVE_OUTPUTS)
list(APPEND CMD_EFFECTIVE_OUTPUTS ${CMD_CWD}/.always_run_${CMD_TARGET})
endif()
@@ -410,10 +399,6 @@ function(register_command)
return()
endif()
if(NOT CMD_EFFECTIVE_OUTPUTS)
message(FATAL_ERROR "register_command: OUTPUTS or ARTIFACTS is required, or set ALWAYS_RUN")
endif()
if(CMD_TARGET)
if(TARGET ${CMD_TARGET})
message(FATAL_ERROR "register_command: TARGET is already registered: ${CMD_TARGET}")
@@ -426,6 +411,8 @@ function(register_command)
if(TARGET clone-${CMD_TARGET})
add_dependencies(${CMD_TARGET} clone-${CMD_TARGET})
endif()
set_property(TARGET ${CMD_TARGET} PROPERTY OUTPUT ${CMD_EFFECTIVE_OUTPUTS} APPEND)
set_property(TARGET ${CMD_TARGET} PROPERTY DEPENDS ${CMD_EFFECTIVE_DEPENDS} APPEND)
endif()
add_custom_command(
@@ -596,298 +583,664 @@ function(register_repository)
${CMAKE_COMMAND}
-DGIT_PATH=${GIT_PATH}
-DGIT_REPOSITORY=${GIT_REPOSITORY}
-DGIT_NAME=${GIT_NAME}
-DGIT_COMMIT=${GIT_COMMIT}
-DGIT_TAG=${GIT_TAG}
-DGIT_BRANCH=${GIT_BRANCH}
-DGIT_TAG=${GIT_TAG}
-DGIT_COMMIT=${GIT_COMMIT}
-DGIT_NAME=${GIT_NAME}
-P ${CWD}/cmake/scripts/GitClone.cmake
OUTPUTS
${GIT_PATH}
${GIT_EFFECTIVE_OUTPUTS}
)
register_outputs(TARGET clone-${GIT_NAME} ${GIT_PATH})
endfunction()
# register_cmake_command()
function(parse_language variable)
if(NOT ${variable})
set(${variable} C CXX PARENT_SCOPE)
endif()
foreach(value ${${variable}})
if(NOT value MATCHES "^(C|CXX)$")
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Invalid language: \"${value}\"")
endif()
endforeach()
endfunction()
function(parse_target variable)
foreach(value ${${variable}})
if(NOT TARGET ${value})
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Invalid target: \"${value}\"")
endif()
endforeach()
endfunction()
function(parse_path variable)
foreach(value ${${variable}})
if(NOT IS_ABSOLUTE ${value})
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: ${variable} is not an absolute path: \"${value}\"")
endif()
if(NOT ${value} MATCHES "^(${CWD}|${BUILD_PATH}|${CACHE_PATH}|${VENDOR_PATH})")
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: ${variable} is not in the source, build, cache, or vendor path: \"${value}\"")
endif()
endforeach()
endfunction()
function(parse_list list variable)
set(result)
macro(check_expression)
if(DEFINED expression)
if(NOT (${expression}))
list(POP_BACK result)
endif()
unset(expression)
endif()
endmacro()
foreach(item ${${list}})
if(item MATCHES "^(ON|OFF|AND|OR|NOT)$")
set(expression ${expression} ${item})
else()
check_expression()
list(APPEND result ${item})
endif()
endforeach()
check_expression()
set(${variable} ${result} PARENT_SCOPE)
endfunction()
# register_target()
# Description:
# Registers a command that builds an external CMake project.
# Registers a target that does nothing.
# Arguments:
# TARGET string - The target to register the command with
# ARGS string[] - The arguments to pass to CMake (e.g. -DKEY=VALUE)
# CWD string - The directory where the CMake files are located
# BUILD_PATH string - The path to build the project to
# LIB_PATH string - The path to the libraries
# TARGETS string[] - The targets to build from CMake
# LIBRARIES string[] - The libraries that are built
# INCLUDES string[] - The include paths
function(register_cmake_command)
set(args TARGET CWD BUILD_PATH LIB_PATH)
set(multiArgs ARGS TARGETS LIBRARIES INCLUDES)
# Use "MAKE" instead of "CMAKE" to prevent conflicts with CMake's own CMAKE_* variables
cmake_parse_arguments(MAKE "" "${args}" "${multiArgs}" ${ARGN})
# target string - The name of the target
function(register_target target)
add_custom_target(${target})
if(NOT MAKE_TARGET)
message(FATAL_ERROR "register_cmake_command: TARGET is required")
endif()
if(TARGET ${MAKE_TARGET})
message(FATAL_ERROR "register_cmake_command: TARGET is already a target: ${MAKE_TARGET}")
endif()
if(NOT MAKE_CWD)
set(MAKE_CWD ${VENDOR_PATH}/${MAKE_TARGET})
endif()
if(NOT MAKE_BUILD_PATH)
set(MAKE_BUILD_PATH ${BUILD_PATH}/${MAKE_TARGET})
endif()
if(MAKE_LIB_PATH)
set(MAKE_LIB_PATH ${MAKE_BUILD_PATH}/${MAKE_LIB_PATH})
else()
set(MAKE_LIB_PATH ${MAKE_BUILD_PATH})
endif()
set(MAKE_EFFECTIVE_ARGS -B${MAKE_BUILD_PATH} ${CMAKE_ARGS})
set(setFlags GENERATOR BUILD_TYPE)
set(appendFlags C_FLAGS CXX_FLAGS LINKER_FLAGS)
set(specialFlags POSITION_INDEPENDENT_CODE)
set(flags ${setFlags} ${appendFlags} ${specialFlags})
foreach(arg ${MAKE_ARGS})
foreach(flag ${flags})
if(arg MATCHES "-DCMAKE_${flag}=(.*)")
if(DEFINED MAKE_${flag})
message(FATAL_ERROR "register_cmake_command: CMAKE_${flag} was already set: \"${MAKE_${flag}}\"")
endif()
set(MAKE_${flag} ${CMAKE_MATCH_1})
set(${arg}_USED ON)
endif()
endforeach()
if(NOT ${arg}_USED)
list(APPEND MAKE_EFFECTIVE_ARGS ${arg})
endif()
endforeach()
foreach(flag ${setFlags})
if(NOT DEFINED MAKE_${flag} AND DEFINED CMAKE_${flag})
set(MAKE_${flag} ${CMAKE_${flag}})
endif()
endforeach()
foreach(flag ${appendFlags})
if(MAKE_${flag})
set(MAKE_${flag} "${CMAKE_${flag}} ${MAKE_${flag}}")
else()
set(MAKE_${flag} ${CMAKE_${flag}})
endif()
endforeach()
if(MAKE_POSITION_INDEPENDENT_CODE AND NOT WIN32)
set(MAKE_C_FLAGS "${MAKE_C_FLAGS} -fPIC")
set(MAKE_CXX_FLAGS "${MAKE_CXX_FLAGS} -fPIC")
elseif(APPLE)
set(MAKE_C_FLAGS "${MAKE_C_FLAGS} -fno-pic -fno-pie")
set(MAKE_CXX_FLAGS "${MAKE_CXX_FLAGS} -fno-pic -fno-pie")
endif()
set(effectiveFlags ${setFlags} ${appendFlags})
foreach(flag ${effectiveFlags})
list(APPEND MAKE_EFFECTIVE_ARGS "-DCMAKE_${flag}=${MAKE_${flag}}")
endforeach()
if(DEFINED FRESH)
list(APPEND MAKE_EFFECTIVE_ARGS --fresh)
endif()
register_command(
COMMENT "Configuring ${MAKE_TARGET}"
TARGET configure-${MAKE_TARGET}
COMMAND ${CMAKE_COMMAND} ${MAKE_EFFECTIVE_ARGS}
CWD ${MAKE_CWD}
OUTPUTS ${MAKE_BUILD_PATH}/CMakeCache.txt
)
if(TARGET clone-${MAKE_TARGET})
add_dependencies(configure-${MAKE_TARGET} clone-${MAKE_TARGET})
endif()
set(MAKE_BUILD_ARGS --build ${MAKE_BUILD_PATH} --config ${MAKE_BUILD_TYPE})
set(MAKE_EFFECTIVE_LIBRARIES)
set(MAKE_ARTIFACTS)
foreach(lib ${MAKE_LIBRARIES})
if(lib MATCHES "^(WIN32|UNIX|APPLE)$")
if(${lib})
continue()
else()
list(POP_BACK MAKE_ARTIFACTS)
endif()
else()
list(APPEND MAKE_EFFECTIVE_LIBRARIES ${lib})
if(lib MATCHES "\\.")
list(APPEND MAKE_ARTIFACTS ${MAKE_LIB_PATH}/${lib})
else()
list(APPEND MAKE_ARTIFACTS ${MAKE_LIB_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${lib}${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
endif()
endforeach()
if(NOT MAKE_TARGETS)
set(MAKE_TARGETS ${MAKE_EFFECTIVE_LIBRARIES})
endif()
foreach(target ${MAKE_TARGETS})
list(APPEND MAKE_BUILD_ARGS --target ${target})
endforeach()
set(MAKE_EFFECTIVE_INCLUDES)
foreach(include ${MAKE_INCLUDES})
if(include STREQUAL ".")
list(APPEND MAKE_EFFECTIVE_INCLUDES ${MAKE_CWD})
else()
list(APPEND MAKE_EFFECTIVE_INCLUDES ${MAKE_CWD}/${include})
endif()
endforeach()
register_command(
COMMENT "Building ${MAKE_TARGET}"
TARGET ${MAKE_TARGET}
TARGETS configure-${MAKE_TARGET}
COMMAND ${CMAKE_COMMAND} ${MAKE_BUILD_ARGS}
CWD ${MAKE_CWD}
ARTIFACTS ${MAKE_ARTIFACTS}
)
if(MAKE_EFFECTIVE_INCLUDES)
target_include_directories(${bun} PRIVATE ${MAKE_EFFECTIVE_INCLUDES})
if(TARGET clone-${MAKE_TARGET} AND NOT BUN_LINK_ONLY)
add_dependencies(${bun} clone-${MAKE_TARGET})
endif()
endif()
# HACK: Workaround for duplicate symbols when linking mimalloc.o
# >| duplicate symbol '_mi_page_queue_append(mi_heap_s*, mi_page_queue_s*, mi_page_queue_s*)' in:
# >| mimalloc/CMakeFiles/mimalloc-obj.dir/src/static.c.o
# >| ld: 287 duplicate symbols for architecture arm64
if(NOT BUN_LINK_ONLY OR NOT MAKE_ARTIFACTS MATCHES "static.c.o")
target_link_libraries(${bun} PRIVATE ${MAKE_ARTIFACTS})
endif()
if(BUN_LINK_ONLY)
target_sources(${bun} PRIVATE ${MAKE_ARTIFACTS})
endif()
set(${target} ${target} PARENT_SCOPE)
set(${target}_CWD ${CWD} PARENT_SCOPE)
set(${target}_BUILD_PATH ${BUILD_PATH} PARENT_SCOPE)
endfunction()
# register_compiler_flag()
# register_vendor_target()
# Description:
# Registers a target that does nothing.
# Arguments:
# target string - The name of the target
function(register_vendor_target target)
add_custom_target(${target})
set(${target} ${target} PARENT_SCOPE)
set(${target}_CWD ${VENDOR_PATH}/${target} PARENT_SCOPE)
set(${target}_BUILD_PATH ${BUILD_PATH}/vendor/${target} PARENT_SCOPE)
if(NOT TARGET vendor)
add_custom_target(vendor)
endif()
add_dependencies(vendor ${target})
endfunction()
# register_outputs()
# Description:
# Registers outputs that are built from a target.
# Arguments:
# TARGET string - The target that builds the outputs
# outputs string[] - The list of outputs
function(register_outputs)
set(args TARGET PATH)
cmake_parse_arguments(OUTPUT "" "${args}" "" ${ARGN})
parse_target(OUTPUT_TARGET)
parse_list(OUTPUT_UNPARSED_ARGUMENTS OUTPUT_PATHS)
parse_path(OUTPUT_PATHS)
foreach(path ${OUTPUT_PATHS})
set_property(GLOBAL PROPERTY ${path} ${OUTPUT_TARGET} APPEND)
set_property(TARGET ${OUTPUT_TARGET} PROPERTY OUTPUT ${path} APPEND)
endforeach()
endfunction()
# register_inputs()
# Description:
# Registers inputs that are required to build a target.
# Arguments:
# TARGET string - The target that builds the inputs
# inputs string[] - The list of inputs
function(register_inputs)
set(args TARGET)
cmake_parse_arguments(INPUT "" "${args}" "" ${ARGN})
parse_target(INPUT_TARGET)
parse_list(INPUT_UNPARSED_ARGUMENTS INPUT_PATHS)
foreach(path ${INPUT_PATHS})
set(search ${path})
set(found OFF)
while(search)
if(EXISTS ${search})
set(found ON)
break()
endif()
get_property(target GLOBAL PROPERTY ${search})
if(TARGET ${target})
set(found ON)
set_property(TARGET ${target} PROPERTY OUTPUT ${path} APPEND)
break()
endif()
get_filename_component(next_search ${search} DIRECTORY)
if(next_search STREQUAL search OR next_search STREQUAL ${CWD} OR next_search STREQUAL ${VENDOR_PATH})
break()
endif()
set(search ${next_search})
endwhile()
if(NOT found)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: ${path} does not have a target")
endif()
endforeach()
endfunction()
# upload_artifacts()
# Description:
# Uploads artifacts after a target has been built.
# Arguments:
# TARGET string - The target to upload artifacts for
# artifacts string[] - The artifacts to upload
function(upload_artifacts)
set(args TARGET)
cmake_parse_arguments(ARTIFACT "" "${args}" "" ${ARGN})
parse_target(ARTIFACT_TARGET)
get_target_property(imported ${ARTIFACT_TARGET} IMPORTED)
if(imported)
return()
endif()
parse_list(ARTIFACT_UNPARSED_ARGUMENTS ARTIFACT_PATHS)
foreach(artifact ${ARTIFACT_PATHS})
file(RELATIVE_PATH filename ${BUILD_PATH} ${artifact})
add_custom_command(
TARGET ${ARTIFACT_TARGET} POST_BUILD
COMMENT "Uploading ${filename}"
COMMAND ${CMAKE_COMMAND} -E chdir ${BUILD_PATH} buildkite-agent artifact upload ${filename}
)
set_property(TARGET ${ARTIFACT_TARGET} PROPERTY OUTPUT ${artifact} APPEND)
endforeach()
endfunction()
# register_compiler_flags()
# Description:
# Registers a compiler flag, similar to `add_compile_options()`, but has more validation and features.
# Arguments:
# flags string[] - The flags to register
# TARGET string - The target to register the flag (default: all)
# LANGUAGE string - The language to register the flag (default: C, CXX)
# DESCRIPTION string - The description of the flag
# LANGUAGES string[] - The languages to register the flag (default: C, CXX)
# TARGETS string[] - The targets to register the flag (default: all)
# flags string[] - The flags to register
function(register_compiler_flags)
set(args DESCRIPTION)
set(multiArgs LANGUAGES TARGETS)
cmake_parse_arguments(COMPILER "" "${args}" "${multiArgs}" ${ARGN})
set(args TARGET LANGUAGE DESCRIPTION)
cmake_parse_arguments(COMPILER "" "${args}" "" ${ARGN})
if(NOT COMPILER_LANGUAGES)
set(COMPILER_LANGUAGES C CXX)
endif()
parse_target(COMPILER_TARGET)
parse_language(COMPILER_LANGUAGE)
parse_list(COMPILER_UNPARSED_ARGUMENTS COMPILER_FLAGS)
set(COMPILER_FLAGS)
foreach(flag ${COMPILER_UNPARSED_ARGUMENTS})
if(flag STREQUAL "ON")
continue()
elseif(flag STREQUAL "OFF")
list(POP_BACK COMPILER_FLAGS)
elseif(flag MATCHES "^(-|/)")
list(APPEND COMPILER_FLAGS ${flag})
else()
message(FATAL_ERROR "register_compiler_flags: Invalid flag: \"${flag}\"")
foreach(flag ${COMPILER_FLAGS})
if(NOT flag MATCHES "^(-|/)")
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Invalid flag: \"${flag}\"")
endif()
endforeach()
foreach(target ${COMPILER_TARGETS})
if(NOT TARGET ${target})
message(FATAL_ERROR "register_compiler_flags: \"${target}\" is not a target")
endif()
endforeach()
foreach(lang ${COMPILER_LANGUAGES})
foreach(language ${COMPILER_LANGUAGE})
list(JOIN COMPILER_FLAGS " " COMPILER_FLAGS_STRING)
if(NOT COMPILER_TARGETS)
set(CMAKE_${lang}_FLAGS "${CMAKE_${lang}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE)
if(NOT COMPILER_TARGET)
set(CMAKE_${language}_FLAGS "${CMAKE_${language}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE)
endif()
foreach(target ${COMPILER_TARGETS})
set(${target}_CMAKE_${lang}_FLAGS "${${target}_CMAKE_${lang}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE)
foreach(target ${COMPILER_TARGET})
set(${target}_CMAKE_${language}_FLAGS "${${target}_CMAKE_${language}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE)
endforeach()
endforeach()
foreach(lang ${COMPILER_LANGUAGES})
foreach(language ${COMPILER_LANGUAGE})
foreach(flag ${COMPILER_FLAGS})
if(NOT COMPILER_TARGETS)
add_compile_options($<$<COMPILE_LANGUAGE:${lang}>:${flag}>)
if(NOT COMPILER_TARGET)
add_compile_options($<$<COMPILE_LANGUAGE:${language}>:${flag}>)
endif()
foreach(target ${COMPILER_TARGETS})
foreach(target ${COMPILER_TARGET})
get_target_property(type ${target} TYPE)
if(type MATCHES "EXECUTABLE|LIBRARY")
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:${lang}>:${flag}>)
get_target_property(imported ${target} IMPORTED)
if(type MATCHES "EXECUTABLE|LIBRARY" AND NOT imported)
target_compile_options(${target} PRIVATE $<$<COMPILE_LANGUAGE:${language}>:${flag}>)
endif()
endforeach()
endforeach()
endforeach()
endfunction()
# register_compiler_definitions()
# Description:
# Registers a compiler definition, similar to `add_compile_definitions()`.
# Arguments:
# TARGET string - The target to register the definitions (default: all)
# LANGUAGE string - The language to register the definitions (default: C, CXX)
# DESCRIPTION string - The description of the definitions
# definitions string[] - The definitions to register
function(register_compiler_definitions)
set(args TARGET LANGUAGE DESCRIPTION)
cmake_parse_arguments(COMPILER "" "${args}" "" ${ARGN})
parse_language(COMPILER_LANGUAGE)
parse_target(COMPILER_TARGET)
parse_list(COMPILER_UNPARSED_ARGUMENTS COMPILER_DEFINITIONS)
foreach(definition ${COMPILER_DEFINITIONS})
if(NOT definition MATCHES "^([A-Z_][A-Z0-9_]*)")
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Invalid definition: \"${definition}\"")
endif()
endforeach()
if(WIN32)
list(TRANSFORM COMPILER_DEFINITIONS PREPEND "/D" OUTPUT_VARIABLE COMPILER_FLAGS)
else()
list(TRANSFORM COMPILER_DEFINITIONS PREPEND "-D" OUTPUT_VARIABLE COMPILER_FLAGS)
endif()
foreach(language ${COMPILER_LANGUAGE})
list(JOIN COMPILER_FLAGS " " COMPILER_FLAGS_STRING)
if(NOT COMPILER_TARGET)
set(CMAKE_${language}_FLAGS "${CMAKE_${language}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE)
endif()
foreach(target ${COMPILER_TARGET})
set(${target}_CMAKE_${language}_FLAGS "${${target}_CMAKE_${language}_FLAGS} ${COMPILER_FLAGS_STRING}" PARENT_SCOPE)
endforeach()
endforeach()
foreach(definition ${COMPILER_DEFINITIONS})
foreach(language ${COMPILER_LANGUAGE})
if(NOT COMPILER_TARGET)
add_compile_definitions($<$<COMPILE_LANGUAGE:${language}>:${definition}>)
endif()
foreach(target ${COMPILER_TARGET})
get_target_property(type ${target} TYPE)
get_target_property(imported ${target} IMPORTED)
if(type MATCHES "EXECUTABLE|LIBRARY" AND NOT imported)
target_compile_definitions(${target} PRIVATE $<$<COMPILE_LANGUAGE:${language}>:${definition}>)
endif()
endforeach()
endforeach()
endforeach()
endfunction()
# register_linker_flags()
# Description:
# Registers a linker flag, similar to `add_link_options()`.
# Arguments:
# flags string[] - The flags to register
# TARGET string - The target to register the flag (default: all)
# DESCRIPTION string - The description of the flag
# flags string[] - The flags to register
function(register_linker_flags)
set(args DESCRIPTION)
set(args TARGET DESCRIPTION)
cmake_parse_arguments(LINKER "" "${args}" "" ${ARGN})
foreach(flag ${LINKER_UNPARSED_ARGUMENTS})
if(flag STREQUAL "ON")
continue()
elseif(flag STREQUAL "OFF")
list(POP_FRONT LINKER_FLAGS)
elseif(flag MATCHES "^(-|/)")
list(APPEND LINKER_FLAGS ${flag})
else()
message(FATAL_ERROR "register_linker_flags: Invalid flag: \"${flag}\"")
parse_target(LINKER_TARGET)
parse_list(LINKER_UNPARSED_ARGUMENTS LINKER_FLAGS)
foreach(flag ${LINKER_FLAGS})
if(NOT flag MATCHES "^(-|/)")
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Invalid flag: \"${flag}\"")
endif()
endforeach()
add_link_options(${LINKER_FLAGS})
list(JOIN LINKER_FLAGS " " LINKER_FLAGS_STRING)
if(NOT LINKER_TARGET)
set(CMAKE_LINKER_FLAGS "${CMAKE_LINKER_FLAGS} ${LINKER_FLAGS_STRING}" PARENT_SCOPE)
endif()
foreach(target ${LINKER_TARGET})
set(${target}_CMAKE_LINKER_FLAGS "${${target}_CMAKE_LINKER_FLAGS} ${LINKER_FLAGS_STRING}" PARENT_SCOPE)
endforeach()
if(NOT LINKER_TARGET)
add_link_options(${LINKER_FLAGS})
endif()
foreach(target ${LINKER_TARGET})
get_target_property(type ${target} TYPE)
if(type MATCHES "EXECUTABLE|LIBRARY")
target_link_options(${target} PUBLIC ${LINKER_FLAGS})
endif()
endforeach()
endfunction()
function(print_compiler_flags)
get_property(targets DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
set(languages C CXX)
foreach(target ${targets})
get_target_property(type ${target} TYPE)
message(STATUS "Target: ${target}")
foreach(lang ${languages})
if(${target}_CMAKE_${lang}_FLAGS)
message(STATUS " ${lang} Flags: ${${target}_CMAKE_${lang}_FLAGS}")
# register_includes()
# Description:
# Registers a include directory, similar to `target_include_directories()`.
# Arguments:
# TARGET string - The target to register the include (default: all)
# LANGUAGE string - The language to register the include (default: C, CXX)
# DESCRIPTION string - The description of the include
# paths string[] - The include paths to register
function(register_includes)
set(args TARGET LANGUAGE DESCRIPTION)
cmake_parse_arguments(INCLUDE "" "${args}" "" ${ARGN})
parse_target(INCLUDE_TARGET)
parse_language(INCLUDE_LANGUAGE)
parse_list(INCLUDE_UNPARSED_ARGUMENTS INCLUDE_PATHS)
parse_path(INCLUDE_PATHS)
register_inputs(TARGET ${INCLUDE_TARGET} ${INCLUDE_PATHS})
list(TRANSFORM INCLUDE_PATHS PREPEND "-I" OUTPUT_VARIABLE INCLUDE_FLAGS)
list(JOIN INCLUDE_FLAGS " " INCLUDE_FLAGS_STRING)
foreach(language ${INCLUDE_LANGUAGE})
if(NOT INCLUDE_TARGET)
set(CMAKE_${language}_FLAGS "${CMAKE_${language}_FLAGS} ${INCLUDE_FLAGS_STRING}" PARENT_SCOPE)
endif()
foreach(target ${INCLUDE_TARGET})
set(${target}_CMAKE_${language}_FLAGS "${${target}_CMAKE_${language}_FLAGS} ${INCLUDE_FLAGS_STRING}" PARENT_SCOPE)
endforeach()
if(NOT INCLUDE_TARGET)
add_include_directories(${INCLUDE_PATHS})
endif()
foreach(target ${INCLUDE_TARGET})
get_target_property(type ${target} TYPE)
get_target_property(imported ${target} IMPORTED)
if(type MATCHES "EXECUTABLE|LIBRARY" AND NOT imported)
target_include_directories(${target} PUBLIC ${INCLUDE_PATHS})
endif()
endforeach()
endforeach()
foreach(lang ${languages})
message(STATUS "Language: ${lang}")
if(CMAKE_${lang}_FLAGS)
message(STATUS " Flags: ${CMAKE_${lang}_FLAGS}")
endfunction()
# register_libraries()
# Description:
# Registers libraries that are built from a target.
# Arguments:
# TARGET string - The target that builds the libraries
# PATH string - The relative path to the libraries
# VARIABLE string - The variable to set to the libraries
# libraries string[] - The libraries to register
function(register_libraries)
set(args TARGET PATH VARIABLE)
cmake_parse_arguments(LIBRARY "" "${args}" "" ${ARGN})
parse_target(LIBRARY_TARGET)
parse_list(LIBRARY_UNPARSED_ARGUMENTS LIBRARY_NAMES)
if(LIBRARY_PATH)
if(NOT IS_ABSOLUTE ${LIBRARY_PATH})
set(LIBRARY_PATH ${${LIBRARY_TARGET}_BUILD_PATH}/${LIBRARY_PATH})
endif()
else()
set(LIBRARY_PATH ${${LIBRARY_TARGET}_BUILD_PATH})
endif()
parse_path(LIBRARY_PATH)
set(LIBRARY_PATHS)
foreach(name ${LIBRARY_NAMES})
if(name MATCHES "\\.")
list(APPEND LIBRARY_PATHS ${LIBRARY_PATH}/${name})
else()
list(APPEND LIBRARY_PATHS ${LIBRARY_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${name}${CMAKE_STATIC_LIBRARY_SUFFIX})
endif()
endforeach()
set_property(TARGET ${LIBRARY_TARGET} PROPERTY OUTPUT ${LIBRARY_PATHS} APPEND)
if(LIBRARY_VARIABLE)
set(${LIBRARY_VARIABLE} ${LIBRARY_PATHS} PARENT_SCOPE)
endif()
endfunction()
function(get_libraries target variable)
get_target_property(libraries ${target} OUTPUT)
if(libraries MATCHES "NOTFOUND")
set(libraries)
endif()
set(${variable} ${libraries} PARENT_SCOPE)
endfunction()
# register_cmake_project()
# Description:
# Registers an external CMake project.
# Arguments:
# TARGET string - The target to associate the project
# CWD string - The working directory of the project
# CMAKE_TARGET string[] - The CMake targets to build
# CMAKE_PATH string - The path to the CMake project (default: CWD)
function(register_cmake_project)
set(args TARGET CWD CMAKE_PATH LIBRARY_PATH)
set(multiArgs CMAKE_TARGET)
cmake_parse_arguments(PROJECT "" "${args}" "${multiArgs}" ${ARGN})
parse_target(PROJECT_TARGET)
if(NOT PROJECT_CWD)
set(PROJECT_CWD ${VENDOR_PATH}/${PROJECT_TARGET})
endif()
parse_path(PROJECT_CWD)
if(PROJECT_CMAKE_PATH)
set(PROJECT_CMAKE_PATH ${PROJECT_CWD}/${PROJECT_CMAKE_PATH})
else()
set(PROJECT_CMAKE_PATH ${PROJECT_CWD})
endif()
parse_path(PROJECT_CMAKE_PATH)
set(PROJECT_BUILD_PATH ${BUILD_PATH}/vendor/${PROJECT_TARGET})
set(PROJECT_TOOLCHAIN_PATH ${PROJECT_BUILD_PATH}/CMakeLists-toolchain.txt)
register_command(
TARGET
configure-${PROJECT_TARGET}
COMMENT
"Configuring ${PROJECT_TARGET}"
COMMAND
${CMAKE_COMMAND}
-G ${CMAKE_GENERATOR}
-B ${PROJECT_BUILD_PATH}
-S ${PROJECT_CMAKE_PATH}
--toolchain ${PROJECT_TOOLCHAIN_PATH}
--fresh
-DCMAKE_POLICY_DEFAULT_CMP0077=NEW
CWD
${PROJECT_CWD}
SOURCES
${PROJECT_TOOLCHAIN_PATH}
OUTPUTS
${PROJECT_BUILD_PATH}/CMakeCache.txt
)
if(TARGET clone-${PROJECT_TARGET})
add_dependencies(configure-${PROJECT_TARGET} clone-${PROJECT_TARGET})
endif()
set(PROJECT_BUILD_ARGS --build ${PROJECT_BUILD_PATH})
parse_list(PROJECT_CMAKE_TARGET PROJECT_CMAKE_TARGET)
foreach(target ${PROJECT_CMAKE_TARGET})
list(APPEND PROJECT_BUILD_ARGS --target ${target})
endforeach()
if(NOT BUN_LINK_ONLY)
get_libraries(${PROJECT_TARGET} PROJECT_OUTPUTS)
endif()
register_command(
TARGET
build-${PROJECT_TARGET}
COMMENT
"Building ${PROJECT_TARGET}"
COMMAND
${CMAKE_COMMAND}
${PROJECT_BUILD_ARGS}
CWD
${PROJECT_CWD}
TARGETS
configure-${PROJECT_TARGET}
ARTIFACTS
${PROJECT_OUTPUTS}
)
add_dependencies(${PROJECT_TARGET} build-${PROJECT_TARGET})
cmake_language(EVAL CODE "cmake_language(DEFER CALL create_toolchain_file ${PROJECT_TOOLCHAIN_PATH} ${PROJECT_TARGET})")
endfunction()
# register_cmake_definitions()
# Description:
# Registers definitions, when compiling an external CMake project.
# Arguments:
# TARGET string - The target to register the definitions (if not defined, sets for all targets)
# DESCRIPTION string - The description of the definitions
# definitions string[] - The definitions to register
function(register_cmake_definitions)
set(args TARGET DESCRIPTION)
cmake_parse_arguments(CMAKE "" "${args}" "" ${ARGN})
parse_target(CMAKE_TARGET)
parse_list(CMAKE_UNPARSED_ARGUMENTS CMAKE_EXTRA_DEFINITIONS)
foreach(definition ${CMAKE_EXTRA_DEFINITIONS})
string(REGEX MATCH "^([^=]+)=(.*)$" match ${definition})
if(NOT match)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Invalid definition: \"${definition}\"")
endif()
endforeach()
if(CMAKE_TARGET)
set(${CMAKE_TARGET}_CMAKE_DEFINITIONS ${${CMAKE_TARGET}_CMAKE_DEFINITIONS} ${CMAKE_EXTRA_DEFINITIONS} PARENT_SCOPE)
else()
set(CMAKE_DEFINITIONS ${CMAKE_DEFINITIONS} ${CMAKE_EXTRA_DEFINITIONS} PARENT_SCOPE)
endif()
endfunction()
# register_link_targets()
# Description:
# Links the libraries of one target to another.
# Arguments:
# TARGET string - The main target
# targets string[] - The targets to link to the main target
function(register_link_targets)
set(args TARGET)
cmake_parse_arguments(LINK "" "${args}" "" ${ARGN})
parse_target(LINK_TARGET)
get_target_property(type ${LINK_TARGET} TYPE)
if(NOT type MATCHES "EXECUTABLE|LIBRARY")
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Target is not an executable or library: ${LINK_TARGET}")
endif()
parse_list(LINK_UNPARSED_ARGUMENTS LINK_TARGETS)
parse_target(LINK_TARGETS)
foreach(target ${LINK_TARGETS})
get_target_property(libraries ${target} OUTPUT)
if(NOT libraries)
message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Target does not have libraries: ${target}")
endif()
register_link_libraries(TARGET ${LINK_TARGET} ${libraries})
endforeach()
endfunction()
function(register_link_libraries)
set(args TARGET)
cmake_parse_arguments(LINK "" "${args}" "" ${ARGN})
parse_target(LINK_TARGET)
parse_list(LINK_UNPARSED_ARGUMENTS LINK_TARGETS)
foreach(target ${LINK_TARGETS})
target_link_libraries(${LINK_TARGET} PUBLIC ${target})
endforeach()
endfunction()
# create_toolchain_file()
# Description:
# Creates a CMake toolchain file.
# Arguments:
# filename string - The path to create the toolchain file
# target string - The target to create the toolchain file
function(create_toolchain_file filename target)
parse_path(filename)
parse_target(target)
set(lines)
if(CMAKE_TOOLCHAIN_FILE)
file(STRINGS ${CMAKE_TOOLCHAIN_FILE} lines)
list(PREPEND lines "# Copied from ${CMAKE_TOOLCHAIN_FILE}")
endif()
list(APPEND lines "# Generated from ${CMAKE_CURRENT_FUNCTION} in ${CMAKE_CURRENT_LIST_FILE}")
set(variables
CMAKE_BUILD_TYPE
CMAKE_EXPORT_COMPILE_COMMANDS
CMAKE_COLOR_DIAGNOSTICS
CMAKE_C_COMPILER
CMAKE_C_COMPILER_LAUNCHER
CMAKE_CXX_COMPILER
CMAKE_CXX_COMPILER_LAUNCHER
CMAKE_LINKER
CMAKE_AR
CMAKE_RANLIB
CMAKE_STRIP
CMAKE_OSX_SYSROOT
CMAKE_OSX_DEPLOYMENT_TARGET
)
macro(append variable value)
if(value MATCHES " ")
list(APPEND lines "set(${variable} \"${value}\")")
else()
list(APPEND lines "set(${variable} ${value})")
endif()
endmacro()
foreach(variable ${variables})
if(DEFINED ${variable})
append(${variable} ${${variable}})
endif()
endforeach()
set(flags
CMAKE_C_FLAGS
CMAKE_CXX_FLAGS
CMAKE_LINKER_FLAGS
)
foreach(flag ${flags})
set(value)
if(DEFINED ${flag})
set(value "${${flag}}")
endif()
if(DEFINED ${target}_${flag})
set(value "${value} ${${target}_${flag}}")
endif()
if(value)
append(${flag} ${value})
endif()
endforeach()
set(definitions
CMAKE_DEFINITIONS
${target}_CMAKE_DEFINITIONS
)
foreach(definition ${definitions})
foreach(entry ${${definition}})
string(REGEX MATCH "^([^=]+)=(.*)$" match ${entry})
if(NOT match)
message(FATAL_ERROR "Invalid definition: ${entry}")
endif()
append(${CMAKE_MATCH_1} ${CMAKE_MATCH_2})
endforeach()
endforeach()
list(JOIN lines "\n" lines)
file(GENERATE OUTPUT ${filename} CONTENT "${lines}\n")
endfunction()

View File

@@ -2,8 +2,8 @@ if(NOT CMAKE_SYSTEM_NAME OR NOT CMAKE_SYSTEM_PROCESSOR)
message(FATAL_ERROR "CMake included this file before project() was called")
endif()
optionx(BUN_LINK_ONLY BOOL "If only the linking step should be built" DEFAULT OFF)
optionx(BUN_CPP_ONLY BOOL "If only the C++ part of Bun should be built" DEFAULT OFF)
optionx(BUN_CPP_ONLY BOOL "If only the C++ library should be built" DEFAULT OFF)
optionx(BUN_LINK_ONLY BOOL "If only the executable should be linked" DEFAULT OFF)
optionx(BUILDKITE BOOL "If Buildkite is enabled" DEFAULT OFF)
optionx(GITHUB_ACTIONS BOOL "If GitHub Actions is enabled" DEFAULT OFF)
@@ -143,15 +143,4 @@ endif()
optionx(USE_STATIC_LIBATOMIC BOOL "If libatomic should be statically linked" DEFAULT ${DEFAULT_STATIC_LIBATOMIC})
if(APPLE)
set(DEFAULT_WEBKIT_ICU OFF)
else()
set(DEFAULT_WEBKIT_ICU ON)
endif()
optionx(USE_WEBKIT_ICU BOOL "Use the ICU libraries from WebKit" DEFAULT ${DEFAULT_WEBKIT_ICU})
optionx(ERROR_LIMIT STRING "Maximum number of errors to show when compiling C++ code" DEFAULT "100")
list(APPEND CMAKE_ARGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON)

View File

@@ -1,5 +1,3 @@
# https://clang.llvm.org/docs/ClangFormat.html
find_command(
VARIABLE
CLANG_FORMAT_PROGRAM
@@ -9,8 +7,6 @@ find_command(
OFF
)
set(CLANG_FORMAT_SOURCES ${BUN_C_SOURCES} ${BUN_CXX_SOURCES})
register_command(
TARGET
clang-format-check
@@ -21,8 +17,8 @@ register_command(
-Werror
--dry-run
--verbose
${CLANG_FORMAT_SOURCES}
ALWAYS_RUN
${BUN_C_SOURCES}
${BUN_CXX_SOURCES}
)
register_command(
@@ -34,38 +30,6 @@ register_command(
${CLANG_FORMAT_PROGRAM}
-i # edits files in-place
--verbose
${CLANG_FORMAT_SOURCES}
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(CLANG_FORMAT_CHANGED_SOURCES)
foreach(source ${CLANG_FORMAT_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND CLANG_FORMAT_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(CLANG_FORMAT_CHANGED_SOURCES)
set(CLANG_FORMAT_DIFF_COMMAND ${CLANG_FORMAT_PROGRAM}
-i # edits files in-place
--verbose
${CLANG_FORMAT_CHANGED_SOURCES}
)
else()
set(CLANG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-format")
endif()
register_command(
TARGET
clang-format-diff
COMMENT
"Running clang-format on changed files"
COMMAND
${CLANG_FORMAT_DIFF_COMMAND}
CWD
${BUILD_PATH}
ALWAYS_RUN
${BUN_C_SOURCES}
${BUN_CXX_SOURCES}
)

View File

@@ -13,13 +13,58 @@ find_command(
set(CLANG_TIDY_SOURCES ${BUN_C_SOURCES} ${BUN_CXX_SOURCES})
set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PROGRAM}
-p ${BUILD_PATH}
--config-file=${CWD}/.clang-tidy
find_command(
VARIABLE
GIT_PROGRAM
COMMAND
git
REQUIRED
OFF
)
if(CMAKE_COLOR_DIAGNOSTICS)
list(APPEND CLANG_TIDY_COMMAND --use-color)
if(GIT_PROGRAM)
execute_process(
COMMAND
${GIT_PROGRAM}
diff
--name-only
--diff-filter=AM
main
WORKING_DIRECTORY
${CWD}
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE
GIT_CHANGED_FILES
ERROR_QUIET
)
string(REPLACE "\n" ";" GIT_CHANGED_FILES ${GIT_CHANGED_FILES})
list(TRANSFORM GIT_CHANGED_FILES PREPEND ${CWD}/)
set(CLANG_TIDY_CHANGED_SOURCES)
foreach(source ${CLANG_TIDY_SOURCES})
list(FIND GIT_CHANGED_FILES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND CLANG_TIDY_CHANGED_SOURCES ${source})
endif()
endforeach()
if(CLANG_TIDY_CHANGED_SOURCES)
set(CLANG_TIDY_SOURCES ${CLANG_TIDY_CHANGED_SOURCES})
else()
set(CLANG_TIDY_COMMAND ${CMAKE_COMMAND} -E echo "No files changed for clang-tidy")
endif()
endif()
if(NOT CLANG_TIDY_COMMAND)
set(CLANG_TIDY_COMMAND ${CLANG_TIDY_PROGRAM}
-p ${BUILD_PATH}
--config-file=${CWD}/.clang-tidy
--fix
--fix-errors
--fix-notes
--use-color
${CLANG_TIDY_SOURCES}
)
endif()
register_command(
@@ -28,58 +73,7 @@ register_command(
COMMENT
"Running clang-tidy"
COMMAND
${CLANG_TIDY_COMMAND}
${CLANG_TIDY_SOURCES}
--fix
--fix-errors
--fix-notes
${CLANG_TIDY_COMMAND}
CWD
${BUILD_PATH}
ALWAYS_RUN
)
register_command(
TARGET
clang-tidy-check
COMMENT
"Checking clang-tidy"
COMMAND
${CLANG_TIDY_COMMAND}
${CLANG_TIDY_SOURCES}
CWD
${BUILD_PATH}
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(CLANG_TIDY_CHANGED_SOURCES)
foreach(source ${CLANG_TIDY_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND CLANG_TIDY_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(CLANG_TIDY_CHANGED_SOURCES)
set(CLANG_TIDY_DIFF_COMMAND ${CLANG_TIDY_PROGRAM}
${CLANG_TIDY_CHANGED_SOURCES}
--fix
--fix-errors
--fix-notes
)
else()
set(CLANG_TIDY_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for clang-tidy")
endif()
register_command(
TARGET
clang-tidy-diff
COMMENT
"Running clang-tidy on changed files"
COMMAND
${CLANG_TIDY_DIFF_COMMAND}
CWD
${BUILD_PATH}
ALWAYS_RUN
)

View File

@@ -1,123 +0,0 @@
if(CMAKE_HOST_WIN32)
setx(PRETTIER_EXECUTABLE ${CWD}/node_modules/.bin/prettier.exe)
else()
setx(PRETTIER_EXECUTABLE ${CWD}/node_modules/.bin/prettier)
endif()
set(PRETTIER_PATHS
${CWD}/src
${CWD}/packages/bun-error
${CWD}/packages/bun-types
${CWD}/packages/bun-inspector-protocol
${CWD}/packages/bun-inspector-frontend
${CWD}/packages/bun-debug-adapter-protocol
${CWD}/packages/bun-vscode
${CWD}/test
${CWD}/bench
${CWD}/.vscode
${CWD}/.buildkite
${CWD}/.github
)
set(PRETTIER_EXTENSIONS
*.jsonc?
*.ya?ml
*.jsx?
*.tsx?
*.mjs
*.cjs
*.mts
*.cts
)
set(PRETTIER_GLOBS)
foreach(path ${PRETTIER_PATHS})
foreach(extension ${PRETTIER_EXTENSIONS})
list(APPEND PRETTIER_GLOBS ${path}/${extension})
endforeach()
endforeach()
file(GLOB_RECURSE PRETTIER_SOURCES ${PRETTIER_GLOBS})
register_command(
COMMAND
${BUN_EXECUTABLE}
install
--frozen-lockfile
SOURCES
${CWD}/package.json
OUTPUTS
${PRETTIER_EXECUTABLE}
)
set(PRETTIER_COMMAND ${PRETTIER_EXECUTABLE}
--config=${CWD}/.prettierrc
--cache
)
register_command(
TARGET
prettier
COMMENT
"Running prettier"
COMMAND
${PRETTIER_COMMAND}
--write
${PRETTIER_SOURCES}
ALWAYS_RUN
)
register_command(
TARGET
prettier-extra
COMMENT
"Running prettier with extra plugins"
COMMAND
${PRETTIER_COMMAND}
--write
--plugin=prettier-plugin-organize-imports
${PRETTIER_SOURCES}
ALWAYS_RUN
)
register_command(
TARGET
prettier-check
COMMENT
"Checking prettier"
COMMAND
${PRETTIER_COMMAND}
--check
${PRETTIER_SOURCES}
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(PRETTIER_CHANGED_SOURCES)
foreach(source ${PRETTIER_SOURCES})
list(FIND PRETTIER_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND PRETTIER_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(PRETTIER_CHANGED_SOURCES)
set(PRETTIER_DIFF_COMMAND ${PRETTIER_COMMAND}
--write
--plugin=prettier-plugin-organize-imports
${PRETTIER_CHANGED_SOURCES}
)
else()
set(PRETTIER_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for prettier")
endif()
register_command(
TARGET
prettier-diff
COMMENT
"Running prettier on changed files"
COMMAND
${PRETTIER_DIFF_COMMAND}
ALWAYS_RUN
)

View File

@@ -1,5 +1,3 @@
set(ZIG_FORMAT_SOURCES ${BUN_ZIG_SOURCES})
register_command(
TARGET
zig-format-check
@@ -9,8 +7,7 @@ register_command(
${ZIG_EXECUTABLE}
fmt
--check
${ZIG_FORMAT_SOURCES}
ALWAYS_RUN
${BUN_ZIG_SOURCES}
)
register_command(
@@ -21,37 +18,5 @@ register_command(
COMMAND
${ZIG_EXECUTABLE}
fmt
${ZIG_FORMAT_SOURCES}
ALWAYS_RUN
)
if(GIT_CHANGED_SOURCES)
set(ZIG_FORMAT_CHANGED_SOURCES)
foreach(source ${ZIG_FORMAT_SOURCES})
list(FIND GIT_CHANGED_SOURCES ${source} index)
if(NOT ${index} EQUAL -1)
list(APPEND ZIG_FORMAT_CHANGED_SOURCES ${source})
endif()
endforeach()
endif()
if(ZIG_FORMAT_CHANGED_SOURCES)
set(ZIG_FORMAT_DIFF_COMMAND ${ZIG_EXECUTABLE}
fmt
${ZIG_FORMAT_CHANGED_SOURCES}
)
else()
set(ZIG_FORMAT_DIFF_COMMAND ${CMAKE_COMMAND} -E echo "No changed files for zig-format")
endif()
register_command(
TARGET
zig-format-diff
COMMENT
"Running zig fmt on changed files"
COMMAND
${ZIG_FORMAT_DIFF_COMMAND}
CWD
${BUILD_PATH}
ALWAYS_RUN
${BUN_ZIG_SOURCES}
)

View File

@@ -125,5 +125,7 @@ else()
file(RENAME ${DOWNLOAD_TMP_FILE} ${DOWNLOAD_PATH})
endif()
get_filename_component(DOWNLOAD_FILENAME ${DOWNLOAD_PATH} NAME)
message(STATUS "Saved ${DOWNLOAD_FILENAME}")
file(REMOVE_RECURSE ${DOWNLOAD_TMP_PATH})
message(STATUS "Saved ${DOWNLOAD_PATH}")

View File

@@ -1,8 +1,21 @@
get_filename_component(SCRIPT_NAME ${CMAKE_CURRENT_LIST_FILE} NAME)
message(STATUS "Running script: ${SCRIPT_NAME}")
if(NOT ZIG_PATH OR NOT ZIG_COMMIT OR NOT ZIG_VERSION)
message(FATAL_ERROR "ZIG_PATH, ZIG_COMMIT, and ZIG_VERSION are required")
if(NOT ZIG_PATH)
message(FATAL_ERROR "ZIG_PATH is required")
endif()
if(ZIG_REPOSITORY)
if(NOT ZIG_COMMIT)
message(FATAL_ERROR "ZIG_COMMIT is required when ZIG_REPOSITORY is set")
endif()
elseif(NOT ZIG_COMMIT)
set(ZIG_REPOSITORY "oven-sh/zig")
set(ZIG_COMMIT "131a009ba2eb127a3447d05b9e12f710429aa5ee")
endif()
if(NOT ZIG_VERSION)
set(ZIG_VERSION "0.13.0")
endif()
if(CMAKE_HOST_APPLE)
@@ -38,6 +51,7 @@ else()
set(ZIG_FILENAME ${ZIG_NAME}.tar.xz)
endif()
message(STATUS "Downloading ${ZIG_EXE} ${ZIG_VERSION} on ${ZIG_OS} ${ZIG_ARCH}...")
set(ZIG_DOWNLOAD_URL https://ziglang.org/download/${ZIG_VERSION}/${ZIG_FILENAME})
execute_process(
@@ -58,7 +72,7 @@ if(NOT ZIG_DOWNLOAD_RESULT EQUAL 0)
endif()
if(NOT EXISTS ${ZIG_PATH}/${ZIG_EXE})
message(FATAL_ERROR "Executable not found: \"${ZIG_PATH}/${ZIG_EXE}\"")
message(FATAL_ERROR "Download failed: executable not found: \"${ZIG_PATH}/${ZIG_EXE}\"")
endif()
# Tools like VSCode need a stable path to the zig executable, on both Unix and Windows
@@ -67,30 +81,31 @@ if(NOT WIN32)
file(CREATE_LINK ${ZIG_PATH}/${ZIG_EXE} ${ZIG_PATH}/zig.exe SYMBOLIC)
endif()
set(ZIG_REPOSITORY_PATH ${ZIG_PATH}/repository)
if(ZIG_REPOSITORY AND ZIG_COMMIT)
message(STATUS "Downloading zig library from ${ZIG_REPOSITORY} at ${ZIG_COMMIT}...")
execute_process(
COMMAND
${CMAKE_COMMAND}
-DGIT_PATH=${ZIG_REPOSITORY_PATH}
-DGIT_REPOSITORY=oven-sh/zig
-DGIT_COMMIT=${ZIG_COMMIT}
-P ${CMAKE_CURRENT_LIST_DIR}/GitClone.cmake
ERROR_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE
ZIG_REPOSITORY_ERROR
RESULT_VARIABLE
ZIG_REPOSITORY_RESULT
)
execute_process(
COMMAND
${CMAKE_COMMAND}
-DGIT_PATH=${ZIG_PATH}/tmp
-DGIT_REPOSITORY=${ZIG_REPOSITORY}
-DGIT_COMMIT=${ZIG_COMMIT}
-P ${CMAKE_CURRENT_LIST_DIR}/GitClone.cmake
ERROR_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE
ZIG_REPOSITORY_ERROR
RESULT_VARIABLE
ZIG_REPOSITORY_RESULT
)
if(NOT ZIG_REPOSITORY_RESULT EQUAL 0)
message(FATAL_ERROR "Download failed: ${ZIG_REPOSITORY_ERROR}")
if(NOT ZIG_REPOSITORY_RESULT EQUAL 0)
message(FATAL_ERROR "Download failed: ${ZIG_REPOSITORY_ERROR}")
endif()
file(REMOVE_RECURSE ${ZIG_PATH}/lib)
file(RENAME ${ZIG_PATH}/tmp/lib ${ZIG_PATH}/lib)
file(REMOVE_RECURSE ${ZIG_PATH}/tmp)
message(STATUS "Saved ${ZIG_PATH}/lib")
endif()
file(REMOVE_RECURSE ${ZIG_PATH}/lib)
# Use copy_directory instead of file(RENAME) because there were
# race conditions in CI where some files were not copied.
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_directory ${ZIG_REPOSITORY_PATH}/lib ${ZIG_PATH}/lib)
file(REMOVE_RECURSE ${ZIG_REPOSITORY_PATH})
message(STATUS "Saved ${ZIG_EXE}")

View File

@@ -1,21 +1,31 @@
register_vendor_target(boringssl)
register_repository(
NAME
boringssl
${boringssl}
REPOSITORY
oven-sh/boringssl
COMMIT
29a2cd359458c9384694b75456026e4b57e3e567
)
register_cmake_command(
register_libraries(
TARGET ${boringssl}
crypto
ssl
decrepit
)
register_cmake_project(
TARGET
boringssl
LIBRARIES
${boringssl}
CMAKE_TARGET
crypto
ssl
decrepit
ARGS
-DBUILD_SHARED_LIBS=OFF
INCLUDES
include
)
register_cmake_definitions(
TARGET ${boringssl}
BUILD_SHARED_LIBS=OFF
)

View File

@@ -1,31 +1,43 @@
register_vendor_target(brotli)
register_repository(
NAME
brotli
${brotli}
REPOSITORY
google/brotli
TAG
v1.1.0
COMMIT
ed738e842d2fbdf2d6459e39267a633c4a9b2f5d
)
register_libraries(
TARGET ${brotli}
brotlicommon
brotlidec
brotlienc
)
register_cmake_project(
TARGET
${brotli}
CMAKE_TARGET
brotlicommon
brotlidec
brotlienc
)
register_cmake_definitions(
TARGET ${brotli}
BUILD_SHARED_LIBS=OFF
BROTLI_BUILD_TOOLS=OFF
BROTLI_EMSCRIPTEN=OFF
BROTLI_DISABLE_TESTS=ON
)
# Tests fail with "BrotliDecompressionError" when LTO is enabled
# only on Linux x64 (non-baseline). It's a mystery.
if(LINUX AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64|X86_64|x64|X64|amd64|AMD64" AND NOT ENABLE_BASELINE)
set(BROTLI_CMAKE_ARGS "-DCMAKE_C_FLAGS=-fno-lto")
if(LINUX AND CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|AMD64|x86_64|X86_64|x64|X64" AND NOT ENABLE_BASELINE)
register_compiler_flags(
TARGET ${brotli}
-fno-lto
)
endif()
register_cmake_command(
TARGET
brotli
LIBRARIES
brotlicommon
brotlidec
brotlienc
ARGS
-DBUILD_SHARED_LIBS=OFF
-DBROTLI_BUILD_TOOLS=OFF
-DBROTLI_EMSCRIPTEN=OFF
-DBROTLI_DISABLE_TESTS=ON
${BROTLI_CMAKE_ARGS}
INCLUDES
c/include
)

View File

@@ -12,15 +12,6 @@ else()
set(bunStrip bun)
endif()
set(bunExe ${bun}${CMAKE_EXECUTABLE_SUFFIX})
if(bunStrip)
set(bunStripExe ${bunStrip}${CMAKE_EXECUTABLE_SUFFIX})
set(buns ${bun} ${bunStrip})
else()
set(buns ${bun})
endif()
# Some commands use this path, and some do not.
# In the future, change those commands so that generated files are written to this path.
optionx(CODEGEN_PATH FILEPATH "Path to the codegen directory" DEFAULT ${BUILD_PATH}/codegen)
@@ -60,8 +51,6 @@ register_command(
${BUN_ZIG_IDENTIFIER_SCRIPT}
SOURCES
${BUN_ZIG_IDENTIFIER_SOURCES}
TARGETS
clone-zig
OUTPUTS
${BUN_ZIG_IDENTIFIER_OUTPUTS}
)
@@ -474,6 +463,19 @@ WEBKIT_ADD_SOURCE_DEPENDENCIES(
${CODEGEN_PATH}/InternalModuleRegistryConstants.h
)
if(WIN32)
if(ENABLE_CANARY)
set(Bun_VERSION_WITH_TAG ${VERSION}-canary.${CANARY_REVISION})
else()
set(Bun_VERSION_WITH_TAG ${VERSION})
endif()
set(BUN_ICO_PATH ${CWD}/src/bun.ico)
configure_file(
${CWD}/src/windows-app-info.rc
${CODEGEN_PATH}/windows-app-info.rc
)
endif()
# --- Zig ---
file(GLOB_RECURSE BUN_ZIG_SOURCES ${CONFIGURE_DEPENDS}
@@ -546,17 +548,15 @@ register_command(
-Dgenerated-code=${CODEGEN_PATH}
ARTIFACTS
${BUN_ZIG_OUTPUT}
TARGETS
clone-zig
SOURCES
${BUN_ZIG_SOURCES}
${BUN_ZIG_GENERATED_SOURCES}
)
set_property(TARGET bun-zig PROPERTY JOB_POOL compile_pool)
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "build.zig")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CWD}/build.zig)
# --- C/C++ Sources ---
# --- C/C++ Object ---
set(BUN_USOCKETS_SOURCE ${CWD}/packages/bun-usockets)
@@ -581,41 +581,15 @@ file(GLOB BUN_C_SOURCES ${CONFIGURE_DEPENDS}
${BUN_USOCKETS_SOURCE}/src/crypto/*.c
)
list(APPEND BUN_C_SOURCES ${VENDOR_PATH}/picohttpparser/picohttpparser.c)
if(WIN32)
list(APPEND BUN_C_SOURCES ${CWD}/src/bun.js/bindings/windows/musl-memmem.c)
endif()
register_repository(
NAME
picohttpparser
REPOSITORY
h2o/picohttpparser
COMMIT
066d2b1e9ab820703db0837a7255d92d30f0c9f5
OUTPUTS
picohttpparser.c
)
set(NODEJS_HEADERS_PATH ${VENDOR_PATH}/nodejs)
register_command(
TARGET
bun-node-headers
COMMENT
"Download node ${NODEJS_VERSION} headers"
COMMAND
${CMAKE_COMMAND}
-DDOWNLOAD_PATH=${NODEJS_HEADERS_PATH}
-DDOWNLOAD_URL=https://nodejs.org/dist/v${NODEJS_VERSION}/node-v${NODEJS_VERSION}-headers.tar.gz
-P ${CWD}/cmake/scripts/DownloadUrl.cmake
OUTPUTS
${NODEJS_HEADERS_PATH}/include/node/node_version.h
)
list(APPEND BUN_CPP_SOURCES
${BUN_C_SOURCES}
${BUN_CXX_SOURCES}
${VENDOR_PATH}/picohttpparser/picohttpparser.c
${BUN_ZIG_GENERATED_CLASSES_OUTPUTS}
${BUN_JS_SINK_OUTPUTS}
${BUN_JAVASCRIPT_OUTPUTS}
@@ -623,22 +597,12 @@ list(APPEND BUN_CPP_SOURCES
)
if(WIN32)
if(ENABLE_CANARY)
set(Bun_VERSION_WITH_TAG ${VERSION}-canary.${CANARY_REVISION})
else()
set(Bun_VERSION_WITH_TAG ${VERSION})
endif()
set(BUN_ICO_PATH ${CWD}/src/bun.ico)
configure_file(
${CWD}/src/windows-app-info.rc
${CODEGEN_PATH}/windows-app-info.rc
)
list(APPEND BUN_CPP_SOURCES ${CODEGEN_PATH}/windows-app-info.rc)
endif()
# --- Executable ---
set(BUN_CPP_OUTPUT ${BUILD_PATH}/${CMAKE_STATIC_LIBRARY_PREFIX}${bun}${CMAKE_STATIC_LIBRARY_SUFFIX})
set(BUN_EXE_OUTPUT ${BUILD_PATH}/${CMAKE_EXECUTABLE_PREFIX}${bun}${CMAKE_EXECUTABLE_SUFFIX})
set(BUN_EXE_STRIP_OUTPUT ${BUILD_PATH}/${CMAKE_EXECUTABLE_PREFIX}bun${CMAKE_EXECUTABLE_SUFFIX})
if(BUN_LINK_ONLY)
add_executable(${bun} ${BUN_CPP_OUTPUT} ${BUN_ZIG_OUTPUT})
@@ -646,30 +610,21 @@ if(BUN_LINK_ONLY)
target_link_libraries(${bun} PRIVATE ${BUN_CPP_OUTPUT})
elseif(BUN_CPP_ONLY)
add_library(${bun} STATIC ${BUN_CPP_SOURCES})
register_command(
TARGET
${bun}
TARGET_PHASE
POST_BUILD
COMMENT
"Uploading ${bun}"
COMMAND
${CMAKE_COMMAND} -E true
ARTIFACTS
${BUN_CPP_OUTPUT}
upload_artifacts(
TARGET ${bun}
${BUN_CPP_OUTPUT}
)
else()
add_executable(${bun} ${BUN_CPP_SOURCES})
target_link_libraries(${bun} PRIVATE ${BUN_ZIG_OUTPUT})
upload_artifacts(
TARGET ${bun}
${BUN_EXE_OUTPUT}
)
endif()
if(NOT bun STREQUAL "bun")
add_custom_target(bun DEPENDS ${bun})
endif()
# --- C/C++ Properties ---
set_target_properties(${bun} PROPERTIES
OUTPUT_NAME ${bun}
CXX_STANDARD 20
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS YES
@@ -679,6 +634,17 @@ set_target_properties(${bun} PROPERTIES
VISIBILITY_INLINES_HIDDEN YES
)
if(BUN_LINK_ONLY)
set_target_properties(${bun} PROPERTIES
OUTPUT_NAME ${bun}
LINKER_LANGUAGE CXX
)
endif()
if(NOT bun STREQUAL "bun")
add_custom_target(bun DEPENDS ${bun})
endif()
# --- C/C++ Includes ---
if(WIN32)
@@ -733,7 +699,6 @@ target_compile_definitions(${bun} PRIVATE
WITH_BORINGSSL=1
STATICALLY_LINKED_WITH_JavaScriptCore=1
STATICALLY_LINKED_WITH_BMALLOC=1
BUILDING_WITH_CMAKE=1
JSC_OBJC_API_ENABLED=0
BUN_SINGLE_THREADED_PER_VM_ENTRY_SCOPE=1
NAPI_EXPERIMENTAL=ON
@@ -750,7 +715,6 @@ if(DEBUG AND NOT CI)
)
endif()
# --- Compiler options ---
if(NOT WIN32)
@@ -925,8 +889,6 @@ set_target_properties(${bun} PROPERTIES LINK_DEPENDS ${BUN_SYMBOLS_PATH})
# --- WebKit ---
include(SetupWebKit)
if(WIN32)
if(DEBUG)
target_link_libraries(${bun} PRIVATE
@@ -963,34 +925,38 @@ endif()
# --- Dependencies ---
set(BUN_DEPENDENCIES
BoringSSL
Brotli
Cares
LibDeflate
LolHtml
Lshpack
Mimalloc
TinyCC
Zlib
LibArchive # must be loaded after zlib
Zstd
register_link_targets(
TARGET ${bun}
${boringssl}
${brotli}
${cares}
${libarchive}
${libdeflate}
${libuv} ${WIN32}
${lolhtml}
${lshpack}
${mimalloc}
${tinycc}
${sqlite} ${USE_STATIC_SQLITE}
${webkit}
${zlib}
${zstd}
)
if(WIN32)
list(APPEND BUN_DEPENDENCIES Libuv)
endif()
if(USE_STATIC_SQLITE)
list(APPEND BUN_DEPENDENCIES SQLite)
endif()
foreach(dependency ${BUN_DEPENDENCIES})
include(Build${dependency})
endforeach()
list(TRANSFORM BUN_DEPENDENCIES TOLOWER OUTPUT_VARIABLE BUN_TARGETS)
add_custom_target(dependencies DEPENDS ${BUN_TARGETS})
register_includes(
TARGET ${bun}
${${picohttpparser}_CWD}
${${boringssl}_CWD}/include
${${brotli}_CWD}/c/include
${${cares}_CWD}/include
${${libarchive}_CWD}/include
${${libdeflate}_CWD}
${${libuv}_CWD}/include ${WIN32}
${${lshpack}_CWD}
${${lshpack}_CWD}/compat/queue ${WIN32}
${${mimalloc}_CWD}/include
${${zlib}_CWD}
)
if(APPLE)
target_link_libraries(${bun} PRIVATE icucore resolv)
@@ -1036,6 +1002,12 @@ endif()
# --- Packaging ---
if(bunStrip)
set(buns ${bun} ${bunStrip})
else()
set(buns ${bun})
endif()
if(NOT BUN_CPP_ONLY)
if(bunStrip)
register_command(
@@ -1047,15 +1019,15 @@ if(NOT BUN_CPP_ONLY)
"Stripping ${bun}"
COMMAND
${CMAKE_STRIP}
${bunExe}
${BUN_EXE_OUTPUT}
--strip-all
--strip-debug
--discard-all
-o ${bunStripExe}
-o ${BUN_EXE_STRIP_OUTPUT}
CWD
${BUILD_PATH}
OUTPUTS
${BUILD_PATH}/${bunStripExe}
${BUN_EXE_STRIP_OUTPUT}
)
endif()
@@ -1068,9 +1040,9 @@ if(NOT BUN_CPP_ONLY)
"Testing ${bun}"
COMMAND
${CMAKE_COMMAND}
-E env BUN_DEBUG_QUIET_LOGS=1
${BUILD_PATH}/${bunExe}
--revision
-E env BUN_DEBUG_QUIET_LOGS=1
${BUN_EXE_OUTPUT}
--revision
CWD
${BUILD_PATH}
)
@@ -1090,7 +1062,7 @@ if(NOT BUN_CPP_ONLY)
BUN_GARBAGE_COLLECTOR_LEVEL=1
BUN_DEBUG_QUIET_LOGS=1
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING=1
${BUILD_PATH}/${bunExe}
${BUN_EXE_OUTPUT}
${BUN_FEATURES_SCRIPT}
CWD
${BUILD_PATH}
@@ -1099,7 +1071,7 @@ if(NOT BUN_CPP_ONLY)
)
endif()
if(CMAKE_HOST_APPLE AND bunStrip)
if(bunStrip AND APPLE)
register_command(
TARGET
${bun}
@@ -1129,7 +1101,7 @@ if(NOT BUN_CPP_ONLY)
set(bunTriplet bun-${OS}-${ARCH})
endif()
string(REPLACE bun ${bunTriplet} bunPath ${bun})
set(bunFiles ${bunExe} features.json)
set(bunFiles ${BUN_EXE_OUTPUT} features.json)
if(WIN32)
list(APPEND bunFiles ${bun}.pdb)
elseif(APPLE)
@@ -1166,7 +1138,7 @@ if(NOT BUN_CPP_ONLY)
COMMAND
${CMAKE_COMMAND} -E rm -rf ${bunStripPath} ${bunStripPath}.zip
&& ${CMAKE_COMMAND} -E make_directory ${bunStripPath}
&& ${CMAKE_COMMAND} -E copy ${bunStripExe} ${bunStripPath}
&& ${CMAKE_COMMAND} -E copy ${${BUN_EXE_STRIP_OUTPUT}} ${bunStripPath}
&& ${CMAKE_COMMAND} -E tar cfv ${bunStripPath}.zip --format=zip ${bunStripPath}
&& ${CMAKE_COMMAND} -E rm -rf ${bunStripPath}
CWD

View File

@@ -1,27 +1,32 @@
register_vendor_target(cares)
register_repository(
NAME
cares
${cares}
REPOSITORY
c-ares/c-ares
COMMIT
d1722e6e8acaf10eb73fa995798a9cd421d9f85e
)
register_cmake_command(
TARGET
cares
TARGETS
c-ares
ARGS
-DCARES_STATIC=ON
-DCARES_STATIC_PIC=ON # FORCE_PIC was set to 1, but CARES_STATIC_PIC was set to OFF??
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DCARES_SHARED=OFF
-DCARES_BUILD_TOOLS=OFF # this was set to ON?
LIB_PATH
lib
LIBRARIES
cares
INCLUDES
include
register_libraries(
TARGET ${cares}
PATH lib
cares
)
register_cmake_project(
TARGET
${cares}
CMAKE_TARGET
c-ares
)
register_cmake_definitions(
TARGET ${cares}
CARES_STATIC=ON
CARES_STATIC_PIC=ON
CARES_SHARED=OFF
CARES_BUILD_TOOLS=OFF
CMAKE_POSITION_INDEPENDENT_CODE=ON
)

View File

@@ -1,53 +1,61 @@
register_vendor_target(libarchive)
register_repository(
NAME
libarchive
${libarchive}
REPOSITORY
libarchive/libarchive
COMMIT
898dc8319355b7e985f68a9819f182aaed61b53a
)
register_cmake_command(
TARGET
libarchive
TARGETS
archive_static
ARGS
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
-DBUILD_SHARED_LIBS=OFF
-DENABLE_INSTALL=OFF
-DENABLE_TEST=OFF
-DENABLE_WERROR=OFF
-DENABLE_BZIP2=OFF
-DENABLE_CAT=OFF
-DENABLE_EXPAT=OFF
-DENABLE_ICONV=OFF
-DENABLE_LIBB2=OFF
-DENABLE_LibGCC=OFF
-DENABLE_LIBXML2=OFF
-DENABLE_LZ4=OFF
-DENABLE_LZMA=OFF
-DENABLE_LZO=OFF
-DENABLE_MBEDTLS=OFF
-DENABLE_NETTLE=OFF
-DENABLE_OPENSSL=OFF
-DENABLE_PCRE2POSIX=OFF
-DENABLE_PCREPOSIX=OFF
-DENABLE_ZSTD=OFF
# libarchive depends on zlib headers, otherwise it will
# spawn a processes to compress instead of using the library.
-DENABLE_ZLIB=OFF
-DHAVE_ZLIB_H=ON
-DCMAKE_C_FLAGS="-I${VENDOR_PATH}/zlib"
LIB_PATH
libarchive
LIBRARIES
archive
INCLUDES
include
register_libraries(
TARGET ${libarchive}
PATH libarchive
archive
)
# Must be loaded after zlib is defined
if(TARGET clone-zlib)
add_dependencies(libarchive clone-zlib)
register_cmake_project(
TARGET
${libarchive}
CMAKE_TARGET
archive_static
)
register_cmake_definitions(
TARGET ${libarchive}
CMAKE_POSITION_INDEPENDENT_CODE=ON
BUILD_SHARED_LIBS=OFF
ENABLE_INSTALL=OFF
ENABLE_TEST=OFF
ENABLE_WERROR=OFF
ENABLE_BZIP2=OFF
ENABLE_CAT=OFF
ENABLE_EXPAT=OFF
ENABLE_ICONV=OFF
ENABLE_LIBB2=OFF
ENABLE_LibGCC=OFF
ENABLE_LIBXML2=OFF
ENABLE_LZ4=OFF
ENABLE_LZMA=OFF
ENABLE_LZO=OFF
ENABLE_MBEDTLS=OFF
ENABLE_NETTLE=OFF
ENABLE_OPENSSL=OFF
ENABLE_PCRE2POSIX=OFF
ENABLE_PCREPOSIX=OFF
ENABLE_ZSTD=OFF
ENABLE_ZLIB=OFF
HAVE_ZLIB_H=ON
)
# libarchive depends on zlib headers, otherwise it will
# spawn a processes to compress instead of using the library.
register_includes(
TARGET ${libarchive}
${VENDOR_PATH}/${zlib}
)
if(TARGET clone-${zlib})
add_dependencies(${libarchive} clone-${zlib})
endif()

View File

@@ -1,24 +1,30 @@
register_vendor_target(libdeflate)
register_repository(
NAME
libdeflate
${libdeflate}
REPOSITORY
ebiggers/libdeflate
COMMIT
dc76454a39e7e83b68c3704b6e3784654f8d5ac5
)
register_cmake_command(
TARGET
libdeflate
TARGETS
libdeflate_static
ARGS
-DLIBDEFLATE_BUILD_STATIC_LIB=ON
-DLIBDEFLATE_BUILD_SHARED_LIB=OFF
-DLIBDEFLATE_BUILD_GZIP=OFF
LIBRARIES
deflatestatic WIN32
deflate UNIX
INCLUDES
.
register_libraries(
TARGET ${libdeflate}
deflatestatic ${WIN32}
deflate ${UNIX}
)
register_cmake_project(
TARGET
${libdeflate}
CMAKE_TARGET
libdeflate_static
)
register_cmake_definitions(
TARGET ${libdeflate}
LIBDEFLATE_BUILD_STATIC_LIB=ON
LIBDEFLATE_BUILD_SHARED_LIB=OFF
LIBDEFLATE_BUILD_GZIP=OFF
)

View File

@@ -1,29 +1,39 @@
register_vendor_target(libuv)
register_repository(
NAME
libuv
${libuv}
REPOSITORY
libuv/libuv
COMMIT
da527d8d2a908b824def74382761566371439003
)
if(WIN32)
set(LIBUV_CMAKE_C_FLAGS "/DWIN32 /D_WINDOWS -Wno-int-conversion")
endif()
register_cmake_command(
TARGET
libuv
TARGETS
uv_a
ARGS
-DLIBUV_BUILD_SHARED=OFF
-DLIBUV_BUILD_TESTS=OFF
-DLIBUV_BUILD_BENCH=OFF
-DCMAKE_C_FLAGS=${LIBUV_CMAKE_C_FLAGS}
LIBRARIES
libuv WIN32
uv UNIX
INCLUDES
include
register_libraries(
TARGET ${libuv}
uv_a ${WIN32}
uv ${UNIX}
)
register_cmake_project(
TARGET
${libuv}
CMAKE_TARGET
uv_a
)
register_cmake_definitions(
TARGET ${libuv}
LIBUV_BUILD_SHARED=OFF
LIBUV_BUILD_TESTS=OFF
LIBUV_BUILD_BENCH=OFF
)
if(WIN32)
register_compiler_flags(
TARGET ${libuv}
/DWIN32
/D_WINDOWS
-Wno-int-conversion
)
endif()

View File

@@ -1,45 +1,50 @@
register_vendor_target(lolhtml)
register_repository(
NAME
lolhtml
${lolhtml}
REPOSITORY
cloudflare/lol-html
COMMIT
8d4c273ded322193d017042d1f48df2766b0f88b
)
set(LOLHTML_CWD ${VENDOR_PATH}/lolhtml/c-api)
set(LOLHTML_BUILD_PATH ${BUILD_PATH}/lolhtml)
if(DEBUG)
set(LOLHTML_BUILD_TYPE debug)
set(${lolhtml}_BUILD_TYPE debug)
else()
set(LOLHTML_BUILD_TYPE release)
set(${lolhtml}_BUILD_TYPE release)
endif()
set(LOLHTML_LIBRARY ${LOLHTML_BUILD_PATH}/${LOLHTML_BUILD_TYPE}/${CMAKE_STATIC_LIBRARY_PREFIX}lolhtml${CMAKE_STATIC_LIBRARY_SUFFIX})
register_libraries(
TARGET ${lolhtml}
PATH ${${lolhtml}_BUILD_TYPE}
VARIABLE ${lolhtml}_LIBRARY
lolhtml
)
set(LOLHTML_BUILD_ARGS
--target-dir ${BUILD_PATH}/lolhtml
set(${lolhtml}_BUILD_COMMAND
${CARGO_EXECUTABLE}
build
--target-dir ${${lolhtml}_BUILD_PATH}
)
if(RELEASE)
list(APPEND LOLHTML_BUILD_ARGS --release)
list(APPEND ${lolhtml}_BUILD_COMMAND --release)
endif()
register_command(
TARGET
lolhtml
build-${lolhtml}
CWD
${LOLHTML_CWD}
${${lolhtml}_CWD}/c-api
COMMAND
${CARGO_EXECUTABLE}
build
${LOLHTML_BUILD_ARGS}
${${lolhtml}_BUILD_COMMAND}
ARTIFACTS
${LOLHTML_LIBRARY}
${${lolhtml}_LIBRARY}
)
target_link_libraries(${bun} PRIVATE ${LOLHTML_LIBRARY})
if(BUN_LINK_ONLY)
target_sources(${bun} PRIVATE ${LOLHTML_LIBRARY})
if(TARGET clone-${lolhtml})
add_dependencies(build-${lolhtml} clone-${lolhtml})
endif()
add_dependencies(${lolhtml} build-${lolhtml})

View File

@@ -1,33 +1,42 @@
register_vendor_target(lshpack)
register_repository(
NAME
lshpack
${lshpack}
REPOSITORY
litespeedtech/ls-hpack
COMMIT
3d0f1fc1d6e66a642e7a98c55deb38aa986eb4b0
)
if(WIN32)
set(LSHPACK_INCLUDES . compat/queue)
else()
set(LSHPACK_INCLUDES .)
endif()
register_cmake_command(
TARGET
lshpack
LIBRARIES
ls-hpack
ARGS
-DSHARED=OFF
-DLSHPACK_XXH=ON
# There are linking errors when built with non-Release
# Undefined symbols for architecture arm64:
# "___asan_handle_no_return", referenced from:
# _lshpack_enc_get_static_nameval in libls-hpack.a(lshpack.c.o)
# _lshpack_enc_get_static_name in libls-hpack.a(lshpack.c.o)
# _update_hash in libls-hpack.a(lshpack.c.o)
-DCMAKE_BUILD_TYPE=Release
INCLUDES
${LSHPACK_INCLUDES}
register_libraries(
TARGET ${lshpack}
ls-hpack
)
register_cmake_project(
TARGET
${lshpack}
CMAKE_TARGET
ls-hpack
)
register_cmake_definitions(
TARGET ${lshpack}
SHARED=OFF
LSHPACK_XXH=ON
BUILD_TESTING=OFF
)
# FIXME: There are linking errors when built with non-Release
# Undefined symbols for architecture arm64:
# "___asan_handle_no_return", referenced from:
# _lshpack_enc_get_static_nameval in libls-hpack.a(lshpack.c.o)
# _lshpack_enc_get_static_name in libls-hpack.a(lshpack.c.o)
# _update_hash in libls-hpack.a(lshpack.c.o)
if(NOT CMAKE_BUILD_TYPE STREQUAL "Release")
register_cmake_definitions(
TARGET ${lshpack}
CMAKE_BUILD_TYPE=Release
)
endif()

View File

@@ -1,60 +1,63 @@
register_vendor_target(mimalloc)
register_repository(
NAME
mimalloc
${mimalloc}
REPOSITORY
oven-sh/mimalloc
COMMIT
4c283af60cdae205df5a872530c77e2a6a307d43
)
set(MIMALLOC_CMAKE_ARGS
-DMI_BUILD_STATIC=ON
-DMI_BUILD_OBJECT=ON
-DMI_BUILD_SHARED=OFF
-DMI_BUILD_TESTS=OFF
-DMI_USE_CXX=ON
-DMI_OVERRIDE=OFF
-DMI_OSX_ZONE=OFF
-DMI_OSX_INTERPOSE=OFF
-DMI_SKIP_COLLECT_ON_EXIT=ON
)
if(DEBUG)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_DEBUG_FULL=ON)
endif()
if(ENABLE_VALGRIND)
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_VALGRIND=ON)
endif()
if(WIN32)
if(DEBUG)
set(MIMALLOC_LIBRARY mimalloc-static-debug)
else()
set(MIMALLOC_LIBRARY mimalloc-static)
endif()
elseif(DEBUG)
set(MIMALLOC_LIBRARY mimalloc-debug)
else()
set(MIMALLOC_LIBRARY mimalloc)
endif()
# Workaround for linker issue on macOS and Linux x64
# https://github.com/microsoft/mimalloc/issues/512
if(APPLE OR (LINUX AND NOT DEBUG))
set(MIMALLOC_LIBRARY CMakeFiles/mimalloc-obj.dir/src/static.c.o)
register_libraries(
TARGET ${mimalloc}
PATH CMakeFiles/mimalloc-obj.dir/src
static.c.o
)
else()
register_libraries(
TARGET ${mimalloc}
mimalloc-static-debug ${WIN32} AND ${DEBUG}
mimalloc-static ${WIN32} AND ${RELEASE}
mimalloc-debug ${UNIX} AND ${DEBUG}
mimalloc ${UNIX} AND ${RELEASE}
)
endif()
register_cmake_command(
register_cmake_project(
TARGET
mimalloc
TARGETS
${mimalloc}
CMAKE_TARGETS
mimalloc-static
mimalloc-obj
ARGS
${MIMALLOC_CMAKE_ARGS}
LIBRARIES
${MIMALLOC_LIBRARY}
INCLUDES
include
)
register_cmake_definitions(
TARGET ${mimalloc}
MI_BUILD_STATIC=ON
MI_BUILD_OBJECT=ON
MI_BUILD_SHARED=OFF
MI_BUILD_TESTS=OFF
MI_USE_CXX=ON
MI_OVERRIDE=OFF
MI_OSX_ZONE=OFF
MI_OSX_INTERPOSE=OFF
MI_SKIP_COLLECT_ON_EXIT=ON
)
if(ENABLE_ASSERTIONS)
register_cmake_definitions(
TARGET ${mimalloc}
MI_DEBUG_FULL=ON
MI_SHOW_ERRORS=ON
)
if(ENABLE_VALGRIND)
register_cmake_definitions(
TARGET ${mimalloc}
MI_VALGRIND=ON
)
endif()
endif()

View File

@@ -0,0 +1,12 @@
register_vendor_target(picohttpparser)
register_repository(
NAME
${picohttpparser}
REPOSITORY
h2o/picohttpparser
COMMIT
066d2b1e9ab820703db0837a7255d92d30f0c9f5
OUTPUTS
picohttpparser.c
)

View File

@@ -1,10 +1,13 @@
register_cmake_command(
register_vendor_target(sqlite)
register_libraries(
TARGET ${sqlite}
sqlite3
)
register_cmake_project(
TARGET
sqlite
${sqlite}
CWD
${CWD}/src/bun.js/bindings/sqlite
LIBRARIES
sqlite3
INCLUDES
.
)

View File

@@ -1,15 +1,20 @@
register_vendor_target(tinycc)
register_repository(
NAME
tinycc
${tinycc}
REPOSITORY
oven-sh/tinycc
COMMIT
29985a3b59898861442fa3b43f663fc1af2591d7
)
register_cmake_command(
TARGET
tinycc
LIBRARIES
tcc
register_libraries(
TARGET ${tinycc}
tcc
)
register_cmake_project(
TARGET
${tinycc}
)

View File

@@ -0,0 +1,88 @@
optionx(WEBKIT_LOCAL BOOL "If a local version of WebKit should be used instead of downloading" DEFAULT OFF)
optionx(WEBKIT_VERSION STRING "The version of WebKit to use" DEFAULT "4a2db3254a9535949a5d5380eb58cf0f77c8e15a")
if(WEBKIT_LOCAL)
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${CMAKE_BUILD_TYPE})
else()
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/webkit)
endif()
optionx(WEBKIT_PATH FILEPATH "The path to the WebKit directory" DEFAULT ${DEFAULT_WEBKIT_PATH})
set(WEBKIT_INCLUDE_PATH ${WEBKIT_PATH}/include)
set(WEBKIT_LIB_PATH ${WEBKIT_PATH}/lib)
register_vendor_target(webkit)
register_libraries(
TARGET ${webkit}
PATH ${WEBKIT_PATH}/lib
JavaScriptCore
WTF
bmalloc ${LINUX}
)
if(WIN32)
register_libraries(
TARGET ${webkit}
PATH ${WEBKIT_PATH}/lib
sicudt ${RELEASE}
sicudtd ${DEBUG}
sicuin ${RELEASE}
sicuind ${DEBUG}
sicuuc ${RELEASE}
sicuucd ${DEBUG}
)
endif()
if(WEBKIT_LOCAL)
# Must be built seperately, in the future this can be integrated into the build process
register_target(build-webkit)
else()
if(WIN32)
set(WEBKIT_OS "windows")
elseif(APPLE)
set(WEBKIT_OS "macos")
elseif(LINUX)
set(WEBKIT_OS "linux")
else()
unsupported(CMAKE_SYSTEM_NAME)
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|ARM64|aarch64|AARCH64")
set(WEBKIT_ARCH "arm64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|AMD64|x86_64|X86_64|x64|X64")
set(WEBKIT_ARCH "amd64")
else()
unsupported(CMAKE_SYSTEM_PROCESSOR)
endif()
if(DEBUG)
set(WEBKIT_SUFFIX "-debug")
elseif(ENABLE_LTO AND NOT WIN32)
set(WEBKIT_SUFFIX "-lto")
else()
set(WEBKIT_SUFFIX "")
endif()
set(WEBKIT_NAME bun-webkit-${WEBKIT_OS}-${WEBKIT_ARCH}${WEBKIT_SUFFIX})
set(WEBKIT_DOWNLOAD_URL https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_VERSION}/${WEBKIT_NAME}.tar.gz)
get_libraries(${webkit} WEBKIT_LIBRARIES)
register_command(
TARGET
clone-${webkit}
COMMENT
"Downloading ${WEBKIT_NAME}"
COMMAND
${CMAKE_COMMAND}
-DDOWNLOAD_PATH=${WEBKIT_PATH}
-DDOWNLOAD_URL=${WEBKIT_DOWNLOAD_URL}
-P ${CWD}/cmake/scripts/DownloadUrl.cmake
OUTPUTS
${WEBKIT_PATH}/package.json
${WEBKIT_LIBRARIES}
)
register_outputs(TARGET clone-${webkit} ${WEBKIT_PATH})
endif()

View File

@@ -1,40 +1,38 @@
register_vendor_target(zlib)
register_repository(
NAME
zlib
${zlib}
REPOSITORY
cloudflare/zlib
COMMIT
886098f3f339617b4243b286f5ed364b9989e245
)
register_libraries(
TARGET ${zlib}
z ${UNIX}
zlib ${WIN32} AND ${RELEASE}
zlibd ${WIN32} AND ${DEBUG}
)
register_cmake_project(
TARGET
${zlib}
CMAKE_TARGET
zlib
)
register_cmake_definitions(
TARGET ${zlib}
BUILD_SHARED_LIBS=OFF
BUILD_EXAMPLES=OFF
)
# https://gitlab.kitware.com/cmake/cmake/-/issues/25755
if(APPLE)
set(ZLIB_CMAKE_C_FLAGS "-fno-define-target-os-macros")
set(ZLIB_CMAKE_CXX_FLAGS "-fno-define-target-os-macros")
register_compiler_flags(
TARGET ${zlib}
-fno-define-target-os-macros
)
endif()
if(WIN32)
if(DEBUG)
set(ZLIB_LIBRARY "zlibd")
else()
set(ZLIB_LIBRARY "zlib")
endif()
else()
set(ZLIB_LIBRARY "z")
endif()
register_cmake_command(
TARGET
zlib
TARGETS
zlib
ARGS
-DBUILD_SHARED_LIBS=OFF
-DBUILD_EXAMPLES=OFF
"-DCMAKE_C_FLAGS=${ZLIB_CMAKE_C_FLAGS}"
"-DCMAKE_CXX_FLAGS=${ZLIB_CMAKE_CXX_FLAGS}"
LIBRARIES
${ZLIB_LIBRARY}
INCLUDES
.
)

View File

@@ -1,26 +1,34 @@
register_vendor_target(zstd)
register_repository(
NAME
zstd
${zstd}
REPOSITORY
facebook/zstd
COMMIT
794ea1b0afca0f020f4e57b6732332231fb23c70
)
register_cmake_command(
TARGET
zstd
TARGETS
libzstd_static
ARGS
-Sbuild/cmake
-DZSTD_BUILD_STATIC=ON
-DZSTD_BUILD_PROGRAMS=OFF
-DZSTD_BUILD_TESTS=OFF
-DZSTD_BUILD_CONTRIB=OFF
LIB_PATH
lib
LIBRARIES
zstd_static WIN32
zstd UNIX
register_libraries(
TARGET ${zstd}
PATH lib
zstd_static ${WIN32}
zstd ${UNIX}
)
register_cmake_project(
TARGET
${zstd}
CMAKE_TARGET
libzstd_static
CMAKE_PATH
build/cmake
)
register_cmake_definitions(
TARGET ${zstd}
ZSTD_BUILD_STATIC=ON
ZSTD_BUILD_PROGRAMS=OFF
ZSTD_BUILD_TESTS=OFF
ZSTD_BUILD_CONTRIB=OFF
)

View File

@@ -1,6 +1,4 @@
optionx(BUILDKITE_CACHE BOOL "If the build can use Buildkite caches, even if not running in Buildkite" DEFAULT ${BUILDKITE})
if(NOT BUILDKITE_CACHE OR NOT BUN_LINK_ONLY)
if(NOT BUN_LINK_ONLY)
return()
endif()
@@ -33,7 +31,7 @@ if(NOT BUILDKITE_BUILD_ID)
endif()
setx(BUILDKITE_BUILD_URL https://buildkite.com/${BUILDKITE_ORGANIZATION_SLUG}/${BUILDKITE_PIPELINE_SLUG}/builds/${BUILDKITE_BUILD_ID})
setx(BUILDKITE_BUILD_PATH ${BUILDKITE_BUILDS_PATH}/builds/${BUILDKITE_BUILD_ID})
setx(BUILDKITE_BUILD_PATH ${BUILDKITE_BUILDS_PATH}/${BUILDKITE_BUILD_ID})
file(
DOWNLOAD ${BUILDKITE_BUILD_URL}
@@ -125,15 +123,14 @@ foreach(i RANGE ${BUILDKITE_JOBS_MAX_INDEX})
set(BUILDKITE_DOWNLOAD_COMMAND curl -L -o ${BUILDKITE_ARTIFACT_PATH} ${BUILDKITE_ARTIFACTS_URL}/${BUILDKITE_ARTIFACT_ID})
endif()
add_custom_command(
COMMENT
"Downloading ${BUILDKITE_ARTIFACT_PATH}"
VERBATIM COMMAND
${BUILDKITE_DOWNLOAD_COMMAND}
WORKING_DIRECTORY
${BUILD_PATH}
OUTPUT
${BUILD_PATH}/${BUILDKITE_ARTIFACT_PATH}
message(STATUS "Downloading ${BUILD_PATH}/${BUILDKITE_ARTIFACT_PATH}")
get_filename_component(BUILDKITE_ARTIFACT_NAME ${BUILDKITE_ARTIFACT_PATH} NAME_WE)
register_command(
TARGET download-${BUILDKITE_ARTIFACT_NAME}
COMMENT "Downloading ${BUILDKITE_ARTIFACT_PATH}"
COMMAND ${BUILDKITE_DOWNLOAD_COMMAND}
CWD ${BUILD_PATH}
OUTPUTS ${BUILD_PATH}/${BUILDKITE_ARTIFACT_PATH}
)
endforeach()

View File

@@ -14,11 +14,8 @@ find_command(
ON
)
set(CCACHE_ARGS CMAKE_C_COMPILER_LAUNCHER CMAKE_CXX_COMPILER_LAUNCHER)
foreach(arg ${CCACHE_ARGS})
setx(${arg} ${CCACHE_PROGRAM})
list(APPEND CMAKE_ARGS -D${arg}=${${arg}})
endforeach()
setx(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
setx(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM})
setenv(CCACHE_DIR ${CACHE_PATH}/ccache)
setenv(CCACHE_BASEDIR ${CWD})

View File

@@ -1,38 +0,0 @@
find_command(
VARIABLE
GIT_PROGRAM
COMMAND
git
REQUIRED
OFF
)
if(NOT GIT_PROGRAM)
return()
endif()
set(GIT_DIFF_COMMAND ${GIT_PROGRAM} diff --no-color --name-only --diff-filter=AMCR)
execute_process(
COMMAND
${GIT_DIFF_COMMAND}
WORKING_DIRECTORY
${CWD}
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE
GIT_DIFF
ERROR_STRIP_TRAILING_WHITESPACE
ERROR_VARIABLE
GIT_DIFF_ERROR
RESULT_VARIABLE
GIT_DIFF_RESULT
)
if(NOT GIT_DIFF_RESULT EQUAL 0)
message(${WARNING} "Command failed: ${GIT_DIFF_COMMAND} ${GIT_DIFF_ERROR}")
return()
endif()
string(REPLACE "\n" ";" GIT_CHANGED_SOURCES "${GIT_DIFF}")
list(TRANSFORM GIT_CHANGED_SOURCES PREPEND ${CWD}/)
list(LENGTH GIT_CHANGED_SOURCES GIT_CHANGED_SOURCES_COUNT)

View File

@@ -1,9 +1,3 @@
optionx(SKIP_LLVM BOOL "If LLVM setup should be skipped" DEFAULT OFF)
if(SKIP_LLVM)
return()
endif()
if(CMAKE_HOST_WIN32 OR CMAKE_HOST_APPLE)
set(DEFAULT_LLVM_VERSION "18.1.8")
else()
@@ -39,7 +33,6 @@ macro(find_llvm_command VARIABLE COMMAND)
PATHS ${LLVM_PATH}
VERSION ${LLVM_VERSION}
)
list(APPEND CMAKE_ARGS -D${VARIABLE}=${${VARIABLE}})
endmacro()
macro(find_llvm_command_no_version VARIABLE COMMAND)
@@ -49,7 +42,6 @@ macro(find_llvm_command_no_version VARIABLE COMMAND)
PATHS ${LLVM_PATH}
REQUIRED ON
)
list(APPEND CMAKE_ARGS -D${VARIABLE}=${${VARIABLE}})
endmacro()
if(WIN32)

View File

@@ -52,8 +52,3 @@ if(CMAKE_OSX_SYSROOT_ERROR)
endif()
optionx(CMAKE_OSX_SYSROOT STRING "The macOS SDK path to target" DEFAULT ${DEFAULT_CMAKE_OSX_SYSROOT})
list(APPEND CMAKE_ARGS
-DCMAKE_OSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET}
-DCMAKE_OSX_SYSROOT=${CMAKE_OSX_SYSROOT}
)

View File

@@ -1,86 +0,0 @@
option(WEBKIT_VERSION "The version of WebKit to use")
option(WEBKIT_LOCAL "If a local version of WebKit should be used instead of downloading")
if(NOT WEBKIT_VERSION)
set(WEBKIT_VERSION a303e09b6535ee560969b0310de50878cb66b454)
endif()
if(WEBKIT_LOCAL)
set(DEFAULT_WEBKIT_PATH ${VENDOR_PATH}/WebKit/WebKitBuild/${CMAKE_BUILD_TYPE})
else()
set(DEFAULT_WEBKIT_PATH ${CACHE_PATH}/webkit-${WEBKIT_VERSION})
endif()
option(WEBKIT_PATH "The path to the WebKit directory")
if(NOT WEBKIT_PATH)
set(WEBKIT_PATH ${DEFAULT_WEBKIT_PATH})
endif()
set(WEBKIT_INCLUDE_PATH ${WEBKIT_PATH}/include)
set(WEBKIT_LIB_PATH ${WEBKIT_PATH}/lib)
if(WEBKIT_LOCAL)
if(EXISTS ${WEBKIT_PATH}/cmakeconfig.h)
# You may need to run:
# make jsc-compile-debug jsc-copy-headers
include_directories(
${WEBKIT_PATH}
${WEBKIT_PATH}/JavaScriptCore/Headers/JavaScriptCore
${WEBKIT_PATH}/JavaScriptCore/PrivateHeaders
${WEBKIT_PATH}/bmalloc/Headers
${WEBKIT_PATH}/WTF/Headers
)
endif()
# After this point, only prebuilt WebKit is supported
return()
endif()
if(EXISTS ${WEBKIT_PATH}/package.json)
file(READ ${WEBKIT_PATH}/package.json WEBKIT_PACKAGE_JSON)
if(WEBKIT_PACKAGE_JSON MATCHES ${WEBKIT_VERSION})
return()
endif()
endif()
if(WIN32)
set(WEBKIT_OS "windows")
elseif(APPLE)
set(WEBKIT_OS "macos")
elseif(UNIX)
set(WEBKIT_OS "linux")
else()
message(FATAL_ERROR "Unsupported operating system: ${CMAKE_SYSTEM_NAME}")
endif()
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
set(WEBKIT_ARCH "arm64")
elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "amd64|x86_64|x64|AMD64")
set(WEBKIT_ARCH "amd64")
else()
message(FATAL_ERROR "Unsupported architecture: ${CMAKE_SYSTEM_PROCESSOR}")
endif()
if(DEBUG)
set(WEBKIT_SUFFIX "-debug")
elseif(ENABLE_LTO AND NOT WIN32)
set(WEBKIT_SUFFIX "-lto")
else()
set(WEBKIT_SUFFIX "")
endif()
set(WEBKIT_NAME bun-webkit-${WEBKIT_OS}-${WEBKIT_ARCH}${WEBKIT_SUFFIX})
set(WEBKIT_FILENAME ${WEBKIT_NAME}.tar.gz)
setx(WEBKIT_DOWNLOAD_URL https://github.com/oven-sh/WebKit/releases/download/autobuild-${WEBKIT_VERSION}/${WEBKIT_FILENAME})
file(DOWNLOAD ${WEBKIT_DOWNLOAD_URL} ${CACHE_PATH}/${WEBKIT_FILENAME} SHOW_PROGRESS)
file(ARCHIVE_EXTRACT INPUT ${CACHE_PATH}/${WEBKIT_FILENAME} DESTINATION ${CACHE_PATH} TOUCH)
file(REMOVE ${CACHE_PATH}/${WEBKIT_FILENAME})
file(REMOVE_RECURSE ${WEBKIT_PATH})
file(RENAME ${CACHE_PATH}/bun-webkit ${WEBKIT_PATH})
if(APPLE)
file(REMOVE_RECURSE ${WEBKIT_INCLUDE_PATH}/unicode)
endif()

View File

@@ -16,8 +16,6 @@ else()
unsupported(CMAKE_SYSTEM_NAME)
endif()
optionx(ZIG_VERSION STRING "The zig version of the compiler to download" DEFAULT "0.13.0")
optionx(ZIG_COMMIT STRING "The zig commit to use in oven-sh/zig" DEFAULT "131a009ba2eb127a3447d05b9e12f710429aa5ee")
optionx(ZIG_TARGET STRING "The zig target to use" DEFAULT ${DEFAULT_ZIG_TARGET})
if(CMAKE_BUILD_TYPE STREQUAL "Release")
@@ -66,18 +64,12 @@ set(CMAKE_ZIG_FLAGS
)
register_command(
TARGET
clone-zig
COMMENT
"Downloading zig"
COMMAND
${CMAKE_COMMAND}
-DZIG_PATH=${ZIG_PATH}
-DZIG_VERSION=${ZIG_VERSION}
-DZIG_COMMIT=${ZIG_COMMIT}
-P ${CWD}/cmake/scripts/DownloadZig.cmake
SOURCES
${CWD}/cmake/scripts/DownloadZig.cmake
OUTPUTS
${ZIG_EXECUTABLE}
)

View File

@@ -1,214 +0,0 @@
---
name: Migrate from npm install to bun install
---
`bun install` is a Node.js compatible npm client designed to be an incredibly fast successor to npm.
We've put a lot of work into making sure that the migration path from `npm install` to `bun install` is as easy as running `bun install` instead of `npm install`.
- **Designed for Node.js & Bun**: `bun install` installs a Node.js compatible `node_modules` folder. You can use it in place of `npm install` for Node.js projects without any code changes and without using Bun's runtime.
- **Automatically converts `package-lock.json`** to bun's `bun.lockb` lockfile format, preserving your existing resolved dependency versions without any manual work on your part. You can secretly use `bun install` in place of `npm install` at work without anyone noticing.
- **`.npmrc` compatible**: bun install reads npm registry configuration from npm's `.npmrc`, so you can use the same configuration for both npm and Bun.
- **Hardlinks**: On Windows and Linux, `bun install` uses hardlinks to conserve disk space and install times.
```bash
# It only takes one command to migrate
$ bun i
# To add dependencies:
$ bun i @types/bun
# To add devDependencies:
$ bun i -d @types/bun
# To remove a dependency:
$ bun rm @types/bun
```
---
## Run package.json scripts faster
Run scripts from package.json, executables from `node_modules/.bin` (sort of like `npx`), and JavaScript/TypeScript files (just like `node`) - all from a single simple command.
| NPM | Bun |
| ------------------ | ---------------- |
| `npm run <script>` | `bun <script>` |
| `npm exec <bin>` | `bun <bin>` |
| `node <file>` | `bun <file>` |
| `npx <package>` | `bunx <package>` |
When you use `bun run <executable>`, it will choose the locally-installed executable
```sh
# Run a package.json script:
$ bun my-script
$ bun run my-script
# Run an executable in node_modules/.bin:
$ bun my-executable # such as tsc, esbuild, etc.
$ bun run my-executable
# Run a JavaScript/TypeScript file:
$ bun ./index.ts
```
---
## Workspaces? Yes.
`bun install` supports workspaces similarly to npm, with more features.
In package.json, you can set `"workspaces"` to an array of relative paths.
```json#package.json
{
"name": "my-app",
"workspaces": ["packages/*", "apps/*"]
}
```
---
### Filter scripts by workspace name
In Bun, the `--filter` flag accepts a glob pattern, and will run the command concurrently for all workspace packages with a `name` that matches the pattern, respecting dependency order.
```sh
$ bun --filter 'lib-*' my-script
# instead of:
# npm run --workspace lib-foo --workspace lib-bar my-script
```
---
## Update dependencies
To update a dependency, you can use `bun update <package>`. This will update the dependency to the latest version that satisfies the semver range specified in package.json.
```sh
# Update a single dependency
$ bun update @types/bun
# Update all dependencies
$ bun update
# Ignore semver, update to the latest version
$ bun update @types/bun --latest
# Update a dependency to a specific version
$ bun update @types/bun@1.1.10
# Update all dependencies to the latest versions
$ bun update --latest
```
---
### View outdated dependencies
To view outdated dependencies, run `bun outdated`. This is like `npm outdated` but with more compact output.
```sh
$ bun outdated
┌────────────────────────────────────────┬─────────┬────────┬────────┐
│ Package │ Current │ Update │ Latest │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ @types/bun (dev) │ 1.1.6 │ 1.1.10 │ 1.1.10 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ @types/react (dev) │ 18.3.3 │ 18.3.8 │ 18.3.8 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ @typescript-eslint/eslint-plugin (dev) │ 7.16.1 │ 7.18.0 │ 8.6.0 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ @typescript-eslint/parser (dev) │ 7.16.1 │ 7.18.0 │ 8.6.0 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ @vscode/debugadapter (dev) │ 1.66.0 │ 1.67.0 │ 1.67.0 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ esbuild (dev) │ 0.21.5 │ 0.21.5 │ 0.24.0 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ eslint (dev) │ 9.7.0 │ 9.11.0 │ 9.11.0 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ mitata (dev) │ 0.1.11 │ 0.1.14 │ 1.0.2 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ prettier-plugin-organize-imports (dev) │ 4.0.0 │ 4.1.0 │ 4.1.0 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ source-map-js (dev) │ 1.2.0 │ 1.2.1 │ 1.2.1 │
├────────────────────────────────────────┼─────────┼────────┼────────┤
│ typescript (dev) │ 5.5.3 │ 5.6.2 │ 5.6.2 │
└────────────────────────────────────────┴─────────┴────────┴────────┘
```
---
## List installed packages
To list installed packages, you can use `bun pm ls`. This will list all the packages that are installed in the `node_modules` folder using Bun's lockfile as the source of truth. You can pass the `-a` flag to list all installed packages, including transitive dependencies.
```sh
# List top-level installed packages:
$ bun pm ls
my-pkg node_modules (781)
├── @types/node@20.16.5
├── @types/react@18.3.8
├── @types/react-dom@18.3.0
├── eslint@8.57.1
├── eslint-config-next@14.2.8
# List all installed packages:
$ bun pm ls -a
my-pkg node_modules
├── @alloc/quick-lru@5.2.0
├── @isaacs/cliui@8.0.2
│ └── strip-ansi@7.1.0
│ └── ansi-regex@6.1.0
├── @jridgewell/gen-mapping@0.3.5
├── @jridgewell/resolve-uri@3.1.2
...
```
---
## Create a package tarball
To create a package tarball, you can use `bun pm pack`. This will create a tarball of the package in the current directory.
```sh
# Create a tarball
$ bun pm pack
Total files: 46
Shasum: 2ee19b6f0c6b001358449ca0eadead703f326216
Integrity: sha512-ZV0lzWTEkGAMz[...]Gl4f8lA9sl97g==
Unpacked size: 0.41MB
Packed size: 117.50KB
```
---
## Shebang
If the package references `node` in the `#!/usr/bin/env node` shebang, `bun run` will by default respect it and use the system's `node` executable. You can force it to use Bun's `node` by passing `--bun` to `bun run`.
When you pass `--bun` to `bun run`, we create a symlink to the locally-installed Bun executable named `"node"` in a temporary directory and add that to your `PATH` for the duration of the script's execution.
```sh
# Force using Bun's runtime instead of node
$ bun --bun my-script
# This also works:
$ bun run --bun my-script
```
---
## Global installs
You can install packages globally using `bun i -g <package>`. This will install into a `.bun/install/global/node_modules` folder inside your home directory by default.
```sh
# Install a package globally
$ bun i -g eslint
# Run a globally-installed package without the `bun run` prefix
$ eslint --init
```

View File

@@ -67,7 +67,6 @@ After Visual Studio, you need the following:
- Perl
- Ruby
- Node.js
- Ccache
{% callout %}
**Note** The Zig compiler is automatically downloaded, installed, and updated by the building process.
@@ -79,12 +78,12 @@ After Visual Studio, you need the following:
```ps1#WinGet
## Select "Add LLVM to the system PATH for all users" in the LLVM installer
> winget install -i LLVM.LLVM -v 18.1.8 && winget install GoLang.Go Rustlang.Rustup NASM.NASM StrawberryPerl.StrawberryPerl RubyInstallerTeam.Ruby.3.2 OpenJS.NodeJS.LTS Ccache.Ccache
> winget install -i LLVM.LLVM -v 18.1.8 && winget install GoLang.Go Rustlang.Rustup NASM.NASM StrawberryPerl.StrawberryPerl RubyInstallerTeam.Ruby.3.2 OpenJS.NodeJS.LTS
```
```ps1#Scoop
> irm https://get.scoop.sh | iex
> scoop install nodejs-lts go rust nasm ruby perl ccache
> scoop install nodejs-lts go rust nasm ruby perl
# scoop seems to be buggy if you install llvm and the rest at the same time
> scoop install llvm@18.1.8
```
@@ -105,10 +104,10 @@ If you intend on building WebKit locally (optional), you should install these pa
{% /codetabs %}
From here on out, it is **expected you use a PowerShell Terminal with `.\scripts\vs-shell.ps1` sourced**. This script is available in the Bun repository and can be loaded by executing it:
From here on out, it is **expected you use a PowerShell Terminal with `.\scripts\env.ps1` sourced**. This script is available in the Bun repository and can be loaded by executing it:
```ps1
> .\scripts\vs-shell.ps1
> .\scripts\env.ps1
```
To verify, you can check for an MSVC-only command line such as `mt.exe`
@@ -118,41 +117,49 @@ To verify, you can check for an MSVC-only command line such as `mt.exe`
```
{% callout %}
It is not recommended to install `ninja` / `cmake` into your global path, because you may run into a situation where you try to build bun without .\scripts\vs-shell.ps1 sourced.
It is not recommended to install `ninja` / `cmake` into your global path, because you may run into a situation where you try to build bun without .\scripts\env.ps1 sourced.
{% /callout %}
## Building
```ps1
> bun run build
> bun install
# after the initial `bun run build` you can use the following to build
> ninja -Cbuild/debug
> .\scripts\env.ps1
> .\scripts\update-submodules.ps1 # this syncs git submodule state
> .\scripts\all-dependencies.ps1 # this builds all dependencies
> .\scripts\make-old-js.ps1 # runs some old code generators
# Configure build environment
> cmake -Bbuild -GNinja -DCMAKE_BUILD_TYPE=Debug
# Build bun
> ninja -Cbuild
```
If this was successful, you should have a `bun-debug.exe` in the `build/debug` folder.
If this was successful, you should have a `bun-debug.exe` in the `build` folder.
```ps1
> .\build\debug\bun-debug.exe --revision
> .\build\bun-debug.exe --revision
```
You should add this to `$Env:PATH`. The simplest way to do so is to open the start menu, type "Path", and then navigate the environment variables menu to add `C:\.....\bun\build\debug` to the user environment variable `PATH`. You should then restart your editor (if it does not update still, log out and log back in).
You should add this to `$Env:PATH`. The simplest way to do so is to open the start menu, type "Path", and then navigate the environment variables menu to add `C:\.....\bun\build` to the user environment variable `PATH`. You should then restart your editor (if it does not update still, log out and log back in).
## Extra paths
- WebKit is extracted to `build/debug/cache/webkit/`
- Zig is extracted to `build/debug/cache/zig/bin/zig.exe`
- WebKit is extracted to `build/bun-webkit`
- Zig is extracted to `.cache/zig/zig.exe`
## Tests
You can run the test suite either using `bun test <path>`, or by using the wrapper script `packages\bun-internal-test`. The internal test package is a wrapper cli to run every test file in a separate instance of bun.exe, to prevent a crash in the test runner from stopping the entire suite.
You can run the test suite either using `bun test`, or by using the wrapper script `packages\bun-internal-test`. The internal test package is a wrapper cli to run every test file in a separate instance of bun.exe, to prevent a crash in the test runner from stopping the entire suite.
```ps1
# Setup
> bun i --cwd packages\bun-internal-test
# Run the entire test suite with reporter
# the package.json script "test" uses "build/debug/bun-debug.exe" by default
# the package.json script "test" uses "build/bun-debug.exe" by default
> bun run test
# Run an individual test file:

View File

@@ -40,7 +40,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionWrite, (JSC::JSGlobalObject * globalObject,
int32_t fd = STDOUT_FILENO;
if (callframe->argumentCount() > 1) {
fd = arg1.toInt32(globalObject);
RETURN_IF_EXCEPTION(scope, {});
RETURN_IF_EXCEPTION(scope, encodedJSValue());
} else {
toWriteArg = arg1;
}
@@ -53,7 +53,7 @@ JSC_DEFINE_HOST_FUNCTION(jsFunctionWrite, (JSC::JSGlobalObject * globalObject,
}
auto string = toWriteArg.toWTFString(globalObject);
RETURN_IF_EXCEPTION(scope, {});
RETURN_IF_EXCEPTION(scope, encodedJSValue());
auto utf8 = string.utf8();
auto length = utf8.length();
auto written = write(fd, utf8.data(), length);

View File

@@ -1,7 +1,7 @@
{
"private": true,
"name": "bun",
"version": "1.1.30",
"version": "1.1.29",
"workspaces": [
"./packages/bun-types"
],
@@ -40,34 +40,25 @@
"build:release:local": "bun ./scripts/build.mjs -GNinja -DCMAKE_BUILD_TYPE=Release -DWEBKIT_LOCAL=ON -B build/release",
"build:release:with_logs": "cmake . -DCMAKE_BUILD_TYPE=Release -DENABLE_LOGS=true -GNinja -Bbuild-release && ninja -Cbuild-release",
"build:debug-zig-release": "cmake . -DCMAKE_BUILD_TYPE=Release -DZIG_OPTIMIZE=Debug -GNinja -Bbuild-debug-zig-release && ninja -Cbuild-debug-zig-release",
"bump": "bun ./scripts/bump.ts",
"typecheck": "tsc --noEmit && cd test && bun run typecheck",
"fmt": "bun run prettier",
"fmt:cpp": "bun run clang-format",
"fmt:zig": "bun run zig-format",
"fmt": "prettier --config=.prettierrc-ci --write --cache './{.vscode,src,test,bench,packages/{bun-types,bun-inspector-*,bun-vscode,bun-debug-adapter-protocol}}/**/*.{mjs,ts,tsx,js,jsx}'",
"fmt:cpp": "bun run build --target clang-format",
"fmt:zig": "bun run build --target zig-format",
"lint": "eslint './**/*.d.ts' --cache",
"lint:fix": "eslint './**/*.d.ts' --cache --fix",
"test": "node scripts/runner.node.mjs --exec-path ./build/debug/bun-debug",
"test:release": "node scripts/runner.node.mjs --exec-path ./build/release/bun",
"test": "node scripts/runner.node.mjs ./build/bun-debug",
"test:release": "node scripts/runner.node.mjs ./build-release/bun",
"banned": "bun packages/bun-internal-test/src/linter.ts",
"zig": "vendor/zig/zig.exe",
"zig:fmt": "bun run zig-format",
"zig:fmt": "bun run fmt:zig",
"zig:check": "bun run zig build check --summary new",
"zig:check-all": "bun run zig build check-all --summary new",
"zig:check-windows": "bun run zig build check-windows --summary new",
"cmake": "bun ./scripts/build.mjs -DCMAKE_BUILD_TYPE=Debug -B build/debug",
"clang-format": "bun run cmake --target clang-format",
"clang-format:check": "bun run cmake --target clang-format-check",
"clang-format:diff": "bun run cmake --target clang-format-diff",
"clang-tidy": "bun run cmake --target clang-tidy",
"clang-tidy:check": "bun run cmake --target clang-tidy-check",
"clang-tidy:diff": "bun run cmake --target clang-tidy-diff",
"zig-format": "bun run cmake --target zig-format",
"zig-format:check": "bun run cmake --target zig-format-check",
"zig-format:diff": "bun run cmake --target zig-format-diff",
"prettier": "bun run cmake --target prettier",
"prettier:check": "bun run cmake --target prettier-check",
"prettier:extra": "bun run cmake --target prettier-extra",
"prettier:diff": "bun run cmake --target prettier-diff"
"clang-format": "bun run build --target clang-format --fresh",
"clang-format:check": "bun run build --target clang-format-check --fresh",
"clang-tidy": "bun run build --target clang-tidy --fresh",
"clang-tidy:check": "bun run build --target clang-tidy --fresh",
"zig-format": "bun run build --target zig-format --fresh",
"zig-format:check": "bun run build --target zig-format-check --fresh"
}
}

View File

@@ -2,27 +2,13 @@ import type { InspectorEventMap } from "../../../bun-inspector-protocol/src/insp
import type { JSC } from "../../../bun-inspector-protocol/src/protocol";
import type { DAP } from "../protocol";
// @ts-ignore
import { ChildProcess, spawn } from "node:child_process";
import type { ChildProcess } from "node:child_process";
import { spawn } from "node:child_process";
import { EventEmitter } from "node:events";
import { AddressInfo, createServer } from "node:net";
import * as path from "node:path";
import { remoteObjectToString, WebSocketInspector } from "../../../bun-inspector-protocol/index";
import { randomUnixPath, TCPSocketSignal, UnixSignal } from "./signal";
import { WebSocketInspector, remoteObjectToString } from "../../../bun-inspector-protocol/index";
import { UnixSignal, randomUnixPath } from "./signal";
import { Location, SourceMap } from "./sourcemap";
export async function getAvailablePort(): Promise<number> {
const server = createServer();
server.listen(0);
return new Promise((resolve, reject) => {
server.on("listening", () => {
const { port } = server.address() as AddressInfo;
server.close(() => {
resolve(port);
});
});
});
}
const capabilities: DAP.Capabilities = {
supportsConfigurationDoneRequest: true,
supportsFunctionBreakpoints: true,
@@ -503,73 +489,36 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
...env,
};
if (process.platform !== "win32") {
// we're on unix
const url = `ws+unix://${randomUnixPath()}`;
const signal = new UnixSignal();
const url = `ws+unix://${randomUnixPath()}`;
const signal = new UnixSignal();
signal.on("Signal.received", () => {
this.#attach({ url });
});
signal.on("Signal.received", () => {
this.#attach({ url });
});
this.once("Adapter.terminated", () => {
signal.close();
});
this.once("Adapter.terminated", () => {
signal.close();
});
const query = stopOnEntry ? "break=1" : "wait=1";
processEnv["BUN_INSPECT"] = `${url}?${query}`;
processEnv["BUN_INSPECT_NOTIFY"] = signal.url;
const query = stopOnEntry ? "break=1" : "wait=1";
processEnv["BUN_INSPECT"] = `${url}?${query}`;
processEnv["BUN_INSPECT_NOTIFY"] = signal.url;
// This is probably not correct, but it's the best we can do for now.
processEnv["FORCE_COLOR"] = "1";
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
// This is probably not correct, but it's the best we can do for now.
processEnv["FORCE_COLOR"] = "1";
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
const started = await this.#spawn({
command: runtime,
args: processArgs,
env: processEnv,
cwd,
isDebugee: true,
});
const started = await this.#spawn({
command: runtime,
args: processArgs,
env: processEnv,
cwd,
isDebugee: true,
});
if (!started) {
throw new Error("Program could not be started.");
}
} else {
// we're on windows
// Create TCPSocketSignal
const url = `ws://127.0.0.1:${await getAvailablePort()}/${getRandomId()}`; // 127.0.0.1 so it resolves correctly on windows
const signal = new TCPSocketSignal(await getAvailablePort());
signal.on("Signal.received", async () => {
this.#attach({ url });
});
this.once("Adapter.terminated", () => {
signal.close();
});
const query = stopOnEntry ? "break=1" : "wait=1";
processEnv["BUN_INSPECT"] = `${url}?${query}`;
processEnv["BUN_INSPECT_NOTIFY"] = signal.url; // 127.0.0.1 so it resolves correctly on windows
// This is probably not correct, but it's the best we can do for now.
processEnv["FORCE_COLOR"] = "1";
processEnv["BUN_QUIET_DEBUG_LOGS"] = "1";
processEnv["BUN_DEBUG_QUIET_LOGS"] = "1";
const started = await this.#spawn({
command: runtime,
args: processArgs,
env: processEnv,
cwd,
isDebugee: true,
});
if (!started) {
throw new Error("Program could not be started.");
}
if (!started) {
throw new Error("Program could not be started.");
}
}
@@ -735,9 +684,6 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
async breakpointLocations(request: DAP.BreakpointLocationsRequest): Promise<DAP.BreakpointLocationsResponse> {
const { line, endLine, column, endColumn, source: source0 } = request;
if (process.platform === "win32") {
source0.path = source0.path ? normalizeWindowsPath(source0.path) : source0.path;
}
const source = await this.#getSource(sourceToId(source0));
const { locations } = await this.send("Debugger.getBreakpointLocations", {
@@ -842,9 +788,6 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
}
async #setBreakpointsByUrl(url: string, requests: DAP.SourceBreakpoint[], unsetOld?: boolean): Promise<Breakpoint[]> {
if (process.platform === "win32") {
url = url ? normalizeWindowsPath(url) : url;
}
const source = this.#getSourceIfPresent(url);
// If the source is not loaded, set a placeholder breakpoint at the start of the file.
@@ -1218,9 +1161,6 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
async gotoTargets(request: DAP.GotoTargetsRequest): Promise<DAP.GotoTargetsResponse> {
const { source: source0 } = request;
if (process.platform === "win32") {
source0.path = source0.path ? normalizeWindowsPath(source0.path) : source0.path;
}
const source = await this.#getSource(sourceToId(source0));
const { breakpoints } = await this.breakpointLocations(request);
@@ -1387,7 +1327,7 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
// 1. If it has a `path`, the client retrieves the source from the file system.
// 2. If it has a `sourceReference`, the client sends a `source` request.
// Moreover, the code is usually shown in a read-only editor.
const isUserCode = path.isAbsolute(url);
const isUserCode = url.startsWith("/");
const sourceMap = SourceMap(sourceMapURL);
const name = sourceName(url);
const presentationHint = sourcePresentationHint(url);
@@ -1706,11 +1646,12 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
// If the source does not have a path or is a builtin module,
// it cannot be retrieved from the file system.
if (typeof sourceId === "number" || !path.isAbsolute(sourceId)) {
if (typeof sourceId === "number" || !sourceId.startsWith("/")) {
throw new Error(`Source not found: ${sourceId}`);
}
// If the source is not present, it may not have been loaded yet.
// In that case, wait for it to be loaded.
let resolves = this.#pendingSources.get(sourceId);
if (!resolves) {
this.#pendingSources.set(sourceId, (resolves = []));
@@ -2166,6 +2107,7 @@ export class DebugAdapter extends EventEmitter<DebugAdapterEventMap> implements
close(): void {
this.#process?.kill();
// this.#signal?.close();
this.#inspector.close();
this.#reset();
}
@@ -2207,10 +2149,10 @@ function titleize(name: string): string {
}
function sourcePresentationHint(url?: string): DAP.Source["presentationHint"] {
if (!url || !path.isAbsolute(url)) {
if (!url || !url.startsWith("/")) {
return "deemphasize";
}
if (url.includes("/node_modules/") || url.includes("\\node_modules\\")) {
if (url.includes("/node_modules/")) {
return "normal";
}
return "emphasize";
@@ -2221,9 +2163,6 @@ function sourceName(url?: string): string {
return "unknown.js";
}
if (isJavaScript(url)) {
if (process.platform === "win32") {
url = url.replaceAll("\\", "/");
}
return url.split("/").pop() || url;
}
return `${url}.js`;
@@ -2628,15 +2567,3 @@ let sequence = 1;
function nextId(): number {
return sequence++;
}
export function getRandomId() {
return Math.random().toString(36).slice(2);
}
export function normalizeWindowsPath(winPath: string): string {
winPath = path.normalize(winPath);
if (winPath[1] === ":" && (winPath[2] === "\\" || winPath[2] === "/")) {
return (winPath.charAt(0).toUpperCase() + winPath.slice(1)).replaceAll("\\\\", "\\");
}
return winPath;
}

View File

@@ -1,5 +1,5 @@
import { EventEmitter } from "node:events";
import type { Server, Socket } from "node:net";
import type { Server } from "node:net";
import { createServer } from "node:net";
import { tmpdir } from "node:os";
import { join } from "node:path";
@@ -85,75 +85,3 @@ function parseUnixPath(path: string | URL): string {
throw new Error(`Invalid UNIX path: ${path}`);
}
}
export type TCPSocketSignalEventMap = {
"Signal.listening": [];
"Signal.error": [Error];
"Signal.closed": [];
"Signal.received": [string];
};
export class TCPSocketSignal extends EventEmitter {
#port: number;
#server: ReturnType<typeof createServer>;
#ready: Promise<void>;
constructor(port: number) {
super();
this.#port = port;
this.#server = createServer((socket: Socket) => {
socket.on("data", data => {
this.emit("Signal.received", data.toString());
});
socket.on("error", error => {
this.emit("Signal.error", error);
});
socket.on("close", () => {
this.emit("Signal.closed");
});
});
this.#ready = new Promise((resolve, reject) => {
this.#server.listen(this.#port, () => {
this.emit("Signal.listening");
resolve();
});
this.#server.on("error", reject);
});
}
emit<E extends keyof TCPSocketSignalEventMap>(event: E, ...args: TCPSocketSignalEventMap[E]): boolean {
if (isDebug) {
console.log(event, ...args);
}
return super.emit(event, ...args);
}
/**
* The TCP port.
*/
get port(): number {
return this.#port;
}
get url(): string {
return `tcp://127.0.0.1:${this.#port}`;
}
/**
* Resolves when the server is listening or rejects if an error occurs.
*/
get ready(): Promise<void> {
return this.#ready;
}
/**
* Closes the server.
*/
close(): void {
this.#server.close();
}
}

View File

@@ -2,27 +2,6 @@ import { AwsClient } from "aws4fetch";
import { getBuild, getRelease, getSemver, getSha } from "../src/github";
import { join, tmp } from "../src/fs";
// The source of truth for the git sha is what's in the local build, extracted from features.json
// NOT the git tag revision.
// Ideally, these are always the same, but mistakes can happen.
const local =
"bun-" +
(
{
darwin: "darwin",
win32: "windows",
linux: "linux",
} as any
)[process.platform] +
"-" +
(
{
arm64: "aarch64",
x64: "x64",
} as any
)[process.arch] +
".zip";
const dryRun = process.argv.includes("--dry-run");
const [tag] = process.argv.slice(2);
@@ -49,68 +28,40 @@ const full_commit_hash = await getSha(tag, "long");
console.log("Found release:", release.tag_name, "with commit hash:", full_commit_hash);
console.log("Found build:", full_commit_hash);
const isCanary = release.tag_name === "canary";
let paths: string[] = [];
async function setPaths(revision: string, isCanary: boolean) {
const releaseSha = `releases/${revision + (isCanary ? "-canary" : "")}`;
if (latest.tag_name === release.tag_name) {
paths = ["releases/latest", `releases/${release.tag_name}`, releaseSha];
} else if (isCanary) {
try {
const build = await getSemver("canary", await getBuild());
paths = ["releases/canary", `releases/${build}`, releaseSha];
} catch (error) {
console.warn(error);
paths = ["releases/canary", releaseSha];
}
} else {
paths = [`releases/${release.tag_name}`, releaseSha];
let paths: string[];
if (latest.tag_name === release.tag_name) {
paths = ["releases/latest", `releases/${release.tag_name}`, `releases/${full_commit_hash}`];
} else if (release.tag_name === "canary") {
try {
const build = await getSemver("canary", await getBuild());
paths = ["releases/canary", `releases/${build}`, `releases/${full_commit_hash}-canary`];
} catch (error) {
console.warn(error);
paths = ["releases/canary"];
}
console.log("Found paths:", paths);
} else {
paths = [`releases/${release.tag_name}`, `releases/${full_commit_hash}`];
}
console.log("Found paths:", paths);
async function getFeaturesJSON(body: ArrayBuffer) {
// extract feature data using the local build
const temp = tmp();
await Bun.write(join(temp, "bun.zip"), body);
let unzip = Bun.spawnSync({
cmd: ["unzip", join(temp, "bun.zip")],
cwd: temp,
});
if (!unzip.success) throw new Error("Failed to unzip");
let data = Bun.spawnSync({
cmd: [
join(temp, local.replace(".zip", ""), "bun"),
"--print",
'JSON.stringify(require("bun:internal-for-testing").crash_handler.getFeatureData())',
],
cwd: temp,
env: {
...process.env,
BUN_DEBUG_QUIET_LOGS: "1",
BUN_GARBAGE_COLLECTOR_LEVEL: "0",
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "1",
},
stdio: ["ignore", "pipe", "inherit"],
});
return data.stdout.toString("utf8").trim();
}
// Make the first asset the local build
for (let i = 0; i < release.assets.length; i++) {
const asset = release.assets[i];
if (asset.name === local) {
release.assets.splice(i, 1);
release.assets.unshift(asset);
break;
}
}
if (release?.assets?.[0]?.name !== local) {
throw new Error("Expected local build to be the first asset");
}
const local =
"bun-" +
(
{
darwin: "darwin",
win32: "windows",
linux: "linux",
} as any
)[process.platform] +
"-" +
(
{
arm64: "aarch64",
x64: "x64",
} as any
)[process.arch] +
".zip";
for (const asset of release.assets) {
const url = asset.browser_download_url;
@@ -132,25 +83,39 @@ for (const asset of release.assets) {
contentType = response.headers.get("Content-Type") || "";
}
console.log("Downloading asset:", name);
const body = await response.arrayBuffer();
if (name == local) {
const text = await getFeaturesJSON(body);
const features = JSON.parse(text);
const sha = features.revision;
if (features.is_canary && !isCanary) {
console.warn("Local build is a canary but release is not tagged as canary.");
}
await setPaths(sha, features.is_canary);
console.log("features.json:", JSON.stringify(features, null, 2));
// extract feature data using the local build
const temp = tmp();
await Bun.write(join(temp, "bun.zip"), body);
let unzip = Bun.spawnSync({
cmd: ["unzip", join(temp, "bun.zip")],
cwd: temp,
});
if (!unzip.success) throw new Error("Failed to unzip");
let data = Bun.spawnSync({
cmd: [
join(temp, local.replace(".zip", ""), "bun"),
"--print",
'JSON.stringify(require("bun:internal-for-testing").crash_handler.getFeatureData())',
],
cwd: temp,
env: {
...process.env,
BUN_DEBUG_QUIET_LOGS: "1",
BUN_GARBAGE_COLLECTOR_LEVEL: "0",
BUN_FEATURE_FLAG_INTERNAL_FOR_TESTING: "1",
},
stdio: ["ignore", "pipe", "inherit"],
});
const json = data.stdout.toString("utf8");
for (const path of paths) {
const key = `${path}/features.json`;
console.log("Uploading:", key);
await uploadToS3({
key,
body: new TextEncoder().encode(text).buffer,
body: new TextEncoder().encode(json).buffer,
headers: {
"Content-Type": contentType,
"Content-Disposition": `attachment; filename="${name}"`,

View File

@@ -2660,7 +2660,7 @@ declare module "bun" {
* @param closeActiveConnections Immediately terminate in-flight requests, websockets, and stop accepting new connections.
* @default false
*/
stop(closeActiveConnections?: boolean): Promise<void>;
stop(closeActiveConnections?: boolean): void;
/**
* Update the `fetch` and `error` handlers without restarting the server.

View File

@@ -1 +0,0 @@
DisableFormat: true

File diff suppressed because it is too large Load Diff

View File

@@ -15,20 +15,21 @@
* limitations under the License.
*/
/* This Server Name Indication hostname tree is written in C++ but could be ported to C.
* Overall it looks like crap, but has no memory allocations in fast path and is O(log n). */
/* This Server Name Indication hostname tree is written in C++ but could be
* ported to C. Overall it looks like crap, but has no memory allocations in
* fast path and is O(log n). */
#ifndef SNI_TREE_H
#define SNI_TREE_H
#ifndef LIBUS_NO_SSL
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <map>
#include <memory>
#include <string_view>
#include <cstring>
#include <cstdlib>
#include <algorithm>
/* We only handle a maximum of 10 labels per hostname */
#define MAX_LABELS 10
@@ -37,180 +38,186 @@
thread_local void (*sni_free_cb)(void *);
struct sni_node {
/* Empty nodes must always hold null */
void *user = nullptr;
std::map<std::string_view, std::unique_ptr<sni_node>> children;
/* Empty nodes must always hold null */
void *user = nullptr;
std::map<std::string_view, std::unique_ptr<sni_node>> children;
~sni_node() {
for (auto &p : children) {
/* The data of our string_views are managed by malloc */
free((void *) p.first.data());
~sni_node() {
for (auto &p : children) {
/* The data of our string_views are managed by malloc */
free((void *)p.first.data());
/* Call destructor passed to sni_free only if we hold data.
* This is important since sni_remove does not have sni_free_cb set */
if (p.second.get()->user) {
sni_free_cb(p.second.get()->user);
}
}
/* Call destructor passed to sni_free only if we hold data.
* This is important since sni_remove does not have sni_free_cb set */
if (p.second.get()->user) {
sni_free_cb(p.second.get()->user);
}
}
}
};
// this can only delete ONE single node, but may cull "empty nodes with null as data"
void *removeUser(struct sni_node *root, unsigned int label, std::string_view *labels, unsigned int numLabels) {
// this can only delete ONE single node, but may cull "empty nodes with null as
// data"
void *removeUser(struct sni_node *root, unsigned int label,
std::string_view *labels, unsigned int numLabels) {
/* If we are in the bottom (past bottom by one), there is nothing to remove */
if (label == numLabels) {
void *user = root->user;
/* Mark us for culling on the way up */
root->user = nullptr;
return user;
}
/* If we are in the bottom (past bottom by one), there is nothing to remove */
if (label == numLabels) {
void *user = root->user;
/* Mark us for culling on the way up */
root->user = nullptr;
return user;
}
/* Is this label a child of root? */
auto it = root->children.find(labels[label]);
if (it == root->children.end()) {
/* We cannot continue */
return nullptr;
}
/* Is this label a child of root? */
auto it = root->children.find(labels[label]);
if (it == root->children.end()) {
/* We cannot continue */
return nullptr;
}
void *removedUser = removeUser(it->second.get(), label + 1, labels, numLabels);
void *removedUser =
removeUser(it->second.get(), label + 1, labels, numLabels);
/* On the way back up, we may cull empty nodes with no children.
* This ends up being where we remove all nodes */
if (it->second.get()->children.empty() && it->second.get()->user == nullptr) {
/* On the way back up, we may cull empty nodes with no children.
* This ends up being where we remove all nodes */
if (it->second.get()->children.empty() && it->second.get()->user == nullptr) {
/* The data of our string_views are managed by malloc */
free((void *) it->first.data());
/* The data of our string_views are managed by malloc */
free((void *)it->first.data());
/* This can only happen with user set to null, otherwise we use sni_free_cb which is unset by sni_remove */
root->children.erase(it);
}
/* This can only happen with user set to null, otherwise we use sni_free_cb
* which is unset by sni_remove */
root->children.erase(it);
}
return removedUser;
return removedUser;
}
void *getUser(struct sni_node *root, unsigned int label, std::string_view *labels, unsigned int numLabels) {
void *getUser(struct sni_node *root, unsigned int label,
std::string_view *labels, unsigned int numLabels) {
/* Do we have labels to match? Otherwise, return where we stand */
if (label == numLabels) {
return root->user;
/* Do we have labels to match? Otherwise, return where we stand */
if (label == numLabels) {
return root->user;
}
/* Try and match by our label */
auto it = root->children.find(labels[label]);
if (it != root->children.end()) {
void *user = getUser(it->second.get(), label + 1, labels, numLabels);
if (user) {
return user;
}
}
/* Try and match by our label */
auto it = root->children.find(labels[label]);
if (it != root->children.end()) {
void *user = getUser(it->second.get(), label + 1, labels, numLabels);
if (user) {
return user;
}
}
/* Try and match by wildcard */
it = root->children.find("*");
if (it == root->children.end()) {
/* Matching has failed for both label and wildcard */
return nullptr;
}
/* Try and match by wildcard */
it = root->children.find("*");
if (it == root->children.end()) {
/* Matching has failed for both label and wildcard */
return nullptr;
}
/* We matched by wildcard */
return getUser(it->second.get(), label + 1, labels, numLabels);
/* We matched by wildcard */
return getUser(it->second.get(), label + 1, labels, numLabels);
}
extern "C" {
void *sni_new() {
return new sni_node;
void *sni_new() { return new sni_node; }
void sni_free(void *sni, void (*cb)(void *)) {
/* We want to run this callback for every remaining name */
sni_free_cb = cb;
delete (sni_node *)sni;
}
/* Returns non-null if this name already exists */
int sni_add(void *sni, const char *hostname, void *user) {
struct sni_node *root = (struct sni_node *)sni;
/* Traverse all labels in hostname */
for (std::string_view view(hostname, strlen(hostname)), label; view.length();
view.remove_prefix(std::min(view.length(), label.length() + 1))) {
/* Label is the token separated by dot */
label = view.substr(0, view.find('.', 0));
auto it = root->children.find(label);
if (it == root->children.end()) {
/* Duplicate this label for our kept string_view of it */
void *labelString = malloc(label.length());
memcpy(labelString, label.data(), label.length());
it = root->children
.emplace(std::string_view((char *)labelString, label.length()),
std::make_unique<sni_node>())
.first; // NOLINT(clang-analyzer-unix.Malloc)
}
void sni_free(void *sni, void (*cb)(void *)) {
/* We want to run this callback for every remaining name */
sni_free_cb = cb;
root = it->second.get();
}
delete (sni_node *) sni;
/* We must never add multiple contexts for the same name, as that would
* overwrite and leak */
if (root->user) {
return 1;
}
root->user = user;
return 0;
}
/* Removes the exact match. Wildcards are treated as the verbatim asterisk char,
* not as an actual wildcard */
void *sni_remove(void *sni, const char *hostname) {
struct sni_node *root = (struct sni_node *)sni;
/* I guess 10 labels is an okay limit */
std::string_view labels[10];
unsigned int numLabels = 0;
/* We traverse all labels first of all */
for (std::string_view view(hostname, strlen(hostname)), label; view.length();
view.remove_prefix(std::min(view.length(), label.length() + 1))) {
/* Label is the token separated by dot */
label = view.substr(0, view.find('.', 0));
/* Anything longer than 10 labels is forbidden */
if (numLabels == 10) {
return nullptr;
}
/* Returns non-null if this name already exists */
int sni_add(void *sni, const char *hostname, void *user) {
struct sni_node *root = (struct sni_node *) sni;
labels[numLabels++] = label;
}
/* Traverse all labels in hostname */
for (std::string_view view(hostname, strlen(hostname)), label;
view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) {
/* Label is the token separated by dot */
label = view.substr(0, view.find('.', 0));
return removeUser(root, 0, labels, numLabels);
}
auto it = root->children.find(label);
if (it == root->children.end()) {
/* Duplicate this label for our kept string_view of it */
void *labelString = malloc(label.length());
memcpy(labelString, label.data(), label.length());
void *sni_find(void *sni, const char *hostname) {
struct sni_node *root = (struct sni_node *)sni;
it = root->children.emplace(std::string_view((char *) labelString, label.length()),
std::make_unique<sni_node>()).first; // NOLINT(clang-analyzer-unix.Malloc)
}
/* I guess 10 labels is an okay limit */
std::string_view labels[10];
unsigned int numLabels = 0;
root = it->second.get();
}
/* We traverse all labels first of all */
for (std::string_view view(hostname, strlen(hostname)), label; view.length();
view.remove_prefix(std::min(view.length(), label.length() + 1))) {
/* Label is the token separated by dot */
label = view.substr(0, view.find('.', 0));
/* We must never add multiple contexts for the same name, as that would overwrite and leak */
if (root->user) {
return 1;
}
root->user = user;
return 0;
/* Anything longer than 10 labels is forbidden */
if (numLabels == 10) {
return nullptr;
}
/* Removes the exact match. Wildcards are treated as the verbatim asterisk char, not as an actual wildcard */
void *sni_remove(void *sni, const char *hostname) {
struct sni_node *root = (struct sni_node *) sni;
/* I guess 10 labels is an okay limit */
std::string_view labels[10];
unsigned int numLabels = 0;
/* We traverse all labels first of all */
for (std::string_view view(hostname, strlen(hostname)), label;
view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) {
/* Label is the token separated by dot */
label = view.substr(0, view.find('.', 0));
/* Anything longer than 10 labels is forbidden */
if (numLabels == 10) {
return nullptr;
}
labels[numLabels++] = label;
}
return removeUser(root, 0, labels, numLabels);
}
void *sni_find(void *sni, const char *hostname) {
struct sni_node *root = (struct sni_node *) sni;
/* I guess 10 labels is an okay limit */
std::string_view labels[10];
unsigned int numLabels = 0;
/* We traverse all labels first of all */
for (std::string_view view(hostname, strlen(hostname)), label;
view.length(); view.remove_prefix(std::min(view.length(), label.length() + 1))) {
/* Label is the token separated by dot */
label = view.substr(0, view.find('.', 0));
/* Anything longer than 10 labels is forbidden */
if (numLabels == 10) {
return nullptr;
}
labels[numLabels++] = label;
}
return getUser(root, 0, labels, numLabels);
}
labels[numLabels++] = label;
}
return getUser(root, 0, labels, numLabels);
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -127,7 +127,9 @@ int us_poll_events(struct us_poll_t *p) {
size_t us_internal_accept_poll_event(struct us_poll_t *p) { return 0; }
int us_internal_poll_type(struct us_poll_t *p) { return p->poll_type & POLL_TYPE_KIND_MASK; }
int us_internal_poll_type(struct us_poll_t *p) {
return p->poll_type & POLL_TYPE_KIND_MASK;
}
void us_internal_poll_set_type(struct us_poll_t *p, int poll_type) {
p->poll_type = poll_type | (p->poll_type & POLL_TYPE_POLLING_MASK);

View File

@@ -371,8 +371,6 @@ void us_poll_init(us_poll_r p, LIBUS_SOCKET_DESCRIPTOR fd, int poll_type);
/* Start, change and stop polling for events */
void us_poll_start(us_poll_r p, us_loop_r loop, int events) nonnull_fn_decl;
/* Returns 0 if successful */
int us_poll_start_rc(us_poll_r p, us_loop_r loop, int events) nonnull_fn_decl;
void us_poll_change(us_poll_r p, us_loop_r loop, int events) nonnull_fn_decl;
void us_poll_stop(us_poll_r p, struct us_loop_t *loop) nonnull_fn_decl;

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,7 @@
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <errno.h>
#ifndef WIN32
@@ -309,11 +310,7 @@ struct us_socket_t *us_socket_from_fd(struct us_socket_context_t *ctx, int socke
#else
struct us_poll_t *p1 = us_create_poll(ctx->loop, 0, sizeof(struct us_socket_t) + socket_ext_size);
us_poll_init(p1, fd, POLL_TYPE_SOCKET);
int rc = us_poll_start_rc(p1, ctx->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
if (rc != 0) {
us_poll_free(p1, ctx->loop);
return 0;
}
us_poll_start(p1, ctx->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
struct us_socket_t *s = (struct us_socket_t *) p1;
s->context = ctx;

View File

@@ -15,8 +15,8 @@
* limitations under the License.
*/
#include "libusockets.h"
#include "internal/internal.h"
#include "libusockets.h"
#include <string.h>
@@ -24,56 +24,65 @@
// return bsd_udp_packet_buffer_ecn((struct udp_recvbuf *)buf, index);
// }
int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index, char *ip) {
return bsd_udp_packet_buffer_local_ip((struct udp_recvbuf *)buf, index, ip);
int us_udp_packet_buffer_local_ip(struct us_udp_packet_buffer_t *buf, int index,
char *ip) {
return bsd_udp_packet_buffer_local_ip((struct udp_recvbuf *)buf, index, ip);
}
char *us_udp_packet_buffer_peer(struct us_udp_packet_buffer_t *buf, int index) {
return bsd_udp_packet_buffer_peer((struct udp_recvbuf *)buf, index);
return bsd_udp_packet_buffer_peer((struct udp_recvbuf *)buf, index);
}
char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf, int index) {
return bsd_udp_packet_buffer_payload((struct udp_recvbuf *)buf, index);
char *us_udp_packet_buffer_payload(struct us_udp_packet_buffer_t *buf,
int index) {
return bsd_udp_packet_buffer_payload((struct udp_recvbuf *)buf, index);
}
int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf, int index) {
return bsd_udp_packet_buffer_payload_length((struct udp_recvbuf *)buf, index);
int us_udp_packet_buffer_payload_length(struct us_udp_packet_buffer_t *buf,
int index) {
return bsd_udp_packet_buffer_payload_length((struct udp_recvbuf *)buf, index);
}
int us_udp_socket_send(struct us_udp_socket_t *s, void** payloads, size_t* lengths, void** addresses, int num) {
if (num == 0) return 0;
int fd = us_poll_fd((struct us_poll_t *) s);
int us_udp_socket_send(struct us_udp_socket_t *s, void **payloads,
size_t *lengths, void **addresses, int num) {
if (num == 0)
return 0;
int fd = us_poll_fd((struct us_poll_t *)s);
struct udp_sendbuf *buf = (struct udp_sendbuf *)s->loop->data.send_buf;
struct udp_sendbuf *buf = (struct udp_sendbuf *)s->loop->data.send_buf;
int total_sent = 0;
while (total_sent < num) {
int count = bsd_udp_setup_sendbuf(buf, LIBUS_SEND_BUFFER_LENGTH, payloads, lengths, addresses, num);
payloads += count;
lengths += count;
addresses += count;
num -= count;
// TODO nohang flag?
int sent = bsd_sendmmsg(fd, buf, MSG_DONTWAIT);
if (sent < 0) {
return sent;
}
total_sent += sent;
if (0 <= sent && sent < num) {
// if we couldn't send all packets, register a writable event so we can call the drain callback
us_poll_change((struct us_poll_t *) s, s->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}
int total_sent = 0;
while (total_sent < num) {
int count = bsd_udp_setup_sendbuf(buf, LIBUS_SEND_BUFFER_LENGTH, payloads,
lengths, addresses, num);
payloads += count;
lengths += count;
addresses += count;
num -= count;
// TODO nohang flag?
int sent = bsd_sendmmsg(fd, buf, MSG_DONTWAIT);
if (sent < 0) {
return sent;
}
return total_sent;
total_sent += sent;
if (0 <= sent && sent < num) {
// if we couldn't send all packets, register a writable event so we can
// call the drain callback
us_poll_change((struct us_poll_t *)s, s->loop,
LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
}
}
return total_sent;
}
int us_udp_socket_bound_port(struct us_udp_socket_t *s) {
return ((struct us_udp_socket_t *) s)->port;
return ((struct us_udp_socket_t *)s)->port;
}
void us_udp_socket_bound_ip(struct us_udp_socket_t *s, char *buf, int *length) {
struct bsd_addr_t addr;
if (bsd_local_addr(us_poll_fd((struct us_poll_t *)s), &addr) || *length < bsd_addr_get_ip_length(&addr)) {
if (bsd_local_addr(us_poll_fd((struct us_poll_t *)s), &addr) ||
*length < bsd_addr_get_ip_length(&addr)) {
*length = 0;
} else {
*length = bsd_addr_get_ip_length(&addr);
@@ -81,9 +90,11 @@ void us_udp_socket_bound_ip(struct us_udp_socket_t *s, char *buf, int *length) {
}
}
void us_udp_socket_remote_ip(struct us_udp_socket_t *s, char *buf, int *length) {
void us_udp_socket_remote_ip(struct us_udp_socket_t *s, char *buf,
int *length) {
struct bsd_addr_t addr;
if (bsd_remote_addr(us_poll_fd((struct us_poll_t *)s), &addr) || *length < bsd_addr_get_ip_length(&addr)) {
if (bsd_remote_addr(us_poll_fd((struct us_poll_t *)s), &addr) ||
*length < bsd_addr_get_ip_length(&addr)) {
*length = 0;
} else {
*length = bsd_addr_get_ip_length(&addr);
@@ -92,71 +103,71 @@ void us_udp_socket_remote_ip(struct us_udp_socket_t *s, char *buf, int *length)
}
void *us_udp_socket_user(struct us_udp_socket_t *s) {
struct us_udp_socket_t *udp = (struct us_udp_socket_t *) s;
struct us_udp_socket_t *udp = (struct us_udp_socket_t *)s;
return udp->user;
return udp->user;
}
void us_udp_socket_close(struct us_udp_socket_t *s) {
struct us_loop_t *loop = s->loop;
struct us_poll_t *p = (struct us_poll_t *) s;
us_poll_stop(p, loop);
bsd_close_socket(us_poll_fd(p));
s->closed = 1;
s->next = loop->data.closed_udp_head;
loop->data.closed_udp_head = s;
s->on_close(s);
struct us_loop_t *loop = s->loop;
struct us_poll_t *p = (struct us_poll_t *)s;
us_poll_stop(p, loop);
bsd_close_socket(us_poll_fd(p));
s->closed = 1;
s->next = loop->data.closed_udp_head;
loop->data.closed_udp_head = s;
s->on_close(s);
}
int us_udp_socket_connect(struct us_udp_socket_t *s, const char* host, unsigned short port) {
return bsd_connect_udp_socket(us_poll_fd((struct us_poll_t *)s), host, port);
int us_udp_socket_connect(struct us_udp_socket_t *s, const char *host,
unsigned short port) {
return bsd_connect_udp_socket(us_poll_fd((struct us_poll_t *)s), host, port);
}
int us_udp_socket_disconnect(struct us_udp_socket_t *s) {
return bsd_disconnect_udp_socket(us_poll_fd((struct us_poll_t *)s));
return bsd_disconnect_udp_socket(us_poll_fd((struct us_poll_t *)s));
}
struct us_udp_socket_t *us_create_udp_socket(
struct us_loop_t *loop,
void (*data_cb)(struct us_udp_socket_t *, void *, int),
void (*drain_cb)(struct us_udp_socket_t *),
void (*close_cb)(struct us_udp_socket_t *),
const char *host,
unsigned short port,
void *user
) {
struct us_udp_socket_t *
us_create_udp_socket(struct us_loop_t *loop,
void (*data_cb)(struct us_udp_socket_t *, void *, int),
void (*drain_cb)(struct us_udp_socket_t *),
void (*close_cb)(struct us_udp_socket_t *),
const char *host, unsigned short port, void *user) {
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port);
if (fd == LIBUS_SOCKET_ERROR) {
return 0;
}
LIBUS_SOCKET_DESCRIPTOR fd = bsd_create_udp_socket(host, port);
if (fd == LIBUS_SOCKET_ERROR) {
return 0;
}
int ext_size = 0;
int fallthrough = 0;
int ext_size = 0;
int fallthrough = 0;
struct us_poll_t *p = us_create_poll(loop, fallthrough, sizeof(struct us_udp_socket_t) + ext_size);
us_poll_init(p, fd, POLL_TYPE_UDP);
struct us_poll_t *p = us_create_poll(
loop, fallthrough, sizeof(struct us_udp_socket_t) + ext_size);
us_poll_init(p, fd, POLL_TYPE_UDP);
struct us_udp_socket_t *udp = (struct us_udp_socket_t *)p;
struct us_udp_socket_t *udp = (struct us_udp_socket_t *)p;
/* Get and store the port once */
struct bsd_addr_t tmp = {0};
bsd_local_addr(fd, &tmp);
udp->port = bsd_addr_get_port(&tmp);
udp->loop = loop;
/* Get and store the port once */
struct bsd_addr_t tmp = {0};
bsd_local_addr(fd, &tmp);
udp->port = bsd_addr_get_port(&tmp);
udp->loop = loop;
/* There is no udp socket context, only user data */
/* This should really be ext like everything else */
udp->user = user;
/* There is no udp socket context, only user data */
/* This should really be ext like everything else */
udp->user = user;
udp->closed = 0;
udp->connected = 0;
udp->on_data = data_cb;
udp->on_drain = drain_cb;
udp->on_close = close_cb;
udp->next = NULL;
udp->closed = 0;
udp->connected = 0;
udp->on_data = data_cb;
udp->on_drain = drain_cb;
udp->on_close = close_cb;
udp->next = NULL;
us_poll_start((struct us_poll_t *) udp, udp->loop, LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
return (struct us_udp_socket_t *) udp;
us_poll_start((struct us_poll_t *)udp, udp->loop,
LIBUS_SOCKET_READABLE | LIBUS_SOCKET_WRITABLE);
return (struct us_udp_socket_t *)udp;
}

View File

@@ -1 +0,0 @@
DisableFormat: true

View File

@@ -1,10 +1,3 @@
# Project files
/node_modules
/extension
/example/.vscode
/.vscode-test
# macOS files
.DS_Store
node_modules
extension
example/.vscode

Binary file not shown.

View File

@@ -0,0 +1,8 @@
console.log("HELLO");
console.log("HELLO 2");
console.log("HELLO 3");
a();
function a() {
console.log("HELLO 4");
}

View File

@@ -1,7 +0,0 @@
type OS = "Windows";
Bun.serve({
fetch(req: Request) {
return new Response(`Hello, ${"Windows" as OS}!`);
},
});

View File

@@ -1,6 +1,6 @@
{
"name": "bun-vscode",
"version": "0.0.15",
"version": "0.0.8",
"author": "oven",
"repository": {
"type": "git",
@@ -8,13 +8,11 @@
},
"main": "dist/extension.js",
"devDependencies": {
"@types/bun": "^1.1.10",
"@types/vscode": "^1.60.0",
"@vscode/debugadapter": "^1.56.0",
"@vscode/debugadapter-testsupport": "^1.56.0",
"@vscode/test-cli": "^0.0.10",
"@vscode/test-electron": "^2.4.1",
"@vscode/vsce": "^2.20.1",
"bun-types": "^0.7.3",
"esbuild": "^0.19.2",
"typescript": "^5.0.0"
},
@@ -45,7 +43,6 @@
"build": "node scripts/build.mjs",
"pretest": "bun run build",
"test": "node scripts/test.mjs",
"dev": "vscode-test --config scripts/dev.mjs",
"prepublish": "npm version patch && bun run build",
"publish": "cd extension && bunx vsce publish"
},

View File

@@ -1,9 +1,9 @@
import { buildSync } from "esbuild";
import { execSync } from "node:child_process";
import { spawnSync } from "node:child_process";
import { cpSync, mkdirSync, rmSync } from "node:fs";
import { dirname } from "node:path";
process.chdir(dirname(import.meta.dirname));
const { pathname } = new URL("..", import.meta.url);
process.chdir(pathname);
buildSync({
entryPoints: ["src/extension.ts", "src/web-extension.ts"],
@@ -26,7 +26,7 @@ cpSync("LICENSE", "extension/LICENSE");
cpSync("package.json", "extension/package.json");
const cmd = process.isBun ? "bunx" : "npx";
execSync(`${cmd} vsce package --no-dependencies`, {
spawnSync(cmd, ["vsce", "package"], {
cwd: "extension",
stdio: "inherit",
});

View File

@@ -1,29 +0,0 @@
import { defineConfig } from "@vscode/test-cli";
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
const workspacePath = dirname(import.meta.dirname);
const vscodePath = join(workspacePath, ".vscode-test");
const vscodeTestPath = join(vscodePath, "launch.test.js");
if (!existsSync(vscodeTestPath)) {
mkdirSync(vscodePath, { recursive: true });
writeFileSync(
vscodeTestPath,
`// Generated by ${import.meta.filename}
// A test that intentionally waits forever and does nothing,
// so you can debug and test the VSCode extension in a clean environment.
suite("VSCode extension", function () {
this.timeout(Number.MAX_SAFE_INTEGER);
test("wait forever", (done) => {});
});
`,
);
}
export default defineConfig({
workspaceFolder: join(workspacePath, "example"),
extensionDevelopmentPath: workspacePath,
skipExtensionDependencies: true,
files: vscodeTestPath,
});

View File

@@ -1,21 +1,21 @@
import { exec } from "node:child_process";
import { spawn } from "node:child_process";
import { readdirSync } from "node:fs";
import { dirname } from "node:path";
process.chdir(dirname(import.meta.dirname));
const { pathname } = new URL("..", import.meta.url);
process.chdir(pathname);
let extPath;
let path;
for (const filename of readdirSync("extension")) {
if (filename.endsWith(".vsix")) {
extPath = `extension/${filename}`;
path = `extension/${filename}`;
break;
}
}
if (!extPath) {
if (!path) {
throw new Error("No .vsix file found");
}
exec(`code --new-window --install-extension=${extPath} --extensionDevelopmentPath=${process.cwd()} example`, {
spawn("code", ["--new-window", `--install-extension=${path}`, `--extensionDevelopmentPath=${pathname}`, "example"], {
stdio: "inherit",
});

View File

@@ -1,14 +1,8 @@
import { DebugSession } from "@vscode/debugadapter";
import { tmpdir } from "node:os";
import * as vscode from "vscode";
import {
DAP,
DebugAdapter,
getAvailablePort,
getRandomId,
TCPSocketSignal,
UnixSignal,
} from "../../../bun-debug-adapter-protocol";
import type { DAP } from "../../../bun-debug-adapter-protocol";
import { DebugAdapter, UnixSignal } from "../../../bun-debug-adapter-protocol";
export const DEBUG_CONFIGURATION: vscode.DebugConfiguration = {
type: "bun",
@@ -87,7 +81,7 @@ function debugFileCommand(resource?: vscode.Uri) {
if (path) debugCommand(path);
}
async function injectDebugTerminal(terminal: vscode.Terminal): Promise<void> {
function injectDebugTerminal(terminal: vscode.Terminal): void {
if (!getConfig("debugTerminal.enabled")) return;
const { name, creationOptions } = terminal;
@@ -103,16 +97,14 @@ async function injectDebugTerminal(terminal: vscode.Terminal): Promise<void> {
const stopOnEntry = getConfig("debugTerminal.stopOnEntry") === true;
const query = stopOnEntry ? "break=1" : "wait=1";
const debugSession = new TerminalDebugSession();
await debugSession.initialize();
const { adapter, signal } = debugSession;
const { adapter, signal } = new TerminalDebugSession();
const debug = vscode.window.createTerminal({
...creationOptions,
name: "JavaScript Debug Terminal",
env: {
...env,
"BUN_INSPECT": `${adapter.url}?${query}`,
"BUN_INSPECT_NOTIFY": signal.url,
"BUN_INSPECT_NOTIFY": `${signal.url}`,
},
});
@@ -161,9 +153,7 @@ class DebugConfigurationProvider implements vscode.DebugConfigurationProvider {
}
class InlineDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory {
async createDebugAdapterDescriptor(
session: vscode.DebugSession,
): Promise<vscode.ProviderResult<vscode.DebugAdapterDescriptor>> {
createDebugAdapterDescriptor(session: vscode.DebugSession): vscode.ProviderResult<vscode.DebugAdapterDescriptor> {
const { configuration } = session;
const { request, url } = configuration;
@@ -176,28 +166,18 @@ class InlineDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory
}
const adapter = new FileDebugSession(session.id);
await adapter.initialize();
return new vscode.DebugAdapterInlineImplementation(adapter);
}
}
class FileDebugSession extends DebugSession {
adapter: DebugAdapter;
sessionId?: string;
readonly adapter: DebugAdapter;
constructor(sessionId?: string) {
super();
this.sessionId = sessionId;
}
const uniqueId = sessionId ?? Math.random().toString(36).slice(2);
const url = `ws+unix://${tmpdir()}/${uniqueId}.sock`;
async initialize() {
const uniqueId = this.sessionId ?? Math.random().toString(36).slice(2);
let url;
if (process.platform === "win32") {
url = `ws://127.0.0.1:${await getAvailablePort()}/${getRandomId()}`;
} else {
url = `ws+unix://${tmpdir()}/${uniqueId}.sock`;
}
this.adapter = new DebugAdapter(url);
this.adapter.on("Adapter.response", response => this.sendResponse(response));
this.adapter.on("Adapter.event", event => this.sendEvent(event));
@@ -224,19 +204,11 @@ class FileDebugSession extends DebugSession {
}
class TerminalDebugSession extends FileDebugSession {
signal: TCPSocketSignal | UnixSignal;
readonly signal: UnixSignal;
constructor() {
super();
}
async initialize() {
await super.initialize();
if (process.platform === "win32") {
this.signal = new TCPSocketSignal(await getAvailablePort());
} else {
this.signal = new UnixSignal();
}
this.signal = new UnixSignal();
this.signal.on("Signal.received", () => {
vscode.debug.startDebugging(undefined, {
...ATTACH_CONFIGURATION,
@@ -250,7 +222,7 @@ class TerminalDebugSession extends FileDebugSession {
name: "Bun Terminal",
env: {
"BUN_INSPECT": `${this.adapter.url}?wait=1`,
"BUN_INSPECT_NOTIFY": this.signal.url,
"BUN_INSPECT_NOTIFY": `${this.signal.url}`,
},
isTransient: true,
iconPath: new vscode.ThemeIcon("debug-console"),

View File

@@ -19,9 +19,9 @@ add_compile_options(-Wall)
if(NOT CMAKE_C_COMPILER_ID MATCHES "tcc")
add_compile_options(
-fno-strict-aliasing
-Wdeclaration-after-statement
-Wno-declaration-after-statement
-Wpointer-sign
-Wsign-compare
-Wno-sign-compare
-Wunused-result
-Wformat-truncation
)

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env node
import { spawn as nodeSpawn } from "node:child_process";
import { existsSync, readFileSync, mkdirSync, cpSync, chmodSync } from "node:fs";
import { basename, join, resolve } from "node:path";
import { existsSync, readFileSync, readdirSync, mkdirSync, cpSync, chmodSync } from "node:fs";
import { basename, join, relative, resolve } from "node:path";
// https://cmake.org/cmake/help/latest/manual/cmake.1.html#generate-a-project-buildsystem
const generateFlags = [
@@ -118,6 +118,26 @@ async function build(args) {
.flatMap(([flag, value]) => [flag, value]);
await spawn("cmake", buildArgs, { env }, "compilation");
const buildFiles = ["ccache.log", "compile_commands.json"];
const buildPaths = [buildPath, ...readdirSync(buildPath).map(path => join(buildPath, path))];
const buildArtifacts = [];
for (const buildPath of buildPaths) {
for (const buildFile of buildFiles) {
const path = join(buildPath, buildFile);
if (existsSync(path)) {
buildArtifacts.push(path);
}
}
}
if (isBuildkite()) {
await Promise.all(
buildArtifacts.map(path =>
spawn("buildkite-agent", ["artifact", "upload", relative(buildPath, path)], { cwd: buildPath, env }),
),
);
}
printDuration("total", Date.now() - startTime);
}

View File

@@ -999,7 +999,7 @@ pub const StandaloneModuleGraph = struct {
bun.JSAst.Expr.Data.Store.reset();
bun.JSAst.Stmt.Data.Store.reset();
}
var json = bun.JSON.ParseJSON(&json_src, &log, arena, false) catch
var json = bun.JSON.ParseJSON(&json_src, &log, arena) catch
return error.InvalidSourceMap;
const mappings_str = json.get("mappings") orelse

View File

@@ -9,7 +9,7 @@ const BrotliEncoder = c.BrotliEncoder;
const mimalloc = bun.Mimalloc;
pub const BrotliAllocator = struct {
const BrotliAllocator = struct {
pub fn alloc(_: ?*anyopaque, len: usize) callconv(.C) *anyopaque {
if (bun.heap_breakdown.enabled) {
const zone = bun.heap_breakdown.getZone("brotli");

View File

@@ -1118,7 +1118,7 @@ pub const Formatter = struct {
// Is this a react element?
if (js_type.isObject()) {
if (value.getOwnTruthy(globalThis, "$$typeof")) |typeof_symbol| {
if (value.get(globalThis, "$$typeof")) |typeof_symbol| {
var reactElement = ZigString.init("react.element");
var react_fragment = ZigString.init("react.fragment");

View File

@@ -435,8 +435,7 @@ pub fn shellEscape(
globalThis.throw("String has invalid utf-16: {s}", .{bunstr.byteSlice()});
return .undefined;
}
var str = bun.String.createUTF8(outbuf.items[0..]);
return str.transferToJS(globalThis);
return bun.String.createUTF8(outbuf.items[0..]).toJS(globalThis);
}
return jsval;
}
@@ -446,8 +445,7 @@ pub fn shellEscape(
globalThis.throwOutOfMemory();
return .undefined;
};
var str = bun.String.createUTF8(outbuf.items[0..]);
return str.transferToJS(globalThis);
return bun.String.createUTF8(outbuf.items[0..]).toJS(globalThis);
}
return jsval;
@@ -477,11 +475,11 @@ pub fn braces(
if (arguments.nextEat()) |opts_val| {
if (opts_val.isObject()) {
if (comptime bun.Environment.allow_assert) {
if (opts_val.getOwnTruthy(globalThis, "tokenize")) |tokenize_val| {
if (opts_val.getTruthy(globalThis, "tokenize")) |tokenize_val| {
tokenize = if (tokenize_val.isBoolean()) tokenize_val.asBoolean() else false;
}
if (opts_val.getOwnTruthy(globalThis, "parse")) |tokenize_val| {
if (opts_val.getTruthy(globalThis, "parse")) |tokenize_val| {
parse = if (tokenize_val.isBoolean()) tokenize_val.asBoolean() else false;
}
}
@@ -609,11 +607,11 @@ pub fn which(
if (arguments.nextEat()) |arg| {
if (!arg.isEmptyOrUndefinedOrNull() and arg.isObject()) {
if (arg.getOwn(globalThis, "PATH")) |str_| {
if (arg.get(globalThis, "PATH")) |str_| {
path_str = str_.toSlice(globalThis, globalThis.bunVM().allocator);
}
if (arg.getOwn(globalThis, "cwd")) |str_| {
if (arg.get(globalThis, "cwd")) |str_| {
cwd_str = str_.toSlice(globalThis, globalThis.bunVM().allocator);
}
}
@@ -662,7 +660,7 @@ pub fn inspect(
const arg1 = arguments[1];
if (arg1.isObject()) {
if (arg1.getOwnTruthy(globalThis, "depth")) |opt| {
if (arg1.getTruthy(globalThis, "depth")) |opt| {
if (opt.isInt32()) {
const arg = opt.toInt32();
if (arg < 0) {
@@ -934,7 +932,7 @@ pub fn openInEditor(
if (arguments.nextEat()) |opts| {
if (!opts.isUndefinedOrNull()) {
if (opts.getOwnTruthy(globalThis, "editor")) |editor_val| {
if (opts.getTruthy(globalThis, "editor")) |editor_val| {
var sliced = editor_val.toSlice(globalThis, arguments.arena.allocator());
const prev_name = edit.name;
@@ -954,11 +952,11 @@ pub fn openInEditor(
}
}
if (opts.getOwnTruthy(globalThis, "line")) |line_| {
if (opts.getTruthy(globalThis, "line")) |line_| {
line = line_.toSlice(globalThis, arguments.arena.allocator()).slice();
}
if (opts.getOwnTruthy(globalThis, "column")) |column_| {
if (opts.getTruthy(globalThis, "column")) |column_| {
column = column_.toSlice(globalThis, arguments.arena.allocator()).slice();
}
}
@@ -1830,7 +1828,7 @@ pub const Crypto = struct {
pub fn fromJS(globalObject: *JSC.JSGlobalObject, value: JSC.JSValue) ?Value {
if (value.isObject()) {
if (value.getOwnTruthy(globalObject, "algorithm")) |algorithm_value| {
if (value.getTruthy(globalObject, "algorithm")) |algorithm_value| {
if (!algorithm_value.isString()) {
globalObject.throwInvalidArgumentType("hash", "algorithm", "string");
return null;
@@ -1847,7 +1845,7 @@ pub const Crypto = struct {
.bcrypt = PasswordObject.Algorithm.Value.bcrpyt_default,
};
if (value.getOwnTruthy(globalObject, "cost")) |rounds_value| {
if (value.getTruthy(globalObject, "cost")) |rounds_value| {
if (!rounds_value.isNumber()) {
globalObject.throwInvalidArgumentType("hash", "cost", "number");
return null;
@@ -1868,7 +1866,7 @@ pub const Crypto = struct {
inline .argon2id, .argon2d, .argon2i => |tag| {
var argon = Algorithm.Argon2Params{};
if (value.getOwnTruthy(globalObject, "timeCost")) |time_value| {
if (value.getTruthy(globalObject, "timeCost")) |time_value| {
if (!time_value.isNumber()) {
globalObject.throwInvalidArgumentType("hash", "timeCost", "number");
return null;
@@ -1884,7 +1882,7 @@ pub const Crypto = struct {
argon.time_cost = @as(u32, @intCast(time_cost));
}
if (value.getOwnTruthy(globalObject, "memoryCost")) |memory_value| {
if (value.getTruthy(globalObject, "memoryCost")) |memory_value| {
if (!memory_value.isNumber()) {
globalObject.throwInvalidArgumentType("hash", "memoryCost", "number");
return null;
@@ -4612,11 +4610,11 @@ fn stringWidth(globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC
var ambiguous_as_wide = false;
if (options_object.isObject()) {
if (options_object.getOwnTruthy(globalObject, "countAnsiEscapeCodes")) |count_ansi_escapes_value| {
if (options_object.getTruthy(globalObject, "countAnsiEscapeCodes")) |count_ansi_escapes_value| {
if (count_ansi_escapes_value.isBoolean())
count_ansi_escapes = count_ansi_escapes_value.toBoolean();
}
if (options_object.getOwnTruthy(globalObject, "ambiguousIsNarrow")) |ambiguous_is_narrow| {
if (options_object.getTruthy(globalObject, "ambiguousIsNarrow")) |ambiguous_is_narrow| {
if (ambiguous_is_narrow.isBoolean())
ambiguous_as_wide = !ambiguous_is_narrow.toBoolean();
}
@@ -4797,7 +4795,7 @@ pub const JSZlib = struct {
library = .zlib;
}
if (options_val.getOwnTruthy(globalThis, "library")) |library_value| {
if (options_val.getTruthy(globalThis, "library")) |library_value| {
if (!library_value.isString()) {
globalThis.throwInvalidArguments("Expected library to be a string", .{});
return .zero;
@@ -4924,7 +4922,7 @@ pub const JSZlib = struct {
library = .zlib;
}
if (options_val.getOwnTruthy(globalThis, "library")) |library_value| {
if (options_val.getTruthy(globalThis, "library")) |library_value| {
if (!library_value.isString()) {
globalThis.throwInvalidArguments("Expected library to be a string", .{});
return .zero;

View File

@@ -100,8 +100,8 @@ pub const JSBundler = struct {
globalThis.throwInvalidArguments("Expected plugin to be an object", .{});
return error.JSError;
}
if (try plugin.getOwnObject(globalThis, "SECRET_SERVER_COMPONENTS_INTERNALS")) |internals| {
if (internals.getOwn(globalThis, "router")) |router_value| {
if (try plugin.getObject(globalThis, "SECRET_SERVER_COMPONENTS_INTERNALS")) |internals| {
if (internals.get(globalThis, "router")) |router_value| {
if (router_value.as(JSC.API.FileSystemRouter) != null) {
this.server_components.router.set(globalThis, router_value);
} else {
@@ -110,7 +110,7 @@ pub const JSBundler = struct {
}
}
const directive_object = (try internals.getOwnObject(globalThis, "directive")) orelse {
const directive_object = (try internals.getObject(globalThis, "directive")) orelse {
globalThis.throwInvalidArguments("Expected directive to be an object", .{});
return error.JSError;
};
@@ -154,7 +154,7 @@ pub const JSBundler = struct {
// };
// defer decl.deinit();
if (plugin.getOwnOptional(globalThis, "name", ZigString.Slice) catch null) |slice| {
if (plugin.getOptional(globalThis, "name", ZigString.Slice) catch null) |slice| {
defer slice.deinit();
if (slice.len == 0) {
globalThis.throwInvalidArguments("Expected plugin to have a non-empty name", .{});
@@ -198,24 +198,24 @@ pub const JSBundler = struct {
}
}
if (config.getOwnTruthy(globalThis, "macros")) |macros_flag| {
if (config.getTruthy(globalThis, "macros")) |macros_flag| {
if (!macros_flag.coerce(bool, globalThis)) {
this.no_macros = true;
}
}
if (try config.getOwnOptionalEnum(globalThis, "target", options.Target)) |target| {
if (try config.getOptionalEnum(globalThis, "target", options.Target)) |target| {
this.target = target;
}
var has_out_dir = false;
if (try config.getOwnOptional(globalThis, "outdir", ZigString.Slice)) |slice| {
if (try config.getOptional(globalThis, "outdir", ZigString.Slice)) |slice| {
defer slice.deinit();
try this.outdir.appendSliceExact(slice.slice());
has_out_dir = true;
}
if (config.getOwnTruthy(globalThis, "sourcemap")) |source_map_js| {
if (config.getTruthy(globalThis, "sourcemap")) |source_map_js| {
if (bun.FeatureFlags.breaking_changes_1_2 and config.isBoolean()) {
if (source_map_js == .true) {
this.source_map = if (has_out_dir)
@@ -232,11 +232,11 @@ pub const JSBundler = struct {
}
}
if (try config.getOwnOptionalEnum(globalThis, "packages", options.PackagesOption)) |packages| {
if (try config.getOptionalEnum(globalThis, "packages", options.PackagesOption)) |packages| {
this.packages = packages;
}
if (try config.getOwnOptionalEnum(globalThis, "format", options.Format)) |format| {
if (try config.getOptionalEnum(globalThis, "format", options.Format)) |format| {
switch (format) {
.esm => {},
else => {
@@ -246,28 +246,28 @@ pub const JSBundler = struct {
}
}
// if (try config.getOwnOptional(globalThis, "hot", bool)) |hot| {
// if (try config.getOptional(globalThis, "hot", bool)) |hot| {
// this.hot = hot;
// }
if (try config.getOwnOptional(globalThis, "splitting", bool)) |hot| {
if (try config.getOptional(globalThis, "splitting", bool)) |hot| {
this.code_splitting = hot;
}
if (config.getOwnTruthy(globalThis, "minify")) |hot| {
if (config.getTruthy(globalThis, "minify")) |hot| {
if (hot.isBoolean()) {
const value = hot.coerce(bool, globalThis);
this.minify.whitespace = value;
this.minify.syntax = value;
this.minify.identifiers = value;
} else if (hot.isObject()) {
if (try hot.getOwnOptional(globalThis, "whitespace", bool)) |whitespace| {
if (try hot.getOptional(globalThis, "whitespace", bool)) |whitespace| {
this.minify.whitespace = whitespace;
}
if (try hot.getOwnOptional(globalThis, "syntax", bool)) |syntax| {
if (try hot.getOptional(globalThis, "syntax", bool)) |syntax| {
this.minify.syntax = syntax;
}
if (try hot.getOwnOptional(globalThis, "identifiers", bool)) |syntax| {
if (try hot.getOptional(globalThis, "identifiers", bool)) |syntax| {
this.minify.identifiers = syntax;
}
} else {
@@ -291,19 +291,19 @@ pub const JSBundler = struct {
return error.JSError;
}
if (config.getOwnTruthy(globalThis, "emitDCEAnnotations")) |flag| {
if (config.getTruthy(globalThis, "emitDCEAnnotations")) |flag| {
if (flag.coerce(bool, globalThis)) {
this.emit_dce_annotations = true;
}
}
if (config.getOwnTruthy(globalThis, "ignoreDCEAnnotations")) |flag| {
if (config.getTruthy(globalThis, "ignoreDCEAnnotations")) |flag| {
if (flag.coerce(bool, globalThis)) {
this.ignore_dce_annotations = true;
}
}
if (config.getOwnTruthy(globalThis, "conditions")) |conditions_value| {
if (config.getTruthy(globalThis, "conditions")) |conditions_value| {
if (conditions_value.isString()) {
var slice = conditions_value.toSliceOrNull(globalThis) orelse {
globalThis.throwInvalidArguments("Expected conditions to be an array of strings", .{});
@@ -329,7 +329,7 @@ pub const JSBundler = struct {
{
const path: ZigString.Slice = brk: {
if (try config.getOwnOptional(globalThis, "root", ZigString.Slice)) |slice| {
if (try config.getOptional(globalThis, "root", ZigString.Slice)) |slice| {
break :brk slice;
}
@@ -358,7 +358,7 @@ pub const JSBundler = struct {
try this.rootdir.appendSliceExact(rootdir);
}
if (try config.getOwnArray(globalThis, "external")) |externals| {
if (try config.getArray(globalThis, "external")) |externals| {
var iter = externals.arrayIterator(globalThis);
while (iter.next()) |entry_point| {
var slice = entry_point.toSliceOrNull(globalThis) orelse {
@@ -370,21 +370,21 @@ pub const JSBundler = struct {
}
}
// if (try config.getOwnOptional(globalThis, "dir", ZigString.Slice)) |slice| {
// if (try config.getOptional(globalThis, "dir", ZigString.Slice)) |slice| {
// defer slice.deinit();
// this.appendSliceExact(slice.slice()) catch unreachable;
// } else {
// this.appendSliceExact(globalThis.bunVM().bundler.fs.top_level_dir) catch unreachable;
// }
if (try config.getOwnOptional(globalThis, "publicPath", ZigString.Slice)) |slice| {
if (try config.getOptional(globalThis, "publicPath", ZigString.Slice)) |slice| {
defer slice.deinit();
try this.public_path.appendSliceExact(slice.slice());
}
if (config.getOwnTruthy(globalThis, "naming")) |naming| {
if (config.getTruthy(globalThis, "naming")) |naming| {
if (naming.isString()) {
if (try config.getOwnOptional(globalThis, "naming", ZigString.Slice)) |slice| {
if (try config.getOptional(globalThis, "naming", ZigString.Slice)) |slice| {
defer slice.deinit();
if (!strings.hasPrefixComptime(slice.slice(), "./")) {
try this.names.owned_entry_point.appendSliceExact("./");
@@ -393,7 +393,7 @@ pub const JSBundler = struct {
this.names.entry_point.data = this.names.owned_entry_point.list.items;
}
} else if (naming.isObject()) {
if (try naming.getOwnOptional(globalThis, "entry", ZigString.Slice)) |slice| {
if (try naming.getOptional(globalThis, "entry", ZigString.Slice)) |slice| {
defer slice.deinit();
if (!strings.hasPrefixComptime(slice.slice(), "./")) {
try this.names.owned_entry_point.appendSliceExact("./");
@@ -402,7 +402,7 @@ pub const JSBundler = struct {
this.names.entry_point.data = this.names.owned_entry_point.list.items;
}
if (try naming.getOwnOptional(globalThis, "chunk", ZigString.Slice)) |slice| {
if (try naming.getOptional(globalThis, "chunk", ZigString.Slice)) |slice| {
defer slice.deinit();
if (!strings.hasPrefixComptime(slice.slice(), "./")) {
try this.names.owned_chunk.appendSliceExact("./");
@@ -411,7 +411,7 @@ pub const JSBundler = struct {
this.names.chunk.data = this.names.owned_chunk.list.items;
}
if (try naming.getOwnOptional(globalThis, "asset", ZigString.Slice)) |slice| {
if (try naming.getOptional(globalThis, "asset", ZigString.Slice)) |slice| {
defer slice.deinit();
if (!strings.hasPrefixComptime(slice.slice(), "./")) {
try this.names.owned_asset.appendSliceExact("./");
@@ -425,7 +425,7 @@ pub const JSBundler = struct {
}
}
if (try config.getOwnObject(globalThis, "define")) |define| {
if (try config.getObject(globalThis, "define")) |define| {
if (!define.isObject()) {
globalThis.throwInvalidArguments("define must be an object", .{});
return error.JSError;
@@ -463,7 +463,7 @@ pub const JSBundler = struct {
}
}
if (try config.getOwnObject(globalThis, "loader")) |loaders| {
if (try config.getObject(globalThis, "loader")) |loaders| {
var loader_iter = JSC.JSPropertyIterator(.{
.skip_empty_name = true,
.include_value = true,
@@ -967,8 +967,6 @@ pub const JSBundler = struct {
else
bun.String.createUTF8(path.namespace);
const path_string = bun.String.createUTF8(path.text);
defer namespace_string.deref();
defer path_string.deref();
return JSBundlerPlugin__anyMatches(this, &namespace_string, &path_string, is_onLoad);
}

View File

@@ -330,7 +330,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
return transpiler;
}
if (object.getOwnTruthy(globalObject, "define")) |define| {
if (object.getIfPropertyExists(globalObject, "define")) |define| {
define: {
if (define.isUndefinedOrNull()) {
break :define;
@@ -379,7 +379,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (object.getOwn(globalThis, "external")) |external| {
if (object.get(globalThis, "external")) |external| {
external: {
if (external.isUndefinedOrNull()) break :external;
@@ -419,7 +419,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (object.getOwn(globalThis, "loader")) |loader| {
if (object.get(globalThis, "loader")) |loader| {
if (Loader.fromJS(globalThis, loader, exception)) |resolved| {
if (!resolved.isJavaScriptLike()) {
JSC.throwInvalidArguments("only JavaScript-like loaders supported for now", .{}, globalObject, exception);
@@ -434,7 +434,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (object.getOwn(globalThis, "target")) |target| {
if (object.get(globalThis, "target")) |target| {
if (Target.fromJS(globalThis, target, exception)) |resolved| {
transpiler.transform.target = resolved.toAPI();
}
@@ -444,7 +444,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (object.getOwn(globalThis, "tsconfig")) |tsconfig| {
if (object.get(globalThis, "tsconfig")) |tsconfig| {
tsconfig: {
if (tsconfig.isUndefinedOrNull()) break :tsconfig;
const kind = tsconfig.jsType();
@@ -483,7 +483,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
else => false,
};
if (object.getOwnTruthy(globalThis, "macro")) |macros| {
if (object.getIfPropertyExists(globalThis, "macro")) |macros| {
macros: {
if (macros.isUndefinedOrNull()) break :macros;
if (macros.isBoolean()) {
@@ -518,39 +518,39 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (object.getOwnOptional(globalThis, "autoImportJSX", bool) catch return transpiler) |flag| {
if (object.getOptional(globalThis, "autoImportJSX", bool) catch return transpiler) |flag| {
transpiler.runtime.auto_import_jsx = flag;
}
if (object.getOwnOptional(globalThis, "allowBunRuntime", bool) catch return transpiler) |flag| {
if (object.getOptional(globalThis, "allowBunRuntime", bool) catch return transpiler) |flag| {
transpiler.runtime.allow_runtime = flag;
}
if (object.getOwnOptional(globalThis, "inline", bool) catch return transpiler) |flag| {
if (object.getOptional(globalThis, "inline", bool) catch return transpiler) |flag| {
transpiler.runtime.inlining = flag;
}
if (object.getOwnOptional(globalThis, "minifyWhitespace", bool) catch return transpiler) |flag| {
if (object.getOptional(globalThis, "minifyWhitespace", bool) catch return transpiler) |flag| {
transpiler.minify_whitespace = flag;
}
if (object.getOwnOptional(globalThis, "deadCodeElimination", bool) catch return transpiler) |flag| {
if (object.getOptional(globalThis, "deadCodeElimination", bool) catch return transpiler) |flag| {
transpiler.dead_code_elimination = flag;
}
if (object.getOwnTruthy(globalThis, "minify")) |hot| {
if (object.getTruthy(globalThis, "minify")) |hot| {
if (hot.isBoolean()) {
transpiler.minify_whitespace = hot.coerce(bool, globalThis);
transpiler.minify_syntax = transpiler.minify_whitespace;
transpiler.minify_identifiers = transpiler.minify_syntax;
} else if (hot.isObject()) {
if (try hot.getOwnOptional(globalThis, "whitespace", bool)) |whitespace| {
if (try hot.getOptional(globalThis, "whitespace", bool)) |whitespace| {
transpiler.minify_whitespace = whitespace;
}
if (try hot.getOwnOptional(globalThis, "syntax", bool)) |syntax| {
if (try hot.getOptional(globalThis, "syntax", bool)) |syntax| {
transpiler.minify_syntax = syntax;
}
if (try hot.getOwnOptional(globalThis, "identifiers", bool)) |syntax| {
if (try hot.getOptional(globalThis, "identifiers", bool)) |syntax| {
transpiler.minify_identifiers = syntax;
}
} else {
@@ -559,7 +559,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (object.getOwn(globalThis, "sourcemap")) |flag| {
if (object.get(globalThis, "sourcemap")) |flag| {
if (flag.isBoolean() or flag.isUndefinedOrNull()) {
if (flag.toBoolean()) {
transpiler.transform.source_map = .@"inline";
@@ -576,21 +576,21 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (try object.getOwnOptionalEnum(globalThis, "packages", options.PackagesOption)) |packages| {
if (try object.getOptionalEnum(globalThis, "packages", options.PackagesOption)) |packages| {
transpiler.transform.packages = packages.toAPI();
}
var tree_shaking: ?bool = null;
if (object.getOwnOptional(globalThis, "treeShaking", bool) catch return transpiler) |treeShaking| {
if (object.getOptional(globalThis, "treeShaking", bool) catch return transpiler) |treeShaking| {
tree_shaking = treeShaking;
}
var trim_unused_imports: ?bool = null;
if (object.getOwnOptional(globalThis, "trimUnusedImports", bool) catch return transpiler) |trimUnusedImports| {
if (object.getOptional(globalThis, "trimUnusedImports", bool) catch return transpiler) |trimUnusedImports| {
trim_unused_imports = trimUnusedImports;
}
if (object.getOwnTruthy(globalThis, "exports")) |exports| {
if (object.getTruthy(globalThis, "exports")) |exports| {
if (!exports.isObject()) {
JSC.throwInvalidArguments("exports must be an object", .{}, globalObject, exception);
return transpiler;
@@ -599,7 +599,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
var replacements = Runtime.Features.ReplaceableExport.Map{};
errdefer replacements.clearAndFree(bun.default_allocator);
if (exports.getOwnTruthy(globalThis, "eliminate")) |eliminate| {
if (exports.getTruthy(globalThis, "eliminate")) |eliminate| {
if (!eliminate.jsType().isArray()) {
JSC.throwInvalidArguments("exports.eliminate must be an array", .{}, globalObject, exception);
return transpiler;
@@ -641,7 +641,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
}
}
if (exports.getOwnTruthy(globalThis, "replace")) |replace| {
if (exports.getTruthy(globalThis, "replace")) |replace| {
if (!replace.isObject()) {
JSC.throwInvalidArguments("replace must be an object", .{}, globalObject, exception);
return transpiler;
@@ -718,7 +718,7 @@ fn transformOptionsFromJSC(globalObject: JSC.C.JSContextRef, temp_allocator: std
transpiler.runtime.replace_exports = replacements;
}
if (object.getOwnTruthy(globalThis, "logLevel")) |logLevel| {
if (object.getTruthy(globalThis, "logLevel")) |logLevel| {
if (logger.Log.Level.Map.fromJS(globalObject, logLevel)) |level| {
transpiler.log.level = level;
} else {

View File

@@ -239,7 +239,7 @@ const Handlers = struct {
.{ "onHandshake", "handshake" },
};
inline for (pairs) |pair| {
if (opts.getOwnTruthyComptime(globalObject, pair.@"1")) |callback_value| {
if (opts.getTruthyComptime(globalObject, pair.@"1")) |callback_value| {
if (!callback_value.isCell() or !callback_value.isCallable(globalObject.vm())) {
exception.* = JSC.toInvalidArguments(comptime std.fmt.comptimePrint("Expected \"{s}\" callback to be a function", .{pair.@"1"}), .{}, globalObject).asObjectRef();
return null;
@@ -254,7 +254,7 @@ const Handlers = struct {
return null;
}
if (opts.getOwnTruthy(globalObject, "binaryType")) |binary_type_value| {
if (opts.getTruthy(globalObject, "binaryType")) |binary_type_value| {
if (!binary_type_value.isString()) {
exception.* = JSC.toInvalidArguments("Expected \"binaryType\" to be a string", .{}, globalObject).asObjectRef();
return null;
@@ -341,13 +341,13 @@ pub const SocketConfig = struct {
}
hostname_or_unix: {
if (opts.getOwnTruthy(globalObject, "fd")) |fd_| {
if (opts.getTruthy(globalObject, "fd")) |fd_| {
if (fd_.isNumber()) {
break :hostname_or_unix;
}
}
if (opts.getOwnTruthy(globalObject, "unix")) |unix_socket| {
if (opts.getTruthy(globalObject, "unix")) |unix_socket| {
if (!unix_socket.isString()) {
exception.* = JSC.toInvalidArguments("Expected \"unix\" to be a string", .{}, globalObject).asObjectRef();
return null;
@@ -365,17 +365,17 @@ pub const SocketConfig = struct {
}
}
if (opts.getOwnTruthy(globalObject, "exclusive")) |_| {
if (opts.getTruthy(globalObject, "exclusive")) |_| {
exclusive = true;
}
if (opts.getOwnTruthy(globalObject, "hostname") orelse opts.getOwnTruthy(globalObject, "host")) |hostname| {
if (opts.getTruthy(globalObject, "hostname") orelse opts.getTruthy(globalObject, "host")) |hostname| {
if (!hostname.isString()) {
exception.* = JSC.toInvalidArguments("Expected \"hostname\" to be a string", .{}, globalObject).asObjectRef();
return null;
}
var port_value = opts.getOwn(globalObject, "port") orelse JSValue.zero;
var port_value = opts.get(globalObject, "port") orelse JSValue.zero;
hostname_or_unix = hostname.getZigString(globalObject).toSlice(bun.default_allocator);
if (port_value.isEmptyOrUndefinedOrNull() and hostname_or_unix.len > 0) {
@@ -423,7 +423,7 @@ pub const SocketConfig = struct {
return null;
}
var handlers = Handlers.fromJS(globalObject, opts.getOwn(globalObject, "socket") orelse JSValue.zero, exception) orelse {
var handlers = Handlers.fromJS(globalObject, opts.get(globalObject, "socket") orelse JSValue.zero, exception) orelse {
hostname_or_unix.deinit();
return null;
};
@@ -542,7 +542,7 @@ pub const Listener = struct {
var exception: JSC.C.JSValueRef = null;
const socket_obj = opts.getOwn(globalObject, "socket") orelse {
const socket_obj = opts.get(globalObject, "socket") orelse {
globalObject.throw("Expected \"socket\" object", .{});
return .zero;
};
@@ -1069,7 +1069,7 @@ pub const Listener = struct {
vm.eventLoop().ensureWaker();
var connection: Listener.UnixOrHost = blk: {
if (opts.getOwnTruthy(globalObject, "fd")) |fd_| {
if (opts.getTruthy(globalObject, "fd")) |fd_| {
if (fd_.isNumber()) {
const fd = fd_.asFileDescriptor();
break :blk .{ .fd = fd };
@@ -1236,30 +1236,46 @@ pub const Listener = struct {
const promise_value = promise.asValue(globalObject);
handlers_ptr.promise.set(globalObject, promise_value);
switch (ssl_enabled) {
inline else => |is_ssl_enabled| {
const SocketType = NewSocket(is_ssl_enabled);
var socket = SocketType.new(.{
.handlers = handlers_ptr,
.this_value = .zero,
.socket = SocketType.Socket.detached,
.connection = connection,
.protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch bun.outOfMemory()) else null,
.server_name = server_name,
.socket_context = socket_context, // owns the socket context
});
if (ssl_enabled) {
var tls = TLSSocket.new(.{
.handlers = handlers_ptr,
.this_value = .zero,
.socket = TLSSocket.Socket.detached,
.connection = connection,
.protos = if (protos) |p| (bun.default_allocator.dupe(u8, p) catch bun.outOfMemory()) else null,
.server_name = server_name,
.socket_context = socket_context, // owns the socket context
});
SocketType.dataSetCached(socket.getThisValue(globalObject), globalObject, default_data);
socket.doConnect(connection) catch {
socket.handleConnectError(@intFromEnum(if (port == null) bun.C.SystemErrno.ENOENT else bun.C.SystemErrno.ECONNREFUSED));
return promise_value;
};
socket.poll_ref.ref(handlers.vm);
TLSSocket.dataSetCached(tls.getThisValue(globalObject), globalObject, default_data);
tls.doConnect(connection) catch {
tls.handleConnectError(@intFromEnum(if (port == null) bun.C.SystemErrno.ENOENT else bun.C.SystemErrno.ECONNREFUSED));
return promise_value;
},
};
tls.poll_ref.ref(handlers.vm);
return promise_value;
} else {
var tcp = TCPSocket.new(.{
.handlers = handlers_ptr,
.this_value = .zero,
.socket = TCPSocket.Socket.detached,
.connection = null,
.protos = null,
.server_name = null,
.socket_context = socket_context, // owns the socket context
});
TCPSocket.dataSetCached(tcp.getThisValue(globalObject), globalObject, default_data);
tcp.doConnect(connection) catch {
tcp.handleConnectError(@intFromEnum(if (port == null) bun.C.SystemErrno.ENOENT else bun.C.SystemErrno.ECONNREFUSED));
return promise_value;
};
tcp.poll_ref.ref(handlers.vm);
return promise_value;
}
}
};
@@ -1355,13 +1371,9 @@ fn NewSocket(comptime ssl: bool) type {
pub fn doConnect(this: *This, connection: Listener.UnixOrHost) !void {
bun.assert(this.socket_context != null);
this.ref();
errdefer {
this.deref();
}
switch (connection) {
.host => |c| {
this.ref();
this.socket = try This.Socket.connectAnon(
normalizeHost(c.host),
c.port,
@@ -1370,6 +1382,8 @@ fn NewSocket(comptime ssl: bool) type {
);
},
.unix => |u| {
this.ref();
this.socket = try This.Socket.connectUnixAnon(
u,
this.socket_context.?,
@@ -1455,14 +1469,10 @@ fn NewSocket(comptime ssl: bool) type {
fn handleConnectError(this: *This, errno: c_int) void {
log("onConnectError({d}, {})", .{ errno, this.ref_count });
// Ensure the socket is still alive for any defer's we have
this.ref();
defer this.deref();
const needs_deref = !this.socket.isDetached();
this.socket = Socket.detached;
defer this.markInactive();
defer if (needs_deref) this.deref();
defer this.markInactive();
const handlers = this.handlers;
const vm = handlers.vm;
@@ -1490,11 +1500,6 @@ fn NewSocket(comptime ssl: bool) type {
if (callback == .zero) {
if (handlers.promise.trySwap()) |promise| {
if (this.this_value != .zero) {
this.this_value = .zero;
}
this.has_pending_activity.store(false, .release);
// reject the promise on connect() error
const err_value = err.toErrorInstance(globalObject);
promise.asPromise().?.rejectOnNextTick(globalObject, err_value);
@@ -1504,9 +1509,6 @@ fn NewSocket(comptime ssl: bool) type {
}
const this_value = this.getThisValue(globalObject);
this.this_value = .zero;
this.has_pending_activity.store(false, .release);
const err_value = err.toErrorInstance(globalObject);
const result = callback.call(globalObject, this_value, &[_]JSValue{
this_value,
@@ -1522,6 +1524,7 @@ fn NewSocket(comptime ssl: bool) type {
var promise = val.asPromise().?;
const err_ = err.toErrorInstance(globalObject);
promise.rejectOnNextTickAsHandled(globalObject, err_);
this.has_pending_activity.store(false, .release);
}
}
pub fn onConnectError(this: *This, _: Socket, errno: c_int) void {
@@ -1563,10 +1566,6 @@ fn NewSocket(comptime ssl: bool) type {
}
pub fn onOpen(this: *This, socket: Socket) void {
// Ensure the socket remains alive until this is finished
this.ref();
defer this.deref();
log("onOpen {} {}", .{ this.socket.isDetached(), this.ref_count });
// update the internal socket instance to the one that was just connected
// This socket must be replaced because the previous one is a connecting socket not a uSockets socket
@@ -1665,9 +1664,6 @@ fn NewSocket(comptime ssl: bool) type {
JSC.markBinding(@src());
log("onEnd", .{});
if (this.socket.isDetached()) return;
// Ensure the socket remains alive until this is finished
this.ref();
defer this.deref();
const handlers = this.handlers;
@@ -2308,7 +2304,7 @@ fn NewSocket(comptime ssl: bool) type {
var exception: JSC.C.JSValueRef = null;
const socket_obj = opts.getOwn(globalObject, "socket") orelse {
const socket_obj = opts.get(globalObject, "socket") orelse {
globalObject.throw("Expected \"socket\" option", .{});
return .zero;
};
@@ -3082,7 +3078,7 @@ fn NewSocket(comptime ssl: bool) type {
return .zero;
}
const socket_obj = opts.getOwn(globalObject, "socket") orelse {
const socket_obj = opts.get(globalObject, "socket") orelse {
globalObject.throw("Expected \"socket\" option", .{});
return .zero;
};
@@ -4007,7 +4003,7 @@ pub fn jsUpgradeDuplexToTLS(globalObject: *JSC.JSGlobalObject, callframe: *JSC.C
return .zero;
}
const socket_obj = opts.getOwn(globalObject, "socket") orelse {
const socket_obj = opts.get(globalObject, "socket") orelse {
globalObject.throw("Expected \"socket\" option", .{});
return .zero;
};
@@ -4125,25 +4121,3 @@ pub fn createNodeTLSBinding(global: *JSC.JSGlobalObject) JSC.JSValue {
JSC.JSFunction.create(global, "isNamedPipeSocket", JSC.toJSHostFunction(jsIsNamedPipeSocket), 1, .{}),
});
}
pub fn jsCreateSocketPair(global: *JSC.JSGlobalObject, _: *JSC.CallFrame) callconv(JSC.conv) JSValue {
JSC.markBinding(@src());
if (Environment.isWindows) {
global.throw("Not implemented on Windows", .{});
return .zero;
}
var fds_: [2]std.c.fd_t = .{ 0, 0 };
const rc = std.c.socketpair(std.posix.AF.UNIX, std.posix.SOCK.STREAM, 0, &fds_);
if (rc != 0) {
const err = bun.sys.Error.fromCode(bun.C.getErrno(rc), .socketpair);
global.throwValue(err.toJSC(global));
return .zero;
}
const array = JSC.JSValue.createEmptyArray(global, 2);
array.putIndex(global, 0, JSC.jsNumber(fds_[0]));
array.putIndex(global, 1, JSC.jsNumber(fds_[1]));
return array;
}

View File

@@ -211,7 +211,6 @@ pub const Subprocess = struct {
killed: bool = false,
has_stdin_destructor_called: bool = false,
finalized: bool = false,
deref_on_stdin_destroyed: bool = false,
};
pub const SignalCode = bun.SignalCode;
@@ -704,13 +703,9 @@ pub const Subprocess = struct {
}
pub fn onStdinDestroyed(this: *Subprocess) void {
const must_deref = this.flags.deref_on_stdin_destroyed;
this.flags.deref_on_stdin_destroyed = false;
defer if (must_deref) this.deref();
this.flags.has_stdin_destructor_called = true;
this.weak_file_sink_stdin_ptr = null;
defer this.deref();
if (!this.flags.finalized) {
// otherwise update the pending activity flag
this.updateHasPendingActivity();
@@ -1245,7 +1240,6 @@ pub const Subprocess = struct {
pipe.writer.setParent(pipe);
subprocess.weak_file_sink_stdin_ptr = pipe;
subprocess.ref();
subprocess.flags.deref_on_stdin_destroyed = true;
subprocess.flags.has_stdin_destructor_called = false;
return Writable{
@@ -1306,7 +1300,6 @@ pub const Subprocess = struct {
subprocess.weak_file_sink_stdin_ptr = pipe;
subprocess.ref();
subprocess.flags.has_stdin_destructor_called = false;
subprocess.flags.deref_on_stdin_destroyed = true;
pipe.writer.handle.poll.flags.insert(.socket);
@@ -1352,23 +1345,12 @@ pub const Subprocess = struct {
.pipe => |pipe| {
this.* = .{ .ignore = {} };
if (subprocess.process.hasExited() and !subprocess.flags.has_stdin_destructor_called) {
// onAttachedProcessExit() can call deref on the
// subprocess. Since we never called ref(), it would be
// unbalanced to do so, leading to a use-after-free.
// So, let's not do that.
// https://github.com/oven-sh/bun/pull/14092
bun.debugAssert(!subprocess.flags.deref_on_stdin_destroyed);
const debug_ref_count: if (Environment.isDebug) u32 else u0 = if (Environment.isDebug) subprocess.ref_count else 0;
pipe.onAttachedProcessExit();
if (comptime Environment.isDebug) {
bun.debugAssert(subprocess.ref_count == debug_ref_count);
}
return pipe.toJS(globalThis);
} else {
subprocess.flags.has_stdin_destructor_called = false;
subprocess.weak_file_sink_stdin_ptr = pipe;
subprocess.ref();
subprocess.flags.deref_on_stdin_destroyed = true;
if (@intFromPtr(pipe.signal.ptr) == @intFromPtr(subprocess)) {
pipe.signal.clear();
}
@@ -1465,7 +1447,6 @@ pub const Subprocess = struct {
if (stdin) |pipe| {
this.weak_file_sink_stdin_ptr = null;
this.flags.has_stdin_destructor_called = true;
// It is okay if it does call deref() here, as in that case it was truly ref'd.
pipe.onAttachedProcessExit();
}
@@ -1716,7 +1697,7 @@ pub const Subprocess = struct {
} else if (!args.isObject()) {
globalThis.throwInvalidArguments("cmd must be an array", .{});
return .zero;
} else if (args.getOwnTruthy(globalThis, "cmd")) |cmd_value_| {
} else if (args.getTruthy(globalThis, "cmd")) |cmd_value_| {
cmd_value = cmd_value_;
} else {
globalThis.throwInvalidArguments("cmd must be an array", .{});
@@ -1724,7 +1705,7 @@ pub const Subprocess = struct {
}
if (args.isObject()) {
if (args.getOwnTruthy(globalThis, "argv0")) |argv0_| {
if (args.getTruthy(globalThis, "argv0")) |argv0_| {
const argv0_str = argv0_.getZigString(globalThis);
if (argv0_str.len > 0) {
argv0 = argv0_str.toOwnedSliceZ(allocator) catch {
@@ -1735,7 +1716,7 @@ pub const Subprocess = struct {
}
// need to update `cwd` before searching for executable with `Which.which`
if (args.getOwnTruthy(globalThis, "cwd")) |cwd_| {
if (args.getTruthy(globalThis, "cwd")) |cwd_| {
const cwd_str = cwd_.getZigString(globalThis);
if (cwd_str.len > 0) {
cwd = cwd_str.toOwnedSliceZ(allocator) catch {
@@ -1820,21 +1801,21 @@ pub const Subprocess = struct {
if (args != .zero and args.isObject()) {
// This must run before the stdio parsing happens
if (!is_sync) {
if (args.getOwnTruthy(globalThis, "ipc")) |val| {
if (args.getTruthy(globalThis, "ipc")) |val| {
if (val.isCell() and val.isCallable(globalThis.vm())) {
maybe_ipc_mode = ipc_mode: {
if (args.getOwnTruthy(globalThis, "serialization")) |mode_val| {
if (args.get(globalThis, "serialization")) |mode_val| {
if (mode_val.isString()) {
break :ipc_mode IPC.Mode.fromJS(globalThis, mode_val) orelse {
if (!globalThis.hasException()) {
globalThis.throwInvalidArguments("serialization must be \"json\" or \"advanced\"", .{});
}
const mode_str = mode_val.toBunString(globalThis);
defer mode_str.deref();
const slice = mode_str.toUTF8(bun.default_allocator);
defer slice.deinit();
break :ipc_mode IPC.Mode.fromString(slice.slice()) orelse {
globalThis.throwInvalidArguments("serialization must be \"json\" or \"advanced\"", .{});
return .zero;
};
} else {
if (!globalThis.hasException()) {
globalThis.throwInvalidArgumentType("spawn", "serialization", "string");
}
globalThis.throwInvalidArguments("serialization must be a 'string'", .{});
return .zero;
}
}
@@ -1846,7 +1827,7 @@ pub const Subprocess = struct {
}
}
if (args.getOwnTruthy(globalThis, "onDisconnect")) |onDisconnect_| {
if (args.getTruthy(globalThis, "onDisconnect")) |onDisconnect_| {
if (!onDisconnect_.isCell() or !onDisconnect_.isCallable(globalThis.vm())) {
globalThis.throwInvalidArguments("onDisconnect must be a function or undefined", .{});
return .zero;
@@ -1858,7 +1839,7 @@ pub const Subprocess = struct {
onDisconnect_.withAsyncContextIfNeeded(globalThis);
}
if (args.getOwnTruthy(globalThis, "onExit")) |onExit_| {
if (args.getTruthy(globalThis, "onExit")) |onExit_| {
if (!onExit_.isCell() or !onExit_.isCallable(globalThis.vm())) {
globalThis.throwInvalidArguments("onExit must be a function or undefined", .{});
return .zero;
@@ -1870,7 +1851,7 @@ pub const Subprocess = struct {
onExit_.withAsyncContextIfNeeded(globalThis);
}
if (args.getOwnTruthy(globalThis, "env")) |object| {
if (args.getTruthy(globalThis, "env")) |object| {
if (!object.isObject()) {
globalThis.throwInvalidArguments("env must be an object", .{});
return .zero;
@@ -1886,7 +1867,7 @@ pub const Subprocess = struct {
};
env_array = envp_managed.moveToUnmanaged();
}
if (args.getOwn(globalThis, "stdio")) |stdio_val| {
if (args.get(globalThis, "stdio")) |stdio_val| {
if (!stdio_val.isEmptyOrUndefinedOrNull()) {
if (stdio_val.jsType().isArray()) {
var stdio_iter = stdio_val.arrayIterator(globalThis);
@@ -1925,44 +1906,44 @@ pub const Subprocess = struct {
}
}
} else {
if (args.getOwn(globalThis, "stdin")) |value| {
if (args.get(globalThis, "stdin")) |value| {
if (!stdio[0].extract(globalThis, 0, value))
return .zero;
}
if (args.getOwn(globalThis, "stderr")) |value| {
if (args.get(globalThis, "stderr")) |value| {
if (!stdio[2].extract(globalThis, 2, value))
return .zero;
}
if (args.getOwn(globalThis, "stdout")) |value| {
if (args.get(globalThis, "stdout")) |value| {
if (!stdio[1].extract(globalThis, 1, value))
return .zero;
}
}
if (comptime !is_sync) {
if (args.getOwn(globalThis, "lazy")) |lazy_val| {
if (args.get(globalThis, "lazy")) |lazy_val| {
if (lazy_val.isBoolean()) {
lazy = lazy_val.toBoolean();
}
}
}
if (args.getOwn(globalThis, "detached")) |detached_val| {
if (args.get(globalThis, "detached")) |detached_val| {
if (detached_val.isBoolean()) {
detached = detached_val.toBoolean();
}
}
if (Environment.isWindows) {
if (args.getOwn(globalThis, "windowsHide")) |val| {
if (args.get(globalThis, "windowsHide")) |val| {
if (val.isBoolean()) {
windows_hide = val.asBoolean();
}
}
if (args.getOwn(globalThis, "windowsVerbatimArguments")) |val| {
if (args.get(globalThis, "windowsVerbatimArguments")) |val| {
if (val.isBoolean()) {
windows_verbatim_arguments = val.asBoolean();
}
@@ -2086,35 +2067,13 @@ pub const Subprocess = struct {
} else {},
};
var spawned = switch (bun.spawn.spawnProcess(
&spawn_options,
@ptrCast(argv.items.ptr),
@ptrCast(env_array.items.ptr),
) catch |err| {
spawn_options.deinit();
globalThis.throwError(err, ": failed to spawn process");
return .zero;
}) {
.err => |err| {
spawn_options.deinit();
globalThis.throwValue(err.toJSC(globalThis));
return .zero;
},
.result => |result| result,
};
const loop = jsc_vm.eventLoop();
const process = spawned.toProcess(loop, is_sync);
var subprocess = Subprocess.new(.{
.globalThis = globalThis,
.process = process,
.process = undefined,
.pid_rusage = null,
.stdin = .{ .ignore = {} },
.stdout = .{ .ignore = {} },
.stderr = .{ .ignore = {} },
.stdin = undefined,
.stdout = undefined,
.stderr = undefined,
.stdio_pipes = .{},
.on_exit_callback = .{},
.on_disconnect_callback = .{},
@@ -2125,15 +2084,51 @@ pub const Subprocess = struct {
},
});
const posix_ipc_fd = if (Environment.isPosix and !is_sync and maybe_ipc_mode != null)
spawned.extra_pipes.items[@intCast(ipc_channel)]
else
bun.invalid_fd;
var spawned = switch (bun.spawn.spawnProcess(
&spawn_options,
@ptrCast(argv.items.ptr),
@ptrCast(env_array.items.ptr),
) catch |err| {
subprocess.deref();
spawn_options.deinit();
globalThis.throwError(err, ": failed to spawn process");
return .zero;
}) {
.err => |err| {
subprocess.deref();
spawn_options.deinit();
globalThis.throwValue(err.toJSC(globalThis));
return .zero;
},
.result => |result| result,
};
var posix_ipc_info: if (Environment.isPosix) IPC.Socket else void = undefined;
if (Environment.isPosix and !is_sync) {
if (maybe_ipc_mode != null) {
posix_ipc_info = IPC.Socket.from(
// we initialize ext later in the function
uws.us_socket_from_fd(
jsc_vm.rareData().spawnIPCContext(jsc_vm),
@sizeOf(*Subprocess),
spawned.extra_pipes.items[@intCast(ipc_channel)].cast(),
) orelse {
subprocess.deref();
spawn_options.deinit();
globalThis.throw("failed to create socket pair", .{});
return .zero;
},
);
}
}
const loop = jsc_vm.eventLoop();
// When run synchronously, subprocess isn't garbage collected
subprocess.* = Subprocess{
.globalThis = globalThis,
.process = process,
.process = spawned.toProcess(loop, is_sync),
.pid_rusage = null,
.stdin = Writable.init(
stdio[0],
@@ -2142,7 +2137,6 @@ pub const Subprocess = struct {
spawned.stdin,
) catch {
globalThis.throwOutOfMemory();
subprocess.deref();
return .zero;
},
.stdout = Readable.init(
@@ -2163,16 +2157,19 @@ pub const Subprocess = struct {
default_max_buffer_size,
is_sync,
),
// 1. JavaScript.
// 2. Process.
.ref_count = 2,
.stdio_pipes = spawned.extra_pipes.moveToUnmanaged(),
.on_exit_callback = if (on_exit_callback != .zero) JSC.Strong.create(on_exit_callback, globalThis) else .{},
.on_disconnect_callback = if (on_disconnect_callback != .zero) JSC.Strong.create(on_disconnect_callback, globalThis) else .{},
.ipc_data = if (!is_sync and comptime Environment.isWindows)
if (maybe_ipc_mode) |ipc_mode| .{
.mode = ipc_mode,
} else null
.ipc_data = if (!is_sync)
if (maybe_ipc_mode) |ipc_mode|
if (Environment.isWindows) .{
.mode = ipc_mode,
} else .{
.socket = posix_ipc_info,
.mode = ipc_mode,
}
else
null
else
null,
.ipc_callback = if (ipc_callback != .zero) JSC.Strong.create(ipc_callback, globalThis) else .{},
@@ -2180,26 +2177,9 @@ pub const Subprocess = struct {
.is_sync = is_sync,
},
};
subprocess.ref(); // + one ref for the process
subprocess.process.setExitHandler(subprocess);
var posix_ipc_info: if (Environment.isPosix) IPC.Socket else void = undefined;
if (Environment.isPosix and !is_sync) {
if (maybe_ipc_mode) |mode| {
if (uws.us_socket_from_fd(
jsc_vm.rareData().spawnIPCContext(jsc_vm),
@sizeOf(*Subprocess),
posix_ipc_fd.cast(),
)) |socket| {
posix_ipc_info = IPC.Socket.from(socket);
subprocess.ipc_data = .{
.socket = posix_ipc_info,
.mode = mode,
};
}
}
}
if (subprocess.ipc_data) |*ipc_data| {
if (Environment.isPosix) {
if (posix_ipc_info.ext(*Subprocess)) |ctx| {

View File

@@ -129,7 +129,7 @@ pub const UDPSocketConfig = struct {
}
const hostname = brk: {
if (options.getOwnTruthy(globalThis, "hostname")) |value| {
if (options.getTruthy(globalThis, "hostname")) |value| {
if (!value.isString()) {
globalThis.throwInvalidArguments("Expected \"hostname\" to be a string", .{});
return null;
@@ -144,7 +144,7 @@ pub const UDPSocketConfig = struct {
defer if (globalThis.hasException()) default_allocator.free(hostname);
const port: u16 = brk: {
if (options.getOwnTruthy(globalThis, "port")) |value| {
if (options.getTruthy(globalThis, "port")) |value| {
const number = value.coerceToInt32(globalThis);
if (number < 0 or number > 0xffff) {
globalThis.throwInvalidArguments("Expected \"port\" to be an integer between 0 and 65535", .{});
@@ -161,13 +161,13 @@ pub const UDPSocketConfig = struct {
.port = port,
};
if (options.getOwnTruthy(globalThis, "socket")) |socket| {
if (options.getTruthy(globalThis, "socket")) |socket| {
if (!socket.isObject()) {
globalThis.throwInvalidArguments("Expected \"socket\" to be an object", .{});
return null;
}
if (options.getOwnTruthy(globalThis, "binaryType")) |value| {
if (options.getTruthy(globalThis, "binaryType")) |value| {
if (!value.isString()) {
globalThis.throwInvalidArguments("Expected \"socket.binaryType\" to be a string", .{});
return null;
@@ -180,7 +180,7 @@ pub const UDPSocketConfig = struct {
}
inline for (handlers) |handler| {
if (socket.getOwnTruthyComptime(globalThis, handler.@"0")) |value| {
if (socket.getTruthyComptime(globalThis, handler.@"0")) |value| {
if (!value.isCell() or !value.isCallable(globalThis.vm())) {
globalThis.throwInvalidArguments("Expected \"socket.{s}\" to be a function", .{handler.@"0"});
return null;
@@ -198,13 +198,13 @@ pub const UDPSocketConfig = struct {
}
}
if (options.getOwnTruthy(globalThis, "connect")) |connect| {
if (options.getTruthy(globalThis, "connect")) |connect| {
if (!connect.isObject()) {
globalThis.throwInvalidArguments("Expected \"connect\" to be an object", .{});
return null;
}
const connect_host_js = connect.getOwnTruthy(globalThis, "hostname") orelse {
const connect_host_js = connect.getTruthy(globalThis, "hostname") orelse {
globalThis.throwInvalidArguments("Expected \"connect.hostname\" to be a string", .{});
return null;
};
@@ -214,7 +214,7 @@ pub const UDPSocketConfig = struct {
return null;
}
const connect_port_js = connect.getOwnTruthy(globalThis, "port") orelse {
const connect_port_js = connect.getTruthy(globalThis, "port") orelse {
globalThis.throwInvalidArguments("Expected \"connect.port\" to be an integer", .{});
return null;
};
@@ -631,8 +631,7 @@ pub const UDPSocket = struct {
};
const slice = bun.fmt.formatIp(address, &text_buf) catch unreachable;
var str = bun.String.createLatin1(slice);
return str.transferToJS(globalThis);
return bun.String.createLatin1(slice).toJS(globalThis);
}
pub fn getAddress(this: *This, globalThis: *JSGlobalObject) JSValue {

View File

@@ -627,7 +627,7 @@ pub const FFI = struct {
}
}
const symbols_object = object.getOwn(globalThis, "symbols") orelse .undefined;
const symbols_object = object.get(globalThis, "symbols") orelse .undefined;
if (!globalThis.hasException() and (symbols_object == .zero or !symbols_object.isObject())) {
_ = globalThis.throwInvalidArgumentTypeValue("symbols", "object", symbols_object);
}
@@ -647,7 +647,7 @@ pub const FFI = struct {
return .zero;
}
if (object.getOwn(globalThis, "library")) |library_value| {
if (object.get(globalThis, "library")) |library_value| {
compile_c.libraries = StringArray.fromJS(globalThis, library_value, "library");
}
@@ -655,7 +655,7 @@ pub const FFI = struct {
return .zero;
}
if (object.getOwnTruthy(globalThis, "flags")) |flags_value| {
if (object.getTruthy(globalThis, "flags")) |flags_value| {
if (flags_value.isArray()) {
var iter = flags_value.arrayIterator(globalThis);
@@ -692,7 +692,7 @@ pub const FFI = struct {
return .zero;
}
if (object.getOwnTruthy(globalThis, "define")) |define_value| {
if (object.getTruthy(globalThis, "define")) |define_value| {
if (define_value.isObject()) {
const Iter = JSC.JSPropertyIterator(.{ .include_value = true, .skip_empty_name = true });
var iter = Iter.init(globalThis, define_value);
@@ -722,7 +722,7 @@ pub const FFI = struct {
return .zero;
}
if (object.getOwnTruthy(globalThis, "include")) |include_value| {
if (object.getTruthy(globalThis, "include")) |include_value| {
compile_c.include_dirs = StringArray.fromJS(globalThis, include_value, "include");
}
@@ -730,7 +730,7 @@ pub const FFI = struct {
return .zero;
}
if (object.getOwn(globalThis, "source")) |source_value| {
if (object.get(globalThis, "source")) |source_value| {
if (source_value.isArray()) {
compile_c.source = .{ .files = .{} };
var iter = source_value.arrayIterator(globalThis);
@@ -1301,7 +1301,7 @@ pub const FFI = struct {
var abi_types = std.ArrayListUnmanaged(ABIType){};
if (value.getOwn(global, "args")) |args| {
if (value.get(global, "args")) |args| {
if (args.isEmptyOrUndefinedOrNull() or !args.jsType().isArray()) {
return ZigString.static("Expected an object with \"args\" as an array").toErrorInstance(global);
}
@@ -1347,11 +1347,11 @@ pub const FFI = struct {
var threadsafe = false;
if (value.getOwnTruthy(global, "threadsafe")) |threadsafe_value| {
if (value.getTruthy(global, "threadsafe")) |threadsafe_value| {
threadsafe = threadsafe_value.toBoolean();
}
if (value.getOwnTruthy(global, "returns")) |ret_value| brk: {
if (value.getTruthy(global, "returns")) |ret_value| brk: {
if (ret_value.isAnyInt()) {
const int = ret_value.toInt32();
switch (int) {

View File

@@ -69,7 +69,7 @@ pub const FileSystemRouter = struct {
var asset_prefix_slice: ZigString.Slice = .{};
var out_buf: [bun.MAX_PATH_BYTES * 2]u8 = undefined;
if (argument.getOwn(globalThis, "style")) |style_val| {
if (argument.get(globalThis, "style")) |style_val| {
if (!style_val.getZigString(globalThis).eqlComptime("nextjs")) {
globalThis.throwInvalidArguments("Only 'nextjs' style is currently implemented", .{});
return null;
@@ -79,7 +79,7 @@ pub const FileSystemRouter = struct {
return null;
}
if (argument.getOwn(globalThis, "dir")) |dir| {
if (argument.get(globalThis, "dir")) |dir| {
if (!dir.isString()) {
globalThis.throwInvalidArguments("Expected dir to be a string", .{});
return null;
@@ -104,7 +104,7 @@ pub const FileSystemRouter = struct {
arena.* = bun.ArenaAllocator.init(globalThis.allocator());
const allocator = arena.allocator();
var extensions = std.ArrayList(string).init(allocator);
if (argument.getOwn(globalThis, "fileExtensions")) |file_extensions| {
if (argument.get(globalThis, "fileExtensions")) |file_extensions| {
if (!file_extensions.jsType().isArray()) {
globalThis.throwInvalidArguments("Expected fileExtensions to be an Array", .{});
origin_str.deinit();
@@ -128,7 +128,7 @@ pub const FileSystemRouter = struct {
}
}
if (argument.getOwnTruthy(globalThis, "assetPrefix")) |asset_prefix| {
if (argument.getTruthy(globalThis, "assetPrefix")) |asset_prefix| {
if (!asset_prefix.isString()) {
globalThis.throwInvalidArguments("Expected assetPrefix to be a string", .{});
origin_str.deinit();
@@ -174,7 +174,7 @@ pub const FileSystemRouter = struct {
return null;
};
if (argument.getOwn(globalThis, "origin")) |origin| {
if (argument.get(globalThis, "origin")) |origin| {
if (!origin.isString()) {
globalThis.throwInvalidArguments("Expected origin to be a string", .{});
arena.deinit();

View File

@@ -120,23 +120,23 @@ const ScanOpts = struct {
return null;
}
if (optsObj.getOwnTruthy(globalThis, "onlyFiles")) |only_files| {
if (optsObj.getTruthy(globalThis, "onlyFiles")) |only_files| {
out.only_files = if (only_files.isBoolean()) only_files.asBoolean() else false;
}
if (optsObj.getOwnTruthy(globalThis, "throwErrorOnBrokenSymlink")) |error_on_broken| {
if (optsObj.getTruthy(globalThis, "throwErrorOnBrokenSymlink")) |error_on_broken| {
out.error_on_broken_symlinks = if (error_on_broken.isBoolean()) error_on_broken.asBoolean() else false;
}
if (optsObj.getOwnTruthy(globalThis, "followSymlinks")) |followSymlinksVal| {
if (optsObj.getTruthy(globalThis, "followSymlinks")) |followSymlinksVal| {
out.follow_symlinks = if (followSymlinksVal.isBoolean()) followSymlinksVal.asBoolean() else false;
}
if (optsObj.getOwnTruthy(globalThis, "absolute")) |absoluteVal| {
if (optsObj.getTruthy(globalThis, "absolute")) |absoluteVal| {
out.absolute = if (absoluteVal.isBoolean()) absoluteVal.asBoolean() else false;
}
if (optsObj.getOwnTruthy(globalThis, "cwd")) |cwdVal| {
if (optsObj.getTruthy(globalThis, "cwd")) |cwdVal| {
if (!cwdVal.isString()) {
globalThis.throw("{s}: invalid `cwd`, not a string", .{fnName});
return null;
@@ -152,7 +152,7 @@ const ScanOpts = struct {
}
}
if (optsObj.getOwnTruthy(globalThis, "dot")) |dot| {
if (optsObj.getTruthy(globalThis, "dot")) |dot| {
out.dot = if (dot.isBoolean()) dot.asBoolean() else false;
}

View File

@@ -0,0 +1,811 @@
const bun = @import("root").bun;
const JSC = bun.JSC;
const std = @import("std");
const brotli = bun.brotli;
const Queue = std.fifo.LinearFifo(JSC.Node.BlobOrStringOrBuffer, .Dynamic);
// We cannot free outside the JavaScript thread.
const FreeList = struct {
write_lock: bun.Lock = .{},
list: std.ArrayListUnmanaged(JSC.Node.BlobOrStringOrBuffer) = .{},
pub fn append(this: *FreeList, slice: []const JSC.Node.BlobOrStringOrBuffer) void {
this.write_lock.lock();
defer this.write_lock.unlock();
this.list.appendSlice(bun.default_allocator, slice) catch bun.outOfMemory();
}
pub fn drain(this: *FreeList) void {
this.write_lock.lock();
defer this.write_lock.unlock();
const out = this.list.items;
for (out) |*item| {
item.deinitAndUnprotect();
}
this.list.clearRetainingCapacity();
}
pub fn deinit(this: *FreeList) void {
this.drain();
this.list.deinit(bun.default_allocator);
}
};
pub const BrotliEncoder = struct {
pub usingnamespace bun.New(@This());
pub usingnamespace JSC.Codegen.JSBrotliEncoder;
stream: brotli.BrotliCompressionStream,
chunkSize: c_uint,
maxOutputLength: usize,
freelist: FreeList = .{},
globalThis: *JSC.JSGlobalObject,
mode: u8,
input: Queue = Queue.init(bun.default_allocator),
input_lock: bun.Lock = .{},
has_called_end: bool = false,
callback_value: JSC.Strong = .{},
output: std.ArrayListUnmanaged(u8) = .{},
output_lock: bun.Lock = .{},
has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
pending_encode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
ref_count: u32 = 1,
write_failure: ?JSC.DeferredError = null,
poll_ref: bun.Async.KeepAlive = .{},
closed: bool = false,
pub fn hasPendingActivity(this: *BrotliEncoder) bool {
return this.has_pending_activity.load(.monotonic) > 0;
}
pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*BrotliEncoder {
globalThis.throw("BrotliEncoder is not constructable", .{});
return null;
}
pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
const arguments = callframe.arguments(4).slice();
if (arguments.len < 4) {
globalThis.throwNotEnoughArguments("BrotliEncoder", 4, arguments.len);
return .zero;
}
const opts = arguments[0];
const callback = arguments[2];
const mode = arguments[3].to(u8);
const chunkSize = globalThis.getInteger(opts, u32, 1024 * 48, .{ .min = 64, .field_name = "chunkSize" }) orelse return .zero;
const maxOutputLength = globalThis.getInteger(opts, usize, 0, .{ .max = std.math.maxInt(u52), .field_name = "maxOutputLength" }) orelse return .zero;
const flush = globalThis.getInteger(opts, u8, 0, .{ .max = 3, .field_name = "flush" }) orelse return .zero;
const finishFlush = globalThis.getInteger(opts, u8, 2, .{ .max = 3, .field_name = "finishFlush" }) orelse return .zero;
const fullFlush = globalThis.getInteger(opts, u8, 1, .{ .max = 3, .field_name = "fullFlush" }) orelse return .zero;
var this: *BrotliEncoder = BrotliEncoder.new(.{
.globalThis = globalThis,
.stream = brotli.BrotliCompressionStream.init(@enumFromInt(flush), @enumFromInt(finishFlush), @enumFromInt(fullFlush)) catch {
globalThis.throw("Failed to create BrotliEncoder", .{});
return .zero;
},
.chunkSize = chunkSize,
.maxOutputLength = maxOutputLength,
.mode = mode,
});
if (opts.get(globalThis, "params")) |params| {
inline for (std.meta.fields(bun.brotli.c.BrotliEncoderParameter)) |f| {
if (!params.isObject()) break;
if (params.hasOwnPropertyValue(globalThis, JSC.ZigString.static(std.fmt.comptimePrint("{d}", .{f.value})).toJS(globalThis))) {
const idx = params.getIndex(globalThis, f.value);
if (!idx.isNumber()) {
globalThis.throwValue(globalThis.ERR_INVALID_ARG_TYPE_static(
JSC.ZigString.static("options.params[key]"),
JSC.ZigString.static("number"),
idx,
));
this.deinit();
return .zero;
}
const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32());
if (!was_set) {
globalThis.ERR_ZLIB_INITIALIZATION_FAILED("Initialization failed", .{}).throw();
this.deinit();
return .zero;
}
}
}
}
if (globalThis.hasException()) return .zero;
const out = this.toJS(globalThis);
this.callback_value.set(globalThis, callback);
return out;
}
pub fn finalize(this: *BrotliEncoder) void {
this.deinit();
}
pub fn deinit(this: *BrotliEncoder) void {
this.callback_value.deinit();
this.freelist.deinit();
this.output.deinit(bun.default_allocator);
this.stream.deinit();
this.input.deinit();
this.destroy();
}
fn drainFreelist(this: *BrotliEncoder) void {
this.freelist.drain();
}
fn collectOutputValue(this: *BrotliEncoder) JSC.JSValue {
this.output_lock.lock();
defer this.output_lock.unlock();
defer this.output.clearRetainingCapacity();
return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items);
}
pub fn runFromJSThread(this: *BrotliEncoder) void {
this.poll_ref.unref(this.globalThis.bunVM());
defer _ = this.has_pending_activity.fetchSub(1, .monotonic);
this.drainFreelist();
_ = this.callback_value.get().?.call(
this.globalThis,
.undefined,
if (this.write_failure != null)
&.{this.write_failure.?.toError(this.globalThis)}
else
&.{ .null, this.collectOutputValue() },
) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err);
}
// We can only run one encode job at a time
// But we don't have an idea of a serial dispatch queue
// So instead, we let you enqueue as many times as you want
// and if one is already running, we just don't do anything
const EncodeJob = struct {
task: JSC.WorkPoolTask = .{ .callback = &runTask },
encoder: *BrotliEncoder,
is_async: bool,
vm: *JSC.VirtualMachine,
pub usingnamespace bun.New(@This());
pub fn runTask(this: *JSC.WorkPoolTask) void {
var job: *EncodeJob = @fieldParentPtr("task", this);
job.run();
job.destroy();
}
pub fn run(this: *EncodeJob) void {
defer {
_ = this.encoder.has_pending_activity.fetchSub(1, .monotonic);
}
var any = false;
if (this.encoder.pending_encode_job_count.fetchAdd(1, .monotonic) >= 0) {
const is_last = this.encoder.has_called_end;
while (true) {
this.encoder.input_lock.lock();
defer this.encoder.input_lock.unlock();
const readable = this.encoder.input.readableSlice(0);
defer this.encoder.input.discard(readable.len);
const pending = readable;
const Writer = struct {
encoder: *BrotliEncoder,
pub const Error = error{OutOfMemory};
pub fn writeAll(writer: @This(), chunk: []const u8) Error!void {
writer.encoder.output_lock.lock();
defer writer.encoder.output_lock.unlock();
try writer.encoder.output.appendSlice(bun.default_allocator, chunk);
}
};
defer {
this.encoder.freelist.append(pending);
}
for (pending) |*input| {
var writer = this.encoder.stream.writer(Writer{ .encoder = this.encoder });
writer.writeAll(input.slice()) catch {
_ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic);
if (!this.is_async) {
this.encoder.closed = true;
this.encoder.globalThis.throw("BrotliError", .{});
return;
}
this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error
return;
};
if (this.encoder.output.items.len > this.encoder.maxOutputLength) {
_ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic);
this.encoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.encoder.maxOutputLength});
return;
}
}
any = any or pending.len > 0;
if (this.encoder.pending_encode_job_count.fetchSub(1, .monotonic) == 0)
break;
}
if (is_last and any) {
var output = &this.encoder.output;
this.encoder.output_lock.lock();
defer this.encoder.output_lock.unlock();
output.appendSlice(bun.default_allocator, this.encoder.stream.end() catch {
_ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic);
this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error
return;
}) catch {
_ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic);
this.encoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error
return;
};
if (output.items.len > this.encoder.maxOutputLength) {
_ = this.encoder.pending_encode_job_count.fetchSub(1, .monotonic);
this.encoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.encoder.maxOutputLength});
return;
}
}
}
if (this.is_async and any) {
_ = this.encoder.has_pending_activity.fetchAdd(1, .monotonic);
this.vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.encoder)));
}
}
};
pub fn transform(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
const arguments = callframe.arguments(3);
if (arguments.len < 3) {
globalThis.throwNotEnoughArguments("BrotliEncoder.encode", 3, arguments.len);
return .zero;
}
if (this.has_called_end) {
globalThis.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{});
return .zero;
}
const input = callframe.argument(0);
const optional_encoding = callframe.argument(1);
const is_last = callframe.argument(2).toBoolean();
const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse {
globalThis.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer");
return .zero;
};
_ = this.has_pending_activity.fetchAdd(1, .monotonic);
if (is_last)
this.has_called_end = true;
var task = EncodeJob.new(.{
.encoder = this,
.is_async = true,
.vm = this.globalThis.bunVM(),
});
{
this.input_lock.lock();
defer this.input_lock.unlock();
// need to protect because no longer on the stack. unprotected in FreeList.deinit
input_to_queue.protect();
this.input.writeItem(input_to_queue) catch bun.outOfMemory();
}
this.poll_ref.ref(task.vm);
JSC.WorkPool.schedule(&task.task);
return .undefined;
}
pub fn transformSync(this: *BrotliEncoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
const arguments = callframe.arguments(4);
if (arguments.len < 3) {
globalThis.throwNotEnoughArguments("BrotliEncoder.encode", 3, arguments.len);
return .zero;
}
if (this.has_called_end) {
globalThis.throw("BrotliEncoder.encode called after BrotliEncoder.end", .{});
return .zero;
}
const input = callframe.argument(0);
const optional_encoding = callframe.argument(1);
const is_last = callframe.argument(2).toBoolean();
const optional_flushFlag = arguments.ptr[3];
const old_flushFlag = this.stream.flushOp;
defer this.stream.flushOp = old_flushFlag;
blk: {
if (!optional_flushFlag.isInt32()) break :blk;
const int = optional_flushFlag.asInt32();
if (int < 0) break :blk;
if (int > 3) break :blk;
this.stream.flushOp = @enumFromInt(int);
}
const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse {
globalThis.throwInvalidArgumentType("BrotliEncoder.encode", "input", "Blob, String, or Buffer");
return .zero;
};
_ = this.has_pending_activity.fetchAdd(1, .monotonic);
if (is_last)
this.has_called_end = true;
var task: EncodeJob = .{
.encoder = this,
.is_async = false,
.vm = this.globalThis.bunVM(),
};
{
this.input_lock.lock();
defer this.input_lock.unlock();
// need to protect because no longer on the stack. unprotected in FreeList.deinit
input_to_queue.protect();
this.input.writeItem(input_to_queue) catch bun.outOfMemory();
}
task.run();
if (!is_last and this.output.items.len == 0) {
return JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis);
}
if (this.write_failure != null) {
globalThis.vm().throwError(globalThis, this.write_failure.?.toError(globalThis));
return .zero;
}
return this.collectOutputValue();
}
pub fn reset(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
_ = this;
_ = globalThis;
_ = callframe;
return .undefined;
}
pub fn getBytesWritten(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.total_in);
}
pub fn getClosed(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsBoolean(this.closed);
}
pub fn close(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
_ = this;
_ = globalThis;
_ = callframe;
return .undefined;
}
pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.chunkSize);
}
pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.flushOp);
}
pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.finishFlushOp);
}
pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.fullFlushOp);
}
pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.maxOutputLength);
}
};
pub const BrotliDecoder = struct {
pub usingnamespace bun.New(@This());
pub usingnamespace JSC.Codegen.JSBrotliDecoder;
globalThis: *JSC.JSGlobalObject,
stream: brotli.BrotliReaderArrayList,
chunkSize: c_uint,
maxOutputLength: usize,
mode: u8,
has_pending_activity: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
ref_count: u32 = 1,
poll_ref: bun.Async.KeepAlive = .{},
write_failure: ?JSC.DeferredError = null,
callback_value: JSC.Strong = .{},
has_called_end: bool = false,
pending_decode_job_count: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
closed: bool = false,
input: Queue = Queue.init(bun.default_allocator),
input_lock: bun.Lock = .{},
output: std.ArrayListUnmanaged(u8) = .{},
output_lock: bun.Lock = .{},
freelist: FreeList = .{},
pub fn hasPendingActivity(this: *BrotliDecoder) bool {
return this.has_pending_activity.load(.monotonic) > 0;
}
pub fn deinit(this: *BrotliDecoder) void {
this.callback_value.deinit();
this.freelist.deinit();
this.output.deinit(bun.default_allocator);
this.stream.brotli.destroyInstance();
this.input.deinit();
this.destroy();
}
pub fn constructor(globalThis: *JSC.JSGlobalObject, _: *JSC.CallFrame) ?*BrotliDecoder {
globalThis.throw("BrotliDecoder is not constructable", .{});
return null;
}
pub fn create(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
const arguments = callframe.arguments(4).slice();
if (arguments.len < 4) {
globalThis.throwNotEnoughArguments("BrotliDecoder", 4, arguments.len);
return .zero;
}
const opts = arguments[0];
const callback = arguments[2];
const mode = arguments[3].to(u8);
const chunkSize = globalThis.getInteger(opts, u32, 1024 * 48, .{ .min = 64, .field_name = "chunkSize" }) orelse return .zero;
const maxOutputLength = globalThis.getInteger(opts, usize, 0, .{ .max = std.math.maxInt(u52), .field_name = "maxOutputLength" }) orelse return .zero;
const flush = globalThis.getInteger(opts, u8, 0, .{ .max = 6, .field_name = "flush" }) orelse return .zero;
const finishFlush = globalThis.getInteger(opts, u8, 2, .{ .max = 6, .field_name = "finishFlush" }) orelse return .zero;
const fullFlush = globalThis.getInteger(opts, u8, 1, .{ .max = 6, .field_name = "fullFlush" }) orelse return .zero;
var this: *BrotliDecoder = BrotliDecoder.new(.{
.globalThis = globalThis,
.stream = undefined, // &this.output needs to be a stable pointer
.chunkSize = chunkSize,
.maxOutputLength = maxOutputLength,
.mode = mode,
});
this.stream = brotli.BrotliReaderArrayList.initWithOptions("", &this.output, bun.default_allocator, .{}, @enumFromInt(flush), @enumFromInt(finishFlush), @enumFromInt(fullFlush)) catch {
globalThis.throw("Failed to create BrotliDecoder", .{});
return .zero;
};
if (opts.get(globalThis, "params")) |params| {
inline for (std.meta.fields(bun.brotli.c.BrotliDecoderParameter)) |f| {
if (!params.isObject()) break;
const idx = params.getIndex(globalThis, f.value);
if (!idx.isNumber()) break;
const was_set = this.stream.brotli.setParameter(@enumFromInt(f.value), idx.toU32());
if (!was_set) {
globalThis.ERR_ZLIB_INITIALIZATION_FAILED("Initialization failed", .{}).throw();
this.deinit();
return .zero;
}
}
}
if (globalThis.hasException()) return .zero;
const out = this.toJS(globalThis);
this.callback_value.set(globalThis, callback);
return out;
}
pub fn finalize(this: *BrotliDecoder) void {
this.deinit();
}
fn collectOutputValue(this: *BrotliDecoder) JSC.JSValue {
this.output_lock.lock();
defer this.output_lock.unlock();
defer this.output.clearRetainingCapacity();
return JSC.ArrayBuffer.createBuffer(this.globalThis, this.output.items);
}
pub fn runFromJSThread(this: *BrotliDecoder) void {
this.poll_ref.unref(this.globalThis.bunVM());
defer _ = this.has_pending_activity.fetchSub(1, .monotonic);
this.drainFreelist();
_ = this.callback_value.get().?.call(
this.globalThis,
.undefined,
if (this.write_failure != null)
&.{this.write_failure.?.toError(this.globalThis)}
else
&.{ .null, this.collectOutputValue() },
) catch |err| this.globalThis.reportActiveExceptionAsUnhandled(err);
}
fn drainFreelist(this: *BrotliDecoder) void {
this.freelist.drain();
}
pub fn transform(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
const arguments = callframe.arguments(3);
if (arguments.len < 3) {
globalThis.throwNotEnoughArguments("BrotliEncoder.decode", 3, arguments.len);
return .zero;
}
if (this.has_called_end) {
globalThis.throw("BrotliEncoder.decode called after BrotliEncoder.end", .{});
return .zero;
}
const input = callframe.argument(0);
const optional_encoding = callframe.argument(1);
const is_last = callframe.argument(2).toBoolean();
const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse {
globalThis.throwInvalidArgumentType("BrotliEncoder.decode", "input", "Blob, String, or Buffer");
return .zero;
};
_ = this.has_pending_activity.fetchAdd(1, .monotonic);
if (is_last)
this.has_called_end = true;
var task = DecodeJob.new(.{
.decoder = this,
.is_async = true,
.vm = this.globalThis.bunVM(),
});
{
this.input_lock.lock();
defer this.input_lock.unlock();
// need to protect because no longer on the stack. unprotected in FreeList.deinit
input_to_queue.protect();
this.input.writeItem(input_to_queue) catch bun.outOfMemory();
}
this.poll_ref.ref(task.vm);
JSC.WorkPool.schedule(&task.task);
return .undefined;
}
pub fn transformSync(this: *BrotliDecoder, globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
const arguments = callframe.arguments(4);
if (arguments.len < 3) {
globalThis.throwNotEnoughArguments("BrotliEncoder.decode", 3, arguments.len);
return .zero;
}
if (this.has_called_end) {
globalThis.throw("BrotliEncoder.decode called after BrotliEncoder.end", .{});
return .zero;
}
const input = callframe.argument(0);
const optional_encoding = callframe.argument(1);
const is_last = callframe.argument(2).toBoolean();
// const optional_flushFlag = arguments.ptr[3];
// const old_flushFlag = this.stream.flushOp;
// defer this.stream.flushOp = old_flushFlag;
// blk: {
// if (!optional_flushFlag.isInt32()) break :blk;
// const int = optional_flushFlag.asInt32();
// if (int < 0) break :blk;
// if (int > 3) break :blk;
// this.stream.flushOp = @enumFromInt(int);
// }
const input_to_queue = JSC.Node.BlobOrStringOrBuffer.fromJSWithEncodingValueMaybeAsync(globalThis, bun.default_allocator, input, optional_encoding, true) orelse {
globalThis.throwInvalidArgumentType("BrotliEncoder.decode", "input", "Blob, String, or Buffer");
return .zero;
};
_ = this.has_pending_activity.fetchAdd(1, .monotonic);
if (is_last)
this.has_called_end = true;
var task: DecodeJob = .{
.decoder = this,
.is_async = false,
.vm = this.globalThis.bunVM(),
};
{
this.input_lock.lock();
defer this.input_lock.unlock();
// need to protect because no longer on the stack. unprotected in FreeList.deinit
input_to_queue.protect();
this.input.writeItem(input_to_queue) catch bun.outOfMemory();
}
task.run();
if (!is_last and this.output.items.len == 0) {
return JSC.Buffer.fromBytes(&.{}, bun.default_allocator, .Uint8Array).toNodeBuffer(globalThis);
}
if (this.write_failure != null) {
globalThis.throwValue(this.write_failure.?.toError(globalThis));
return .zero;
}
return this.collectOutputValue();
}
// We can only run one decode job at a time
// But we don't have an idea of a serial dispatch queue
// So instead, we let you enqueue as many times as you want
// and if one is already running, we just don't do anything
const DecodeJob = struct {
task: JSC.WorkPoolTask = .{ .callback = &runTask },
decoder: *BrotliDecoder,
is_async: bool,
vm: *JSC.VirtualMachine,
pub usingnamespace bun.New(@This());
pub fn runTask(this: *JSC.WorkPoolTask) void {
var job: *DecodeJob = @fieldParentPtr("task", this);
job.run();
job.destroy();
}
pub fn run(this: *DecodeJob) void {
defer {
_ = this.decoder.has_pending_activity.fetchSub(1, .monotonic);
}
var any = false;
if (this.decoder.pending_decode_job_count.fetchAdd(1, .monotonic) >= 0) {
const is_last = this.decoder.has_called_end;
while (true) {
this.decoder.input_lock.lock();
defer this.decoder.input_lock.unlock();
if (!is_last) break;
const pending = this.decoder.input.readableSlice(0);
defer {
this.decoder.freelist.append(pending);
}
var input_list = std.ArrayListUnmanaged(u8){};
defer input_list.deinit(bun.default_allocator);
if (pending.len > 1) {
var count: usize = 0;
for (pending) |input| {
count += input.slice().len;
}
input_list.ensureTotalCapacityPrecise(bun.default_allocator, count) catch bun.outOfMemory();
for (pending) |*input| {
input_list.appendSliceAssumeCapacity(input.slice());
}
}
{
this.decoder.output_lock.lock();
defer this.decoder.output_lock.unlock();
const input = if (pending.len <= 1) pending[0].slice() else input_list.items;
this.decoder.stream.input = input;
this.decoder.stream.readAll(false) catch {
any = true;
_ = this.decoder.pending_decode_job_count.fetchSub(1, .monotonic);
if (!this.is_async) {
this.decoder.closed = true;
this.decoder.globalThis.throw("BrotliError", .{});
return;
}
this.decoder.write_failure = JSC.DeferredError.from(.plainerror, .ERR_OPERATION_FAILED, "BrotliError", .{}); // TODO propogate better error
break;
};
if (this.decoder.output.items.len > this.decoder.maxOutputLength) {
any = true;
_ = this.decoder.pending_decode_job_count.fetchSub(1, .monotonic);
this.decoder.write_failure = JSC.DeferredError.from(.rangeerror, .ERR_BUFFER_TOO_LARGE, "Cannot create a Buffer larger than {d} bytes", .{this.decoder.maxOutputLength});
break;
}
}
any = any or pending.len > 0;
if (this.decoder.pending_decode_job_count.fetchSub(1, .monotonic) == 0)
break;
}
}
if (this.is_async and any) {
_ = this.decoder.has_pending_activity.fetchAdd(1, .monotonic);
this.vm.enqueueTaskConcurrent(JSC.ConcurrentTask.create(JSC.Task.init(this.decoder)));
}
}
};
pub fn reset(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) JSC.JSValue {
_ = this;
_ = globalThis;
_ = callframe;
return .undefined;
}
pub fn getBytesWritten(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.total_in);
}
pub fn getClosed(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsBoolean(this.closed);
}
pub fn close(this: *@This(), globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
_ = this;
_ = globalThis;
_ = callframe;
return .undefined;
}
pub fn getChunkSize(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.chunkSize);
}
pub fn getFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.flushOp);
}
pub fn getFinishFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.finishFlushOp);
}
pub fn getFullFlush(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.stream.fullFlushOp);
}
pub fn getMaxOutputLength(this: *@This(), globalObject: *JSC.JSGlobalObject) JSC.JSValue {
_ = globalObject;
return JSC.JSValue.jsNumber(this.maxOutputLength);
}
};

1014
src/bun.js/api/js_zlib.zig Normal file

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More