diff --git a/.github/actions/build-native-deps/action.yml b/.github/actions/build-native-deps/action.yml new file mode 100644 index 0000000..47a6eb1 --- /dev/null +++ b/.github/actions/build-native-deps/action.yml @@ -0,0 +1,156 @@ +name: Build Native Dependencies +description: Build llhttp, libuv, libsodium, mbedtls, zlib as needed + +inputs: + deps: + description: Space-separated list of deps to build (llhttp libuv libsodium mbedtls zlib) + required: true + prefix: + description: Install prefix + required: true + cc: + description: C compiler + required: true + cxx: + description: C++ compiler + required: false + default: '' + ar: + description: AR tool + required: false + default: ar + ranlib: + description: RANLIB tool + required: false + default: ranlib + cmake_generator: + description: CMake generator (Ninja, MinGW Makefiles, etc) + required: false + default: '' + llhttp_version: + required: true + libuv_version: + required: true + libsodium_version: + required: true + mbedtls_version: + required: true + zlib_version: + required: true + +runs: + using: composite + steps: + - name: Compute deps hash + id: deps-hash + shell: bash + run: echo "hash=$(echo '${{ inputs.deps }}' | tr ' ' '-')" >> $GITHUB_OUTPUT + + - name: Cache native deps + id: cache + uses: actions/cache@v4 + with: + path: ${{ inputs.prefix }} + key: deps-${{ runner.os }}-${{ runner.arch }}-${{ steps.deps-hash.outputs.hash }}-${{ hashFiles('.github/versions.json') }}-v1 + + - name: Prepare build environment + if: steps.cache.outputs.cache-hit != 'true' + shell: bash + run: | + mkdir -p ${{ inputs.prefix }} + + - name: Build llhttp + if: steps.cache.outputs.cache-hit != 'true' && contains(inputs.deps, 'llhttp') + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + export PATH="$(cygpath "$RUNNER_TEMP/msys64/mingw64/bin"):$PATH" + PREFIX=$(cygpath -m "${{ inputs.prefix }}") + else + PREFIX="${{ inputs.prefix }}" + fi + + git clone --depth 1 --branch release/v${{ inputs.llhttp_version }} https://github.com/nodejs/llhttp.git /tmp/llhttp + cd /tmp/llhttp + CMAKE_GENERATOR="${{ inputs.cmake_generator }}" + if [[ "$RUNNER_OS" != "macOS" ]]; then + export AR="${{ inputs.ar }}" + export RANLIB="${{ inputs.ranlib }}" + fi + CC=${{ inputs.cc }} CXX=${{ inputs.cxx || inputs.cc }} cmake -B build \ + ${CMAKE_GENERATOR:+-G "$CMAKE_GENERATOR"} \ + -DCMAKE_INSTALL_PREFIX="$PREFIX" \ + -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON \ + -DCMAKE_BUILD_TYPE=MinSizeRel + cmake --build build + cmake --install build + echo "=== PKG_CONFIG files in $PREFIX/lib/pkgconfig ===" + ls -la "$PREFIX/lib/pkgconfig/" || echo "pkgconfig dir not found" + cp "$PREFIX/lib/pkgconfig/libllhttp.pc" "$PREFIX/lib/pkgconfig/llhttp.pc" + echo "=== llhttp.pc contents ===" + cat "$PREFIX/lib/pkgconfig/llhttp.pc" + echo "=== After copy ===" + ls -la "$PREFIX/lib/pkgconfig/" + + - name: Build libuv + if: steps.cache.outputs.cache-hit != 'true' && contains(inputs.deps, 'libuv') + shell: bash + run: | + git clone --depth 1 --branch v${{ inputs.libuv_version }} https://github.com/libuv/libuv.git /tmp/libuv + cd /tmp/libuv + CMAKE_GENERATOR="${{ inputs.cmake_generator }}" + if [[ "$RUNNER_OS" != "macOS" ]]; then + export AR="${{ inputs.ar }}" + export RANLIB="${{ inputs.ranlib }}" + fi + CC=${{ inputs.cc }} cmake -B build \ + ${CMAKE_GENERATOR:+-G "$CMAKE_GENERATOR"} \ + -DCMAKE_INSTALL_PREFIX=${{ inputs.prefix }} \ + -DBUILD_TESTING=OFF -DLIBUV_BUILD_SHARED=OFF \ + -DCMAKE_BUILD_TYPE=MinSizeRel + cmake --build build + cmake --install build + + - name: Build libsodium + if: steps.cache.outputs.cache-hit != 'true' && contains(inputs.deps, 'libsodium') + shell: bash + run: | + git clone --depth 1 --branch ${{ inputs.libsodium_version }}-RELEASE https://github.com/jedisct1/libsodium.git /tmp/libsodium + cd /tmp/libsodium + ./configure CC=${{ inputs.cc }} --prefix=${{ inputs.prefix }} --disable-shared --enable-static + make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) + make install + + - name: Build mbedtls + if: steps.cache.outputs.cache-hit != 'true' && contains(inputs.deps, 'mbedtls') + shell: bash + run: | + git clone --depth 1 --branch mbedtls-${{ inputs.mbedtls_version }} --recurse-submodules https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls + cd /tmp/mbedtls + CC=${{ inputs.cc }} cmake -B build \ + -DCMAKE_INSTALL_PREFIX=${{ inputs.prefix }} \ + -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF \ + -DCMAKE_BUILD_TYPE=Release \ + -DUSE_STATIC_MBEDTLS_LIBRARY=ON -DUSE_SHARED_MBEDTLS_LIBRARY=OFF + cmake --build build -j$(nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4) + cmake --install build + + - name: Build zlib + if: steps.cache.outputs.cache-hit != 'true' && contains(inputs.deps, 'zlib') + shell: bash + run: | + git clone --depth 1 --branch v${{ inputs.zlib_version }} https://github.com/madler/zlib.git /tmp/zlib + cd /tmp/zlib + CMAKE_GENERATOR="${{ inputs.cmake_generator }}" + if [[ "$RUNNER_OS" != "macOS" ]]; then + export AR="${{ inputs.ar }}" + export RANLIB="${{ inputs.ranlib }}" + fi + CC=${{ inputs.cc }} cmake -B build \ + ${CMAKE_GENERATOR:+-G "$CMAKE_GENERATOR"} \ + -DCMAKE_INSTALL_PREFIX=${{ inputs.prefix }} \ + -DBUILD_SHARED_LIBS=OFF \ + -DCMAKE_BUILD_TYPE=MinSizeRel + cmake --build build + cmake --install build + rm -f ${{ inputs.prefix }}/lib/libz.so* 2>/dev/null || true diff --git a/.github/actions/build-project/action.yml b/.github/actions/build-project/action.yml new file mode 100644 index 0000000..60f74c7 --- /dev/null +++ b/.github/actions/build-project/action.yml @@ -0,0 +1,161 @@ +name: Build Project +description: Run meson build with appropriate settings + +outputs: + version: + description: Build version string + value: ${{ steps.version.outputs.version }} + +inputs: + cc: + description: C compiler + required: true + cxx: + description: C++ compiler + required: false + default: '' + ar: + description: AR tool + required: false + default: '' + ranlib: + description: RANLIB tool + required: false + default: '' + ld: + description: Linker + required: false + default: '' + strip: + description: Strip tool + required: false + default: strip + deps_prefix: + description: Prefix where deps are installed + required: true + build_timestamp: + description: Build timestamp + required: true + tls_library: + description: TLS library (openssl or mbedtls) + required: false + default: 'openssl' + static_link: + description: Enable static linking + required: false + default: 'false' + extra_meson_args: + description: Additional meson arguments + required: false + default: '' + binary_name: + description: Output binary name (ant or ant.exe) + required: false + default: 'ant' + +runs: + using: composite + steps: + - name: npm install + shell: bash + run: npm ci + working-directory: src/tools + + - name: Cache vendor deps + uses: actions/cache@v4 + with: + path: | + vendor/*/ + build/vendor + key: vendor-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('vendor/*.wrap') }} + + - name: Configure and build + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + MSYS2_PATH=$(cygpath "$RUNNER_TEMP/msys64/mingw64/bin") + export PATH="$MSYS2_PATH:$PATH" + DEPS_PREFIX=$(cygpath -m "${{ inputs.deps_prefix }}") + export PKG_CONFIG_PATH="$DEPS_PREFIX/lib/pkgconfig;$PKG_CONFIG_PATH" + export CMAKE_PREFIX_PATH="$DEPS_PREFIX;$CMAKE_PREFIX_PATH" + else + DEPS_PREFIX="${{ inputs.deps_prefix }}" + export PKG_CONFIG_PATH="$DEPS_PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH" + export CMAKE_PREFIX_PATH="$DEPS_PREFIX:$CMAKE_PREFIX_PATH" + fi + export CC="${{ inputs.cc }}" + + echo "=== Debug: PKG_CONFIG_PATH ===" + echo "PKG_CONFIG_PATH=$PKG_CONFIG_PATH" + echo "=== Debug: Contents of $DEPS_PREFIX/lib/pkgconfig ===" + ls -la "$DEPS_PREFIX/lib/pkgconfig/" 2>/dev/null || echo "Directory not found" + echo "=== Debug: pkg-config --variable pc_path pkg-config ===" + pkg-config --variable pc_path pkg-config || true + echo "=== Debug: pkg-config --modversion llhttp ===" + pkg-config --modversion llhttp || echo "llhttp not found by pkg-config" + + if [[ -n "${{ inputs.cxx }}" ]]; then + export CXX="${{ inputs.cxx }}" + fi + if [[ -n "${{ inputs.ar }}" ]]; then + export AR="${{ inputs.ar }}" + export CMAKE_AR="${{ inputs.ar }}" + fi + if [[ -n "${{ inputs.ranlib }}" ]]; then + export RANLIB="${{ inputs.ranlib }}" + export CMAKE_RANLIB="${{ inputs.ranlib }}" + fi + if [[ -n "${{ inputs.ld }}" ]]; then + export CC_LD="${{ inputs.ld }}" + fi + + MESON_ARGS="-Db_lto=true --buildtype=release" + MESON_ARGS="$MESON_ARGS -Dbuild_timestamp=${{ inputs.build_timestamp }}" + + if [[ "${{ inputs.tls_library }}" == "mbedtls" ]]; then + MESON_ARGS="$MESON_ARGS -Dtls_library=mbedtls" + fi + + if [[ "${{ inputs.static_link }}" == "true" ]]; then + MESON_ARGS="$MESON_ARGS --prefer-static -Dstatic_link=true" + fi + + if [[ -n "${{ inputs.extra_meson_args }}" ]]; then + MESON_ARGS="$MESON_ARGS ${{ inputs.extra_meson_args }}" + fi + + echo "Running: meson setup build $MESON_ARGS" + meson setup build $MESON_ARGS + meson compile -C build + + - name: Verify build + id: version + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + MSYS2_PATH=$(cygpath "$RUNNER_TEMP/msys64/mingw64/bin") + export PATH="$MSYS2_PATH:$PATH" + fi + VERSION=$(./build/${{ inputs.binary_name }} --version-raw) + echo "Version: $VERSION" + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Strip binary + shell: bash + run: | + if [[ "$RUNNER_OS" == "Windows" ]]; then + MSYS2_PATH=$(cygpath "$RUNNER_TEMP/msys64/mingw64/bin") + export PATH="$MSYS2_PATH:$PATH" + fi + ${{ inputs.strip }} build/${{ inputs.binary_name }} + + - name: Bundle Windows DLLs + if: runner.os == 'Windows' + shell: bash + run: | + MINGW_BIN=$(cygpath "$RUNNER_TEMP/msys64/mingw64/bin") + cp "$MINGW_BIN/libssl-3-x64.dll" build/ + cp "$MINGW_BIN/libcrypto-3-x64.dll" build/ + cp "$MINGW_BIN/libsodium-26.dll" build/ + echo "Bundled DLLs:" + ls -la build/*.dll diff --git a/.github/actions/setup-llvm/action.yml b/.github/actions/setup-llvm/action.yml new file mode 100644 index 0000000..bc7e5a1 --- /dev/null +++ b/.github/actions/setup-llvm/action.yml @@ -0,0 +1,88 @@ +name: Setup LLVM +description: Install LLVM/Clang toolchain + +inputs: + version: + description: LLVM major version + required: true + os_type: + description: Target OS type (linux-glibc, linux-musl, macos, windows) + required: true + +outputs: + cc: + description: C compiler path + value: ${{ steps.paths.outputs.cc }} + cxx: + description: C++ compiler path + value: ${{ steps.paths.outputs.cxx }} + ar: + description: AR path + value: ${{ steps.paths.outputs.ar }} + ranlib: + description: RANLIB path + value: ${{ steps.paths.outputs.ranlib }} + strip: + description: Strip path + value: ${{ steps.paths.outputs.strip }} + ld: + description: Linker path + value: ${{ steps.paths.outputs.ld }} + +runs: + using: composite + steps: + - name: Install LLVM (Linux glibc) + if: inputs.os_type == 'linux-glibc' + shell: bash + env: + DEBIAN_FRONTEND: noninteractive + run: | + wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc + echo "deb https://apt.llvm.org/jammy/ llvm-toolchain-jammy-${{ inputs.version }} main" > /etc/apt/sources.list.d/llvm.list + apt-get update + apt-get install -y clang-${{ inputs.version }} lld-${{ inputs.version }} llvm-${{ inputs.version }} + + # linux-musl: LLVM installed via apk in workflow setup step + # macos: LLVM installed via brew in workflow setup step + # windows: Using MinGW toolchain + + - name: Set output paths + id: paths + shell: bash + run: | + case "${{ inputs.os_type }}" in + linux-glibc) + echo "cc=clang-${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "cxx=clang++-${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "ar=llvm-ar-${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "ranlib=llvm-ranlib-${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "strip=llvm-strip-${{ inputs.version }}" >> $GITHUB_OUTPUT + echo "ld=lld-${{ inputs.version }}" >> $GITHUB_OUTPUT + ;; + linux-musl) + echo "cc=clang" >> $GITHUB_OUTPUT + echo "cxx=clang++" >> $GITHUB_OUTPUT + echo "ar=llvm-ar" >> $GITHUB_OUTPUT + echo "ranlib=llvm-ranlib" >> $GITHUB_OUTPUT + echo "strip=llvm-strip" >> $GITHUB_OUTPUT + echo "ld=lld" >> $GITHUB_OUTPUT + ;; + macos) + LLVM_PREFIX=$(brew --prefix llvm) + echo "cc=$LLVM_PREFIX/bin/clang" >> $GITHUB_OUTPUT + echo "cxx=$LLVM_PREFIX/bin/clang++" >> $GITHUB_OUTPUT + echo "ar=$LLVM_PREFIX/bin/llvm-ar" >> $GITHUB_OUTPUT + echo "ranlib=$LLVM_PREFIX/bin/llvm-ranlib" >> $GITHUB_OUTPUT + echo "strip=strip" >> $GITHUB_OUTPUT + echo "ld=" >> $GITHUB_OUTPUT + ;; + windows) + echo "cc=gcc" >> $GITHUB_OUTPUT + echo "cxx=g++" >> $GITHUB_OUTPUT + echo "ar=ar" >> $GITHUB_OUTPUT + echo "ranlib=ranlib" >> $GITHUB_OUTPUT + echo "strip=strip" >> $GITHUB_OUTPUT + echo "ld=" >> $GITHUB_OUTPUT + ;; + esac diff --git a/.github/actions/setup-zig/action.yml b/.github/actions/setup-zig/action.yml new file mode 100644 index 0000000..f8c7180 --- /dev/null +++ b/.github/actions/setup-zig/action.yml @@ -0,0 +1,54 @@ +name: Setup Zig +description: Install Zig compiler + +inputs: + version: + description: Zig version + required: true + +runs: + using: composite + steps: + - name: Determine platform + id: platform + shell: bash + run: | + case "${{ runner.os }}-${{ runner.arch }}" in + Linux-X64) echo "slug=x86_64-linux" >> $GITHUB_OUTPUT; echo "ext=tar.xz" >> $GITHUB_OUTPUT ;; + Linux-ARM64) echo "slug=aarch64-linux" >> $GITHUB_OUTPUT; echo "ext=tar.xz" >> $GITHUB_OUTPUT ;; + macOS-X64) echo "slug=x86_64-macos" >> $GITHUB_OUTPUT; echo "ext=tar.xz" >> $GITHUB_OUTPUT ;; + macOS-ARM64) echo "slug=aarch64-macos" >> $GITHUB_OUTPUT; echo "ext=tar.xz" >> $GITHUB_OUTPUT ;; + Windows-X64) echo "slug=x86_64-windows" >> $GITHUB_OUTPUT; echo "ext=zip" >> $GITHUB_OUTPUT ;; + *) echo "Unsupported platform: ${{ runner.os }}-${{ runner.arch }}" >&2; exit 1 ;; + esac + echo "install_path=$RUNNER_TEMP/zig" >> $GITHUB_OUTPUT + + - name: Cache Zig + id: cache + uses: actions/cache@v4 + with: + path: ${{ steps.platform.outputs.install_path }} + key: zig-${{ steps.platform.outputs.slug }}-${{ inputs.version }} + + - name: Download and install Zig + if: steps.cache.outputs.cache-hit != 'true' + shell: bash + run: | + cd "$RUNNER_TEMP" + URL="https://ziglang.org/download/${{ inputs.version }}/zig-${{ steps.platform.outputs.slug }}-${{ inputs.version }}.${{ steps.platform.outputs.ext }}" + echo "Downloading: $URL" + curl -fSL -o zig.${{ steps.platform.outputs.ext }} "$URL" + ls -la zig.${{ steps.platform.outputs.ext }} + if [[ "${{ steps.platform.outputs.ext }}" == "zip" ]]; then + unzip -q zig.${{ steps.platform.outputs.ext }} + else + tar -xJf zig.${{ steps.platform.outputs.ext }} + fi + + INSTALL_PATH="${{ steps.platform.outputs.install_path }}" + mkdir -p "$(dirname "$INSTALL_PATH")" + mv zig-${{ steps.platform.outputs.slug }}-${{ inputs.version }} "$INSTALL_PATH" + + - name: Add to PATH + shell: bash + run: echo "${{ steps.platform.outputs.install_path }}" >> $GITHUB_PATH diff --git a/.github/versions.json b/.github/versions.json new file mode 100644 index 0000000..bfd700c --- /dev/null +++ b/.github/versions.json @@ -0,0 +1,133 @@ +{ + "tools": { + "zig": "0.15.2", + "llvm": "21", + "node": "22" + }, + "dependencies": { + "llhttp": "9.2.1", + "libuv": "1.51.0", + "libsodium": "1.0.20", + "mbedtls": "3.6.5", + "zlib": "1.3.1" + }, + "targets": { + "linux-glibc-x64": { + "os_type": "linux-glibc", + "arch": "x86_64", + "zig_target": "x86_64-linux-gnu", + "rust_target": "x86_64-unknown-linux-gnu", + "artifact_name": "ant-linux-x64", + "binary_name": "ant", + "deps": ["llhttp", "libuv"], + "tls_library": "openssl", + "static_link": false, + "cmake_generator": null, + "extra_meson_args": null + }, + "linux-glibc-aarch64": { + "os_type": "linux-glibc", + "arch": "aarch64", + "zig_target": "aarch64-linux-gnu", + "rust_target": "aarch64-unknown-linux-gnu", + "artifact_name": "ant-linux-aarch64", + "binary_name": "ant", + "deps": ["llhttp", "libuv"], + "tls_library": "openssl", + "static_link": false, + "cmake_generator": null, + "extra_meson_args": null + }, + "linux-musl-x64": { + "os_type": "linux-musl", + "arch": "x86_64", + "zig_target": "x86_64-linux-musl", + "rust_target": "x86_64-unknown-linux-musl", + "artifact_name": "ant-linux-x64-musl", + "binary_name": "ant", + "deps": ["llhttp", "libuv", "zlib"], + "tls_library": "openssl", + "static_link": true, + "cmake_generator": "Ninja", + "extra_meson_args": null + }, + "linux-musl-aarch64": { + "os_type": "linux-musl", + "arch": "aarch64", + "zig_target": "aarch64-linux-musl", + "rust_target": "aarch64-unknown-linux-musl", + "artifact_name": "ant-linux-aarch64-musl", + "binary_name": "ant", + "deps": ["llhttp", "libuv", "zlib"], + "tls_library": "openssl", + "static_link": true, + "cmake_generator": "Ninja", + "extra_meson_args": null + }, + "macos-x64": { + "os_type": "macos", + "arch": "x86_64", + "zig_target": "x86_64-macos", + "rust_target": "x86_64-apple-darwin", + "artifact_name": "ant-darwin-x64", + "binary_name": "ant", + "deps": ["llhttp", "libsodium"], + "tls_library": "openssl", + "static_link": false, + "cmake_generator": null, + "extra_meson_args": null + }, + "macos-aarch64": { + "os_type": "macos", + "arch": "aarch64", + "zig_target": "aarch64-macos", + "rust_target": "aarch64-apple-darwin", + "artifact_name": "ant-darwin-aarch64", + "binary_name": "ant", + "deps": ["llhttp", "libsodium"], + "tls_library": "openssl", + "static_link": false, + "cmake_generator": null, + "extra_meson_args": null + }, + "macos-x64-mbedtls": { + "os_type": "macos", + "arch": "x86_64", + "zig_target": "x86_64-macos", + "rust_target": "x86_64-apple-darwin", + "artifact_name": "ant-darwin-x64-mbedtls", + "binary_name": "ant", + "deps": ["llhttp", "libsodium", "mbedtls"], + "tls_library": "mbedtls", + "static_link": false, + "cmake_generator": null, + "extra_meson_args": null + }, + "macos-aarch64-mbedtls": { + "os_type": "macos", + "arch": "aarch64", + "zig_target": "aarch64-macos", + "rust_target": "aarch64-apple-darwin", + "artifact_name": "ant-darwin-aarch64-mbedtls", + "binary_name": "ant", + "deps": ["llhttp", "libsodium", "mbedtls"], + "tls_library": "mbedtls", + "static_link": false, + "cmake_generator": null, + "extra_meson_args": null + }, + "windows-x64": { + "os_type": "windows", + "arch": "x86_64", + "zig_target": "x86_64-windows-gnu", + "rust_target": "x86_64-pc-windows-gnu", + "artifact_name": "ant-windows-x64", + "binary_name": "ant.exe", + "deps": ["llhttp"], + "tls_library": "openssl", + "static_link": false, + "cmake_generator": "MinGW Makefiles", + "extra_meson_args": "-Dc_std=gnu2x" + } + } +} diff --git a/.github/workflows/build-linux-glibc-aarch64.yml b/.github/workflows/build-linux-glibc-aarch64.yml deleted file mode 100644 index caeaf5f..0000000 --- a/.github/workflows/build-linux-glibc-aarch64.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Build Linux aarch64 [glibc] - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - linux-aarch64: - runs-on: ubuntu-24.04-arm - container: ubuntu:22.04 - steps: - - name: Install git and clang-21 - env: - DEBIAN_FRONTEND: noninteractive - run: | - apt-get update - apt-get install -y git ca-certificates gnupg wget software-properties-common curl - wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc - echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" > /etc/apt/sources.list.d/llvm.list - curl -fsSL https://deb.nodesource.com/setup_22.x | bash - - apt-get update - apt-get install -y clang-21 lld-21 llvm-21 nodejs - git config --global --add safe.directory "$GITHUB_WORKSPACE" - - uses: actions/checkout@v4 - with: - submodules: recursive - - env: - DEBIAN_FRONTEND: noninteractive - run: | - apt-get install -y python3-pip ninja-build cmake pkg-config \ - uuid-dev libssl-dev libsodium-dev - pip3 install meson - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-linux-glibc-aarch64-llhttp-9.2.1-libuv-1.51.0-v1 - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar-21 - export RANLIB=llvm-ranlib-21 - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=clang-21 CXX=clang++-21 cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - - name: Build libuv - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar-21 - export RANLIB=llvm-ranlib-21 - git clone --depth 1 --branch v1.51.0 https://github.com/libuv/libuv.git /tmp/libuv - cd /tmp/libuv - CC=clang-21 cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_TESTING=OFF -DLIBUV_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - - run: npm ci - working-directory: src/tools - - name: Install Rust - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - build/oxc-target - key: cargo-linux-arm64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: cargo-linux-arm64- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-linux-glibc-aarch64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - CC=clang-21 CC_LD=lld-21 meson setup build -Db_lto=true --buildtype=release -Dbuild_timestamp=${{ inputs.build_timestamp }} && meson compile -C build - - run: ./build/ant --version - - run: llvm-strip-21 build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-linux-aarch64 - path: build/ant diff --git a/.github/workflows/build-linux-glibc-x64.yml b/.github/workflows/build-linux-glibc-x64.yml deleted file mode 100644 index e5511f4..0000000 --- a/.github/workflows/build-linux-glibc-x64.yml +++ /dev/null @@ -1,95 +0,0 @@ -name: Build Linux x64 [glibc] - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - linux-x64: - runs-on: ubuntu-24.04 - container: ubuntu:22.04 - steps: - - name: Install git and clang-21 - env: - DEBIAN_FRONTEND: noninteractive - run: | - apt-get update - apt-get install -y git ca-certificates gnupg wget software-properties-common curl - wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc - echo "deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-21 main" > /etc/apt/sources.list.d/llvm.list - curl -fsSL https://deb.nodesource.com/setup_22.x | bash - - apt-get update - apt-get install -y clang-21 lld-21 llvm-21 nodejs - git config --global --add safe.directory "$GITHUB_WORKSPACE" - - uses: actions/checkout@v4 - with: - submodules: recursive - - env: - DEBIAN_FRONTEND: noninteractive - run: | - apt-get install -y python3-pip ninja-build cmake pkg-config \ - uuid-dev libssl-dev libsodium-dev - pip3 install meson - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-linux-glibc-x64-llhttp-9.2.1-libuv-1.51.0-v1 - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar-21 - export RANLIB=llvm-ranlib-21 - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=clang-21 CXX=clang++-21 cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - - name: Build libuv - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar-21 - export RANLIB=llvm-ranlib-21 - git clone --depth 1 --branch v1.51.0 https://github.com/libuv/libuv.git /tmp/libuv - cd /tmp/libuv - CC=clang-21 cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_TESTING=OFF -DLIBUV_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - - run: npm ci - working-directory: src/tools - - name: Install Rust - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - build/oxc-target - key: cargo-linux-amd64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: cargo-linux-amd64- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-linux-glibc-x64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - CC=clang-21 CC_LD=lld-21 meson setup build -Db_lto=true --buildtype=release -Dbuild_timestamp=${{ inputs.build_timestamp }} && meson compile -C build - - run: ./build/ant --version - - run: llvm-strip-21 build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-linux-x64 - path: build/ant diff --git a/.github/workflows/build-linux-musl-aarch64.yml b/.github/workflows/build-linux-musl-aarch64.yml deleted file mode 100644 index 3817723..0000000 --- a/.github/workflows/build-linux-musl-aarch64.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Build Linux aarch64 [musl] - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - linux-musl-aarch64: - runs-on: ubuntu-24.04-arm - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-linux-musl-aarch64-llhttp-9.2.1-libuv-1.51.0-zlib-1.3.1-v1 - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-linux-musl-aarch64-${{ hashFiles('vendor/*.wrap') }} - - name: Cache cargo - uses: actions/cache@v4 - with: - path: | - ~/.cargo-cache/registry - ~/.cargo-cache/git - build/oxc-target - key: cargo-linux-musl-aarch64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: cargo-linux-musl-aarch64- - - name: Build in Alpine container - run: | - mkdir -p ~/.cargo-cache - docker run --rm -v "$PWD":/work -v "$HOME/deps-cache":/deps-cache -v "$HOME/.cargo-cache":/root/.cargo -w /work alpine:edge sh -c ' - apk add --no-cache git clang lld llvm meson ninja cmake pkgconf curl npm nodejs \ - musl-dev openssl-dev openssl-libs-static libsodium-dev libsodium-static \ - util-linux-dev util-linux-static linux-headers libunwind-dev libunwind-static rust cargo - - git config --global --add safe.directory /work - - export CFLAGS="-Os -flto" - export AR=llvm-ar - export RANLIB=llvm-ranlib - - if [ ! -f /deps-cache/lib/libllhttp.a ]; then - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=clang cmake -G Ninja -B build -DCMAKE_INSTALL_PREFIX=/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - fi - - if [ ! -f /deps-cache/lib/libuv.a ]; then - git clone --depth 1 --branch v1.51.0 https://github.com/libuv/libuv.git /tmp/libuv - cd /tmp/libuv - CC=clang cmake -G Ninja -B build -DCMAKE_INSTALL_PREFIX=/deps-cache -DBUILD_TESTING=OFF -DLIBUV_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - fi - - if [ ! -f /deps-cache/lib/libz.a ]; then - git clone --depth 1 --branch v1.3.1 https://github.com/madler/zlib.git /tmp/zlib - cd /tmp/zlib - CC=clang cmake -G Ninja -B build -DCMAKE_INSTALL_PREFIX=/deps-cache -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - rm -f /deps-cache/lib/libz.so* - fi - - cd /work - npm ci --prefix src/tools - export PKG_CONFIG_PATH="/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="/deps-cache:$CMAKE_PREFIX_PATH" - CC=clang CC_LD=lld meson setup build --prefer-static -Db_lto=true --buildtype=release -Dstatic_link=true -Dbuild_timestamp=${{ inputs.build_timestamp }} && meson compile -C build - llvm-strip build/ant - ' - - run: ./build/ant --version - - uses: actions/upload-artifact@v4 - with: - name: ant-linux-aarch64-musl - path: build/ant diff --git a/.github/workflows/build-linux-musl-x64.yml b/.github/workflows/build-linux-musl-x64.yml deleted file mode 100644 index b848fa1..0000000 --- a/.github/workflows/build-linux-musl-x64.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Build Linux x64 [musl] - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - linux-musl-x64: - runs-on: ubuntu-24.04 - container: alpine:edge - steps: - - name: Install dependencies - run: | - apk add --no-cache git clang lld llvm meson ninja cmake pkgconf curl npm nodejs \ - musl-dev openssl-dev openssl-libs-static libsodium-dev libsodium-static \ - util-linux-dev util-linux-static linux-headers libunwind-dev libunwind-static \ - tar zstd - git config --global --add safe.directory "$GITHUB_WORKSPACE" - - uses: actions/checkout@v4 - with: - submodules: recursive - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-linux-musl-x64-llhttp-9.2.1-libuv-1.51.0-zlib-1.3.1-v1 - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar - export RANLIB=llvm-ranlib - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=clang cmake -G Ninja -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - - name: Build libuv - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar - export RANLIB=llvm-ranlib - git clone --depth 1 --branch v1.51.0 https://github.com/libuv/libuv.git /tmp/libuv - cd /tmp/libuv - CC=clang cmake -G Ninja -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_TESTING=OFF -DLIBUV_BUILD_SHARED=OFF -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - - name: Build zlib - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export CFLAGS="-Os -flto" - export AR=llvm-ar - export RANLIB=llvm-ranlib - git clone --depth 1 --branch v1.3.1 https://github.com/madler/zlib.git /tmp/zlib - cd /tmp/zlib - CC=clang cmake -G Ninja -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=MinSizeRel - cmake --build build - cmake --install build - rm -f $HOME/deps-cache/lib/libz.so* - - run: npm ci - working-directory: src/tools - - name: Install Rust - run: | - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-host x86_64-unknown-linux-musl - echo "$HOME/.cargo/bin" >> $GITHUB_PATH - - uses: actions/cache@v4 - with: - path: | - ~/.cargo/registry - ~/.cargo/git - build/oxc-target - key: cargo-linux-musl-amd64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: cargo-linux-musl-amd64- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-linux-musl-x64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - CC=clang CC_LD=lld meson setup build --prefer-static -Db_lto=true --buildtype=release -Dstatic_link=true -Dbuild_timestamp=${{ inputs.build_timestamp }} && meson compile -C build - - run: ./build/ant --version - - run: llvm-strip build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-linux-x64-musl - path: build/ant diff --git a/.github/workflows/build-macos-aarch64-mbedtls.yml b/.github/workflows/build-macos-aarch64-mbedtls.yml deleted file mode 100644 index b50fae0..0000000 --- a/.github/workflows/build-macos-aarch64-mbedtls.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Build macOS aarch64 [mbedtls] - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - macos-aarch64-mbedtls: - runs-on: macos-15 - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: actions/setup-node@v4 - with: - node-version: '22' - - run: brew install meson ninja llvm - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-macos-aarch64-mbedtls-libsodium-1.0.20-mbedtls-3.6.5-llhttp-9.2.1-v1 - - name: Build libsodium - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch 1.0.20-RELEASE https://github.com/jedisct1/libsodium.git /tmp/libsodium - cd /tmp/libsodium - ./configure CC=$LLVM_PREFIX/bin/clang --prefix=$HOME/deps-cache --disable-shared --enable-static - make -j$(sysctl -n hw.ncpu) - make install - - name: Build mbedTLS - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch mbedtls-3.6.5 --recurse-submodules https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls - cd /tmp/mbedtls - CC=$LLVM_PREFIX/bin/clang cmake -B build \ - -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache \ - -DENABLE_PROGRAMS=OFF \ - -DENABLE_TESTING=OFF \ - -DCMAKE_BUILD_TYPE=Release \ - -DUSE_STATIC_MBEDTLS_LIBRARY=ON \ - -DUSE_SHARED_MBEDTLS_LIBRARY=OFF - cmake --build build -j$(sysctl -n hw.ncpu) - cmake --install build - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=$LLVM_PREFIX/bin/clang cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=Release - cmake --build build - cmake --install build - - run: npm ci - working-directory: src/tools - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - workspaces: src/strip - - name: Cache oxc build - uses: actions/cache@v4 - with: - path: build/oxc-target - key: oxc-macos-aarch64-mbedtls-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: oxc-macos-aarch64-mbedtls- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-macos-aarch64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - export LLVM_PREFIX=$(brew --prefix llvm) - export CC=$LLVM_PREFIX/bin/clang - export AR=$LLVM_PREFIX/bin/llvm-ar - export RANLIB=$LLVM_PREFIX/bin/llvm-ranlib - export CMAKE_C_COMPILER=$CC - export CMAKE_AR=$AR - export CMAKE_RANLIB=$RANLIB - meson setup build -Db_lto=true --buildtype=release -Dbuild_timestamp=${{ inputs.build_timestamp }} -Dtls_library=mbedtls && meson compile -C build - - run: ./build/ant --version - - run: strip build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-darwin-aarch64-mbedtls - path: build/ant diff --git a/.github/workflows/build-macos-aarch64.yml b/.github/workflows/build-macos-aarch64.yml deleted file mode 100644 index 131834b..0000000 --- a/.github/workflows/build-macos-aarch64.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Build macOS aarch64 - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - macos-aarch64: - runs-on: macos-15 - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: actions/setup-node@v4 - with: - node-version: '22' - - run: brew install meson ninja openssl@3 llvm - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-macos-aarch64-libsodium-1.0.20-llhttp-9.2.1-v1 - - name: Build libsodium - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch 1.0.20-RELEASE https://github.com/jedisct1/libsodium.git /tmp/libsodium - cd /tmp/libsodium - ./configure CC=$LLVM_PREFIX/bin/clang --prefix=$HOME/deps-cache --disable-shared --enable-static - make -j$(sysctl -n hw.ncpu) - make install - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=$LLVM_PREFIX/bin/clang cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=Release - cmake --build build - cmake --install build - - run: npm ci - working-directory: src/tools - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - workspaces: src/strip - - name: Cache oxc build - uses: actions/cache@v4 - with: - path: build/oxc-target - key: oxc-macos-aarch64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: oxc-macos-aarch64- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-macos-aarch64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - export LLVM_PREFIX=$(brew --prefix llvm) - export CC=$LLVM_PREFIX/bin/clang - export AR=$LLVM_PREFIX/bin/llvm-ar - export RANLIB=$LLVM_PREFIX/bin/llvm-ranlib - export CMAKE_C_COMPILER=$CC - export CMAKE_AR=$AR - export CMAKE_RANLIB=$RANLIB - meson setup build -Db_lto=true --buildtype=release -Dbuild_timestamp=${{ inputs.build_timestamp }} && meson compile -C build - - run: ./build/ant --version - - run: strip build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-darwin-aarch64 - path: build/ant diff --git a/.github/workflows/build-macos-x64-mbedtls.yml b/.github/workflows/build-macos-x64-mbedtls.yml deleted file mode 100644 index 9201f5e..0000000 --- a/.github/workflows/build-macos-x64-mbedtls.yml +++ /dev/null @@ -1,96 +0,0 @@ -name: Build macOS x64 [mbedtls] - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - macos-x64-mbedtls: - runs-on: macos-15-intel - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: actions/setup-node@v4 - with: - node-version: '22' - - run: brew install meson ninja llvm - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-macos-x64-mbedtls-libsodium-1.0.20-mbedtls-3.6.5-llhttp-9.2.1-v1 - - name: Build libsodium - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch 1.0.20-RELEASE https://github.com/jedisct1/libsodium.git /tmp/libsodium - cd /tmp/libsodium - ./configure CC=$LLVM_PREFIX/bin/clang --prefix=$HOME/deps-cache --disable-shared --enable-static - make -j$(sysctl -n hw.ncpu) - make install - - name: Build mbedTLS - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch mbedtls-3.6.5 --recurse-submodules https://github.com/Mbed-TLS/mbedtls.git /tmp/mbedtls - cd /tmp/mbedtls - CC=$LLVM_PREFIX/bin/clang cmake -B build \ - -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache \ - -DENABLE_PROGRAMS=OFF \ - -DENABLE_TESTING=OFF \ - -DCMAKE_BUILD_TYPE=Release \ - -DUSE_STATIC_MBEDTLS_LIBRARY=ON \ - -DUSE_SHARED_MBEDTLS_LIBRARY=OFF - cmake --build build -j$(sysctl -n hw.ncpu) - cmake --install build - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=$LLVM_PREFIX/bin/clang cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=Release - cmake --build build - cmake --install build - - run: npm ci - working-directory: src/tools - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - workspaces: src/strip - - name: Cache oxc build - uses: actions/cache@v4 - with: - path: build/oxc-target - key: oxc-macos-x64-mbedtls-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: oxc-macos-x64-mbedtls- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-macos-x64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - export LLVM_PREFIX=$(brew --prefix llvm) - export CC=$LLVM_PREFIX/bin/clang - export AR=$LLVM_PREFIX/bin/llvm-ar - export RANLIB=$LLVM_PREFIX/bin/llvm-ranlib - export CMAKE_C_COMPILER=$CC - export CMAKE_AR=$AR - export CMAKE_RANLIB=$RANLIB - meson setup build -Db_lto=true --buildtype=release -Dbuild_timestamp=${{ inputs.build_timestamp }} -Dtls_library=mbedtls && meson compile -C build - - run: ./build/ant --version - - run: strip build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-darwin-x64-mbedtls - path: build/ant diff --git a/.github/workflows/build-macos-x64.yml b/.github/workflows/build-macos-x64.yml deleted file mode 100644 index 3516a48..0000000 --- a/.github/workflows/build-macos-x64.yml +++ /dev/null @@ -1,81 +0,0 @@ -name: Build macOS x64 - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - macos-x64: - runs-on: macos-15-intel - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: actions/setup-node@v4 - with: - node-version: '22' - - run: brew install meson ninja openssl@3 llvm - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ~/deps-cache - key: deps-macos-x64-libsodium-1.0.20-llhttp-9.2.1-v1 - - name: Build libsodium - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch 1.0.20-RELEASE https://github.com/jedisct1/libsodium.git /tmp/libsodium - cd /tmp/libsodium - ./configure CC=$LLVM_PREFIX/bin/clang --prefix=$HOME/deps-cache --disable-shared --enable-static - make -j$(sysctl -n hw.ncpu) - make install - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - export LLVM_PREFIX=$(brew --prefix llvm) - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - CC=$LLVM_PREFIX/bin/clang cmake -B build -DCMAKE_INSTALL_PREFIX=$HOME/deps-cache -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON -DCMAKE_BUILD_TYPE=Release - cmake --build build - cmake --install build - - run: npm ci - working-directory: src/tools - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 - with: - workspaces: src/strip - - name: Cache oxc build - uses: actions/cache@v4 - with: - path: build/oxc-target - key: oxc-macos-x64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: oxc-macos-x64- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-macos-x64-${{ hashFiles('vendor/*.wrap') }} - - run: | - export PKG_CONFIG_PATH="$HOME/deps-cache/lib/pkgconfig:$PKG_CONFIG_PATH" - export CMAKE_PREFIX_PATH="$HOME/deps-cache:$CMAKE_PREFIX_PATH" - export LLVM_PREFIX=$(brew --prefix llvm) - export CC=$LLVM_PREFIX/bin/clang - export AR=$LLVM_PREFIX/bin/llvm-ar - export RANLIB=$LLVM_PREFIX/bin/llvm-ranlib - export CMAKE_C_COMPILER=$CC - export CMAKE_AR=$AR - export CMAKE_RANLIB=$RANLIB - meson setup build -Db_lto=true --buildtype=release -Dbuild_timestamp=${{ inputs.build_timestamp }} && meson compile -C build - - run: ./build/ant --version - - run: strip build/ant - - uses: actions/upload-artifact@v4 - with: - name: ant-darwin-x64 - path: build/ant diff --git a/.github/workflows/build-platform.yml b/.github/workflows/build-platform.yml new file mode 100644 index 0000000..f9ded97 --- /dev/null +++ b/.github/workflows/build-platform.yml @@ -0,0 +1,232 @@ +name: Build Platform + +on: + workflow_call: + inputs: + platform: + description: 'Platform key from versions.json targets' + required: true + type: string + build_timestamp: + required: true + type: string + outputs: + version: + description: Build version + value: ${{ jobs.build.outputs.version }} + +jobs: + build: + outputs: + version: ${{ steps.build.outputs.version }} + # runs-on/container/shell must be hardcoded + runs-on: |- + ${{ case( + inputs.platform == 'linux-glibc-x64', 'ubuntu-24.04', + inputs.platform == 'linux-glibc-aarch64', 'ubuntu-24.04-arm', + inputs.platform == 'linux-musl-x64', 'ubuntu-24.04', + inputs.platform == 'linux-musl-aarch64', 'ubuntu-24.04-arm', + inputs.platform == 'macos-x64', 'macos-15-intel', + inputs.platform == 'macos-x64-mbedtls', 'macos-15-intel', + inputs.platform == 'macos-aarch64', 'macos-15', + inputs.platform == 'macos-aarch64-mbedtls', 'macos-15', + inputs.platform == 'windows-x64', 'windows-latest', + 'ubuntu-24.04' + ) }} + + container: |- + ${{ case( + startsWith(inputs.platform, 'linux-glibc'), 'ubuntu:22.04', + startsWith(inputs.platform, 'linux-musl'), 'alpine:edge', + '' + ) }} + + defaults: + run: + shell: |- + ${{ case( + startsWith(inputs.platform, 'linux-glibc'), 'bash', + startsWith(inputs.platform, 'linux-musl'), 'sh', + startsWith(inputs.platform, 'macos'), 'bash', + inputs.platform == 'windows-x64', 'msys2 {0}', + 'bash' + ) }} + + steps: + # === ALPINE ARM64 WORKAROUND (must be first) === + - name: Setup Alpine ARM64 support + uses: laverdet/alpine-arm64@v1 + if: startsWith(inputs.platform, 'linux-musl') + + # === LINUX GLIBC SETUP === + - name: Setup Linux glibc environment + if: startsWith(inputs.platform, 'linux-glibc') + env: + DEBIAN_FRONTEND: noninteractive + run: | + apt-get update + apt-get install -y git ca-certificates gnupg wget software-properties-common curl \ + python3-pip ninja-build cmake pkg-config uuid-dev libssl-dev libsodium-dev jq + curl -fsSL https://deb.nodesource.com/setup_22.x | bash - + apt-get install -y nodejs + pip3 install meson + git config --global --add safe.directory "$GITHUB_WORKSPACE" + + # === LINUX MUSL SETUP === + - name: Setup Linux musl environment + if: startsWith(inputs.platform, 'linux-musl') + run: | + apk add --no-cache git clang lld llvm meson ninja cmake pkgconf curl npm nodejs \ + musl-dev openssl-dev openssl-libs-static libsodium-dev libsodium-static \ + util-linux-dev util-linux-static linux-headers libunwind-dev libunwind-static \ + tar xz zstd jq bash + git config --global --add safe.directory "$GITHUB_WORKSPACE" + + # === MACOS SETUP === + - name: Setup macOS environment + if: startsWith(inputs.platform, 'macos') + run: | + for pkg in meson ninja llvm jq; do + brew list "$pkg" &>/dev/null || brew install "$pkg" + done + if [[ "${{ inputs.platform }}" != *"mbedtls"* ]]; then + brew list openssl@3 &>/dev/null || brew install openssl@3 + fi + + # === WINDOWS SETUP === + - name: Disable Windows Defender real-time monitoring + if: inputs.platform == 'windows-x64' + shell: pwsh + run: Set-MpPreference -DisableRealtimeMonitoring $true + + - name: Setup Windows MSYS2 + if: inputs.platform == 'windows-x64' + uses: msys2/setup-msys2@v2 + with: + msystem: MINGW64 + cache: true + install: >- + mingw-w64-x86_64-toolchain mingw-w64-x86_64-meson mingw-w64-x86_64-ninja + mingw-w64-x86_64-cmake mingw-w64-x86_64-openssl mingw-w64-x86_64-libsodium + mingw-w64-x86_64-lld mingw-w64-x86_64-nodejs git jq + + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: recursive + + # === LOAD TARGET CONFIG === + - name: Load target configuration + id: config + shell: bash + run: | + CONFIG=$(jq -r '.targets["${{ inputs.platform }}"]' .github/versions.json) + TOOLS=$(jq -r '.tools' .github/versions.json) + DEPS=$(jq -r '.dependencies' .github/versions.json) + + # Tools + echo "zig_version=$(echo $TOOLS | jq -r '.zig')" >> $GITHUB_OUTPUT + echo "llvm_version=$(echo $TOOLS | jq -r '.llvm')" >> $GITHUB_OUTPUT + echo "node_version=$(echo $TOOLS | jq -r '.node')" >> $GITHUB_OUTPUT + + # Target config + echo "os_type=$(echo $CONFIG | jq -r '.os_type')" >> $GITHUB_OUTPUT + echo "arch=$(echo $CONFIG | jq -r '.arch')" >> $GITHUB_OUTPUT + echo "zig_target=$(echo $CONFIG | jq -r '.zig_target')" >> $GITHUB_OUTPUT + echo "rust_target=$(echo $CONFIG | jq -r '.rust_target')" >> $GITHUB_OUTPUT + echo "artifact_name=$(echo $CONFIG | jq -r '.artifact_name')" >> $GITHUB_OUTPUT + echo "binary_name=$(echo $CONFIG | jq -r '.binary_name')" >> $GITHUB_OUTPUT + echo "tls_library=$(echo $CONFIG | jq -r '.tls_library')" >> $GITHUB_OUTPUT + echo "static_link=$(echo $CONFIG | jq -r '.static_link')" >> $GITHUB_OUTPUT + echo "cmake_generator=$(echo $CONFIG | jq -r '.cmake_generator // empty')" >> $GITHUB_OUTPUT + echo "extra_meson_args=$(echo $CONFIG | jq -r '.extra_meson_args // empty')" >> $GITHUB_OUTPUT + + # Deps as space-separated string + echo "deps=$(echo $CONFIG | jq -r '.deps | join(" ")')" >> $GITHUB_OUTPUT + + # Dep versions + echo "llhttp_version=$(echo $DEPS | jq -r '.llhttp')" >> $GITHUB_OUTPUT + echo "libuv_version=$(echo $DEPS | jq -r '.libuv')" >> $GITHUB_OUTPUT + echo "libsodium_version=$(echo $DEPS | jq -r '.libsodium')" >> $GITHUB_OUTPUT + echo "mbedtls_version=$(echo $DEPS | jq -r '.mbedtls')" >> $GITHUB_OUTPUT + echo "zlib_version=$(echo $DEPS | jq -r '.zlib')" >> $GITHUB_OUTPUT + + # === SETUP ZIG === + - name: Setup Zig + uses: ./.github/actions/setup-zig + with: + version: ${{ steps.config.outputs.zig_version }} + + # === SETUP LLVM === + - name: Setup LLVM + id: llvm + uses: ./.github/actions/setup-llvm + with: + version: ${{ steps.config.outputs.llvm_version }} + os_type: ${{ steps.config.outputs.os_type }} + + # === SETUP RUST === + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ steps.config.outputs.rust_target }} + + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + with: + workspaces: src/strip -> ../../build/oxc-target + + # === BUILD NATIVE DEPS === + - name: Build native dependencies + uses: ./.github/actions/build-native-deps + with: + deps: ${{ steps.config.outputs.deps }} + prefix: ${{ runner.temp }}/deps-cache + cc: ${{ steps.llvm.outputs.cc }} + cxx: ${{ steps.llvm.outputs.cxx }} + ar: ${{ steps.llvm.outputs.ar }} + ranlib: ${{ steps.llvm.outputs.ranlib }} + cmake_generator: ${{ steps.config.outputs.cmake_generator }} + llhttp_version: ${{ steps.config.outputs.llhttp_version }} + libuv_version: ${{ steps.config.outputs.libuv_version }} + libsodium_version: ${{ steps.config.outputs.libsodium_version }} + mbedtls_version: ${{ steps.config.outputs.mbedtls_version }} + zlib_version: ${{ steps.config.outputs.zlib_version }} + + # === BUILD PROJECT === + - name: Build project + id: build + uses: ./.github/actions/build-project + with: + cc: ${{ steps.llvm.outputs.cc }} + cxx: ${{ steps.llvm.outputs.cxx }} + ar: ${{ steps.llvm.outputs.ar }} + ranlib: ${{ steps.llvm.outputs.ranlib }} + ld: ${{ steps.llvm.outputs.ld }} + strip: ${{ steps.llvm.outputs.strip }} + deps_prefix: ${{ runner.temp }}/deps-cache + build_timestamp: ${{ inputs.build_timestamp }} + tls_library: ${{ steps.config.outputs.tls_library }} + static_link: ${{ steps.config.outputs.static_link }} + extra_meson_args: ${{ steps.config.outputs.extra_meson_args }} + binary_name: ${{ steps.config.outputs.binary_name }} + + # === UPLOAD ARTIFACTS === + - name: Upload binary + uses: actions/upload-artifact@v4 + with: + name: ${{ steps.config.outputs.artifact_name }} + path: | + build/${{ steps.config.outputs.binary_name }} + build/*.dll + + - name: Save version info + shell: bash + run: echo "${{ steps.build.outputs.version }}" > version.txt + + - name: Upload version info + uses: actions/upload-artifact@v4 + with: + name: version-${{ inputs.platform }} + path: version.txt + retention-days: 1 diff --git a/.github/workflows/build-single.yml b/.github/workflows/build-single.yml new file mode 100644 index 0000000..56895dc --- /dev/null +++ b/.github/workflows/build-single.yml @@ -0,0 +1,35 @@ +name: Build Single Platform + +on: + workflow_dispatch: + inputs: + platform: + description: 'Target platform' + required: true + type: choice + options: + - linux-glibc-x64 + - linux-glibc-aarch64 + - linux-musl-x64 + - linux-musl-aarch64 + - macos-x64 + - macos-aarch64 + - macos-x64-mbedtls + - macos-aarch64-mbedtls + - windows-x64 + +jobs: + prepare: + runs-on: ubuntu-latest + outputs: + timestamp: ${{ steps.timestamp.outputs.value }} + steps: + - id: timestamp + run: echo "value=$(date +%s)" >> $GITHUB_OUTPUT + + build: + needs: prepare + uses: ./.github/workflows/build-platform.yml + with: + platform: ${{ inputs.platform }} + build_timestamp: ${{ needs.prepare.outputs.timestamp }} diff --git a/.github/workflows/build-windows-x64.yml b/.github/workflows/build-windows-x64.yml deleted file mode 100644 index fc903d6..0000000 --- a/.github/workflows/build-windows-x64.yml +++ /dev/null @@ -1,80 +0,0 @@ -name: Build Windows x64 - -on: - workflow_dispatch: - workflow_call: - inputs: - build_timestamp: - required: true - type: string - -jobs: - windows-x64: - runs-on: windows-latest - defaults: - run: - shell: msys2 {0} - steps: - - uses: actions/checkout@v4 - with: - submodules: recursive - - uses: msys2/setup-msys2@v2 - with: - msystem: MINGW64 - install: >- - mingw-w64-x86_64-toolchain - mingw-w64-x86_64-meson - mingw-w64-x86_64-ninja - mingw-w64-x86_64-cmake - mingw-w64-x86_64-openssl - mingw-w64-x86_64-libsodium - mingw-w64-x86_64-nodejs - git - - name: Cache native dependencies - id: cache-deps - uses: actions/cache@v4 - with: - path: ${{ runner.temp }}/deps-cache - key: deps-windows-x64-llhttp-9.2.1-v1 - - name: Build llhttp - if: steps.cache-deps.outputs.cache-hit != 'true' - run: | - DEPS_CACHE=$(cygpath "${{ runner.temp }}/deps-cache") - git clone --depth 1 --branch release/v9.2.1 https://github.com/nodejs/llhttp.git /tmp/llhttp - cd /tmp/llhttp - cmake -G "MinGW Makefiles" -B build -DCMAKE_INSTALL_PREFIX=$DEPS_CACHE -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON - cmake --build build - cmake --install build - ln -sf $DEPS_CACHE/lib/pkgconfig/libllhttp.pc $DEPS_CACHE/lib/pkgconfig/llhttp.pc - - run: npm ci - working-directory: src/tools - - uses: dtolnay/rust-toolchain@stable - with: - targets: x86_64-pc-windows-gnu - - uses: Swatinem/rust-cache@v2 - with: - workspaces: src/strip - - name: Cache oxc build - uses: actions/cache@v4 - with: - path: build/oxc-target - key: oxc-windows-x64-${{ hashFiles('src/strip/Cargo.lock') }} - restore-keys: oxc-windows-x64- - - name: Cache vendor dependencies - uses: actions/cache@v4 - with: - path: | - vendor/*/ - build/vendor - key: vendor-windows-x64-${{ hashFiles('vendor/*.wrap') }} - - run: | - DEPS_CACHE=$(cygpath "${{ runner.temp }}/deps-cache") - DEPS_CACHE_WIN=$(cygpath -m "${{ runner.temp }}/deps-cache") - export PATH="/c/Users/$USER/.cargo/bin:$PATH" - export PKG_CONFIG_PATH="$DEPS_CACHE/lib/pkgconfig:$PKG_CONFIG_PATH" - meson setup build -Dc_std=gnu2x -Dbuild_timestamp=${{ inputs.build_timestamp }} -Ddeps_prefix_cmake="$DEPS_CACHE_WIN" && meson compile -C build - - run: ./build/ant --version - - uses: actions/upload-artifact@v4 - with: - name: ant-windows-x64 - path: build/ant.exe diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9740b69..2ddee0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,65 +13,58 @@ jobs: prepare: runs-on: ubuntu-latest if: | - github.event_name == 'workflow_dispatch' || - github.event_name == 'pull_request' || - (github.event_name == 'push' && contains(github.event.head_commit.message, '[ci] trigger build')) + github.ref_type != 'tag' && + !contains(github.event.head_commit.message || '', '(ci-ignore)') && + !contains(github.event.pull_request.title || '', '(ci-ignore)') && ( + github.event_name == 'workflow_dispatch' || + github.event_name == 'pull_request' || + (github.event_name == 'push' && contains(github.event.head_commit.message, '[ci] trigger build')) + ) outputs: timestamp: ${{ steps.timestamp.outputs.value }} steps: - id: timestamp run: echo "value=$(date +%s)" >> $GITHUB_OUTPUT - macos-aarch64: + build: needs: prepare - uses: ./.github/workflows/build-macos-aarch64.yml + strategy: + fail-fast: false + matrix: + platform: + - linux-glibc-x64 + - linux-glibc-aarch64 + - linux-musl-x64 + - linux-musl-aarch64 + - macos-x64 + - macos-aarch64 + - macos-x64-mbedtls + - macos-aarch64-mbedtls + - windows-x64 + uses: ./.github/workflows/build-platform.yml with: + platform: ${{ matrix.platform }} build_timestamp: ${{ needs.prepare.outputs.timestamp }} - macos-x64: - needs: prepare - uses: ./.github/workflows/build-macos-x64.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} - - macos-aarch64-mbedtls: - needs: prepare - uses: ./.github/workflows/build-macos-aarch64-mbedtls.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} - - macos-x64-mbedtls: - needs: prepare - uses: ./.github/workflows/build-macos-x64-mbedtls.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} - - linux-glibc-x64: - needs: prepare - uses: ./.github/workflows/build-linux-glibc-x64.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} - - linux-glibc-aarch64: - needs: prepare - uses: ./.github/workflows/build-linux-glibc-aarch64.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} - - linux-musl-x64: - needs: prepare - uses: ./.github/workflows/build-linux-musl-x64.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} - - linux-musl-aarch64: - needs: prepare - uses: ./.github/workflows/build-linux-musl-aarch64.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} + summary: + needs: build + runs-on: ubuntu-latest + if: ${{ !cancelled() && needs.build.result != 'skipped' }} + steps: + - name: Download version artifacts + uses: actions/download-artifact@v4 + with: + pattern: version-* + path: versions - windows-x64: - needs: prepare - uses: ./.github/workflows/build-windows-x64.yml - with: - build_timestamp: ${{ needs.prepare.outputs.timestamp }} + - name: Generate summary + run: | + echo "## Build Versions" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Platform | Version |" >> $GITHUB_STEP_SUMMARY + echo "|----------|---------|" >> $GITHUB_STEP_SUMMARY + for dir in versions/version-*; do + platform=$(basename "$dir" | sed 's/version-//') + version=$(cat "$dir/version.txt" 2>/dev/null || echo "N/A") + echo "| $platform | \`$version\` |" >> $GITHUB_STEP_SUMMARY + done diff --git a/.gitignore b/.gitignore index ae0854b..7bb55bb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,8 @@ /build /traces + +/src/pkg/.zig-cache /src/strip/target /test262 diff --git a/include/cli/pkg.h b/include/cli/pkg.h new file mode 100644 index 0000000..5abf29c --- /dev/null +++ b/include/cli/pkg.h @@ -0,0 +1,22 @@ +#ifndef PKG_CMDS_H +#define PKG_CMDS_H + +#include + +extern bool pkg_verbose; + +int pkg_cmd_init(int argc, char **argv); +int pkg_cmd_install(int argc, char **argv); +int pkg_cmd_add(int argc, char **argv); +int pkg_cmd_remove(int argc, char **argv); +int pkg_cmd_trust(int argc, char **argv); +int pkg_cmd_run(int argc, char **argv); +int pkg_cmd_exec(int argc, char **argv); +int pkg_cmd_why(int argc, char **argv); +int pkg_cmd_info(int argc, char **argv); +int pkg_cmd_ls(int argc, char **argv); +int pkg_cmd_cache(int argc, char **argv); + +bool pkg_script_exists(const char *package_json_path, const char *script_name); + +#endif diff --git a/include/pkg.h b/include/pkg.h new file mode 100644 index 0000000..7c4bfd8 --- /dev/null +++ b/include/pkg.h @@ -0,0 +1,324 @@ +#ifndef PKG_H +#define PKG_H + +#include +#include +#include + +typedef enum { + PKG_OK = 0, + PKG_OUT_OF_MEMORY = -1, + PKG_INVALID_LOCKFILE = -2, + PKG_IO_ERROR = -3, + PKG_NETWORK_ERROR = -4, + PKG_CACHE_ERROR = -5, + PKG_EXTRACT_ERROR = -6, + PKG_RESOLVE_ERROR = -7, + PKG_INVALID_ARGUMENT = -8, + PKG_NOT_FOUND = -9, + PKG_INTEGRITY_MISMATCH = -10, +} pkg_error_t; + +typedef enum { + PKG_PHASE_RESOLVING = 0, + PKG_PHASE_FETCHING = 1, + PKG_PHASE_EXTRACTING = 2, + PKG_PHASE_LINKING = 3, + PKG_PHASE_CACHING = 4, + PKG_PHASE_POSTINSTALL = 5, +} pkg_phase_t; + +typedef void (*pkg_progress_cb)( + void *user_data, + pkg_phase_t phase, + uint32_t current, + uint32_t total, + const char *message +); + +typedef struct { + const char *cache_dir; + const char *registry_url; + uint32_t max_connections; + pkg_progress_cb progress_callback; + void *user_data; + bool verbose; +} pkg_options_t; + +typedef struct pkg_context pkg_context_t; + +const char *pkg_error_string(const pkg_context_t *ctx); + +pkg_context_t *pkg_init(const pkg_options_t *options); + +pkg_error_t pkg_install( + pkg_context_t *ctx, + const char *package_json_path, + const char *lockfile_path, + const char *node_modules_path +); + +pkg_error_t pkg_resolve_and_install( + pkg_context_t *ctx, + const char *package_json_path, + const char *lockfile_path, + const char *node_modules_path +); + +pkg_error_t pkg_add( + pkg_context_t *ctx, + const char *package_json_path, + const char *package_spec, + bool dev +); + +pkg_error_t pkg_remove( + pkg_context_t *ctx, + const char *package_json_path, + const char *package_name +); + +void pkg_free(pkg_context_t *ctx); +void pkg_cache_sync(pkg_context_t *ctx); + +typedef struct { + uint64_t total_size; + uint64_t db_size; + uint32_t package_count; +} pkg_cache_stats_t; + +pkg_error_t pkg_cache_stats(pkg_context_t *ctx, pkg_cache_stats_t *out); + +int32_t pkg_cache_prune(pkg_context_t *ctx, uint32_t max_age_days); + +typedef struct { + uint32_t package_count; + uint32_t cache_hits; + uint32_t cache_misses; + uint32_t files_linked; + uint32_t files_copied; + uint32_t packages_installed; + uint32_t packages_skipped; + uint64_t elapsed_ms; +} pkg_install_result_t; + +typedef struct { + const char *name; + const char *version; + bool direct; +} pkg_added_package_t; + +typedef struct { + const char *name; + const char *script; +} pkg_lifecycle_script_t; + +uint32_t pkg_get_added_count(const pkg_context_t *ctx); + +pkg_error_t pkg_discover_lifecycle_scripts( + pkg_context_t *ctx, + const char *node_modules_path +); + +uint32_t pkg_get_lifecycle_script_count(const pkg_context_t *ctx); + +pkg_error_t pkg_get_lifecycle_script( + const pkg_context_t *ctx, + uint32_t index, + pkg_lifecycle_script_t *out +); + +pkg_error_t pkg_run_postinstall( + pkg_context_t *ctx, + const char *node_modules_path, + const char **package_names, + uint32_t count +); + +pkg_error_t pkg_add_trusted_dependencies( + const char *package_json_path, + const char **package_names, + uint32_t count +); + +pkg_error_t pkg_get_install_result( + pkg_context_t *ctx, + pkg_install_result_t *out +); + +pkg_error_t pkg_get_added_package( + const pkg_context_t *ctx, + uint32_t index, + pkg_added_package_t *out +); + +int pkg_get_bin_path( + const char *node_modules_path, + const char *bin_name, + char *out_path, + size_t out_path_len +); + +typedef void (*pkg_bin_callback)( + const char *name, + void *user_data +); + +int pkg_list_bins( + const char *node_modules_path, + pkg_bin_callback callback, + void *user_data +); + +int pkg_list_package_bins( + const char *node_modules_path, + const char *package_name, + pkg_bin_callback callback, + void *user_data +); + +int pkg_get_script( + const char *package_json_path, + const char *script_name, + char *out_script, + size_t out_script_len +); + +typedef struct { + int exit_code; + int signal; +} pkg_script_result_t; + +pkg_error_t pkg_run_script( + const char *package_json_path, + const char *script_name, + const char *node_modules_path, + const char *extra_args, + pkg_script_result_t *result +); + +typedef void (*pkg_script_callback)( + const char *name, + const char *command, + void *user_data +); + +int pkg_list_scripts( + const char *package_json_path, + pkg_script_callback callback, + void *user_data +); + +typedef struct { + uint8_t peer: 1; + uint8_t dev: 1; + uint8_t optional: 1; + uint8_t direct: 1; + uint8_t _reserved: 4; +} pkg_dep_type_t; + +typedef void (*pkg_why_callback)( + const char *name, + const char *version, + const char *constraint, + pkg_dep_type_t dep_type, + void *user_data +); + +typedef struct { + char target_version[64]; + bool found; + bool is_peer; + bool is_dev; + bool is_direct; +} pkg_why_info_t; + +int pkg_why_info( + const char *lockfile_path, + const char *package_name, + pkg_why_info_t *out +); + +int pkg_why( + const char *lockfile_path, + const char *package_name, + pkg_why_callback callback, + void *user_data +); + +typedef struct { + const char *name; + const char *version; + const char *description; + const char *license; + const char *homepage; + const char *tarball; + const char *shasum; + const char *integrity; + const char *keywords; + const char *published; + uint32_t dep_count; + uint32_t version_count; + uint64_t unpacked_size; +} pkg_info_t; + +typedef struct { + const char *tag; + const char *version; +} pkg_dist_tag_t; + +typedef struct { + const char *name; + const char *email; +} pkg_maintainer_t; + +pkg_error_t pkg_info( + pkg_context_t *ctx, + const char *package_spec, + pkg_info_t *out +); + +uint32_t pkg_info_dist_tag_count(const pkg_context_t *ctx); +pkg_error_t pkg_info_get_dist_tag(const pkg_context_t *ctx, uint32_t index, pkg_dist_tag_t *out); + +uint32_t pkg_info_maintainer_count(const pkg_context_t *ctx); +pkg_error_t pkg_info_get_maintainer(const pkg_context_t *ctx, uint32_t index, pkg_maintainer_t *out); + +typedef struct { + const char *name; + const char *version; +} pkg_dependency_t; + +uint32_t pkg_info_dependency_count(const pkg_context_t *ctx); +pkg_error_t pkg_info_get_dependency(const pkg_context_t *ctx, uint32_t index, pkg_dependency_t *out); + +pkg_error_t pkg_exec_temp( + pkg_context_t *ctx, + const char *package_spec, + char *out_bin_path, + size_t out_bin_path_len +); + +pkg_error_t pkg_add_global( + pkg_context_t *ctx, + const char *package_spec +); + +pkg_error_t pkg_remove_global( + pkg_context_t *ctx, + const char *package_name +); + +typedef void (*pkg_global_list_callback)( + const char *name, + const char *version, + void *user_data +); + +pkg_error_t pkg_list_global( + pkg_context_t *ctx, + pkg_global_list_callback callback, + void *user_data +); + +#endif diff --git a/include/progress.h b/include/progress.h new file mode 100644 index 0000000..007f004 --- /dev/null +++ b/include/progress.h @@ -0,0 +1,315 @@ +#ifndef PROGRESS_H +#define PROGRESS_H + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#define PROGRESS_ISATTY(fd) _isatty(fd) +#define PROGRESS_FILENO(f) _fileno(f) +#else +#include +#include +#define PROGRESS_ISATTY(fd) isatty(fd) +#define PROGRESS_FILENO(f) fileno(f) +#endif + +#define PROGRESS_MSG_SIZE 256 + +#ifdef __has_include +#if __has_include() +#include +#define PROGRESS_HAS_PTHREADS 1 +#endif +#endif + +typedef struct { +#if defined(PROGRESS_HAS_PTHREADS) + pthread_mutex_t mtx; +#elif defined(_WIN32) + CRITICAL_SECTION cs; +#else + int dummy; +#endif +} progress_mutex_t; + +typedef struct { + FILE *terminal; + bool is_windows_terminal; + bool supports_ansi; + bool dont_print_on_dumb; + uint64_t start_time_ns; + uint64_t prev_refresh_ns; + uint64_t refresh_rate_ns; + uint64_t initial_delay_ns; + bool done; + bool timer_valid; + size_t columns_written; + progress_mutex_t mutex; + char buffer[PROGRESS_MSG_SIZE]; + char msg_buffer[PROGRESS_MSG_SIZE]; +} progress_t; + +static inline void progress_mutex_init(progress_mutex_t *m) { +#if defined(PROGRESS_HAS_PTHREADS) + pthread_mutex_init(&m->mtx, NULL); +#elif defined(_WIN32) + InitializeCriticalSection(&m->cs); +#else + (void)m; +#endif +} + +static inline void progress_mutex_destroy(progress_mutex_t *m) { +#if defined(PROGRESS_HAS_PTHREADS) + pthread_mutex_destroy(&m->mtx); +#elif defined(_WIN32) + DeleteCriticalSection(&m->cs); +#else + (void)m; +#endif +} + +static inline void progress_mutex_lock(progress_mutex_t *m) { +#if defined(PROGRESS_HAS_PTHREADS) + pthread_mutex_lock(&m->mtx); +#elif defined(_WIN32) + EnterCriticalSection(&m->cs); +#else + (void)m; +#endif +} + +static inline void progress_mutex_unlock(progress_mutex_t *m) { +#if defined(PROGRESS_HAS_PTHREADS) + pthread_mutex_unlock(&m->mtx); +#elif defined(_WIN32) + LeaveCriticalSection(&m->cs); +#else + (void)m; +#endif +} + +static inline bool progress_mutex_trylock(progress_mutex_t *m) { +#if defined(PROGRESS_HAS_PTHREADS) + return pthread_mutex_trylock(&m->mtx) == 0; +#elif defined(_WIN32) + return TryEnterCriticalSection(&m->cs) != 0; +#else + (void)m; + return true; +#endif +} + +static inline uint64_t progress_now_ns(void) { + struct timespec ts; +#if defined(CLOCK_MONOTONIC) + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { + return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; + } +#elif defined(_WIN32) + static LARGE_INTEGER freq = {0}; + if (freq.QuadPart == 0) { + QueryPerformanceFrequency(&freq); + } + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return (uint64_t)(counter.QuadPart * 1000000000ULL / freq.QuadPart); +#endif + if (clock_gettime(CLOCK_REALTIME, &ts) == 0) { + return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec; + } + return 0; +} + +static inline bool progress_detect_ansi(FILE *f) { + if (!f) return false; + + int fd = PROGRESS_FILENO(f); + if (!PROGRESS_ISATTY(fd)) return false; + +#ifdef _WIN32 + HANDLE h = (HANDLE)_get_osfhandle(fd); + DWORD mode; + if (GetConsoleMode(h, &mode)) { + if (SetConsoleMode(h, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)) return true; + } + const char *term = getenv("TERM"); + return term && strstr(term, "xterm") + || term && strstr(term, "vt100") + || term && strstr(term, "color") + || term && strstr(term, "ansi"); +#else + const char *term = getenv("TERM"); + if (!term) return false; + if (strcmp(term, "dumb") == 0) return false; + return true; +#endif +} + +static void progress_refresh_locked(progress_t *p); +static void progress_clear_locked(progress_t *p, size_t *end); + +static inline void progress_start(progress_t *p, const char *message) { + memset(p, 0, sizeof(*p)); + progress_mutex_init(&p->mutex); + + FILE *f = stderr; + int fd = PROGRESS_FILENO(f); + + if (PROGRESS_ISATTY(fd)) { + p->terminal = f; + p->supports_ansi = progress_detect_ansi(f); +#ifdef _WIN32 + if (!p->supports_ansi) p->is_windows_terminal = true; +#endif + } else { + p->terminal = f; + p->supports_ansi = false; + p->is_windows_terminal = false; + } + + if (message) { + strncpy(p->msg_buffer, message, PROGRESS_MSG_SIZE - 1); + p->msg_buffer[PROGRESS_MSG_SIZE - 1] = '\0'; + } else p->msg_buffer[0] = '\0'; + + p->refresh_rate_ns = 50 * 1000000ULL; + p->initial_delay_ns = 500 * 1000000ULL; + p->start_time_ns = progress_now_ns(); + p->prev_refresh_ns = 0; + p->timer_valid = (p->start_time_ns != 0); + + p->columns_written = 0; + p->done = false; + + progress_refresh_locked(p); +} + +static inline void progress_maybe_refresh(progress_t *p) { + if (!p->timer_valid) return; + if (!progress_mutex_trylock(&p->mutex)) return; + + uint64_t now = progress_now_ns(); + uint64_t elapsed = now - p->start_time_ns; + + if (elapsed < p->initial_delay_ns) { + progress_mutex_unlock(&p->mutex); + return; + } + + if (now < p->prev_refresh_ns || (now - p->prev_refresh_ns) < p->refresh_rate_ns) { + progress_mutex_unlock(&p->mutex); + return; + } + + progress_refresh_locked(p); + progress_mutex_unlock(&p->mutex); +} + +static inline void progress_update(progress_t *p, const char *message) { + progress_mutex_lock(&p->mutex); + if (message) { + strncpy(p->msg_buffer, message, PROGRESS_MSG_SIZE - 1); + p->msg_buffer[PROGRESS_MSG_SIZE - 1] = '\0'; + } else p->msg_buffer[0] = '\0'; + + progress_mutex_unlock(&p->mutex); + progress_maybe_refresh(p); +} + +static inline void progress_refresh(progress_t *p) { + if (!progress_mutex_trylock(&p->mutex)) return; + progress_refresh_locked(p); + progress_mutex_unlock(&p->mutex); +} + +static void progress_clear_locked(progress_t *p, size_t *end) { + if (!p->terminal) return; + + size_t pos = *end; + + if (p->columns_written > 0) { + if (p->supports_ansi) { + int written = snprintf(p->buffer + pos, sizeof(p->buffer) - pos, "\x1b[%zuD\x1b[0K", p->columns_written); + if (written > 0) pos += (size_t)written; + } else if (p->is_windows_terminal) { +#ifdef _WIN32 + HANDLE h = (HANDLE)_get_osfhandle(PROGRESS_FILENO(p->terminal)); + CONSOLE_SCREEN_BUFFER_INFO info; + if (GetConsoleScreenBufferInfo(h, &info)) { + COORD cursor = { + .X = (SHORT)(info.dwCursorPosition.X - (SHORT)p->columns_written), + .Y = info.dwCursorPosition.Y + }; + if (cursor.X < 0) cursor.X = 0; + + DWORD fill_len = (DWORD)(info.dwSize.X - cursor.X); + DWORD written; + FillConsoleOutputAttribute(h, info.wAttributes, fill_len, cursor, &written); + FillConsoleOutputCharacterA(h, ' ', fill_len, cursor, &written); + SetConsoleCursorPosition(h, cursor); + } +#endif + } else { + if (pos < sizeof(p->buffer)) p->buffer[pos++] = '\n'; + } + p->columns_written = 0; + } + + *end = pos; +} + +static void progress_refresh_locked(progress_t *p) { + bool is_dumb = !p->supports_ansi && !p->is_windows_terminal; + if (is_dumb && p->dont_print_on_dumb) return; + if (!p->terminal) return; + + size_t end = 0; + progress_clear_locked(p, &end); + + if (!p->done) { + if (p->msg_buffer[0]) { + int written = snprintf(p->buffer + end, sizeof(p->buffer) - end, " %s", p->msg_buffer); + if (written > 0) { + size_t amt = ( + (size_t)written < sizeof(p->buffer) - end) + ? (size_t)written + : sizeof(p->buffer) - end - 1; + end += amt; + p->columns_written = amt; + } + } + } + + if (end > 0) { + fwrite(p->buffer, 1, end, p->terminal); + fflush(p->terminal); + } + + p->prev_refresh_ns = progress_now_ns(); +} + +static inline void progress_stop(progress_t *p) { + progress_mutex_lock(&p->mutex); + p->done = true; + + size_t end = 0; + progress_clear_locked(p, &end); + if (end > 0 && p->terminal) { + fwrite(p->buffer, 1, end, p->terminal); + fflush(p->terminal); + } + + progress_mutex_unlock(&p->mutex); + progress_mutex_destroy(&p->mutex); +} + +#endif diff --git a/include/utils.h b/include/utils.h index e69981a..2ed59a5 100644 --- a/include/utils.h +++ b/include/utils.h @@ -5,8 +5,12 @@ #include #include +const char *ant_semver(void); uint64_t hash_key(const char *key, size_t len); + int is_typescript_file(const char *filename); int ant_version(void *argtable[]); +void *try_oom(size_t size); + #endif \ No newline at end of file diff --git a/libant/scripts/bundle.sh b/libant/scripts/bundle.sh index c497337..d3f09ba 100755 --- a/libant/scripts/bundle.sh +++ b/libant/scripts/bundle.sh @@ -18,9 +18,10 @@ bundle_lib() { LIBS=$(find "$BUILD_DIR" -name '*.a' \ ! -name 'libant.a' \ ! -name 'libant-lto.a' \ + ! -name 'libpkg.a' \ ! -path '*/oxc-target/release/deps/*' \ ! -path '*/.external/*' \ - 2>/dev/null | grep -v "$EXCLUDE" | sort -u) + 2>/dev/null | grep -E -v "$EXCLUDE" | sort -u) cd "$TMPDIR" for lib in $LIBS; do @@ -62,6 +63,17 @@ if [ -f "$BUILD_DIR/libant.h" ]; then echo "Created: $DIST_DIR/ant.h" fi +pkg_lib_path=$(find "$BUILD_DIR" -name 'libpkg.a' -print | head -n 1) +if [ -n "$pkg_lib_path" ] && [ -f "$pkg_lib_path" ]; then + cp "$pkg_lib_path" "$DIST_DIR/libpkg.a" + echo "Created: $DIST_DIR/libpkg.a ($(du -h "$pkg_lib_path" | cut -f1))" +fi + +if [ -f "$ROOT_DIR/include/pkg.h" ]; then + cp "$ROOT_DIR/include/pkg.h" "$DIST_DIR/pkg.h" + echo "Created: $DIST_DIR/pkg.h" +fi + echo "" echo "Done! Distribution files in $DIST_DIR:" ls -lh "$DIST_DIR"/ 2>/dev/null || echo "No files found" diff --git a/maidfile.toml b/maidfile.toml index 2fb13d8..10295f5 100644 --- a/maidfile.toml +++ b/maidfile.toml @@ -27,6 +27,7 @@ script = ['rm spec.txt', ''' [tasks.setup] script = ['meson subprojects download', ''' bash -c 'CC="ccache $(which clang)" \ + CC_LD="$(which ld64.lld)" \ meson setup build --wipe' '''] @@ -38,8 +39,15 @@ script = ['meson subprojects download', ''' [tasks.install] script = ["maid build -q", "maid strip", ''' - bash -c 'which ant && cp ./build/ant "$(which ant)" || - { mkdir -p ~/.local/bin && cp ./build/ant ~/.local/bin/; }' + bash -c 'if which ant >/dev/null 2>&1; then + dir=$(dirname "$(which ant)") + cp ./build/ant "$dir/ant" + ln -sf "$dir/ant" "$dir/antx" + else + mkdir -p ~/.ant/bin + cp ./build/ant ~/.ant/bin/ + ln -sf ~/.ant/bin/ant ~/.ant/bin/antx + fi' '''] [tasks.debug] @@ -47,9 +55,3 @@ script = ['meson subprojects download', ''' bash -c 'CC="ccache $(which clang)" \ meson setup build --wipe --buildtype=debug -Doptimization=0 -Db_lto=false -Dstrip=false -Db_lundef=false -Dunity=off' '''] - -[tasks.embed_example] -script = [''' - bash -c 'CC="ccache $(which clang)" \ - meson setup build -Dbuild_examples=true 2>/dev/null || true' -''', "meson compile -C build", "./build/embed_example"] diff --git a/meson.build b/meson.build index d76fbd4..c3fdc77 100644 --- a/meson.build +++ b/meson.build @@ -34,6 +34,7 @@ lib_sources = files( 'src/runtime.c', 'src/snapshot.c', 'src/esm/remote.c', + 'src/cli/pkg.c', ) + files(module_files) include = include_directories('include') @@ -51,12 +52,40 @@ libant = static_library( install: true ) +pkg_lib = custom_target( + 'pkg_zig', + output: 'libpkg.a', + command: [ + 'sh', '-c', + '"$ZIG" build --build-file "$PKG_ZIG_DIR/build.zig" --prefix "$PKG_BUILD_DIR" && cp "$PKG_BUILD_DIR/lib/$PKG_LIB_NAME" "@OUTPUT@"' + ], + env: { + 'ZIG': zig.full_path(), + 'ANT_VERSION': ant_version, + + 'PKG_ZIG_DIR': pkg_zig_dir, 'PKG_BUILD_DIR': pkg_build_dir, + 'LMDB_INCLUDE': lmdb_include_path, 'ZLIB_INCLUDE': zlib_include_path, + 'LIBUV_INCLUDE': libuv_include_path, 'YYJSON_INCLUDE': yyjson_include_path, + + 'PKG_TARGET': host_machine.cpu_family() + '-' + host_machine.system(), + 'PKG_LIB_NAME': host_machine.system() == 'windows' ? 'pkg.lib' : 'libpkg.a', + }, + build_by_default: true, + build_always_stale: true +) + +pkg_dep = declare_dependency( + link_with: [pkg_lib], + include_directories: include, + dependencies: [lmdb_dep, tlsuv_dep, libuv_dep, nghttp2_dep] +) + libant_dep = declare_dependency( link_with: libant, include_directories: [ include, version_include, build_include ], - dependencies: ant_deps + [oxc_dep] + dependencies: ant_deps + [oxc_dep, pkg_dep] ) link_args = [] @@ -70,14 +99,4 @@ executable( include_directories: [strip_include], dependencies: libant_dep, link_args: link_args -) - -if get_option('build_examples') - executable( - 'embed_example', - files('examples/embed/embed.c'), - include_directories: [include, build_include], - link_with: libant, - dependencies: ant_deps + [oxc_dep] - ) -endif +) \ No newline at end of file diff --git a/meson/deps/meson.build b/meson/deps/meson.build index 4cb9398..57886f8 100644 --- a/meson/deps/meson.build +++ b/meson/deps/meson.build @@ -80,22 +80,37 @@ endif tlsuv_opts.append_compile_args('c', tlsuv_compile_args) tlsuv_dep = cmake.subproject('tlsuv', options: tlsuv_opts).dependency('tlsuv') -pcre2_dep = subproject('pcre2', default_options: ['warning_level=0']).get_variable('libpcre2_8') uthash_dep = subproject('uthash').get_variable('uthash_dep') yyjson_dep = subproject('yyjson').get_variable('yyjson_dep') uuidv7_dep = subproject('uuidv7').get_variable('uuidv7_dep') argtable3_dep = subproject('argtable3').get_variable('argtable3_dep') minicoro_dep = subproject('minicoro').get_variable('minicoro_dep') -zlib_dep = subproject('zlib-ng', - default_options: ['b_lto=false', 'warning_level=0'] -).get_variable('zlib_ng_dep') +lmdb_dep = subproject('lmdb', default_options: [ + 'warning_level=0', + 'programs=disabled' +]).get_variable('lmdb_dep') + +zlib_dep = subproject('zlib-ng', default_options: [ + 'b_lto=false', + 'warning_level=0' +]).get_variable('zlib_ng_dep') + +nghttp2_dep = subproject('nghttp2', default_options: [ + 'warning_level=0' +]).get_variable('libnghttp2_dep') libffi_dep = subproject('libffi', default_options: [ 'warning_level=0', 'tests=false' ]).get_variable('ffi_dep') +pcre2_dep = subproject('pcre2', default_options: [ + 'warning_level=0', + 'grep=false', + 'test=false' +]).get_variable('libpcre2_8') + cargo = find_program('cargo', required: true) cp = find_program('cp', required: true) @@ -116,15 +131,47 @@ oxc_lib = custom_target( output: oxc_output_name, command: [ 'sh', '-c', - cargo.full_path() + ' build --release' + rust_target_arg + ' ' + - '--manifest-path ' + src_root / 'src' / 'strip' / 'Cargo.toml' + ' ' + - '--target-dir ' + meson.project_build_root() / 'oxc-target' + - ' && ' + cp.full_path() + ' ' + oxc_release_dir / oxc_lib_name + ' @OUTPUT@' + '"' + cargo.full_path() + '"' + ' build --release' + rust_target_arg + ' ' + + '--manifest-path "' + src_root / 'src' / 'strip' / 'Cargo.toml' + '" ' + + '--target-dir "' + meson.project_build_root() / 'oxc-target' + '"' + + ' && "' + cp.full_path() + '" "' + oxc_release_dir / oxc_lib_name + '" @OUTPUT@' ], build_by_default: true ) oxc_dep = declare_dependency(link_with: oxc_lib) +zig = find_program('zig', required: true) + +deps_info = { + 'lmdb': { + 'dir': 'openldap-LMDB_0.9.33', + 'inc': 'libraries/liblmdb', + }, + 'yyjson': { + 'dir': 'yyjson-0.12.0', + 'inc': 'src', + }, + 'libuv': { + 'dir': 'libuv-v1.51.0', + 'inc': 'include', + }, + 'zlib-ng': { + 'dir': 'zlib-ng-2.3.2', + 'inc': '', + 'build_dir': true, + }, +} + +subprojects_src = src_root / 'vendor' +subprojects_build = meson.project_build_root() / 'vendor' + +lmdb_include_path = subprojects_src / deps_info['lmdb']['dir'] / deps_info['lmdb']['inc'] +yyjson_include_path = subprojects_src / deps_info['yyjson']['dir'] / deps_info['yyjson']['inc'] +libuv_include_path = subprojects_src / deps_info['libuv']['dir'] / deps_info['libuv']['inc'] +zlib_include_path = subprojects_build / deps_info['zlib-ng']['dir'] / deps_info['zlib-ng']['inc'] + +pkg_zig_dir = src_root / 'src' / 'pkg' +pkg_build_dir = meson.current_build_dir() ant_deps = [ libffi_dep, uuid_dep, diff --git a/meson/version/meson.build b/meson/version/meson.build index c54f80a..5388f33 100644 --- a/meson/version/meson.build +++ b/meson/version/meson.build @@ -4,7 +4,7 @@ git_hash = git_hash != '' ? git_hash : 'unknown' timestamp_opt = get_option('build_timestamp') timestamp = timestamp_opt != '' ? timestamp_opt : run_command('date', '+%s', check: true).stdout().strip() -ant_version = '0.5.0.' + timestamp + '-g' + git_hash +ant_version = '0.5.1.' + timestamp + '-g' + git_hash cmd_cc = meson.get_compiler('c') target_triple = run_command(cmd_cc.cmd_array(), '-dumpmachine', check: true).stdout().strip() diff --git a/meson_options.txt b/meson_options.txt index ab9b8fa..41e4d61 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,4 +2,3 @@ option('static_link', type: 'boolean', value: false, description: 'statically li option('build_timestamp', type: 'string', value: '', description: 'build timestamp (defaults to current time if empty)') option('tls_library', type: 'combo', choices: ['openssl', 'mbedtls'], value: 'openssl', description: 'TLS library to use') option('deps_prefix_cmake', type: 'string', value: '', description: 'prefix path for finding dependencies in cmake subprojects') -option('build_examples', type: 'boolean', value: false, description: 'build embedding examples') diff --git a/src/cli/pkg.c b/src/cli/pkg.c new file mode 100644 index 0000000..f1d0701 --- /dev/null +++ b/src/cli/pkg.c @@ -0,0 +1,1504 @@ +#include // IWYU pragma: keep + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utils.h" +#include "config.h" +#include "progress.h" + +extern bool io_no_color; +bool pkg_verbose = false; + +#define C(color) (io_no_color ? "" : (color)) +#define C_RESET C("\x1b[0m") +#define C_BOLD C("\x1b[1m") +#define C_DIM C("\x1b[2m") +#define C_UL C("\x1b[4m") +#define C_UL_OFF C("\x1b[24m") +#define C_GREEN C("\x1b[32m") +#define C_YELLOW C("\x1b[33m") +#define C_BLUE C("\x1b[34m") +#define C_MAGENTA C("\x1b[35m") +#define C_CYAN C("\x1b[36m") +#define C_WHITE C("\x1b[37m") +#define C_RED C("\x1b[31m") + +static void progress_callback(void *user_data, pkg_phase_t phase, uint32_t current, uint32_t total, const char *message) { + progress_t *progress = (progress_t *)user_data; + if (!progress || !message || !message[0]) return; + + const char *icon; + switch (phase) { + case PKG_PHASE_RESOLVING: icon = "🔍"; break; + case PKG_PHASE_FETCHING: icon = "🚚"; break; + case PKG_PHASE_EXTRACTING: icon = "📦"; break; + case PKG_PHASE_LINKING: icon = "🔗"; break; + case PKG_PHASE_CACHING: icon = "💾"; break; + case PKG_PHASE_POSTINSTALL: icon = "⚙️ "; break; + default: icon = "📦"; break; + } + + char msg[PROGRESS_MSG_SIZE]; + if (total > 0) snprintf(msg, sizeof(msg), "%s %s [%u/%u]", icon, message, current, total); + else if (current > 0) snprintf(msg, sizeof(msg), "%s %s [%u]", icon, message, current); + else snprintf(msg, sizeof(msg), "%s %s", icon, message); + + progress_update(progress, msg); +} + +static void print_added_packages(pkg_context_t *ctx) { + uint32_t count = pkg_get_added_count(ctx); + uint32_t printed = 0; + if (count > 0) printf("\n"); + + for (uint32_t i = 0; i < count; i++) { + pkg_added_package_t pkg; + if (pkg_get_added_package(ctx, i, &pkg) == PKG_OK && pkg.direct) { + printf("%s+%s %s%s%s@%s%s%s\n", + C_GREEN, C_RESET, + C_BOLD, pkg.name, C_RESET, + C_DIM, pkg.version, C_RESET + ); printed++; + } + } + + if (printed > 0) printf("\n"); +} + +static uint64_t timespec_diff_ms(struct timespec *start, struct timespec *end) { + int64_t sec = end->tv_sec - start->tv_sec; + int64_t nsec = end->tv_nsec - start->tv_nsec; + if (nsec < 0) { sec--; nsec += 1000000000; } + return (uint64_t)sec * 1000 + (uint64_t)nsec / 1000000; +} + +static void print_elapsed(uint64_t elapsed_ms) { + fputs(C_BOLD, stdout); + if (elapsed_ms < 1000) { + printf("%llums", (unsigned long long)elapsed_ms); + } else printf("%.2fs", (double)elapsed_ms / 1000.0); + fputs(C_RESET, stdout); +} + +static void print_install_header(const char *cmd) { + const char *version = ant_semver(); + + printf("%sant %s%s v%s %s(%s)%s\n", + C_BOLD, cmd, C_RESET, version, + C_DIM, ANT_GIT_HASH, C_RESET + ); +} + +static void print_bin_callback(const char *name, void *user_data) { + (void)user_data; + printf(" %s-%s %s\n", C_DIM, C_RESET, name); +} + +static void prompt_with_default(const char *prompt, const char *def, char *buf, size_t buf_size) { + if (def && def[0]) { + printf("%s%s%s %s(%s)%s: ", C_CYAN, prompt, C_RESET, C_DIM, def, C_RESET); + } else printf("%s%s%s: ", C_CYAN, prompt, C_RESET); + fflush(stdout); + + if (fgets(buf, (int)buf_size, stdin)) { + size_t len = strlen(buf); + if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; + } + + if (buf[0] == '\0' && def) { + strncpy(buf, def, buf_size - 1); + buf[buf_size - 1] = '\0'; + } +} + +typedef struct { + const char *target; + int count; +} why_ctx_t; + +static void print_why_callback(const char *name, const char *version, const char *constraint, pkg_dep_type_t dep_type, void *user_data) { + why_ctx_t *ctx = (why_ctx_t *)user_data; + + if (strcmp(name, "package.json") == 0) { + const char *type_str = dep_type.dev ? "devDependencies" : "dependencies"; + printf(" %s└%s %s%s%s %s(%s)%s\n", + C_DIM, C_RESET, + C_GREEN, name, C_RESET, + C_DIM, type_str, C_RESET); + } else { + const char *type_str = dep_type.peer ? "peer" : (dep_type.dev ? "dev" : (dep_type.optional ? "optional" : "")); + if (type_str[0]) { + printf(" %s└%s %s %s%s%s@%s%s%s %s\"%s\"%s\n", + C_DIM, C_RESET, + type_str, + C_BOLD, name, C_RESET, + C_DIM, version, C_RESET, + C_CYAN, constraint, C_RESET); + } else { + printf(" %s└%s %s%s%s@%s%s%s %s\"%s\"%s\n", + C_DIM, C_RESET, + C_BOLD, name, C_RESET, + C_DIM, version, C_RESET, + C_CYAN, constraint, C_RESET); + } + } + + ctx->count++; +} + +static void print_script(const char *name, const char *command, void *ud) { + (void)ud; + if (strlen(command) > 50) { + printf(" %-15s %.47s...\n", name, command); + } else { + printf(" %-15s %s\n", name, command); + } +} + +static void print_bin_name(const char *name, void *ud) { + (void)ud; + printf(" %s\n", name); +} + +bool pkg_script_exists(const char *package_json_path, const char *script_name) { + char script_cmd[4096]; + return pkg_get_script(package_json_path, script_name, script_cmd, sizeof(script_cmd)) >= 0; +} + +static const char *get_global_dir(void) { + static char global_dir[4096] = {0}; + if (global_dir[0] == '\0') { + const char *home = getenv("HOME"); + if (home) snprintf(global_dir, sizeof(global_dir), "%s/.ant/pkg/global", home); + } + return global_dir; +} + +static int cmd_add_global(const char *package_spec) { + print_install_header("add -g"); + + progress_t progress; + if (!pkg_verbose) progress_start(&progress, "🔍 Resolving [1/1]"); + + pkg_options_t opts = { + .progress_callback = pkg_verbose ? NULL : progress_callback, + .user_data = pkg_verbose ? NULL : &progress, + .verbose = pkg_verbose + }; + pkg_context_t *ctx = pkg_init(&opts); + if (!ctx) { + if (!pkg_verbose) progress_stop(&progress); + fprintf(stderr, "Error: Failed to initialize package manager\n"); + return EXIT_FAILURE; + } + + pkg_error_t err = pkg_add_global(ctx, package_spec); + if (!pkg_verbose) progress_stop(&progress); + + if (err != PKG_OK) { + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + + pkg_install_result_t result; + if (pkg_get_install_result(ctx, &result) == PKG_OK) { + printf("\n%sinstalled globally%s %s%s%s\n", + C_GREEN, C_RESET, C_BOLD, package_spec, C_RESET); + printf(" %s(binaries linked to ~/.ant/bin)%s\n", C_DIM, C_RESET); + printf("\n%s[%s", C_DIM, C_RESET); + print_elapsed(result.elapsed_ms); + printf("%s]%s done\n", C_DIM, C_RESET); + } + + pkg_free(ctx); + return EXIT_SUCCESS; +} + +static int cmd_remove_global(const char *package_name) { + print_install_header("remove -g"); + + progress_t progress; + if (!pkg_verbose) progress_start(&progress, "🔍 Resolving"); + + pkg_options_t opts = { + .progress_callback = pkg_verbose ? NULL : progress_callback, + .user_data = pkg_verbose ? NULL : &progress, + .verbose = pkg_verbose + }; + pkg_context_t *ctx = pkg_init(&opts); + if (!ctx) { + if (!pkg_verbose) progress_stop(&progress); + fprintf(stderr, "Error: Failed to initialize package manager\n"); + return EXIT_FAILURE; + } + + pkg_error_t err = pkg_remove_global(ctx, package_name); + if (!pkg_verbose) progress_stop(&progress); + + if (err == PKG_NOT_FOUND) { + printf("\nPackage '%s' not found in global dependencies\n", package_name); + pkg_free(ctx); + return EXIT_SUCCESS; + } + + if (err != PKG_OK) { + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + + printf("\n%s-%s Removed globally: %s%s%s\n", C_RED, C_RESET, C_BOLD, package_name, C_RESET); + + pkg_free(ctx); + return EXIT_SUCCESS; +} + +static int cmd_install(void) { + print_install_header("install"); + + progress_t progress; + + if (!pkg_verbose) { + progress_start(&progress, "🔍 Resolving [1/1]"); + } + + pkg_options_t opts = { + .progress_callback = pkg_verbose ? NULL : progress_callback, + .user_data = pkg_verbose ? NULL : &progress, + .verbose = pkg_verbose + }; + pkg_context_t *ctx = pkg_init(&opts); + if (!ctx) { + fprintf(stderr, "Error: Failed to initialize package manager\n"); + return EXIT_FAILURE; + } + + struct stat st; + bool needs_resolve = (stat("ant.lockb", &st) != 0); + + if (needs_resolve) { + if (stat("package.json", &st) != 0) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: No package.json found\n"); + pkg_free(ctx); + return EXIT_FAILURE; + } + + pkg_error_t err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules"); + if (err != PKG_OK) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + } else { + pkg_error_t err = pkg_install(ctx, "package.json", "ant.lockb", "node_modules"); + if (err != PKG_OK) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + } + + if (!pkg_verbose) { + progress_stop(&progress); + + } + + pkg_install_result_t result; + if (pkg_get_install_result(ctx, &result) == PKG_OK) { + if (result.packages_installed > 0) { + print_added_packages(ctx); + printf("%s%u%s package%s installed", + C_GREEN, result.packages_installed, C_RESET, + result.packages_installed == 1 ? "" : "s"); + if (result.cache_hits > 0) { + printf(" %s(%u cached)%s", C_DIM, result.cache_hits, C_RESET); + } + printf(" %s[%s", C_DIM, C_RESET); + print_elapsed(result.elapsed_ms); + printf("%s]%s\n", C_DIM, C_RESET); + } else { + printf("\n%sChecked%s %s%u%s installs across %s%u%s packages %s(no changes)%s %s[%s", + C_DIM, C_RESET, + C_GREEN, result.packages_installed + result.packages_skipped, C_RESET, + C_GREEN, result.package_count, C_RESET, + C_DIM, C_RESET, + C_DIM, C_RESET); + print_elapsed(result.elapsed_ms); + printf("%s]%s\n", C_DIM, C_RESET); + } + } + + if (pkg_discover_lifecycle_scripts(ctx, "node_modules") == PKG_OK) { + uint32_t script_count = pkg_get_lifecycle_script_count(ctx); + if (script_count > 0) { + printf("\n%s%u%s package%s need%s to run lifecycle scripts:\n", + C_YELLOW, script_count, C_RESET, + script_count == 1 ? "" : "s", + script_count == 1 ? "s" : ""); + + for (uint32_t i = 0; i < script_count; i++) { + pkg_lifecycle_script_t script; + if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) { + printf(" %s•%s %s%s%s %s(%s)%s\n", + C_DIM, C_RESET, + C_CYAN, script.name, C_RESET, + C_DIM, script.script, C_RESET); + } + } + + printf("\nRun: %sant trust %s or %sant trust --all%s\n", C_DIM, C_RESET, C_DIM, C_RESET); + } + } + + pkg_free(ctx); + return EXIT_SUCCESS; +} + +static int cmd_add(const char *package_spec, bool dev) { + print_install_header(dev ? "add -D" : "add"); + + char pkg_name[256]; + const char *at_pos = strchr(package_spec, '@'); + if (at_pos == package_spec) { + at_pos = strchr(package_spec + 1, '@'); + } + if (at_pos) { + size_t name_len = (size_t)(at_pos - package_spec); + if (name_len >= sizeof(pkg_name)) name_len = sizeof(pkg_name) - 1; + memcpy(pkg_name, package_spec, name_len); + pkg_name[name_len] = '\0'; + } else { + strncpy(pkg_name, package_spec, sizeof(pkg_name) - 1); + pkg_name[sizeof(pkg_name) - 1] = '\0'; + } + + progress_t progress; + + if (!pkg_verbose) { + progress_start(&progress, "🔍 Resolving [1/1]"); + } + + pkg_options_t opts = { + .progress_callback = pkg_verbose ? NULL : progress_callback, + .user_data = pkg_verbose ? NULL : &progress, + .verbose = pkg_verbose + }; + pkg_context_t *ctx = pkg_init(&opts); + if (!ctx) { + fprintf(stderr, "Error: Failed to initialize package manager\n"); + return EXIT_FAILURE; + } + + pkg_error_t err = pkg_add(ctx, "package.json", package_spec, dev); + if (err != PKG_OK) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + + err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules"); + if (err != PKG_OK) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + + if (!pkg_verbose) progress_stop(&progress); + + pkg_added_package_t added_pkg = {0}; + uint32_t added_count = pkg_get_added_count(ctx); + for (uint32_t i = 0; i < added_count; i++) { + pkg_added_package_t pkg; + if (pkg_get_added_package(ctx, i, &pkg) == PKG_OK && pkg.direct) { + if (strcmp(pkg.name, pkg_name) == 0) { + added_pkg = pkg; break; + } + } + } + + pkg_install_result_t result; + if (pkg_get_install_result(ctx, &result) == PKG_OK) { + printf("\n"); + + if (added_pkg.name) { + int bin_count = pkg_list_package_bins("node_modules", added_pkg.name, NULL, NULL); + + if (bin_count > 0) { + printf("%sinstalled%s %s%s@%s%s with binaries:\n", + C_GREEN, C_RESET, + C_BOLD, added_pkg.name, added_pkg.version, C_RESET); + pkg_list_package_bins("node_modules", added_pkg.name, print_bin_callback, NULL); + } else { + printf("%sinstalled%s %s%s@%s%s\n", + C_GREEN, C_RESET, + C_BOLD, added_pkg.name, added_pkg.version, C_RESET); + } + } + + printf("\n%s[%s", C_DIM, C_RESET); + print_elapsed(result.elapsed_ms); + printf("%s]%s done\n", C_DIM, C_RESET); + } + + pkg_free(ctx); + return EXIT_SUCCESS; +} + +static int cmd_remove(const char *package_name) { + print_install_header("remove"); + progress_t progress; + + if (!pkg_verbose) { + progress_start(&progress, "🔍 Resolving"); + } + + pkg_options_t opts = { + .progress_callback = pkg_verbose ? NULL : progress_callback, + .user_data = pkg_verbose ? NULL : &progress, + .verbose = pkg_verbose + }; + pkg_context_t *ctx = pkg_init(&opts); + if (!ctx) { + fprintf(stderr, "Error: Failed to initialize package manager\n"); + return EXIT_FAILURE; + } + + pkg_error_t err = pkg_remove(ctx, "package.json", package_name); + if (err != PKG_OK && err != PKG_NOT_FOUND) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + + if (err == PKG_NOT_FOUND) { + if (!pkg_verbose) { progress_stop(&progress); } + printf("\n%s[%s", C_DIM, C_RESET); + printf("%s0ms%s", C_BOLD, C_RESET); + printf("%s]%s done\n", C_DIM, C_RESET); + pkg_free(ctx); + return EXIT_SUCCESS; + } + + err = pkg_resolve_and_install(ctx, "package.json", "ant.lockb", "node_modules"); + if (err != PKG_OK) { + if (!pkg_verbose) { progress_stop(&progress); } + fprintf(stderr, "Error: %s\n", pkg_error_string(ctx)); + pkg_free(ctx); + return EXIT_FAILURE; + } + + if (!pkg_verbose) { + progress_stop(&progress); + + } + + pkg_install_result_t result; + if (pkg_get_install_result(ctx, &result) == PKG_OK) { + printf("\n%s%u%s package%s installed %s[%s", + C_GREEN, result.packages_installed, C_RESET, + result.packages_installed == 1 ? "" : "s", + C_DIM, C_RESET); + print_elapsed(result.elapsed_ms); + printf("%s]%s\n", C_DIM, C_RESET); + } + + printf("%s-%s Removed: %s%s%s\n", C_RED, C_RESET, C_BOLD, package_name, C_RESET); + pkg_free(ctx); + + return EXIT_SUCCESS; +} + +static int cmd_trust(const char **pkgs, int count, bool all) { + print_install_header("trust"); + + struct timespec start_time; + clock_gettime(CLOCK_MONOTONIC, &start_time); + + pkg_options_t opts = { .verbose = pkg_verbose }; + pkg_context_t *ctx = pkg_init(&opts); + if (!ctx) { + fprintf(stderr, "Error: Failed to initialize package manager\n"); + return EXIT_FAILURE; + } + + if (pkg_discover_lifecycle_scripts(ctx, "node_modules") != PKG_OK) { + fprintf(stderr, "Error: Failed to scan node_modules\n"); + pkg_free(ctx); + return EXIT_FAILURE; + } + + uint32_t script_count = pkg_get_lifecycle_script_count(ctx); + if (script_count == 0) { + printf("No packages need lifecycle scripts to run.\n"); + pkg_free(ctx); + return EXIT_SUCCESS; + } + + const char **to_run = NULL; + uint32_t to_run_count = 0; + + if (all) { + to_run = try_oom(script_count * sizeof(char *)); + if (to_run) { + for (uint32_t i = 0; i < script_count; i++) { + pkg_lifecycle_script_t script; + if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) { + to_run[to_run_count++] = script.name; + } + } + } + } else if (count > 0) { + to_run = try_oom(count * sizeof(char *)); + if (to_run) { + for (int i = 0; i < count; i++) { + bool found = false; + for (uint32_t j = 0; j < script_count; j++) { + pkg_lifecycle_script_t script; + if (pkg_get_lifecycle_script(ctx, j, &script) == PKG_OK) { + if (strcmp(pkgs[i], script.name) == 0) { + to_run[to_run_count++] = script.name; + found = true; break; + } + } + } + if (!found) fprintf(stderr, "Warning: %s has no pending lifecycle script\n", pkgs[i]); + } + } + } else { + printf("%s%u%s package%s with lifecycle scripts:\n", + C_YELLOW, script_count, C_RESET, + script_count == 1 ? "" : "s"); + + for (uint32_t i = 0; i < script_count; i++) { + pkg_lifecycle_script_t script; + if (pkg_get_lifecycle_script(ctx, i, &script) == PKG_OK) { + printf(" %s•%s %s%s%s %s(%s)%s\n", + C_DIM, C_RESET, + C_CYAN, script.name, C_RESET, + C_DIM, script.script, C_RESET); + } + } + printf("\nRun: %sant trust %s or %sant trust --all%s\n", C_DIM, C_RESET, C_DIM, C_RESET); + pkg_free(ctx); + return EXIT_SUCCESS; + } + + if (to_run && to_run_count > 0) { + if (pkg_verbose) { + printf("[trust] adding %u packages to trustedDependencies\n", to_run_count); + for (uint32_t i = 0; i < to_run_count; i++) { + printf("[trust] %s\n", to_run[i]); + } + } + pkg_error_t add_err = pkg_add_trusted_dependencies("package.json", to_run, to_run_count); + if (add_err == PKG_OK) { + printf("Added %s%u%s package%s to %strustedDependencies%s in package.json\n", + C_GREEN, to_run_count, C_RESET, + to_run_count == 1 ? "" : "s", + C_BOLD, C_RESET); + } else { + if (pkg_verbose) printf("[trust] failed to add trustedDependencies: error %d\n", add_err); + } + + printf("Running lifecycle scripts for %s%u%s package%s...\n", + C_GREEN, to_run_count, C_RESET, + to_run_count == 1 ? "" : "s"); + pkg_run_postinstall(ctx, "node_modules", to_run, to_run_count); + + struct timespec end_time; + clock_gettime(CLOCK_MONOTONIC, &end_time); + uint64_t elapsed_ms = timespec_diff_ms(&start_time, &end_time); + + printf("\n%s%u%s package%s trusted %s[%s", + C_GREEN, to_run_count, C_RESET, + to_run_count == 1 ? "" : "s", + C_DIM, C_RESET); + print_elapsed(elapsed_ms); + printf("%s]%s\n", C_DIM, C_RESET); + free((void *)to_run); + } + + pkg_free(ctx); + return EXIT_SUCCESS; +} + +static int cmd_init(void) { + FILE *fp = fopen("package.json", "r"); + if (fp) { + fclose(fp); + fprintf(stderr, "Error: package.json already exists\n"); + return EXIT_FAILURE; + } + + char cwd[PATH_MAX]; + const char *default_name = "my-project"; + if (getcwd(cwd, sizeof(cwd))) { + char *base = strrchr(cwd, '/'); + if (base && base[1]) default_name = base + 1; + } + + bool interactive = isatty(fileno(stdin)); + + char name[256] = {0}; + char version[64] = {0}; + char entry[256] = {0}; + + if (interactive) { + printf("%sant init%s\n\n", C_BOLD, C_RESET); + + prompt_with_default("package name", default_name, name, sizeof(name)); + prompt_with_default("version", "1.0.0", version, sizeof(version)); + prompt_with_default("entry point", "index.js", entry, sizeof(entry)); + + printf("\n"); + } else { + strncpy(name, default_name, sizeof(name) - 1); + strncpy(version, "1.0.0", sizeof(version) - 1); + strncpy(entry, "index.js", sizeof(entry) - 1); + } + + fp = fopen("package.json", "w"); + if (!fp) { + fprintf(stderr, "Error: Could not create package.json\n"); + return EXIT_FAILURE; + } + + yyjson_mut_doc *doc = yyjson_mut_doc_new(NULL); + yyjson_mut_val *root = yyjson_mut_obj(doc); + yyjson_mut_doc_set_root(doc, root); + + yyjson_mut_obj_add_str(doc, root, "name", name); + yyjson_mut_obj_add_str(doc, root, "version", version); + yyjson_mut_obj_add_str(doc, root, "type", "module"); + yyjson_mut_obj_add_str(doc, root, "main", entry); + + yyjson_mut_val *scripts = yyjson_mut_obj_add_obj(doc, root, "scripts"); + char start_cmd[300]; + snprintf(start_cmd, sizeof(start_cmd), "ant %s", entry); + yyjson_mut_obj_add_str(doc, scripts, "start", start_cmd); + + yyjson_mut_obj_add_obj(doc, root, "dependencies"); + yyjson_mut_obj_add_obj(doc, root, "devDependencies"); + + size_t len; char *json_str = yyjson_mut_write( + doc, YYJSON_WRITE_PRETTY_TWO_SPACES + | YYJSON_WRITE_ESCAPE_UNICODE, &len + ); + + if (json_str) { + fwrite(json_str, 1, len, fp); + free(json_str); + } + + yyjson_mut_doc_free(doc); + fclose(fp); + + printf("%s+%s Created %spackage.json%s\n", C_GREEN, C_RESET, C_BOLD, C_RESET); + return EXIT_SUCCESS; +} + +static int cmd_why(const char *package_name) { + struct stat st; + if (stat("ant.lockb", &st) != 0) { + fprintf(stderr, "Error: No lockfile found. Run 'ant install' first.\n"); + return EXIT_FAILURE; + } + + pkg_why_info_t info; + if (pkg_why_info("ant.lockb", package_name, &info) < 0) { + fprintf(stderr, "Error: Failed to read lockfile\n"); + return EXIT_FAILURE; + } + + if (!info.found) { + printf("\n%s%s%s is not installed\n\n", C_BOLD, package_name, C_RESET); + return EXIT_SUCCESS; + } + + const char *type_label = info.is_peer ? " peer" : (info.is_dev ? " dev" : ""); + printf("\n%s%s%s@%s%s%s%s%s%s\n", C_BOLD, package_name, C_RESET, C_DIM, info.target_version, C_RESET, C_YELLOW, type_label, C_RESET); + + why_ctx_t ctx = { .target = package_name, .count = 0 }; + int result = pkg_why("ant.lockb", package_name, print_why_callback, &ctx); + + if (result < 0) { + fprintf(stderr, "Error: Failed to read lockfile\n"); + return EXIT_FAILURE; + } + + if (ctx.count == 0) { + printf(" %s(no dependents)%s\n", C_DIM, C_RESET); + } + + printf("\n"); + return EXIT_SUCCESS; +} + +int pkg_cmd_init(int argc, char **argv) { + struct arg_lit *help = arg_lit0("h", "help", "display help"); + struct arg_end *end = arg_end(5); + + void *argtable[] = { help, end }; + int nerrors = arg_parse(argc, argv, argtable); + + int exitcode = EXIT_SUCCESS; + if (help->count > 0) { + printf("Usage: ant init\n\n"); + printf("Create a new package.json\n"); + } else if (nerrors > 0) { + arg_print_errors(stdout, end, "ant init"); + exitcode = EXIT_FAILURE; + } else { + exitcode = cmd_init(); + } + + arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0])); + return exitcode; +} + +int pkg_cmd_install(int argc, char **argv) { + struct arg_str *pkgs = arg_strn(NULL, NULL, "", 0, 100, NULL); + struct arg_lit *global = arg_lit0("g", "global", "install globally"); + struct arg_lit *dev = arg_lit0("D", "save-dev", "add as devDependency"); + struct arg_lit *help = arg_lit0("h", "help", "display help"); + struct arg_end *end = arg_end(5); + + void *argtable[] = { pkgs, global, dev, help, end }; + int nerrors = arg_parse(argc, argv, argtable); + + int exitcode = EXIT_SUCCESS; + if (help->count > 0) { + printf("Usage: ant install [packages...] [-g] [-D] [--verbose]\n\n"); + printf("Install from lockfile, or add packages if specified.\n"); + printf("\nOptions:\n -g, --global Install globally to ~/.ant/pkg/global\n"); + printf(" -D, --save-dev Add as devDependency\n"); + } else if (nerrors > 0) { + arg_print_errors(stdout, end, "ant install"); + exitcode = EXIT_FAILURE; + } else if (pkgs->count == 0) { + exitcode = cmd_install(); + } else { + bool is_dev = dev->count > 0; + for (int i = 0; i < pkgs->count && exitcode == EXIT_SUCCESS; i++) { + exitcode = global->count > 0 ? cmd_add_global(pkgs->sval[i]) : cmd_add(pkgs->sval[i], is_dev); + } + } + + arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0])); + return exitcode; +} + +int pkg_cmd_add(int argc, char **argv) { + struct arg_str *pkgs = arg_strn(NULL, NULL, "", 1, 100, NULL); + struct arg_lit *global = arg_lit0("g", "global", "install globally"); + struct arg_lit *dev = arg_lit0("D", "save-dev", "add as devDependency"); + struct arg_lit *help = arg_lit0("h", "help", "display help"); + struct arg_end *end = arg_end(5); + + void *argtable[] = { pkgs, global, dev, help, end }; + int nerrors = arg_parse(argc, argv, argtable); + + int exitcode = EXIT_SUCCESS; + if (help->count > 0) { + printf("Usage: ant add ... [options]\n\n"); + printf("Add packages to dependencies.\n"); + printf("\nOptions:\n -g, --global Install globally to ~/.ant/pkg/global\n"); + printf(" -D, --save-dev Add as devDependency\n"); + } else if (nerrors > 0) { + arg_print_errors(stdout, end, "ant add"); + exitcode = EXIT_FAILURE; + } else { + bool is_dev = dev->count > 0; + for (int i = 0; i < pkgs->count && exitcode == EXIT_SUCCESS; i++) { + exitcode = global->count > 0 ? cmd_add_global(pkgs->sval[i]) : cmd_add(pkgs->sval[i], is_dev); + } + } + + arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0])); + return exitcode; +} + +int pkg_cmd_remove(int argc, char **argv) { + struct arg_str *pkgs = arg_strn(NULL, NULL, "", 1, 100, NULL); + struct arg_lit *global = arg_lit0("g", "global", "remove from global packages"); + struct arg_lit *help = arg_lit0("h", "help", "display help"); + struct arg_end *end = arg_end(5); + + void *argtable[] = { pkgs, global, help, end }; + int nerrors = arg_parse(argc, argv, argtable); + + int exitcode = EXIT_SUCCESS; + if (help->count > 0) { + printf("Usage: ant remove ... [-g]\n\n"); + printf("Remove packages from dependencies.\n"); + printf("\nOptions:\n -g, --global Remove from global packages\n"); + } else if (nerrors > 0) { + arg_print_errors(stdout, end, "ant remove"); + exitcode = EXIT_FAILURE; + } else { + for (int i = 0; i < pkgs->count && exitcode == EXIT_SUCCESS; i++) { + exitcode = global->count > 0 ? cmd_remove_global(pkgs->sval[i]) : cmd_remove(pkgs->sval[i]); + } + } + + arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0])); + return exitcode; +} + +int pkg_cmd_trust(int argc, char **argv) { + struct arg_str *pkgs = arg_strn(NULL, NULL, "", 0, 100, NULL); + struct arg_lit *all = arg_lit0("a", "all", "trust all packages with lifecycle scripts"); + struct arg_lit *help = arg_lit0("h", "help", "display help"); + struct arg_end *end = arg_end(5); + + void *argtable[] = { pkgs, all, help, end }; + int nerrors = arg_parse(argc, argv, argtable); + + int exitcode = EXIT_SUCCESS; + if (help->count > 0) { + printf("Usage: ant trust [packages...] [--all]\n\n"); + printf("Run lifecycle scripts for packages.\n"); + printf(" --all, -a Trust and run all pending lifecycle scripts\n"); + } else if (nerrors > 0) { + arg_print_errors(stdout, end, "ant trust"); + exitcode = EXIT_FAILURE; + } else { + exitcode = cmd_trust(pkgs->sval, pkgs->count, all->count > 0); + } + + arg_freetable(argtable, sizeof(argtable)/sizeof(argtable[0])); + return exitcode; +} + +int pkg_cmd_run(int argc, char **argv) { + if (argc < 2) { + printf("Usage: ant run