diff --git a/Dockerfile b/Dockerfile index 0031311..10fc963 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,62 +1,52 @@ +ARG DOCKCROSS_REGISTRY=docker.io/dockcross ARG DOCKCROSS_IMAGE=linux-arm64 ARG DOCKCROSS_VERSION=latest -FROM dockcross/$DOCKCROSS_IMAGE:$DOCKCROSS_VERSION as cannelloni-builder -ARG VERSION=1.1.0 -ARG HASH=0dcb9277b21f916f5646574b9b2229d3b8e97d5e99b935a4d0b7509a5f0ccdcd +FROM $DOCKCROSS_REGISTRY/$DOCKCROSS_IMAGE:$DOCKCROSS_VERSION as cannelloni-builder +# Version arguments that may be controlled via docker build argument +ARG CANNELLONI_VERSION=1.1.0 +ARG CANNELLONI_HASH=0dcb9277b21f916f5646574b9b2229d3b8e97d5e99b935a4d0b7509a5f0ccdcd + +# Output definitions +ENV TARGET_DIR=/tmp/cannelloni +ENV CANNELLONI_BUILD_LOG=$TARGET_DIR/build-metadata-log.txt +RUN mkdir $TARGET_DIR + +# Document docker build log arguments +ARG DOCKCROSS_REGISTRY=docker.io/dockcross ARG DOCKCROSS_IMAGE=linux-arm64 ARG DOCKCROSS_VERSION=latest +RUN echo -e "Built with $DOCKCROSS_REGISTRY/$DOCKCROSS_IMAGE:$DOCKCROSS_VERSION\n" | tee -a ${CANNELLONI_BUILD_LOG} -RUN mkdir /tmp/cannelloni - +# Version definitions +ENV CANNELLONI_VERSION=$CANNELLONI_VERSION +ENV CANNELLONI_HASH=$CANNELLONI_HASH ENV LIBSCTP_VERSION=1.0.19 -WORKDIR /tmp/libsctp -# copy installed libraries to expected target location for cmake toolchain -COPY build_libsctp.sh /build_libsctp.sh -RUN /build_libsctp.sh +ENV LIBSCTP_HASH=9251b1368472fb55aaeafe4787131bdde4e96758f6170620bc75b638449cef01 -WORKDIR /tmp/cannelloni -RUN wget https://github.com/mguentner/cannelloni/archive/refs/tags/v$VERSION.tar.gz -O cannelloni-$VERSION.tar.gz -RUN echo "$HASH cannelloni-$VERSION.tar.gz" | sha256sum --check --status -RUN tar --strip-components=1 -xvf cannelloni-$VERSION.tar.gz - -# Document build metadata -RUN echo "Built with dockcross/$DOCKCROSS_IMAGE:$DOCKCROSS_VERSION\n" | tee -a /tmp/cannelloni/build-metadata.txt - -RUN cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DSCTP_INCLUDE_DIR=/tmp/libsctp/src/include/netinet/ -DSCTP_LIBRARY=/tmp/libsctp/src/lib/.libs/ -DSCTP_SUPPORT=ON 2>&1 | tee -a /tmp/cannelloni/build-metadata.txt -RUN make 2>&1 | tee -a /tmp/cannelloni/build-metadata.txt +# Build libsctp +ENV CANNELLONI_BUILD_DIR=/tmp/cannelloni_build/ +ENV LIBSCTP_BUILD_DIR=/tmp/libsctp_build/ +WORKDIR $LIBSCTP_BUILD_DIR +COPY build_libsctp.sh /build_libsctp.sh +# https://github.com/docker/docs/blob/da4ccc81e6b1bbd585c9f5ac9a35b9c8684c5d57/content/build/building/best-practices.md#using-pipes +# If you want the command to fail due to an error at any stage in the pipe, prepend set -o pipefail && to ensure that an unexpected error prevents the build from inadvertently succeeding. For example: +RUN set -o pipefail && /build_libsctp.sh | tee -a ${CANNELLONI_BUILD_LOG} -COPY checklib.sh /checklib.sh -RUN /checklib.sh 2>&1 | tee -a /tmp/cannelloni/build-metadata.txt +# Build cannelloni +WORKDIR $CANNELLONI_BUILD_DIR +COPY build_cannelloni.sh /build_cannelloni.sh +RUN set -o pipefail && /build_cannelloni.sh | tee -a ${CANNELLONI_BUILD_LOG} +# Bundle result files: create tar file as output WORKDIR /tmp/ +RUN find $TARGET_DIR +RUN tar cf /tmp/cannelloni.tar.gz cannelloni/* -# rename libcannelloni in target directory -RUN cp --remove-destination /tmp/cannelloni/libcannelloni-common.so.0.0.1 /tmp/cannelloni/libcannelloni-common.so.0 -RUN mv cannelloni/gpl-2.0.txt cannelloni/cannelloni-license-gpl-2.0.txt -# copy libsctp license to target directory -RUN curl https://raw.githubusercontent.com/sctp/lksctp-tools/master/COPYING.lib --output /tmp/cannelloni/libsctp-license.txt -# add note about source -RUN echo "You can find a copy of the cannelloni source code here: https://github.com/mguentner/cannelloni/archive/refs/tags/v$VERSION.tar.gz " > /tmp/cannelloni/SOURCES.md -RUN echo "You can find a copy of the libsctp source code here: https://github.com/sctp/lksctp-tools" >> /tmp/cannelloni/SOURCES.md - -# create tar file as output -RUN tar cf /tmp/cannelloni.tar.gz \ - cannelloni/SOURCES.md \ - cannelloni/cannelloni cannelloni/libcannelloni-common.so.0 cannelloni/cannelloni-license-gpl-2.0.txt cannelloni/README.md \ - cannelloni/build-metadata.txt \ - cannelloni/libsctp-license.txt cannelloni/libsctp*.so* - -# create directory as output -# RUN mkdir -p /cannelloni_${DOCKCROSS_IMAGE}_${VERSION}/ -# RUN cp /tmp/cannelloni/libcannelloni-common.so.0 /tmp/cannelloni/cannelloni /tmp/cannelloni/README.md /tmp/cannelloni/gpl-2.0.txt /cannelloni_${DOCKCROSS_IMAGE}_${VERSION}/ - +# Separate build stage, all files in this stage are added to the final output FROM scratch AS export-stage ARG DOCKCROSS_IMAGE=linux-arm64 ARG VERSION=1.1.0 # copy tar file from builder COPY --from=cannelloni-builder /tmp/cannelloni.tar.gz /cannelloni_${DOCKCROSS_IMAGE}_${VERSION}.tar.gz - -# copy directory from builder -# COPY --from=cannelloni-builder /cannelloni_${DOCKCROSS_IMAGE}_${VERSION}/ /cannelloni_${DOCKCROSS_IMAGE}_${VERSION}/ diff --git a/README.md b/README.md index 91c1ca7..45a0c57 100644 --- a/README.md +++ b/README.md @@ -12,33 +12,44 @@ Uses [dockcross](https://github.com/dockcross/dockcross) to compile for the spec ## Build for multiple architectures +If building with podman, it requires at least version 4.1 (requires the output flag implemented [here](https://github.com/containers/buildah/pull/3823)). Run `python build.py` to build for all architectures. +### Description of build workflow + +The build workflow is run in docker containers (dockcross). +1. Build libsctp + * Build in temporary directory `LIBSCTP_BUILD_DIR=/tmp/libsctp_build/` + * Copy libraries and headers to cannelloni build directory `CANNELLONI_BUILD_DIR=/tmp/cannelloni_build/` + * Copy release artifacts to target directory `TARGET_DIR=/tmp/cannelloni` + * Add notes about source code origin to `${TARGET_DIR}/SOURCES.md` +2. Build cannelloni + * Build in temporary directory `CANNELLONI_BUILD_DIR=/tmp/cannelloni_build/` + * Copy release artifacts to `TARGET_DIR=/tmp/cannelloni` + * Add notes about source code origin to `${TARGET_DIR}/SOURCES.md` +3. Bundle files from `TARGET_DIR` into final release artifact. +4. Separate docker build stage is used to collect the final release artifact + ## Install cannelloni * Download appropriate architecture. ```shell - wget https://github.com/eclipse-opendut/cannelloni/releases/download/v1.1.0/cannelloni_linux-x64_1.1.0.tar.gz - tar xf cannelloni_linux-x64_1.1.0.tar.gz - ``` -* Install dependencies on Ubuntu/Debian - ```shell - # libsctp1: user-space access to Linux kernel SCTP - shared library - apt-get install -y libsctp1 - # can-utils: SocketCAN userspace utilities and tools - apt-get install -y can-utils + wget https://github.com/eclipse-opendut/cannelloni/releases/download/v1.1.0/cannelloni_linux-x64_1.1.0.tar.gz -O /tmp/cannelloni.tar.gz + cd /tmp + tar xf cannelloni.tar.gz ``` +* The archive comes with a matching `libsctp` included. * Extract archive on your target system and copy to system location: ```shell cp cannelloni/libcannelloni-common.so.0 /lib/ cp cannelloni/cannelloni /usr/local/bin/ cannelloni # run cannelloni ``` -* Or to custom library location: +* Or copy to custom location: ```shell - cp cannelloni/libcannelloni-common.so.0 /usr/local/lib/ - cp cannelloni/cannelloni /usr/local/bin/ - export LD_LIBRARY_PATH="/usr/local/lib/:$LD_LIBRARY_PATH" + sudo cp /tmp/cannelloni/ /opt/cannelloni + export LD_LIBRARY_PATH="/opt/cannelloni/:$LD_LIBRARY_PATH" + export PATH="/opt/cannelloni:$PATH" cannelloni # run cannelloni ``` diff --git a/build.py b/build.py index 919fa4f..a7b2108 100644 --- a/build.py +++ b/build.py @@ -14,45 +14,52 @@ class BuildMetadata: DEFAULT_VERSION = "1.1.0" DEFAULT_HASH = "0dcb9277b21f916f5646574b9b2229d3b8e97d5e99b935a4d0b7509a5f0ccdcd" +DEFAULT_DOCKCROSS_REGISTRY = "docker.io/dockcross" # Dockcross image names: https://github.com/dockcross/dockcross?tab=readme-ov-file#summary-cross-compilers # Debian architectures: https://wiki.debian.org/SupportedArchitectures BUILD_TARGETS = { - #"linux-x64": BuildMetadata(dockcross_image="linux-x64", dockcross_version="20240418-88c04a4"), - #"linux-armv6": BuildMetadata(dockcross_image="linux-armv6", dockcross_version="20240418-88c04a4"), + # "linux-x64": BuildMetadata(dockcross_image="linux-x64", dockcross_version="20240418-88c04a4"), + # "linux-armv6": BuildMetadata(dockcross_image="linux-armv6", dockcross_version="20240418-88c04a4"), "linux-armv6-lts": BuildMetadata(dockcross_image="linux-armv6-lts", dockcross_version="20240418-88c04a4"), - #"linux-armv7": BuildMetadata(dockcross_image="linux-armv7", dockcross_version="20240418-88c04a4"), + # "linux-armv7": BuildMetadata(dockcross_image="linux-armv7", dockcross_version="20240418-88c04a4"), "linux-armv7-lts": BuildMetadata(dockcross_image="linux-armv7-lts", dockcross_version="20240418-88c04a4"), - #"linux-arm64": BuildMetadata(dockcross_image="linux-arm64", dockcross_version="20240418-88c04a4"), + # "linux-arm64": BuildMetadata(dockcross_image="linux-arm64", dockcross_version="20240418-88c04a4"), "linux-arm64-lts": BuildMetadata(dockcross_image="linux-arm64-lts", dockcross_version="20240418-88c04a4"), "manylinux_2_28-x64": BuildMetadata(dockcross_image="manylinux_2_28-x64", dockcross_version="20240418-88c04a4"), } -def build_cannelloni(build_metadata: BuildMetadata, build_version: str, build_hash: str): +def build_cannelloni(build_metadata: BuildMetadata, build_version: str, build_hash: str, no_build_cache: bool): print(f""" Build cannelloni for your architecture. - Dockcross image: dockcross/{build_metadata.dockcross_image}:{build_metadata.dockcross_version} + Dockcross image: {DEFAULT_DOCKCROSS_REGISTRY}/{build_metadata.dockcross_image}:{build_metadata.dockcross_version} """) cmd = f"""docker build - --build-arg VERSION={build_version} - --build-arg HASH={build_hash} + --build-arg CANNELLONI_VERSION={build_version} + --build-arg CANNELLONI_HASH={build_hash} + --build-arg DOCKCROSS_REGISTRY={DEFAULT_DOCKCROSS_REGISTRY} --build-arg DOCKCROSS_IMAGE={build_metadata.dockcross_image} --build-arg DOCKCROSS_VERSION={build_metadata.dockcross_version} --output type=local,dest=out .""" + if no_build_cache: + print("Disabling docker build cache.") + cmd += " --no-cache" + my_env = os.environ.copy() my_env["BUILDKIT_PROGRESS"] = "plain" # show progress output when building with docker result = subprocess.run(cmd.split(), env=my_env) if result.returncode != 0: raise RuntimeError("Failed to build docker image for architecture={}".format(build_metadata.dockcross_image)) + def show_version(build_metadata: BuildMetadata): print(f""" -Dockcross image : dockcross/{build_metadata.dockcross_image}:{build_metadata.dockcross_version}""") +Dockcross image : {DEFAULT_DOCKCROSS_REGISTRY}/{build_metadata.dockcross_image}:{build_metadata.dockcross_version}""") - cmd = f"""docker run --entrypoint= --rm -v ./show_version.sh:/show_version.sh dockcross/{build_metadata.dockcross_image}:{build_metadata.dockcross_version} /show_version.sh""".split() + cmd = f"""docker run --entrypoint= --rm -v ./show_version.sh:/show_version.sh {DEFAULT_DOCKCROSS_REGISTRY}/{build_metadata.dockcross_image}:{build_metadata.dockcross_version} /show_version.sh""".split() result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if result.returncode == 0: print(result.stdout.decode().strip()) @@ -65,12 +72,15 @@ def show_version(build_metadata: BuildMetadata): parser.add_argument('--architecture', help='Architecture to build.', choices=BUILD_TARGETS.keys(), required=False) parser.add_argument('--version', help='Cannelloni release version to build.') parser.add_argument('--hash', help='Cannelloni release hash to do integrity check.') - parser.add_argument('--show', help='Show toolchain versions of dockcross images.', required=False, action='store_true') + parser.add_argument('--show', help='Show toolchain versions of dockcross images.', required=False, + action='store_true') + parser.add_argument('--no-cache', help='Disable docker build cache.', required=False, action='store_true') args = parser.parse_args() given_architecture = args.architecture build_version = args.version or DEFAULT_VERSION build_hash = args.hash or DEFAULT_HASH + no_build_cache = args.no_cache if given_architecture is None: target_list = list(BUILD_TARGETS.keys()) @@ -83,4 +93,4 @@ def show_version(build_metadata: BuildMetadata): if args.show: show_version(metadata) else: - build_cannelloni(metadata, build_version, build_hash) + build_cannelloni(metadata, build_version, build_hash, no_build_cache) diff --git a/build_cannelloni.sh b/build_cannelloni.sh new file mode 100755 index 0000000..dd32c09 --- /dev/null +++ b/build_cannelloni.sh @@ -0,0 +1,65 @@ +#!/bin/bash +set -x +set -e + +############################################ +# CONFIG +############################################ +# assumes to be run in a temporary directory +VERSION="${CANNELLONI_VERSION:-1.1.0}" +HASH="${CANNELLONI_HASH:-0dcb9277b21f916f5646574b9b2229d3b8e97d5e99b935a4d0b7509a5f0ccdcd}" +TARGET_DIR="${TARGET_DIR:-/tmp/cannelloni}" +DOWNLOAD_URL="https://github.com/mguentner/cannelloni/archive/refs/tags/v${VERSION}.tar.gz" + +# Download source code +wget "$DOWNLOAD_URL" -O "cannelloni-$VERSION.tar.gz" +echo "$HASH cannelloni-$VERSION.tar.gz" | sha256sum --check --status + +# Extract archive +tar --strip-components=1 -xvf "cannelloni-$VERSION.tar.gz" + +# Check if libsctp is at the expected location +if [ ! -e "netinet/sctp.h" ]; then + echo "Could not find sctp header file! Aborting." + exit 1 +fi +if [ ! -e "libsctp.so.1" ]; then + echo "Could not find sctp library file! Aborting." + exit 1 +fi + +############################################ +# Build cannelloni +############################################ +cmake -DCMAKE_BUILD_TYPE=MinSizeRel -DSCTP_INCLUDE_DIR=./netinet/ -DSCTP_LIBRARY=./ -DSCTP_SUPPORT=ON 2>&1 +make 2>&1 + +echo Finished building cannelloni + +############################################ +# COPY results to target directory +############################################ +mkdir -p "${TARGET_DIR}/sources/" +cp --remove-destination libcannelloni-common.so.0.0.1 "${TARGET_DIR}"/libcannelloni-common.so.0 +cp cannelloni "${TARGET_DIR}"/cannelloni +cp cannelloni-"$VERSION".tar.gz "${TARGET_DIR}"/sources/ +mv gpl-2.0.txt "${TARGET_DIR}"/cannelloni-license-gpl-2.0.txt +echo "You can find a copy of the cannelloni source code attached to this archive." >> "${TARGET_DIR}/SOURCES.md" +echo "The cannelloni source code was downloaded from here: $DOWNLOAD_URL." >> "${TARGET_DIR}/SOURCES.md" + +############################################ +# CHECK result +############################################ +# for other architectures than x86_64, copy libraries to this location for cmake toolchain +TARGET_XCC_DIR="$(ls -d /usr/xcc/*/*/sysroot/ 2>/dev/null || echo)" + +if [ -n "$TARGET_XCC_DIR" ]; then + echo "Built with crosstool-ng" + "$TARGET_XCC_DIR"/usr/bin/ldd --version + echo -e "\nPrint shared object information on cannelloni library:" + readelf -h "${TARGET_DIR}"/cannelloni +else + echo "Not a cross build." + ldd --version + ldd "${TARGET_DIR}"/cannelloni +fi diff --git a/build_libsctp.sh b/build_libsctp.sh index 3e27fc7..5d1c49d 100755 --- a/build_libsctp.sh +++ b/build_libsctp.sh @@ -2,9 +2,20 @@ set -x set -e +############################################ +# CONFIG +############################################ +# assumes to be run in a temporary directory +# has outputs that are added to the build directory of cannelloni! + LIBSCTP_VERSION="${LIBSCTP_VERSION:-1.0.19}" LIBSCTP_HASH="${LIBSCTP_HASH:-9251b1368472fb55aaeafe4787131bdde4e96758f6170620bc75b638449cef01}" +TARGET_DIR="${TARGET_DIR:-/tmp/target}" +CANNELLONI_BUILD_DIR="${CANNELLONI_BUILD_DIR:-/tmp/cannelloni_build}" +DOWNLOAD_URL="https://github.com/sctp/lksctp-tools/archive/refs/tags/v${LIBSCTP_VERSION}.tar.gz" +############################################ +# Check if environment variable is present if [ -z "$LIBSCTP_VERSION" ]; then echo "No library version for libsctp specified! Set environment variable 'LIBSCTP_VERSION'!" exit 1 @@ -16,12 +27,15 @@ TARGET_XCC_DIR="$(ls -d /usr/xcc/*/*/sysroot/)" TARGET_TRIPLE="$(ls /usr/xcc/)" set -e -wget https://github.com/sctp/lksctp-tools/archive/refs/tags/v"$LIBSCTP_VERSION".tar.gz -O libsctp-"$LIBSCTP_VERSION".tar.gz +wget "$DOWNLOAD_URL" -O libsctp-"$LIBSCTP_VERSION".tar.gz echo "$LIBSCTP_HASH libsctp-$LIBSCTP_VERSION.tar.gz" | sha256sum --check --status tar --strip-components=1 -xvf libsctp-"$LIBSCTP_VERSION".tar.gz ./bootstrap +############################################ +# Build libsctp +############################################ # CFLAGS are set to "MinSizeRel" export CFLAGS="-Os -DNDEBUG" # CFLAGS are set to "Release" @@ -37,14 +51,35 @@ make find . -name "libsctp*" find . -name "sctp.h" +# Copy library and its headers to a location where the compiler will find it if [ -n "$TARGET_XCC_DIR" ]; then cp ./src/include/netinet/sctp.h "$TARGET_XCC_DIR"/usr/include/netinet/ cp src/lib/.libs/libsctp.* "$TARGET_XCC_DIR"/usr/lib/ else - echo "Not a cross build. Nothing to do for current architecture: $ARCHITECTURE" + echo "Not a cross build. Architecture: $ARCHITECTURE" cp ./src/include/netinet/sctp.h /usr/include/netinet/ cp src/lib/.libs/libsctp.* /usr/lib/ fi -cp -a ./src/include/netinet/ /tmp/cannelloni/ -cp --no-dereference --preserve=links src/lib/.libs/libsctp.* /tmp/cannelloni/ +############################################ +# COPY results to cannelloni build directory +############################################ +mkdir -p "${CANNELLONI_BUILD_DIR}" +# copy header files +cp -a ./src/include/netinet/ "${CANNELLONI_BUILD_DIR}"/ +# copy object files +cp --no-dereference --preserve=links src/lib/.libs/libsctp.{a,so*} "${CANNELLONI_BUILD_DIR}"/ + +############################################ +# COPY results to target directory +############################################ +# copy source code +mkdir -p "${TARGET_DIR}/sources/" +cp libsctp-"$LIBSCTP_VERSION".tar.gz "${TARGET_DIR}/sources/" +# copy license +cp COPYING.lib "${TARGET_DIR}/libsctp-license.txt" +# copy library files +cp --no-dereference --preserve=links src/lib/.libs/libsctp.{a,so*} "${TARGET_DIR}"/ + +echo "You can find a copy of the libsctp source code attached to this archive." >> "${TARGET_DIR}/SOURCES.md" +echo "The libsctp source code was downloaded from here: $DOWNLOAD_URL" >> "${TARGET_DIR}/SOURCES.md" diff --git a/checklib.sh b/checklib.sh deleted file mode 100755 index 11b7987..0000000 --- a/checklib.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -set -x - -# for other architectures than x86_64, copy libraries to this location for cmake toolchain -TARGET_XCC_DIR="$(ls -d /usr/xcc/*/*/sysroot/)" - -if [ -n "$TARGET_XCC_DIR" ]; then - echo "Built with crosstool-ng" - "$TARGET_XCC_DIR"/usr/bin/ldd --version - echo -e "\nPrint shared object information on cannelloni library:" - readelf -h /tmp/cannelloni/cannelloni -else - echo "Not a cross build." - ldd --version - ldd /tmp/cannelloni/cannelloni -fi