diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed02739b..3b9aadc5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,7 +3,7 @@ name: CI permissions: contents: read -on: [push, pull_request] +on: [push, pull_request, workflow_dispatch] jobs: build-linux: @@ -87,18 +87,76 @@ jobs: .github/workflows/freebsd-build.sh x86_64 .github/workflows/ci-run-tests.sh + build-alpine: + runs-on: ubuntu-22.04 + container: + image: alpine:3.20 + # Needed to attach to a binary + options: --security-opt seccomp=unconfined + name: Build and test Alpine executable + steps: + - uses: actions/checkout@v4 + - name: Install tools and dependencies + run: | + apk add --update --no-cache \ + libcurl \ + libdw \ + zlib \ + libgcc \ + libstdc++ \ + \ + bash \ + patchelf \ + binutils \ + python3 \ + coreutils-env \ + \ + binutils-dev \ + build-base \ + cmake \ + git \ + curl-dev \ + curl-static \ + libdw \ + openssl-dev \ + ninja-build \ + python3 \ + zlib-dev \ + elfutils-dev \ + libstdc++-dev + - name: Run the tests + run: | + chmod u+x .github/workflows/freebsd-build.sh .github/workflows/ci-run-tests.sh + .github/workflows/freebsd-build.sh x86_64 + .github/workflows/ci-run-tests.sh + build-and-push-docker-image: name: Build and push to Docker Hub needs: [build-linux, build-osx, build-freebsd] runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - { + BUILD_OS: "alpine", + IMAGE_TAG_SUFFIX: "-alpine", + PLATFORM: "linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/riscv64" + } + - { + BUILD_OS: "debian", + IMAGE_TAG_SUFFIX: "", + PLATFORM: "linux/386,linux/amd64,linux/arm/v5,linux/arm/v7,linux/arm64/v8" + } environment: name: "Docker Hub" url: https://hub.docker.com/r/kcov/kcov - if: github.ref == 'refs/heads/master' + if: ${{ github.ref == 'refs/heads/master' }} or ${{ github.event_name == 'workflow_dispatch' }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 + fetch-tags: true - name: Docker Hub login uses: docker/login-action@v3 @@ -113,29 +171,25 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push the docker image (latest) - # https://github.com/docker/buildx#building - run: | - docker buildx build \ - --tag $IMAGE_TAG \ - --platform $PLATFORM \ - --push \ - . + - name: Build and push the docker image (latest/${{ matrix.BUILD_OS }}) + run: .github/workflows/docker-build.sh ${{ matrix.BUILD_OS }} env: - IMAGE_TAG: kcov/kcov:latest - # All (build on the base image): + ACTION: push + IMAGE_TAG: kcov/kcov:latest${{ matrix.IMAGE_TAG_SUFFIX }} + # All (built on the base image): # - linux/386 # - linux/amd64 - # - linux/arm/v6 + # - linux/arm/v6 -> Only Alpine + # - linux/arm/v5 -> Only Debian # - linux/arm/v7 # - linux/arm64/v8 # - linux/ppc64le # - linux/mips64le - # - linux/riscv64 + # - linux/riscv64 -> Only Alpine # - linux/s390x # Does not build: # - linux/s390x # - linux/mips64le # No users on this platform: # - linux/ppc64le - PLATFORM: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/riscv64 + PLATFORM: ${{ matrix.PLATFORM }} diff --git a/.github/workflows/docker-build.sh b/.github/workflows/docker-build.sh new file mode 100755 index 00000000..c1777de8 --- /dev/null +++ b/.github/workflows/docker-build.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +set -eu + +if [ "${1:-}" = "alpine" ]; then + BUILD_OS="alpine" + BUILD_BASE="3.20" +elif [ "${1:-}" = "debian" ]; then + BUILD_OS="debian" + BUILD_BASE="bookworm-slim" +else + echo "Usage: docker-build.sh alpine" + echo "Usage: docker-build.sh debian" + exit 1 +fi + +if [ -z "${RELEASE_VERSION:-}" ]; then + RELEASE_VERSION="$(git describe --abbrev=4 --tags HEAD || git describe --abbrev=4 HEAD || git rev-parse HEAD)" +fi + +set -x + +echo "Building for: ${BUILD_OS}" +echo "Release: ${RELEASE_VERSION}" + +# https://github.com/docker/buildx#building +docker buildx build \ + --build-arg VCS_REF="$(git rev-parse HEAD)" \ + --build-arg BUILD_DATE="$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \ + --build-arg BUILD_OS="${BUILD_OS}" \ + --build-arg RELEASE_VERSION="${RELEASE_VERSION}" \ + --build-arg BUILD_BASE="${BUILD_BASE}" \ + --tag ${IMAGE_TAG:-kcov} \ + --progress ${PROGRESS_MODE:-plain} \ + --platform ${PLATFORM:-linux/amd64} \ + --pull \ + --${ACTION:-load} \ + . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6cff164e..dd4a1ba3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -37,6 +37,20 @@ jobs: name: Build and push to Docker Hub needs: [build] runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - { + BUILD_OS: "alpine", + IMAGE_TAG_SUFFIX: "-alpine", + PLATFORM: "linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/riscv64" + } + - { + BUILD_OS: "debian", + IMAGE_TAG_SUFFIX: "", + PLATFORM: "linux/386,linux/amd64,linux/arm/v5,linux/arm/v7,linux/arm64/v8" + } environment: name: "Docker Hub" url: https://hub.docker.com/r/kcov/kcov @@ -44,6 +58,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + fetch-tags: true - name: Docker Hub login uses: docker/login-action@v3 @@ -58,32 +73,28 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push the docker image (${{ env.RELEASE_TAG }}) - # https://github.com/docker/buildx#building - run: | - docker buildx build \ - --tag $IMAGE_TAG \ - --platform $PLATFORM \ - --push \ - . + - name: Build and push the docker image (v${{ env.RELEASE_TAG }}/${{ matrix.BUILD_OS }}) + run: .github/workflows/docker-build.sh ${{ matrix.BUILD_OS }} env: - IMAGE_TAG: kcov/kcov:${{ env.RELEASE_TAG }} - # All (build on the base image): + ACTION: push + IMAGE_TAG: kcov/kcov:${{ env.RELEASE_TAG }}${{ matrix.IMAGE_TAG_SUFFIX }} + # All (built on the base image): # - linux/386 # - linux/amd64 - # - linux/arm/v6 + # - linux/arm/v6 -> Only Alpine + # - linux/arm/v5 -> Only Debian # - linux/arm/v7 # - linux/arm64/v8 # - linux/ppc64le # - linux/mips64le - # - linux/riscv64 + # - linux/riscv64 -> Only Alpine # - linux/s390x # Does not build: # - linux/s390x # - linux/mips64le # No users on this platform: # - linux/ppc64le - PLATFORM: linux/386,linux/amd64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/riscv64 + PLATFORM: ${{ matrix.PLATFORM }} create_release: name: Create release diff --git a/Dockerfile b/Dockerfile index c0045717..2eab6e81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,45 +1,75 @@ -FROM alpine:3.20 AS builder +ARG BUILD_OS +ARG BUILD_BASE -RUN apk add --update --no-cache \ +FROM ${BUILD_OS}:${BUILD_BASE} AS builder + +ARG BUILD_OS +ARG BUILD_BASE + +RUN set -eux; \ + echo "Building for: ${BUILD_OS}"; \ + if [ "${BUILD_OS}" = "alpine" ]; then \ + apk add --update --no-cache \ binutils-dev \ - # Debian: build-essential build-base \ cmake \ git \ - # Debian: libcurl4-openssl-dev curl-dev \ curl-static \ - # Debian: libdw-dev libdw \ - # Debian, alpine bundled in binutils-dev - # libiberty-dev \ - # Debian: libssl-dev openssl-dev \ ninja-build \ python3 \ - # Debian: zlib1g-dev zlib-dev \ - # Debian: libelf-dev elfutils-dev \ - # Debian: libstdc++-12-dev libstdc++-dev \ - ; + ; \ + elif [ "${BUILD_OS}" = "debian" ]; then \ + apt-get update; \ + apt-get install -y --no-install-recommends --no-install-suggests \ + binutils-dev \ + build-essential \ + cmake \ + git \ + libcurl4-openssl-dev \ + libdw-dev \ + # Alpine: bundled in binutils-dev + libiberty-dev \ + libssl-dev \ + ninja-build \ + python3 \ + zlib1g-dev \ + libelf-dev \ + libstdc++-12-dev \ + ; \ + fi ADD . /src/ -RUN mkdir /src/build && \ - cd /src/build && \ - export PATH="$PATH:/usr/lib/ninja-build/bin/" && \ - cmake -G 'Ninja' .. && \ - cmake --build . && \ - cmake --build . --target install +RUN set -eux; \ + mkdir /src/build; \ + cd /src/build; \ + if [ "${BUILD_OS}" = "alpine" ]; then \ + export PATH="$PATH:/usr/lib/ninja-build/bin/"; \ + fi; \ + cmake -G 'Ninja' ..; \ + cmake --build .; \ + cmake --build . --target install; + +ARG BUILD_OS +ARG BUILD_BASE + +FROM ${BUILD_OS}:${BUILD_BASE} -FROM alpine:3.20 +ARG BUILD_OS +ARG BUILD_BASE COPY --from=builder /usr/local/bin/kcov* /usr/local/bin/ COPY --from=builder /usr/local/share/doc/kcov /usr/local/share/doc/kcov RUN set -eux; \ + echo "Building for: ${BUILD_OS}"; \ + if [ "${BUILD_OS}" = "alpine" ]; then \ apk add --update --no-cache \ bash \ libcurl \ @@ -47,9 +77,26 @@ RUN set -eux; \ zlib \ libgcc \ libstdc++ \ + # libbfd.so + binutils-dev \ + # To avoid changing the value of --python-parser + python3 \ ; \ + elif [ "${BUILD_OS}" = "debian" ]; then \ + apt-get update; \ + apt-get install -y --no-install-recommends --no-install-suggests \ + libcurl4 \ + libdw1 \ + zlib1g \ + libbfd-dev \ + # To avoid changing the value of --python-parser + python-is-python3 \ + ; \ + apt-get clean; \ + rm -rf /var/lib/apt/lists/*; \ + fi; \ # Write a test script - echo -e '#!/usr/bin/env bash\nif [[ true ]]; then\necho "Hello, kcov!"\nfi' > /tmp/test-executable.sh; \ + printf '#!/usr/bin/env bash\nif [[ true ]]; then\necho "Hello, kcov!"\nfi' > /tmp/test-executable.sh; \ # Test kcov kcov --include-pattern=/tmp/test-executable.sh /tmp/coverage /tmp/test-executable.sh; \ # Display @@ -60,4 +107,29 @@ RUN set -eux; \ # Cleanup rm -r /tmp/coverage /tmp/test-executable.sh; +# Metadata +LABEL org.label-schema.vendor="Kcov" \ + org.label-schema.url="https://github.com/SimonKagstrom/kcov#readme" \ + org.label-schema.name="The docker image for kcov" \ + org.label-schema.description="A docker image for kcov" \ + org.label-schema.version=${RELEASE_VERSION} \ + org.label-schema.vcs-url="https://github.com/SimonKagstrom/kcov.git" \ + org.label-schema.vcs-ref=${VCS_REF} \ + org.label-schema.build-date=${BUILD_DATE} \ + org.label-schema.docker.schema-version="1.0" \ + \ + com.docker.extension.publisher-url="https://github.com/SimonKagstrom" \ + \ + org.opencontainers.image.title="The docker image for kcov" \ + org.opencontainers.image.description="A docker image for kcov" \ + org.opencontainers.image.authors="https://github.com/SimonKagstrom" \ + org.opencontainers.image.url="https://github.com/SimonKagstrom/kcov#readme" \ + org.opencontainers.image.documentation="https://github.com/SimonKagstrom/kcov#readme" \ + org.opencontainers.image.source="https://github.com/SimonKagstrom/kcov" \ + org.opencontainers.image.vendor="Kcov" \ + org.opencontainers.image.licenses="GPL-2.0" \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.version=${RELEASE_VERSION} \ + org.opencontainers.image.revision=${VCS_REF} + CMD ["/usr/local/bin/kcov"] diff --git a/INSTALL.md b/INSTALL.md index dc298e85..a1cab838 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -10,14 +10,24 @@ elfutils-devel and *not* elfutils-libelf-devel. FreeBSD ------- + +```sh pkg install binutils cmake elfutils python +``` -Ubuntu ------- -Install +Alpine +------- +```sh +apk add --update --no-cache curl-dev curl-static libdw openssl-dev zlib-dev elfutils-dev libstdc++-dev ``` - apt-get install binutils-dev libssl-dev libcurl4-openssl-dev zlib1g-dev libdw-dev libiberty-dev + +Ubuntu / Debian +--------------- +Install + +```sh +apt-get install binutils-dev libssl-dev libcurl4-openssl-dev libelf-dev libstdc++-12-dev zlib1g-dev libdw-dev libiberty-dev ``` Fedora / Centos / RHEL @@ -28,7 +38,7 @@ Mac OS X -------- Install dependencies: -``` +```sh brew install zlib bash cmake pkgconfig dwarfutils openssl ``` @@ -36,7 +46,7 @@ OSX build instructions (see Issue #166 / Issue #357): Create an empty build dir and do the following steps (adjust openssl path, can also be in /opt): -``` +```sh cd cmake -G make -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl -DOPENSSL_LIBRARIES=/usr/local/opt/openssl/lib make @@ -48,7 +58,7 @@ If the binary needs to be signed, try these instructions: First get a code signing identity, since debug support is needed to run as non-root: -``` +```sh codesign -s - --entitlements /osx-entitlements.xml -f ./src/Release/kcov ``` @@ -57,16 +67,18 @@ Building Create an empty build dir and do the following steps: - cmake [options] - make - make install +```sh +cmake [options] +make +make install +``` Useful options include -DCMAKE_INSTALL_PREFIX= (installation prefix), -DCMAKE_BUILD_TYPE= and -DCMAKE_C_FLAGS=. Basic example: -``` +```sh cd /path/to/kcov/source/dir mkdir build cd build @@ -77,7 +89,7 @@ Basic example: More advanced example: -``` +```sh cd /path/to/kcov/source/dir mkdir build cd build @@ -100,7 +112,7 @@ Troubleshooting If you have elfutils installed, but cmake fails to find it, specify elfutils install prefix explicitly to cmake. Here is an example: -``` +```sh cd kcov CMAKE_PREFIX_PATH=/opt/elfutils-dir/ \ cmake . diff --git a/README.md b/README.md index 5556dfff..5cbd546c 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ For a video introduction, [look at this presentation from SwedenCPP](https://www Installing ---------- -Refer to the [INSTALL](INSTALL.md) file for build instructions, or use our official Docker images: +Refer to the [INSTALL](INSTALL.md) file for build instructions, or use our official Docker image (`kcov/kcov`): * [kcov/kcov](https://hub.docker.com/r/kcov/kcov/) for releases since v31. @@ -27,7 +27,7 @@ How to use it ------------- Basic usage is straight-forward: -``` +```sh kcov /path/to/outdir executable [args for the executable] ``` @@ -41,7 +41,7 @@ Filtering output It's often useful to filter output, since e.g., /usr/include is seldom of interest. This can be done in two ways: -``` +```sh kcov --exclude-pattern=/usr/include --include-pattern=part/of/path,other/path \ /path/to/outdir executable ``` @@ -50,7 +50,7 @@ which will do a string-comparison and include everything which contains *part/of/path* or *other/path* but exclude everything that has the */usr/include* string in it. -``` +```sh kcov --include-path=/my/src/path /path/to/outdir executable kcov --exclude-path=/usr/include /path/to/outdir executable ``` @@ -63,7 +63,7 @@ Kcov can also merge the results of multiple earlier runs. To use this mode, call kcov with `--merge`, an output path and one or more paths to an earlier run, e.g., -``` +```sh kcov --merge /tmp/merged-output /tmp/kcov-output1 /tmp/kcov-output2 kcov --merge /tmp/merged-output /tmp/kcov-output* # With a wildcard ``` diff --git a/doc/docker.md b/doc/docker.md new file mode 100644 index 00000000..a51f58af --- /dev/null +++ b/doc/docker.md @@ -0,0 +1,16 @@ +# The docker image + +Kcov has a docker image: [kcov/kcov](https://hub.docker.com/r/kcov/kcov/) and tags since v31. + +There is two variants (tags): +- `latest` or `vXX` that are Debian based +- `latest-alpine` or `vXX-alpine` that are Alpine based + +## Permissions + +kcov needs access the following system calls: +- [`ptrace`](https://linux.die.net/man/2/ptrace) +- [`process_vm_readv`](https://linux.die.net/man/2/process_vm_readv)/[`process_vm_writev`](https://linux.die.net/man/2/process_vm_writev) +- [`personality`](https://linux.die.net/man/2/personality) + +You may need to use `--security-opt seccomp=unconfined` as a docker run option to attach to processes. diff --git a/tests/tools/test_basic.py b/tests/tools/test_basic.py index b8f9d10e..064020aa 100644 --- a/tests/tools/test_basic.py +++ b/tests/tools/test_basic.py @@ -26,7 +26,7 @@ def runTest(self): class lookup_binary_in_path(libkcov.TestCase): @unittest.expectedFailure def runTest(self): - os.environ["PATH"] += self.sources + "/tests/python" + os.environ["PATH"] += os.pathsep + self.sources + "/tests/python" noKcovRv, o = self.do(self.sources + "/tests/python/main 5") rv, o = self.do(self.kcov + " " + self.outbase + "/kcov " + "main 5") diff --git a/tests/tools/test_compiled.py b/tests/tools/test_compiled.py index 69fa61e8..ff2a3054 100644 --- a/tests/tools/test_compiled.py +++ b/tests/tools/test_compiled.py @@ -292,6 +292,8 @@ def runTest(self): + self.binaries + "/issue31", False, + # 60 seconds + timeout=60 ) dom = cobertura.parseFile(self.outbase + "/kcov/issue31/cobertura.xml") self.skipTest("Fickle test, ignoring") @@ -313,6 +315,8 @@ def runTest(self): + self.binaries + "/thread-test", False, + # 60 seconds + timeout=60 ) dom = cobertura.parseFile(self.outbase + "/kcov/thread-test/cobertura.xml") self.skipTest("Fickle test, ignoring")