diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..fa651c2 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "Pskel (for Codespaces)", + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "maelvalais.autoconf", + "ms-azuretools.vscode-docker", + "editorconfig.editorconfig", + "markis.code-coverage" + ] + } + }, + "dockerComposeFile": "./../compose.yaml", + "service": "shell" +} diff --git a/.devcontainer/local/devcontainer.json b/.devcontainer/local/devcontainer.json new file mode 100644 index 0000000..f7491f2 --- /dev/null +++ b/.devcontainer/local/devcontainer.json @@ -0,0 +1,21 @@ +{ + "name": "Pskel (for Local)", + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools", + "ms-vscode.cpptools-extension-pack", + "maelvalais.autoconf", + "ms-azuretools.vscode-docker", + "editorconfig.editorconfig", + "markis.code-coverage" + ] + } + }, + "dockerComposeFile": "./../../compose.yaml", + "service": "shell", + "mounts": [ + "source=${localWorkspaceFolder},target=/workspaces/pskel,type=bind,consistency=cached" + ], + "workspaceFolder": "/workspaces/pskel" +} diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..723dc5c --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +**/.git +**/README.md +**/LICENSE +**/.editorconfig diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..10f7f6b --- /dev/null +++ b/.editorconfig @@ -0,0 +1,46 @@ +# https://editorconfig.org/ + +root = true + +[*] +trim_trailing_whitespace = true +insert_final_newline = true +end_of_line = lf +charset = utf-8 +tab_width = 4 + +[{*.{awk,bat,c,cpp,d,dasc,h,l,re,skl,w32,y},Makefile*}] +indent_size = 4 +indent_style = tab + +[*.{dtd,html,inc,php,phpt,rng,wsdl,xml,xsd,xsl}] +indent_size = 4 +indent_style = space + +[*.{ac,m4,sh,yml}] +indent_size = 2 +indent_style = space + +[*.md] +indent_style = space +max_line_length = 80 + +[COMMIT_EDITMSG] +indent_size = 4 +indent_style = space +max_line_length = 80 + +[*.patch] +trim_trailing_whitespace = false + +[*.json] +indent_size = 2 +indent_style = space + +[compose.yaml] +indent_size = 2 +indent_style = space + +[Dockerfile] +indent_size = 2 +indent_style = space diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..2bbfda4 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "docker" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/octocov.yml b/.github/octocov.yml new file mode 100644 index 0000000..8c11969 --- /dev/null +++ b/.github/octocov.yml @@ -0,0 +1,17 @@ +coverage: + paths: + - ../lcov.info + acceptable: current >= 60% +testExecutionTime: + if: false +comment: + if: is_pull_request +diff: + datastores: + - artifact://${GITHUB_REPOSITORY} +comment: + if: is_pull_request +report: + if: is_default_branch + datastores: + - artifact://${GITHUB_REPOSITORY} diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..59133f3 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,94 @@ +name: CI +on: + push: + branches: + - main + pull_request: + schedule: + - cron: '0 0 * * 1' +permissions: + contents: write + pull-requests: write +jobs: + Linux: + runs-on: ubuntu-latest + strategy: + matrix: + platform: ['linux/amd64', 'linux/arm64/v8', 'linux/s390x'] + version: ['8.1', '8.2', '8.3'] + type: ['cli', 'zts'] + distro: ['bookworm', 'alpine'] + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + - name: Setup QEMU + uses: docker/setup-qemu-action@v3 + with: + platforms: "arm64,s390x" + - name: Setup buildx + uses: docker/setup-buildx-action@v3 + - name: Build container + run: | + docker compose build --pull --no-cache --build-arg PLATFORM="${{ matrix.platform }}" --build-arg IMAGE="php" --build-arg TAG="${{ matrix.version }}-${{ matrix.type }}-${{ matrix.distro }}" + - name: Test extension with Bundled PHP + run: | + docker compose run --rm shell pskel test + - name: Test extension with PHP Debug Build + if: matrix.platform == 'linux/amd64' + run: | + docker compose run --rm shell pskel test debug + - name: Test extension with Valgrind + if: matrix.platform == 'linux/amd64' + run: | + docker compose run --rm shell pskel test valgrind + - name: Test extension with LLVM Sanitizer (MemorySanitizer) + if: matrix.platform == 'linux/amd64' && matrix.distro != 'alpine' + run: | + docker compose run --rm shell pskel test msan + - name: Test extension with LLVM Sanitizer (AddressSanitizer) + if: matrix.platform == 'linux/amd64' && matrix.distro != 'alpine' + run: | + docker compose run --rm shell pskel test asan + - name: Test extension with LLVM Sanitizer (UndefinedBehaviorSanitizer) + if: matrix.platform == 'linux/amd64' && matrix.distro != 'alpine' + run: | + docker compose run --rm shell pskel test ubsan + # Windows: + # runs-on: windows-2022 + # defaults: + # run: + # shell: cmd + # strategy: + # matrix: + # platform: ["x64"] + # version: ["8.1", "8.2", "8.3"] + # ts: ["nts", "ts"] + # steps: + # - name: Checkout + # uses: actions/checkout@v4 + # - name: Setup PHP + # id: setup-php + # uses: php/setup-php-sdk@v0.8 + # with: + # platform: ${{ matrix.platform }} + # version: ${{ matrix.version }} + # ts: ${{ matrix.ts }} + # - name: Enable developer command prompt + # uses: ilammy/msvc-dev-cmd@v1 + # with: + # platform: ${{ matrix.platform }} + # toolset: ${{ steps.setup-php.outputs.toolset }} + # - name: phpize + # working-directory: ext + # run: phpize + # - name: configure + # working-directory: ext + # run: configure --enable-SKELETON_NAME --with-prefix=${{ steps.setup-php.outputs.prefix }} + # - name: make + # working-directory: ext + # run: nmake + # - name: test + # working-directory: ext + # run: nmake test TESTS="--show-diff tests" diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml new file mode 100644 index 0000000..89ca889 --- /dev/null +++ b/.github/workflows/coverage.yaml @@ -0,0 +1,65 @@ +name: Coverage +permissions: + contents: write + pull-requests: write +on: + push: + branches: + - main + pull_request: + types: + - opened + - synchronize + - reopened +jobs: + Linux: + runs-on: ubuntu-latest + strategy: + matrix: + version: ['8.3'] + type: ['cli', 'zts'] + distro: ['bookworm'] + outputs: + matrix: ${{ toJson(matrix) }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + submodules: true + - name: Setup buildx + uses: docker/setup-buildx-action@v3 + - name: Build container + run: | + docker compose build --pull --no-cache --build-arg PLATFORM="linux/amd64" --build-arg IMAGE="php" --build-arg TAG="${{ matrix.version }}-${{ matrix.type }}-${{ matrix.distro }}" + - name: Test with gcov + run: | + docker compose run -v "$(pwd)/ext:/ext" --rm shell pskel coverage + - name: Upload coverage to artifact + uses: actions/upload-artifact@v4 + with: + name: coverage-${{ matrix.version }}-${{ matrix.type }}-${{ matrix.distro }} + path: ${{ github.workspace }}/ext/lcov.info + Coverage: + needs: [Linux] + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Download coverage artifacts + uses: actions/download-artifact@v4 + - name: Merge coverages + run: | + sudo apt-get install -y "lcov" + LCOV_FILES="$(find . -name "lcov.info")" + CMD="$(which "lcov")" + for LCOV_FILE in ${LCOV_FILES}; do + CMD+=" -a ${LCOV_FILE}" + done + CMD+=" -o lcov.info" + echo "Merging coverages: ${LCOV_FILES}" + ${CMD} + - name: Report coverage + uses: k1LoW/octocov-action@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + config: .github/octocov.yml diff --git a/.gitignore b/.gitignore index d7cc116..09b9410 100644 --- a/.gitignore +++ b/.gitignore @@ -1,45 +1,4 @@ -*.lo -*.la -*.dep -.libs -acinclude.m4 -aclocal.m4 -autom4te.cache -build -config.guess -config.h -config.h.in -config.h.in~ -config.log -config.nice -config.status -config.sub -configure -configure~ -configure.ac -configure.in -include -install-sh -libtool -ltmain.sh -Makefile -Makefile.fragments -Makefile.global -Makefile.objects -missing -mkinstalldirs -modules -php_test_results_*.txt -phpt.* -run-test-info.php -run-tests.php -tests/**/*.diff -tests/**/*.out -tests/**/*.php -tests/**/*.exp -tests/**/*.log -tests/**/*.sh -tests/**/*.db -tests/**/*.mem -tmp-php.ini -xpass-*.tgz +/ext/*.dep +/ext/*~ +*.DS_Store +lcov.info diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..a65a6c5 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,21 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/local/include/php", + "/usr/local/include/php/TSRM", + "/usr/local/include/php/Zend", + "/usr/local/include/php/ext", + "/usr/local/include/php/include", + "/usr/local/include/php/main", + "/usr/local/include/php/sapi" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "c99" + } + ], + "version": 4 +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..968d4dc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,29 @@ +{ + "files.associations": { + "*.phpt": "php", + "*.c": "c", + "*.h": "c" + }, + "files.exclude": { + "**/.git": true, + "**/.DS_Store": true, + "**/Thumbs.db": true, + "**/.libs": true, + "**/*.cache": true, + "ext/**/*.dep": true, + "ext/**/*.la": true, + "ext/**/*.lo": true, + "ext/build": true, + "ext/config.h": true, + "ext/config.h.*": true, + "ext/config.nice": true, + "ext/config.status": true, + "ext/configure.ac": true, + "ext/configure~": true, + "ext/libtool": true, + "ext/Makefile.*": true, + }, + "markiscodecoverage.enableDecorations": true, + "markiscodecoverage.enableOnStartup": true, + "markiscodecoverage.searchCriteria": "ext/lcov.info" +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..72abe24 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,52 @@ +ARG PLATFORM=${BUILDPLATFORM:-linux/amd64} +ARG IMAGE=php +ARG TAG=8.3-cli-bookworm + +FROM --platform=${PLATFORM} ${IMAGE}:${TAG} + +ENV USE_ZEND_ALLOC=0 +ENV USE_TRACKED_ALLOC=1 +ENV ZEND_DONT_UNLOAD_MODULES=1 +ENV LC_ALL="C" + +RUN docker-php-source extract \ + && if test -f "/etc/debian_version"; then \ + echo "deb http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main" > "/etc/apt/sources.list.d/llvm.list" \ + && echo "deb-src http://apt.llvm.org/bookworm/ llvm-toolchain-bookworm main" >> "/etc/apt/sources.list.d/llvm.list" \ + && curl -fsSL "https://apt.llvm.org/llvm-snapshot.gpg.key" -o "/etc/apt/trusted.gpg.d/apt.llvm.org.asc" \ + && apt-get update \ + && DEBIAN_FRONTEND="noninteractive" apt-get install -y "bison" "re2c" "zlib1g-dev" "libsqlite3-dev" "libxml2-dev" \ + "autoconf" "pkg-config" "make" "gcc" "valgrind" "rsync" "git" "ssh" \ + "clang-20" \ + "lcov" "gzip" \ + "vim" \ + && update-alternatives --install "/usr/bin/clang" clang "/usr/bin/clang-20" 100 \ + && update-alternatives --install "/usr/bin/clang++" clang++ "/usr/bin/clang++-20" 100; \ + else \ + apk add --no-cache "bison" "zlib-dev" "sqlite-dev" "libxml2-dev" \ + "autoconf" "pkgconfig" "make" "gcc" "g++" "valgrind" "valgrind-dev" \ + "musl-dev" "rsync" "git" "openssh" \ + "patch" "lcov" "gzip" \ + "vim"; \ + fi + +COPY ./pskel.sh /usr/local/bin/pskel +COPY ./patches /patches +COPY ./ext /ext + +# ---- + +ARG CFLAGS="" + +RUN if test -f "/etc/debian_version"; then \ + apt-get update && apt-get install -y "libtool"; \ + else \ + apk add --no-cache "libtool" "automake" "linux-headers"; \ + fi \ + && git clone --depth=1 --branch="v4.4.36" "https://github.com/besser82/libxcrypt.git" "/libxcrypt" \ + && cd "/libxcrypt" \ + && ./autogen.sh \ + && CFLAGS="${CFLAGS}" ./configure --enable-hashes="all" \ + && make -j"$(nproc)" \ + && make install \ + && cd - diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..4e3de7a --- /dev/null +++ b/compose.yaml @@ -0,0 +1,22 @@ +services: + shell: + build: + context: ./ + dockerfile: ./Dockerfile + cap_add: + - SYS_ADMIN + security_opt: + - seccomp:unconfined + privileged: true + tty: true + # depends_on: + # - mysql + # mysql: + # image: mysql:8.0 + # environment: + # MYSQL_ROOT_PASSWORD: testing + # MYSQL_DATABASE: testing + # MYSQL_USER: testing + # MYSQL_PASSWORD: testing + # volumes: + # - ./docker/mysql/etc/mysql/conf.d/my.cnf:/etc/mysql/conf.d/my.cnf diff --git a/ext/.gitignore b/ext/.gitignore new file mode 100644 index 0000000..d7cc116 --- /dev/null +++ b/ext/.gitignore @@ -0,0 +1,45 @@ +*.lo +*.la +*.dep +.libs +acinclude.m4 +aclocal.m4 +autom4te.cache +build +config.guess +config.h +config.h.in +config.h.in~ +config.log +config.nice +config.status +config.sub +configure +configure~ +configure.ac +configure.in +include +install-sh +libtool +ltmain.sh +Makefile +Makefile.fragments +Makefile.global +Makefile.objects +missing +mkinstalldirs +modules +php_test_results_*.txt +phpt.* +run-test-info.php +run-tests.php +tests/**/*.diff +tests/**/*.out +tests/**/*.php +tests/**/*.exp +tests/**/*.log +tests/**/*.sh +tests/**/*.db +tests/**/*.mem +tmp-php.ini +xpass-*.tgz diff --git a/bench.php b/ext/bench.php similarity index 100% rename from bench.php rename to ext/bench.php diff --git a/config.m4 b/ext/config.m4 similarity index 100% rename from config.m4 rename to ext/config.m4 diff --git a/php_xpass.h b/ext/php_xpass.h similarity index 100% rename from php_xpass.h rename to ext/php_xpass.h diff --git a/tests/sha512.phpt b/ext/tests/sha512.phpt similarity index 100% rename from tests/sha512.phpt rename to ext/tests/sha512.phpt diff --git a/tests/xpass.phpt b/ext/tests/xpass.phpt similarity index 100% rename from tests/xpass.phpt rename to ext/tests/xpass.phpt diff --git a/tests/yescrypt.phpt b/ext/tests/yescrypt.phpt similarity index 100% rename from tests/yescrypt.phpt rename to ext/tests/yescrypt.phpt diff --git a/xpass.c b/ext/xpass.c similarity index 100% rename from xpass.c rename to ext/xpass.c diff --git a/package.xml b/package.xml index a4f9d1e..ab4c630 100644 --- a/package.xml +++ b/package.xml @@ -32,18 +32,20 @@ distributions, using extended crypt library (libxcrypt): - - - + + + + + + + + + + - - - - - diff --git a/patches/ltmain.sh.patch b/patches/ltmain.sh.patch new file mode 100644 index 0000000..70d0822 --- /dev/null +++ b/patches/ltmain.sh.patch @@ -0,0 +1,11 @@ +--- ext/build/ltmain.sh 2024-08-20 13:47:39.489351765 +0000 ++++ ext/build/ltmain.sh 2024-08-20 13:51:02.825496572 +0000 +@@ -3467,7 +3467,7 @@ EOF + tempremovelist=`$echo "$output_objdir/*"` + for p in $tempremovelist; do + case $p in +- *.$objext) ++ *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then diff --git a/pskel.sh b/pskel.sh new file mode 100755 index 0000000..5621c27 --- /dev/null +++ b/pskel.sh @@ -0,0 +1,213 @@ +#!/bin/sh + +set -e + +get_ext_dir() { + PSKEL_EXT_DIR="/ext" + + if [ -d "${CODESPACE_VSCODE_FOLDER}" ]; then + echo "[Pskel] GitHub Codespace workspace detected, using \"${CODESPACE_VSCODE_FOLDER}/ext\"." >&2 + PSKEL_EXT_DIR="${CODESPACE_VSCODE_FOLDER}/ext" + elif [ -d "/workspaces/pskel/ext" ]; then + echo "[Pskel] Development Containers workspace detected, using \"/workspaces/pskel/ext\"." >&2 + PSKEL_EXT_DIR="/workspaces/pskel/ext" + elif [ -f "/ext/.gitkeep" ] && [ "$(cat "/ext/.gitkeep")" = "pskel_uninitialized" ] && [ "${1}" != "--no-init" ]; then + echo "[Pskel] Uninitialized project detected, initializing default skeleton." >&2 + cmd_init "skeleton" >&2 + fi + + echo "${PSKEL_EXT_DIR}" +} + +cmd_usage() { + cat << EOF +Usage: ${0} [task] ... + +Available commands: + init create new extension + test test extension + build build PHP runtime + coverage generate code coverage +EOF +} + +cmd_init() { + case "${1}" in + -h|--help) + cat << EOF +Usage: ${0} init [extension_name] [ext_skel.php options...] +EOF + return 0 + ;; + "") + echo "Error: Extension name is required." >&2 + return 1 + ;; + esac + + PSKEL_EXT_DIR="$(get_ext_dir --no-init)" + /usr/local/bin/php "/usr/src/php/ext/ext_skel.php" --ext "${1}" --dir "/tmp" "${@}" + rm "${PSKEL_EXT_DIR}/.gitkeep" + rsync -av "/tmp/${1}/" "${PSKEL_EXT_DIR}/" + rm -rf "/tmp/${1}" +} + +cmd_test() { + case "${1}" in + -h|--help) + cat << EOF +Usage: ${0} test [test_type|php_binary_name] +Environment variables: + CFLAGS, CPPFLAGS: Compilation flags + TEST_PHP_ARGS: Test flags +EOF + return 0 + ;; + debug|gcov|valgrind) + CC="$(command -v "gcc")" + CXX="$(command -v "g++")" + case "${1}" in + debug) build_php_if_not_exists "debug";; + gcov) + CONFIGURE_OPTS="--enable-gcov" + build_php_if_not_exists "gcov" + CFLAGS="${CFLAGS} --coverage" + ;; + valgrind) + CONFIGURE_OPTS="--with-valgrind" + build_php_if_not_exists "valgrind" + TEST_PHP_ARGS="${TEST_PHP_ARGS} -m" + ;; + esac + CMD="$(basename "${CC}")-${1}-php" + ;; + msan|asan|ubsan) + CC="$(command -v "clang")" + CXX="$(command -v "clang++")" + case "${1}" in + msan) + CONFIGURE_OPTS="--enable-memory-sanitizer" + build_php_if_not_exists "msan" + CFLAGS="${CFLAGS} -fsanitize=memory" + LDFLAGS="${LDFLAGS} -fsanitize=memory" + ;; + asan) + CONFIGURE_OPTS="--enable-address-sanitizer" + build_php_if_not_exists "asan" + CFLAGS="${CFLAGS} -fsanitize=address" + LDFLAGS="${LDFLAGS} -fsanitize=address" + ;; + ubsan) + CONFIGURE_OPTS="--enable-undefined-sanitizer" + build_php_if_not_exists "ubsan" + CFLAGS="${CFLAGS} -fsanitize=undefined" + LDFLAGS="${LDFLAGS} -fsanitize=undefined" + ;; + esac + CMD="$(basename "${CC}")-${1}-php" + ;; + "") + CMD="php" + ;; + *) + CMD="${1}" + ;; + esac + + for BIN in "${CMD}" "${CMD}ize" "${CMD}-config"; do + if ! command -v "${BIN}" >/dev/null 2>&1; then + echo "Error: Invalid argument '${CMD}', executable file not found" >&2 + exit 1 + fi + done + + PSKEL_EXT_DIR="$(get_ext_dir)" + + cd "${PSKEL_EXT_DIR}" + "${CMD}ize" + if [ "$("${CMD}" -r "echo PHP_VERSION_ID;")" -lt "80400" ]; then + patch "./build/ltmain.sh" "./../patches/ltmain.sh.patch" + echo "[Pskel] ltmain.sh patched" >&2 + fi + CC="${CC}" CXX="${CXX}" CFLAGS="${CFLAGS}" CPPFLAGS="${CPPFLAGS}" LDFLAGS="${LDFLAGS}" ./configure --with-php-config="$(command -v "${CMD}-config")" + make clean + make -j"$(nproc)" + TEST_PHP_ARGS="${TEST_PHP_ARGS} --show-diff -q" make test + cd - +} + +build_php_if_not_exists() { + if ! command -v "$(basename "${CC}")-${1}-php" >/dev/null 2>&1; then + CC="${CC}" \ + CXX="${CXX}" \ + CFLAGS="-DZEND_TRACK_ARENA_ALLOC" \ + CPPFLAGS="${CFLAGS}" \ + LDFLAGS="${LDFLAGS}" \ + CONFIGURE_OPTS="${CONFIGURE_OPTS} --enable-debug $(php -r "echo PHP_ZTS === 1 ? '--enable-zts' : '';") --enable-option-checking=fatal --disable-phpdbg --disable-cgi --disable-fpm --enable-cli --without-pcre-jit --disable-opcache-jit --disable-zend-max-execution-timers" \ + cmd_build "$(basename "${CC}")-${1}" + fi +} + +cmd_build() { + case "${1}" in + -h|--help) + cat << EOF +Usage: ${0} build [php_binary_prefix] +Environment variables: + CFLAGS, CPPFLAGS: Compilation flags + CONFIGURE_OPTS: ./configure options +EOF + return 0 + ;; + ?*) + CONFIGURE_OPTS="--program-prefix=${1}- --includedir=/usr/local/include/${1}-php ${CONFIGURE_OPTS}" + ;; + esac + + cd "/usr/src/php" + ./buildconf --force + ./configure ${CONFIGURE_OPTS} + make clean + make -j"$(nproc)" + make install + make clean + cd - +} + +cmd_coverage() { + case "${1}" in + -h|--help) + cat << EOF +Usage: ${0} coverage +Environment variables: + LCOV_OPTS: lcov capture options +EOF + return 0 + ;; + esac + + cmd_test "gcov" + + PSKEL_EXT_DIR="$(get_ext_dir)" + + lcov --capture --directory "${PSKEL_EXT_DIR}" ${LCOV_OPTS} --exclude "/usr/local/include/*" --output-file "${PSKEL_EXT_DIR}/lcov.info" + lcov --list "${PSKEL_EXT_DIR}/lcov.info" +} + +if [ $# -eq 0 ]; then + cmd_usage + exit 1 +fi + +case "${1}" in + help) shift; cmd_usage;; + init) shift; cmd_init "${@}";; + test) shift; cmd_test "${@}";; + build) shift; cmd_build "${@}";; + coverage) shift; cmd_coverage "${@}";; + *) + echo "${0} error: invalid command: '${1}'" >&2 + cmd_usage + exit 1 + ;; +esac