diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dcfd9f3..2f9cdac 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,23 +24,49 @@ jobs: - name: Dependencies run: | sudo apt-get update - sudo apt-get install -y libxml2-dev libusb-1.0-0-dev help2man + + sudo apt-get install -y libxml2-dev libusb-1.0-0-dev meson ninja-build help2man + + - name: Configure + run: | + # Get current Meson version string (e.g. "1.5.1") + meson_version="$(meson --version 2>/dev/null | awk -F. '{print $1 "." $2}')" + + # Convert version to comparable integer, e.g. "0.45" -> 45, "1.5" -> 105 + version_to_num() { + local major minor + IFS='.' read -r major minor <<<"$1" + printf "%d%02d" "$major" "$minor" + } + + current="$(version_to_num "$meson_version_short")" + threshold="$(version_to_num "1.1")" + # According to https://mesonbuild.com/Build-options.html for versions < 1.1 we should use + # meson_options.txt for build options + if (( current < threshold )); then + if [[ -f "meson.options" ]]; then + echo "Detected Meson $meson_version (< 1.1) - renaming meson.options -> meson_options.txt" + mv -f meson.options meson_options.txt + fi + fi + + meson setup build - name: Build - run: make + run: meson compile -C build - name: Run tests - run: make tests + run: meson test -C build - name: Generate man pages - run: make manpages + run: meson compile manpages -C build - name: Package run: | mkdir dist cp `pkg-config --variable=libdir libusb-1.0`/libusb-1.0.so.0 dist chmod 0644 dist/* - cp qdl dist + cp ./build/qdl dist patchelf --set-rpath '$ORIGIN' dist/qdl - name: Upload artifact @@ -67,16 +93,19 @@ jobs: - name: Dependencies run: | - brew install libxml2 help2man + brew install libxml2 meson ninja help2man + + - name: Configure + run: meson setup build - name: Build - run: make + run: meson compile -C build - name: Generate man pages - run: make manpages + run: meson compile manpages -C build - name: Run tests - run: make tests + run: meson test -C build - name: Package run: | @@ -85,7 +114,7 @@ jobs: cp `pkg-config --variable=libdir libusb-1.0`/libusb-1.0.0.dylib dist cp `pkg-config --variable=libdir liblzma`/liblzma.5.dylib dist chmod 0644 dist/* - cp qdl dist + cp ./build/qdl dist if uname -a | grep -q arm64; then LIBUSB_DIR=/opt/homebrew/opt/libusb/lib @@ -132,23 +161,27 @@ jobs: git help2man mingw-w64-x86_64-gcc - mingw-w64-x86_64-make - mingw-w64-x86_64-pkg-config + mingw-w64-x86_64-meson + mingw-w64-x86_64-ninja mingw-w64-x86_64-libxml2 mingw-w64-x86_64-libusb + - name: Configure + run: meson setup build + shell: msys2 {0} + - name: Build run: | git config --global core.autocrlf true - make + meson compile -C build shell: msys2 {0} - name: Generate man pages - run: make manpages + run: meson compile manpages -C build shell: msys2 {0} - name: Run tests - run: make tests + run: meson test -C build shell: msys2 {0} - name: Package @@ -165,7 +198,7 @@ jobs: Copy-Item (Join-Path $BIN_DIR "liblzma-5.dll") $DistDir Copy-Item (Join-Path $BIN_DIR "libiconv-2.dll") $DistDir - Copy-Item "qdl.exe" $DistDir + Copy-Item "./build/qdl.exe" $DistDir - name: Upload artifact uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 98cdd14..c7f6a65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ -*.1 +/build/ +/tests/data/*.elf +/tests/data/*.bin +/tests/data/*.img *.o qdl qdl-ramdump @@ -6,7 +9,6 @@ ks *.exe compile_commands.json .cache -.version.h version.h -scripts +.scripts .checkpatch-camelcase.git. diff --git a/Makefile b/Makefile deleted file mode 100644 index 7ec4a17..0000000 --- a/Makefile +++ /dev/null @@ -1,91 +0,0 @@ -QDL := qdl -RAMDUMP := qdl-ramdump -VERSION := $(or $(VERSION), $(shell git describe --dirty --always --tags 2>/dev/null), "unknown-version") - -CFLAGS += -O2 -Wall -g `pkg-config --cflags libxml-2.0 libusb-1.0` -LDFLAGS += `pkg-config --libs libxml-2.0 libusb-1.0` -ifeq ($(OS),Windows_NT) -LDFLAGS += -lws2_32 -endif -prefix := /usr/local - -QDL_SRCS := firehose.c io.c qdl.c sahara.c util.c patch.c program.c read.c sha2.c sim.c ufs.c usb.c ux.c oscompat.c vip.c sparse.c gpt.c -QDL_OBJS := $(QDL_SRCS:.c=.o) - -RAMDUMP_SRCS := ramdump.c sahara.c io.c sim.c usb.c util.c ux.c oscompat.c -RAMDUMP_OBJS := $(RAMDUMP_SRCS:.c=.o) - -KS_OUT := ks -KS_SRCS := ks.c sahara.c util.c ux.c oscompat.c -KS_OBJS := $(KS_SRCS:.c=.o) - -CHECKPATCH_SOURCES := $(shell find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.sh" \) ! -name "sha2.c" ! -name "sha2.h" ! -name "*version.h" ! -name "list.h") -CHECKPATCH_ROOT := https://raw.githubusercontent.com/torvalds/linux/v6.15/scripts -CHECKPATCH_URL := $(CHECKPATCH_ROOT)/checkpatch.pl -CHECKPATCH_SP_URL := $(CHECKPATCH_ROOT)/spelling.txt -CHECKPATCH := ./.scripts/checkpatch.pl -CHECKPATCH_SP := ./.scripts/spelling.txt - -MANPAGES := ks.1 qdl-ramdump.1 qdl.1 - -default: $(QDL) $(RAMDUMP) $(KS_OUT) - -$(QDL): $(QDL_OBJS) - $(CC) -o $@ $^ $(LDFLAGS) - -$(RAMDUMP): $(RAMDUMP_OBJS) - $(CC) -o $@ $^ $(LDFLAGS) - -$(KS_OUT): $(KS_OBJS) - $(CC) -o $@ $^ $(LDFLAGS) - -compile_commands.json: $(QDL_SRCS) $(KS_SRCS) - @echo -n $^ | jq -snR "[inputs|split(\" \")[]|{directory:\"$(PWD)\", command: \"$(CC) $(CFLAGS) -c \(.)\", file:.}]" > $@ - -manpages: $(KS_OUT) $(RAMDUMP) $(QDL) - help2man -N -n "KS" -o ks.1 ./ks - help2man -N -n "Qualcomm Download" -o qdl.1 ./qdl - help2man -N -n "Qualcomm Download Ramdump" -o qdl-ramdump.1 ./qdl-ramdump - -version.h:: - @echo "#define VERSION \"$(VERSION)\"" > .version.h - @cmp -s .version.h version.h || cp .version.h version.h - -util.o: version.h - -clean: - rm -f $(QDL) $(QDL_OBJS) - rm -f $(RAMDUMP) $(RAMDUMP_OBJS) - rm -f $(KS_OUT) $(KS_OBJS) - rm -f $(MANPAGES) - rm -f compile_commands.json - rm -f version.h .version.h - rm -f $(CHECKPATCH) - rm -f $(CHECKPATCH_SP) - if [ -d .scripts ]; then rmdir .scripts; fi - -install: $(QDL) $(RAMDUMP) $(KS_OUT) - install -d $(DESTDIR)$(prefix)/bin - install -m 755 $^ $(DESTDIR)$(prefix)/bin - -tests: default -tests: - @./tests/run_tests.sh - -# Target to download checkpatch.pl if not present -$(CHECKPATCH): - @echo "Downloading checkpatch.pl..." - @mkdir -p $(dir $(CHECKPATCH)) - @curl -sSfL $(CHECKPATCH_URL) -o $(CHECKPATCH) - @curl -sSfL $(CHECKPATCH_SP_URL) -o $(CHECKPATCH_SP) - @chmod +x $(CHECKPATCH) - -check: $(CHECKPATCH) - @echo "Running checkpatch on source files (excluding sha2.c and sha2.h)..." - @for file in $(CHECKPATCH_SOURCES); do \ - perl $(CHECKPATCH) --no-tree -f $$file || exit 1; \ - done - -check-cached: $(CHECKPATCH) - @echo "Running checkpatch on staged changes..." - @git diff --cached -- . | perl $(CHECKPATCH) --no-tree - diff --git a/README.md b/README.md index b914b25..7b02bb1 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,9 @@ loader and use this to flash images. ### Linux ```bash -sudo apt install libxml2 libusb-1.0-0-dev help2man -make +sudo apt install libxml2 libusb-1.0-0-dev meson ninja-build help2man +meson setup build +meson compile -C build ``` ### MacOS @@ -20,15 +21,17 @@ make For Homebrew users, ```bash -brew install libxml2 pkg-config libusb help2man -make +brew install libxml2 libusb meson ninja help2man +meson setup build +meson compile -C build ``` For MacPorts users ```bash -sudo port install libxml2 pkgconfig libusb help2man -make +sudo port install libxml2 libusb meson ninja help2man +meson setup build +meson compile -C build ``` ### Windows @@ -42,16 +45,17 @@ pacman -S base-devel --needed pacman -S git pacman -S help2man pacman -S mingw-w64-x86_64-gcc -pacman -S mingw-w64-x86_64-make -pacman -S mingw-w64-x86_64-pkg-config +pacman -S mingw-w64-x86_64-meson +pacman -S mingw-w64-x86_64-ninja pacman -S mingw-w64-x86_64-libusb pacman -S mingw-w64-x86_64-libxml2 ``` -Then use the `make` tool to build QDL: +Then use the `meson` tool to build QDL: ```bash -make +meson setup build +meson compile -C build ``` ## Use QDL @@ -215,18 +219,19 @@ served, in order to reach Firehose mode. ## Run tests -To run the integration test suite for QDL, use the `make tests` target: +To run the integration test suite for QDL, use the `meson` tool with `test` +param: ```bash -make tests +meson test -C build ``` ## Generate man pages -Manpages can be generated using `make manpages` target: +Manpages can be generated using `manpages` target: ```bash -make manpages +meson compile manpages -C build ``` ## Contributing @@ -238,7 +243,7 @@ and submit the pull request. The preferred coding style for this tool is [Linux kernel coding style](https://www.kernel.org/doc/html/v6.15/process/coding-style.html). Before creating a commit, please ensure that your changes adhere to the coding style -by using the `make check-cached` target, for example: +by using the `meson compile check-cached -C build` target, for example: ```bash $ git status @@ -248,7 +253,8 @@ Changes to be committed: modified: qdl.c modified: qdl.h -$ make check-cached +$ meson compile check-cached -C build +[0/1] Running external command check-cached (wrapped by meson to set env) Running checkpatch on staged changes... ERROR: trailing whitespace #28: FILE: qdl.h:32: diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..dea5124 --- /dev/null +++ b/meson.build @@ -0,0 +1,141 @@ +project('qdl', 'c', + default_options : ['warning_level=2', 'optimization=2'] +) + +# Determine version +git_check_ver = run_command('git', 'describe', '--dirty', '--always', '--tags', check: false).stdout().strip() +git_v = git_check_ver != '' ? git_check_ver : 'unknown-version' +env_v = get_option('VERSION') +ver = env_v != '' ? env_v : git_v + +# Dependencies +libusb_dep = dependency('libusb-1.0') +libxml_dep = dependency('libxml-2.0') + +# Common include dirs +inc = include_directories('.') + +# Windows-specific link flags +ws2_dep = [] +if host_machine.system() == 'windows' + ws2_dep = meson.get_compiler('c').find_library('ws2_32', required : false) +endif + +common_dep = [libusb_dep, libxml_dep, ws2_dep] + +# --- version header generation --- +conf = configuration_data() +conf.set('version', ver) + +version_h = configure_file( + input: 'version.h.in', + output: 'version.h', + configuration: conf +) + +common_sources = files( + 'sahara.c', 'util.c', 'ux.c', 'oscompat.c' +) + +# --- qdl executable --- +qdl_sources = files( + 'firehose.c', + 'io.c', 'qdl.c', 'patch.c', + 'program.c', 'read.c', 'sha2.c', 'sim.c', 'ufs.c', 'usb.c', + 'vip.c', 'sparse.c', 'gpt.c' +) + +qdl_exe = executable('qdl', + sources : qdl_sources + common_sources + [version_h], + dependencies : common_dep, + include_directories : inc, + install : true +) + +# --- qdl-ramdump executable --- +ramdump_sources = files( + 'ramdump.c', 'io.c', 'sim.c', 'usb.c' +) + +qdl_ramdump_exe = executable('qdl-ramdump', + sources : ramdump_sources + common_sources + [version_h], + dependencies : common_dep, + include_directories : inc, + install : true +) + +# --- ks executable --- +ks_sources = files('ks.c') + +ks_exe = executable('ks', + sources : ks_sources + common_sources + [version_h], + dependencies : common_dep, + include_directories : inc, + install : true +) + +# -- Tests --- +test( + 'run_tests.sh', + find_program('bash'), + args: [meson.current_source_dir() / 'tests/run_tests.sh', '--builddir', meson.current_build_dir()] +) + +# --- checkpatch targets --- +# Absolute path to the script (works from build dir) +check_script = join_paths(meson.project_source_root(), 'scripts', 'checkpatch_wrapper.sh') + +run_target('download-checkpatch', + command: ['bash', '-lc', check_script + ' download'], +) + +run_target('check', + command: ['bash', '-lc', check_script + ' check'], +) + +run_target('check-cached', + command: ['bash', '-lc', check_script + ' check-cached'], +) + +# --- man generation --- +man_ks = custom_target( + 'ks-manpage', + output: 'ks.1', + command: [ + 'help2man', '-N', '-n', 'KS', + '-o', '@OUTPUT@', + ks_exe, + ], + depends: ks_exe, + install: true, + install_dir: get_option('mandir') / 'man1', +) + +man_qdl = custom_target( + 'qdl-manpage', + output: 'qdl.1', + command: [ + 'help2man', '-N', '-n', 'Qualcomm Download', + '-o', '@OUTPUT@', + qdl_exe, + ], + depends: qdl_exe, + install: true, + install_dir: get_option('mandir') / 'man1', +) + +man_qdl_ramdump = custom_target( + 'qdl-ramdump-manpage', + output: 'qdl-ramdump.1', + command: [ + 'help2man', '-N', '-n', 'Qualcomm Download Ramdump', + '-o', '@OUTPUT@', + qdl_ramdump_exe, + ], + depends: qdl_ramdump_exe, + install: true, + install_dir: get_option('mandir') / 'man1', +) + +# Optional: replicate the "manpages" phony target +alias_target('manpages', [man_ks, man_qdl, man_qdl_ramdump]) \ No newline at end of file diff --git a/meson.options b/meson.options new file mode 100644 index 0000000..2fec6ba --- /dev/null +++ b/meson.options @@ -0,0 +1 @@ +option('VERSION', type: 'string', value: '', description: 'Project version') \ No newline at end of file diff --git a/scripts/checkpatch_wrapper.sh b/scripts/checkpatch_wrapper.sh new file mode 100755 index 0000000..71562bb --- /dev/null +++ b/scripts/checkpatch_wrapper.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# SPDX-License-Identifier: BSD-3-Clause + +set -euo pipefail + +ROOT_URL="https://raw.githubusercontent.com/torvalds/linux/v6.15/scripts" +CHECKPATCH_URL="${ROOT_URL}/checkpatch.pl" +SPELLING_URL="${ROOT_URL}/spelling.txt" + +ROOT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )/../" +CHECKPATCH_DIR="${ROOT_PATH}/.scripts" +CHECKPATCH="${CHECKPATCH_DIR}/checkpatch.pl" +SPELLING="${CHECKPATCH_DIR}/spelling.txt" + +# Just to repo root to work consistently from anywhere +cd "$ROOT_PATH" + +# List only tracked source files (not ignored/untracked) +CHECKPATCH_SOURCES=$(git ls-files | grep -E '\.(c|h|sh)$' | \ + grep -vE '(^|/)sha2\.(c|h)$' | \ + grep -vE 'version\.h$' | \ + grep -vE 'list\.h$' +) + +ensure_checkpatch() { + if [[ ! -x "$CHECKPATCH" ]]; then + echo "Downloading checkpatch.pl and spelling.txt..." + mkdir -p "$CHECKPATCH_DIR" + curl -sSfL "$CHECKPATCH_URL" -o "$CHECKPATCH" + curl -sSfL "$SPELLING_URL" -o "$SPELLING" + chmod +x "$CHECKPATCH" + fi +} + +do_check_all() { + ensure_checkpatch + echo "Running checkpatch on tracked source files (excluding sha2.c, sha2.h)..." + for file in ${CHECKPATCH_SOURCES}; do + perl "${CHECKPATCH}" --no-tree -f "$file" || exit 1 + done +} + +do_check_cached() { + ensure_checkpatch + echo "Running checkpatch on staged changes..." + git diff --cached -- . | perl "$CHECKPATCH" --no-tree - +} + +usage() { + echo "Usage: $0 {download|check|check-cached}" + exit 2 +} + +case "${1:-}" in + download) ensure_checkpatch ;; + check) do_check_all ;; + check-cached) do_check_cached ;; + *) usage ;; +esac diff --git a/tests/run_tests.sh b/tests/run_tests.sh index 12369db..0419ce4 100755 --- a/tests/run_tests.sh +++ b/tests/run_tests.sh @@ -6,6 +6,24 @@ set -e SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" FLAT_BUILD_PATH=$SCRIPT_PATH/data +while [[ $# -gt 0 ]]; do + case "$1" in + --builddir) + builddir="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +if [[ -z "${builddir}" ]]; then + echo "Error: --builddir is required." >&2 + exit 1 +fi + echo "####### Generate a FLAT build" $FLAT_BUILD_PATH/generate_flat_build.sh @@ -13,7 +31,7 @@ echo "####### Run QDL tests" cd $SCRIPT_PATH for t in test_*.sh; do echo "###### Run $t" - bash $t + bash $t --builddir $builddir if [ $? -eq 0 ]; then echo "####### Test $t: OK" else diff --git a/tests/test_vip_generation.sh b/tests/test_vip_generation.sh index 85053e3..8d232fe 100755 --- a/tests/test_vip_generation.sh +++ b/tests/test_vip_generation.sh @@ -5,17 +5,49 @@ set -e SCRIPT_PATH="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" +while [[ $# -gt 0 ]]; do + case "$1" in + --builddir) + builddir="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +if [[ -z "${builddir}" ]]; then + echo "Error: --builddir is required." >&2 + exit 1 +fi + FLAT_BUILD=${SCRIPT_PATH}/data REP_ROOT=${SCRIPT_PATH}/.. +QDL_PATH=$builddir VIP_PATH=${FLAT_BUILD}/vip EXPECTED_DIGEST="a05e1124edbe34dc504a327544fb66572591353dc3fa25e6e7eafbe4803e63e0" VIP_TABLE_FILE=${VIP_PATH}/DigestsToSign.bin +uname_out="$(uname -s)" +case "${uname_out}" in + Linux*|Darwin*) + QDL=qdl + ;; + CYGWIN*|MINGW*|MSYS*) + QDL=qdl.exe + ;; + *) + exit 1 + ;; +esac + mkdir -p $VIP_PATH cd $FLAT_BUILD -${REP_ROOT}/qdl --dry-run --create-digests=${VIP_PATH} \ +${QDL_PATH}/${QDL} --dry-run --create-digests=${VIP_PATH} \ prog_firehose_ddr.elf rawprogram*.xml patch*.xml if command -v sha256sum >/dev/null 2>&1; then diff --git a/version.h.in b/version.h.in new file mode 100644 index 0000000..eaab018 --- /dev/null +++ b/version.h.in @@ -0,0 +1 @@ +#define VERSION "@version@"