diff --git a/CMakeLists.txt b/CMakeLists.txt index 7ad0ed804a..29e94c8c6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,9 +21,17 @@ cmake_policy(SET CMP0042 NEW) # Enable support for MSVC_RUNTIME_LIBRARY cmake_policy(SET CMP0091 NEW) -project(capstone - VERSION 5.0.3 -) +# Check if VERSION is provided externally, otherwise default to 5.0.3 +if(NOT DEFINED PROJECT_VERSION) + set(PROJECT_VERSION "5.0.3") +endif() + +# Extract the major, minor, and patch versions +string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.[0-9]+" PROJECT_VERSION_BASE ${PROJECT_VERSION}) + +# Set the project version without the pre-release identifier +project(capstone VERSION ${PROJECT_VERSION_BASE}) + if (MSVC) add_compile_options(/W1 /w14189) @@ -731,7 +739,6 @@ source_group("Include\\TriCore" FILES ${HEADERS_TRICORE}) ## installation if(CAPSTONE_INSTALL) include(GNUInstallDirs) - install(FILES ${HEADERS_COMMON} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/capstone) # Support absolute installation paths (discussion: https://github.com/NixOS/nixpkgs/issues/144170) @@ -752,6 +759,8 @@ if(CAPSTONE_INSTALL) configure_file(capstone.pc.in ${CMAKE_BINARY_DIR}/capstone.pc @ONLY) install(FILES ${CMAKE_BINARY_DIR}/capstone.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) + # Remove empty directories + install(CODE "execute_process(COMMAND rmdir --ignore-fail-on-non-empty ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig)") include(CMakePackageConfigHelpers) set(CAPSTONE_CMAKE_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/capstone") diff --git a/capstone.pc.in b/capstone.pc.in index 97fcf947f6..d77460b6c8 100644 --- a/capstone.pc.in +++ b/capstone.pc.in @@ -10,4 +10,3 @@ URL: https://www.capstone-engine.org/ archive=${libdir}/libcapstone.a Libs: -L${libdir} -lcapstone Cflags: -I${includedir}/capstone -archs=@CAPSTONE_ARCHITECTURES@ diff --git a/packages/deb/.gitignore b/packages/deb/.gitignore new file mode 100644 index 0000000000..c636824af1 --- /dev/null +++ b/packages/deb/.gitignore @@ -0,0 +1,2 @@ +*.deb +*.txt \ No newline at end of file diff --git a/packages/deb/Dockerfile b/packages/deb/Dockerfile new file mode 100644 index 0000000000..bdbffc63df --- /dev/null +++ b/packages/deb/Dockerfile @@ -0,0 +1,69 @@ +ARG VERSION="" + +# Run in the root of the repo +# docker build -f ./packages/deb/Dockerfile -t packager . +FROM debian:bookworm-slim + +# Install necessary tools for packaging +RUN apt-get -qq update && \ + DEBIAN_FRONTEND=noninteractive apt-get -qq install -y \ + fakeroot dpkg-dev dos2unix cmake + +# Copy project files into the container +RUN mkdir /capstone +COPY . /capstone +WORKDIR /capstone/ + +# Using cmake, see BUILDING.md file +# For debug build change "Release" to "Debug" +ARG VERSION +RUN cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1 -DPROJECT_VERSION=${VERSION} -DCMAKE_INSTALL_PREFIX=/usr +RUN cmake --build build + +# List files before cmake install +# RUN find / -type f > /before-install.txt + +# Make directories as needed +RUN mkdir -p /package-root/usr/include/capstone/ +RUN mkdir -p /package-root/usr/lib/pkgconfig/ +RUN mkdir -p /package-root/usr/bin/ +RUN mkdir -p /package-root/usr/share/doc/libcapstone-dev + +# Run cmake install +RUN cmake --install build --prefix /package-root/usr/ + +# List files after cmake install +# RUN find / -type f > /after-install.txt + +# Create DEBIAN directory and control file +COPY ./packages/deb/control /package-root/DEBIAN/control + +# Copy documentation over +COPY ./ChangeLog /package-root/usr/share/doc/libcapstone-dev +COPY ./CREDITS.TXT /package-root/usr/share/doc/libcapstone-dev +COPY ./HACK.TXT /package-root/usr/share/doc/libcapstone-dev +COPY ./LICENSE.TXT /package-root/usr/share/doc/libcapstone-dev +COPY ./README.md /package-root/usr/share/doc/libcapstone-dev +COPY ./RELEASE_NOTES /package-root/usr/share/doc/libcapstone-dev +COPY ./SPONSORS.TXT /package-root/usr/share/doc/libcapstone-dev + +# Generate MD5 checksums for all files and save to DEBIAN/md5sums +RUN cd /package-root && \ + find . -type f ! -path './DEBIAN/*' -exec md5sum {} + | sed 's| \./| |' > /package-root/DEBIAN/md5sums + +# Update capstone.pc file with the correct version and remove archs field +# Update control file with the correct version +ARG VERSION +# Calculate the installed size and update the control file in a single RUN command +RUN INSTALLED_SIZE=$(du -sk /package-root | cut -f1) && \ + sed -i "s/^Installed-Size:.*/Installed-Size: ${INSTALLED_SIZE}/" /package-root/DEBIAN/control +RUN sed -i "s/^Version:.*/Version: ${VERSION}/" /package-root/DEBIAN/control + +# Add triggers script to run ldconfig after installation +COPY ./packages/deb/triggers /package-root/DEBIAN/triggers + +# Build the package +RUN fakeroot dpkg-deb --build /package-root /libcapstone-dev_${VERSION}_amd64.deb + +# The user can now extract the .deb file from the container with something like +# docker run --rm -v $(pwd):/out packager bash -c "cp /libcapstone-dev.deb /out" diff --git a/packages/deb/README.md b/packages/deb/README.md new file mode 100644 index 0000000000..fc79aa4e0e --- /dev/null +++ b/packages/deb/README.md @@ -0,0 +1,7 @@ +Incomplete Debian package implementation. +It can be used to generate an easily installable Capstone, but misses a lot of +mandatory things to add it in the Debian repos (`debian/control` is incomplete, no dependencies added etc.). + +You can build the package by dispatching the `Build Debian Package` workflow or executing the commands in the `Dockerfile`. +It assumes the current commit is tagged and `"$(git describe --tags --abbrev=0)"` returns a valid version number. +The package is uploaded as artifact. diff --git a/packages/deb/check_capstone.sh b/packages/deb/check_capstone.sh new file mode 100644 index 0000000000..a4746b3c8c --- /dev/null +++ b/packages/deb/check_capstone.sh @@ -0,0 +1,68 @@ +#!/bin/bash + +# Usage: ./check_capstone_pc.sh + +DEB_FILE=$1 +EXPECTED_VERSION=$2 + +# Check if the deb file exists +if [[ ! -f "$DEB_FILE" ]]; then + echo "Debian package file not found!" + exit 1 +fi + +# Create a temporary directory to extract the deb file +TEMP_DIR=$(mktemp -d) + +# Extract the deb file +dpkg-deb -x "$DEB_FILE" "$TEMP_DIR" + +# Path to the capstone.pc file +CAPSTONE_PC="$TEMP_DIR/usr/lib/pkgconfig/capstone.pc" + +# Check if the capstone.pc file exists +if [[ ! -f "$CAPSTONE_PC" ]]; then + echo "capstone.pc file not found in the package!" + rm -rf "$TEMP_DIR" + exit 1 +fi + +# Remove leading 'v' if present, e. g. v1.5.1 -> 1.5.1 +if [[ "$EXPECTED_VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + EXPECTED_VERSION=${EXPECTED_VERSION:1} +fi + +# Check if the version follows the format for Debian Packages +if [[ ! "$version" =~ ^[0-9]+(.[0-9]+)*(-[A-Za-z0-9]+)?$ ]]; then + echo "ERROR: Version must be in a valid Debian package format" + exit 1 +fi + + +# Check the version in the capstone.pc file +ACTUAL_VERSION=$(grep "^Version:" "$CAPSTONE_PC" | awk '{print $2}') +if [[ "$ACTUAL_VERSION" != "$EXPECTED_VERSION" ]]; then + echo "Version mismatch! Expected: $EXPECTED_VERSION, Found: $ACTUAL_VERSION" + rm -rf "$TEMP_DIR" + exit 1 +fi + +# Check if libcapstone.a is included in the package +LIBCAPSTONE_A="$TEMP_DIR/usr/lib/libcapstone.a" +if [[ ! -f "$LIBCAPSTONE_A" ]]; then + echo "libcapstone.a not found in the package!" + rm -rf "$TEMP_DIR" + exit 1 +fi + +# Check if libcapstone.so is included in the package +LIBCAPSTONE_SO="$TEMP_DIR/usr/lib/libcapstone.so" +if [[ ! -f "$LIBCAPSTONE_SO" ]]; then + echo "libcapstone.so not found in the package!" + rm -rf "$TEMP_DIR" + exit 1 +fi + +echo "libcapstone-dev.deb file is correct." +rm -rf "$TEMP_DIR" +exit 0 diff --git a/packages/deb/control b/packages/deb/control new file mode 100644 index 0000000000..0d803c13a6 --- /dev/null +++ b/packages/deb/control @@ -0,0 +1,32 @@ +Package: libcapstone-dev +Source: capstone +Version: +Architecture: amd64 +Maintainer: Rot127 +Original-Maintainer: Debian Security Tools +Installed-Size: +Depends: libc6 (>= 2.2.5) +Section: libdevel +Priority: optional +Multi-Arch: same +Homepage: https://www.capstone-engine.org/ +Description: lightweight multi-architecture disassembly framework - devel files + Capstone is a lightweight multi-platform, multi-architecture disassembly + framework. + . + These are the development headers and libraries. + Features: + - Support hardware architectures: ARM, ARM64 (aka ARMv8), Mips, PowerPC & + Intel. + - Clean/simple/lightweight/intuitive architecture-neutral API. + - Provide details on disassembled instructions (called "decomposer" by some + others). + - Provide some semantics of the disassembled instruction, such as list of + implicit registers read & written. + - Implemented in pure C language, with bindings for Java, OCaml and Python + ready to use and Ruby, C#, GO & Vala available on git repos. + - Native support for Windows & *nix (with OS X, Linux, *BSD & Solaris + confirmed). + - Thread-safe by design. + - Special support for embedding into firmware or OS kernel. + - Distributed under the open source BSD license. diff --git a/packages/deb/setup.sh b/packages/deb/setup.sh new file mode 100644 index 0000000000..0c65f97863 --- /dev/null +++ b/packages/deb/setup.sh @@ -0,0 +1,53 @@ +# !/bin/bash +set -eu + +# Function to get the current Ubuntu version +get_os_version() { + lsb_release -i -s 2>/dev/null +} + +# Check if the script is running in the ./packages/deb folder +if [[ $(basename "$PWD") != "deb" ]]; then + echo "ERROR: Script must be run from the ./deb directory" + exit 1 +fi + +OS_VERSION=$(get_os_version) +if [[ "$OS_VERSION" != "Ubuntu" && "$OS_VERSION" != "Debian" ]]; then + echo "ERROR: OS is not Ubuntu or Debian and unsupported" + exit 1 +fi + +# Get the version number as an input +# Check if version argument is provided +if [[ $# -ne 1 ]]; then + echo "ERROR: Version argument is required" + exit 1 +fi + +# Get the version number as an input +version=$1 + +# Remove leading 'v' if present, e. g. v1.5.1 -> 1.5.1 +if [[ "$version" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + version=${version:1} +fi + +# Check if the version follows the format for Debian Packages +if [[ ! "$version" =~ ^[0-9]+(.[0-9]+)*(-[A-Za-z0-9]+)?$ ]]; then + echo "ERROR: Version must be in a valid Debian package format" + exit 1 +fi + +# Now build the packager container from that +pushd ../../ +docker build -f ./packages/deb/Dockerfile -t packager --build-arg VERSION="${version}" . +popd + +# Copy deb file out of container to host +docker run --rm -v $(pwd):/out packager bash -c "cp /*.deb /out" + +# Check which files existed before and after 'make install' was executed. +# docker run --rm -v $(pwd):/out packager bash -c "cp /before-install.txt /out" +# docker run --rm -v $(pwd):/out packager bash -c "cp /after-install.txt /out" +# diff before-install.txt after-install.txt diff --git a/packages/deb/triggers b/packages/deb/triggers new file mode 100644 index 0000000000..eafa63be77 --- /dev/null +++ b/packages/deb/triggers @@ -0,0 +1,2 @@ +# Trigger ldconfig after install +activate-noawait ldconfig