Skip to content

Commit

Permalink
Change CI to create Debian Package to Release (#2521)
Browse files Browse the repository at this point in the history
* Updating CI to create Debian package and version is assigned by tag
version. Also updating release CI to not use end-of-life workflows

* Clear up usage of static libraries.

- Python bindings only use the dynamic lib. But built and copied the static ones sometimes nonetheless.
- Add toggles to build only static, static/dyn or only dynamic.

---------

Co-authored-by: Rot127 <[email protected]>
  • Loading branch information
AndrewQuijano and Rot127 authored Nov 4, 2024
1 parent f6f9679 commit d7be5f9
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 78 deletions.
21 changes: 21 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Ignore source control directories
.git
.svn

# Ignore build directories
build
dist

# Ignore dependency directories
node_modules
vendor

# Ignore temporary files
*.log
*.tmp

# Ignore environment files
.env

# Ignore tests
tests
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
/arch/**/*.inc linguist-language=C

# Ensure shell scripts have LF line endings
*.sh text eol=lf
50 changes: 32 additions & 18 deletions .github/workflows/build_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,32 @@ jobs:
name: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true

- name: Make setup.sh and check_capstone.sh are executable
run: |
chmod +x debian/setup.sh
chmod +x debian/check_capstone.sh
- name: Build Debian Package
working-directory: ./debian
run: ./setup.sh ${{ github.event.release.tag_name }}

- name: Run sanity checks on the Debian package
working-directory: ./debian
run: |
./check_capstone.sh ./libcapstone-dev.deb ${{ github.event.release.tag_name }}
- name: Upload debian package to release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.event.release.tag_name }}
files: |
./debian/*.deb
- name: archive
id: archive
Expand All @@ -27,24 +50,15 @@ jobs:
TARBALL=$PKGNAME.tar.xz
tar cJf $TARBALL $PKGNAME
sha256sum $TARBALL > $SHASUM
echo "::set-output name=tarball::$TARBALL"
echo "::set-output name=shasum::$SHASUM"
- name: upload tarball
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./${{ steps.archive.outputs.tarball }}
asset_name: ${{ steps.archive.outputs.tarball }}
asset_content_type: application/gzip
echo "tarball=$TARBALL" >> $GITHUB_OUTPUT
echo "shasum=$SHASUM" >> $GITHUB_OUTPUT
- name: upload shasum
uses: actions/upload-release-asset@v1
- name: Upload tarball and shasum to release
uses: softprops/action-gh-release@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./${{ steps.archive.outputs.shasum }}
asset_name: ${{ steps.archive.outputs.shasum }}
asset_content_type: text/plain
tag_name: ${{ github.event.release.tag_name }}
files: |
${{ steps.archive.outputs.tarball }}
${{ steps.archive.outputs.shasum }}
54 changes: 41 additions & 13 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ endif()
# to configure the options specify them in the command line or change them in the cmake UI.
# Don't edit the makefile!
option(BUILD_SHARED_LIBS "Build shared library" OFF)
option(CAPSTONE_BUILD_STATIC_RUNTIME "Embed static runtime" ${BUILD_SHARED_LIBS})
option(BUILD_STATIC_LIBS "Build static library" ON)
option(BUILD_STATIC_RUNTIME "Embed static MSVC runtime (Windows only). Always set if BUILD_SHARED_LIBS=ON" ${BUILD_SHARED_LIBS})
option(CAPSTONE_BUILD_MACOS_THIN "Disable universal2 builds on macOS" OFF)
option(CAPSTONE_BUILD_DIET "Build diet library" OFF)
option(CAPSTONE_BUILD_LEGACY_TESTS "Build legacy tests" ${PROJECT_IS_TOP_LEVEL})
Expand All @@ -69,6 +70,10 @@ option(CAPSTONE_INSTALL "Generate install target" ${PROJECT_IS_TOP_LEVEL})
option(ENABLE_ASAN "Enable address sanitizer" OFF)
option(ENABLE_COVERAGE "Enable test coverage" OFF)

if (NOT BUILD_SHARED_LIBS AND NOT BUILD_STATIC_LIBS)
FATAL_ERROR("BUILD_SHARED_LIBS and BUILD_STATIC_LIBS are both unset. Nothing to build.")
endif()

if (ENABLE_ASAN)
message("Enabling ASAN")
add_definitions(-DASAN_ENABLED)
Expand Down Expand Up @@ -154,7 +159,7 @@ if(CAPSTONE_DEBUG OR CMAKE_BUILD_TYPE STREQUAL "Debug")
endif()

# Force static runtime libraries
if(CAPSTONE_BUILD_STATIC_RUNTIME)
if(BUILD_STATIC_RUNTIME)
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()

Expand Down Expand Up @@ -761,19 +766,34 @@ set(ALL_HEADERS
set_property(GLOBAL PROPERTY VERSION ${PROJECT_VERSION})

## targets
add_library(capstone ${ALL_SOURCES} ${ALL_HEADERS})
add_library(capstone::capstone ALIAS capstone)
add_library(capstone OBJECT ${ALL_SOURCES} ${ALL_HEADERS})
set_property(TARGET capstone PROPERTY C_STANDARD 99)
target_include_directories(capstone PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
)
set_property(TARGET capstone PROPERTY C_STANDARD 99)

if(BUILD_STATIC_LIBS)
add_library(capstone_static STATIC $<TARGET_OBJECTS:capstone>)
# Use normal capstone name. Otherwise we get libcapstone_static.a
set_target_properties(capstone_static PROPERTIES OUTPUT_NAME "capstone")
target_include_directories(capstone_static PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
)
endif()

if(BUILD_SHARED_LIBS)
target_compile_definitions(capstone PUBLIC CAPSTONE_SHARED)
set_target_properties(capstone PROPERTIES
set_property(TARGET capstone PROPERTY POSITION_INDEPENDENT_CODE 1)
add_library(capstone_shared SHARED $<TARGET_OBJECTS:capstone>)
# Use normal capstone name. Otherwise we get libcapstone_shared.so
set_target_properties(capstone_shared PROPERTIES OUTPUT_NAME "capstone")
set_target_properties(capstone_shared PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
target_include_directories(capstone_shared PUBLIC
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
)
target_compile_definitions(capstone PUBLIC CAPSTONE_SHARED)
endif()

# Fuzzer if this is moved to it's own CMakeLists.txt (as it should be)
Expand Down Expand Up @@ -878,12 +898,20 @@ if(CAPSTONE_INSTALL)
DESTINATION ${CAPSTONE_CMAKE_CONFIG_INSTALL_DIR}
)

install(TARGETS capstone
EXPORT capstone-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
if(BUILD_SHARED_LIBS)
set(LIB_INSTALL_TARGETS capstone_shared)
endif()

if (BUILD_STATIC_LIBS)
set(LIB_INSTALL_TARGETS ${LIB_INSTALL_TARGETS} capstone_static)
endif()

install(TARGETS ${LIB_INSTALL_TARGETS}
EXPORT capstone-targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
)

install(EXPORT capstone-targets
Expand Down
16 changes: 3 additions & 13 deletions bindings/python/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,12 @@
if SYSTEM == 'darwin':
VERSIONED_LIBRARY_FILE = "libcapstone.{PKG_MAJOR}.dylib".format(**VERSION_DATA)
LIBRARY_FILE = "libcapstone.dylib"
STATIC_LIBRARY_FILE = 'libcapstone.a'
elif SYSTEM in ('win32', 'cygwin'):
VERSIONED_LIBRARY_FILE = "capstone.dll"
LIBRARY_FILE = "capstone.dll"
STATIC_LIBRARY_FILE = None
else:
VERSIONED_LIBRARY_FILE = "libcapstone.so.{PKG_MAJOR}".format(**VERSION_DATA)
LIBRARY_FILE = "libcapstone.so"
STATIC_LIBRARY_FILE = 'libcapstone.a'


def clean_bins():
Expand Down Expand Up @@ -123,12 +120,9 @@ def build_libraries():
shutil.copytree(os.path.join(BUILD_DIR, 'include', 'capstone'), os.path.join(HEADERS_DIR, 'capstone'))

# if prebuilt libraries are available, use those and cancel build
if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)) and \
(not STATIC_LIBRARY_FILE or os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE))):
if os.path.exists(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE)):
logger.info('Using prebuilt libraries')
shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', LIBRARY_FILE), LIBS_DIR)
if STATIC_LIBRARY_FILE is not None:
shutil.copy(os.path.join(ROOT_DIR, 'prebuilt', STATIC_LIBRARY_FILE), LIBS_DIR)
return

os.chdir(BUILD_DIR)
Expand All @@ -141,8 +135,8 @@ def build_libraries():
os.chdir("build_py")
print("Build Directory: {}\n".format(os.getcwd()))
# Only build capstone.dll / libcapstone.dylib
if SYSTEM == "win32":
os.system('cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DCAPSTONE_BUILD_LEGACY_TESTS=OFF -DCAPSTONE_BUILD_CSTOOL=OFF -G "NMake Makefiles" ..')
if SYSTEM in ('win32', 'cygwin'):
os.system('cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_STATIC_LIBS=OFF -DCAPSTONE_BUILD_LEGACY_TESTS=OFF -DCAPSTONE_BUILD_CSTOOL=OFF -G "NMake Makefiles" ..')
elif 'AFL_NOOPT' in os.environ:
# build for test_corpus
os.system('cmake -DBUILD_SHARED_LIBS=ON -DCAPSTONE_BUILD_LEGACY_TESTS=OFF -DCAPSTONE_BUILD_CSTOOL=OFF ..')
Expand All @@ -151,10 +145,6 @@ def build_libraries():
os.system("cmake --build .")

shutil.copy(VERSIONED_LIBRARY_FILE, os.path.join(LIBS_DIR, LIBRARY_FILE))

# only copy static library if it exists (it's a build option)
if STATIC_LIBRARY_FILE and os.path.exists(STATIC_LIBRARY_FILE):
shutil.copy(STATIC_LIBRARY_FILE, LIBS_DIR)
os.chdir(cwd)


Expand Down
2 changes: 2 additions & 0 deletions debian/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
*.deb
*.txt
61 changes: 61 additions & 0 deletions debian/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
ARG VERSION=""

# Assume this is run from capstone/debian directory
# Run in the root of the repo
# docker build -f ./debian/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 your 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"
RUN cmake -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=1
RUN cmake --build build

# List files before cmake install
# RUN find / -type f > /before-install.txt

# Run cmake install, by default everything goes into /usr/local
RUN cmake --install build

# List files after cmake install
# RUN find / -type f > /after-install.txt

# Make directories as needed
RUN mkdir -p /package-root/usr/local/include/capstone/
RUN mkdir -p /package-root/usr/local/lib/pkgconfig/
RUN mkdir -p /package-root/usr/local/bin/

# Copy /usr/local/include/capstone/ to /package-root/usr/local/include/capstone/ and all other cases
RUN cp -r /usr/local/include/capstone/* /package-root/usr/local/include/capstone/
RUN cp -r /usr/local/lib/libcapstone* /package-root/usr/local/lib/
RUN cp -r /usr/local/lib/pkgconfig/capstone* /package-root/usr/local/lib/pkgconfig/
RUN cp -r /usr/local/bin/cstool /package-root/usr/local/bin/

# Create DEBIAN directory and control file
COPY ./debian/control /package-root/DEBIAN/control

# Update capstone.pc file with the correct version and remove archs field
# Update control file with the correct version
ARG VERSION
RUN sed -i "s/^Version:.*/Version: ${VERSION}/" /package-root/DEBIAN/control
RUN sed -i "s/^Version:.*/Version: ${VERSION}/" /package-root/usr/local/lib/pkgconfig/capstone.pc
RUN sed -i "/^archs=/d" /package-root/usr/local/lib/pkgconfig/capstone.pc

# Add postinst script to run ldconfig after installation
COPY ./debian/postinst /package-root/DEBIAN/postinst
RUN chmod 755 /package-root/DEBIAN/postinst

# Build the package
RUN fakeroot dpkg-deb --build /package-root /libcapstone-dev.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"
68 changes: 68 additions & 0 deletions debian/check_capstone.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#!/bin/bash

# Usage: ./check_capstone_pc.sh <path_to_deb_file> <expected_version>

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/local/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 X.Y.Z, e. g. 1.5.1 or 1.9.1
if [[ ! "$EXPECTED_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "ERROR: Version must be in the format X.Y.Z"
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/local/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/local/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
8 changes: 8 additions & 0 deletions debian/control
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Package: capstone
Version: <version-placeholder>
Architecture: all
Maintainer: Rot127 <[email protected]>
Description: Capstone is a lightweight multi-platform, multi-architecture disassembly framework.
Capstone supports the following frameworks;
Alpha, BPF, Ethereum VM, HPPA, LoongArch, M68K, M680X, Mips, MOS65XX, PPC, RISC-V(rv32G/rv64G),
SH, Sparc, SystemZ, TMS320C64X, TriCore, Webassembly, XCore and X86.
6 changes: 6 additions & 0 deletions debian/postinst
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/bash
# postinst script for capstone package
set -e

# Update the shared library cache
ldconfig
Loading

0 comments on commit d7be5f9

Please sign in to comment.