From 9f5a6300f7ee782c2ee65afd0a77b2c6997ecc9c Mon Sep 17 00:00:00 2001 From: ping-ke Date: Sun, 30 Aug 2026 10:54:46 +0800 Subject: [PATCH 1/2] fix: make C test builds reliable across platforms --- Makefile | 39 +++++++++------- README.md | 33 +++++++++++++ build.sh | 18 ++++++-- test/c/CMakeLists.txt | 90 +++++++++++++++++------------------- test/c/test.cpp | 5 +- test/c/test.sh | 9 ++++ test/python/test_pyethash.py | 9 +--- 7 files changed, 125 insertions(+), 78 deletions(-) diff --git a/Makefile b/Makefile index f7496492..45b2b160 100644 --- a/Makefile +++ b/Makefile @@ -4,27 +4,28 @@ # redeclaration bug (map.go vs linkname_swiss.go conflict). export GOEXPERIMENT=noswissmap -BUILD_DIR := build -TEST_BIN := $(BUILD_DIR)/test/c/Test -BENCH_LIGHT := $(BUILD_DIR)/src/benchmark/Benchmark_LIGHT -BENCH_FULL := $(BUILD_DIR)/src/benchmark/Benchmark_FULL +BUILD_DIR := build +BENCH_DIR := $(BUILD_DIR)/src/benchmark + +BUILD_INPUTS := $(shell find src test/c cmake -type f \( \ + -name '*.c' -o -name '*.h' -o -name '*.cpp' -o \ + -name '*.cmake' -o -name 'CMakeLists.txt' \)) +BUILD_INPUTS += $(wildcard *.go) go.mod go.sum CMakeLists.txt Makefile build.sh \ + setup.py test/python/requirements.txt # --------------------------------------------------------------------------- # Build targets # --------------------------------------------------------------------------- # Stamp file: touched by build.sh on success to track freshness. -$(BUILD_DIR)/.built: $(shell find src -name '*.c' -o -name '*.h' -o -name '*.cpp') \ - ethash.go ethashc.go compat.go go.mod CMakeLists.txt +$(BUILD_DIR)/.built: $(BUILD_INPUTS) ./build.sh - @touch $(BUILD_DIR)/.built build: $(BUILD_DIR)/.built + @test -n "$$(find $(BUILD_DIR)/test/c -type f \( -name Test -o -name Test.exe \) -print 2>/dev/null | head -n 1)" || { echo "Required C test binary is missing under $(BUILD_DIR)/test/c" >&2; exit 1; } -$(BENCH_LIGHT) $(BENCH_FULL): $(BUILD_DIR)/.built - cd $(BUILD_DIR) && make Benchmark_LIGHT Benchmark_FULL - -build-bench: $(BENCH_LIGHT) $(BENCH_FULL) +build-bench: $(BUILD_DIR)/.built + cmake --build $(BUILD_DIR) --config Release --target Benchmark_LIGHT Benchmark_FULL # --------------------------------------------------------------------------- # Test targets (each depends only on the build it actually needs) @@ -46,11 +47,17 @@ test-all: $(BUILD_DIR)/.built # Benchmark targets # --------------------------------------------------------------------------- -bench-light: $(BENCH_LIGHT) - cd $(BUILD_DIR) && ./src/benchmark/Benchmark_LIGHT - -bench-full: $(BENCH_FULL) - cd $(BUILD_DIR) && ./src/benchmark/Benchmark_FULL +bench-light: build-bench + @BENCH_BIN="$$(find "$(abspath $(BENCH_DIR))" -type f \( -name Benchmark_LIGHT -o -name Benchmark_LIGHT.exe \) -path '*/Release/*' -print -quit)"; \ + [ -n "$$BENCH_BIN" ] || BENCH_BIN="$$(find "$(abspath $(BENCH_DIR))" -type f \( -name Benchmark_LIGHT -o -name Benchmark_LIGHT.exe \) -print -quit)"; \ + [ -n "$$BENCH_BIN" ] || { echo "Benchmark_LIGHT is missing under $(BENCH_DIR)" >&2; exit 1; }; \ + cd $(BUILD_DIR) && "$$BENCH_BIN" + +bench-full: build-bench + @BENCH_BIN="$$(find "$(abspath $(BENCH_DIR))" -type f \( -name Benchmark_FULL -o -name Benchmark_FULL.exe \) -path '*/Release/*' -print -quit)"; \ + [ -n "$$BENCH_BIN" ] || BENCH_BIN="$$(find "$(abspath $(BENCH_DIR))" -type f \( -name Benchmark_FULL -o -name Benchmark_FULL.exe \) -print -quit)"; \ + [ -n "$$BENCH_BIN" ] || { echo "Benchmark_FULL is missing under $(BENCH_DIR)" >&2; exit 1; }; \ + cd $(BUILD_DIR) && "$$BENCH_BIN" bench: bench-light bench-full diff --git a/README.md b/README.md index 2b2c3b54..aec3a10a 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,39 @@ For details on this project, please see the Ethereum wiki: https://github.com/ethereum/wiki/wiki/Ethash +## Building + +The C test suite requires CMake, a C/C++ compiler, and the Boost filesystem, +system, and unit test framework components. Missing components cause the build +to fail during CMake configuration rather than silently skipping the tests. + +On Ubuntu/Debian: + +```sh +sudo apt update +sudo apt install -y cmake build-essential libboost-test-dev \ + libboost-filesystem-dev libboost-system-dev python3-venv python3-dev +``` + +On macOS with Homebrew: + +```sh +brew install cmake boost python@3.13 +``` + +If using a versioned, keg-only Boost formula, expose its prefix to CMake: + +```sh +export BOOST_ROOT="$(brew --prefix boost@1.85)" +``` + +Then build and run all tests: + +```sh +make rebuild +make test-all +``` + ### Coding Style for C++ code: Follow the same exact style as in [cpp-ethereum](https://github.com/ethereum/cpp-ethereum/blob/develop/CodingStandards.txt) diff --git a/build.sh b/build.sh index 22ef5eb4..d410ed14 100755 --- a/build.sh +++ b/build.sh @@ -29,6 +29,8 @@ fi # C/C++ build (cmake → libethash.a + Test binary + Python extension) # --------------------------------------------------------------------------- mkdir -p "$BUILD_DIR" +# Never leave a stale success marker after a failed rebuild. +rm -f "$BUILD_DIR/.built" cd "$BUILD_DIR" cmake "$REPO_ROOT" -DCMAKE_BUILD_TYPE=Release > /dev/null 2>&1 || { @@ -44,13 +46,22 @@ if grep -q "microsoft\|WSL" /proc/version 2>/dev/null; then touch "$REPO_ROOT/src/python/core.c" fi -make +# Test is a required build artifact. Build it explicitly so a missing CMake +# target fails this script before the success stamp is written. Use CMake's +# build command so this also works with Ninja and non-Makefile generators. +cmake --build "$BUILD_DIR" --config Release --target Test + +TEST_BIN="$(find "$BUILD_DIR/test/c" -type f \( -name Test -o -name Test.exe \) -print 2>/dev/null | head -n 1 || true)" +if [[ -z "$TEST_BIN" ]]; then + echo "[build/c] Required C test binary was not generated under $BUILD_DIR/test/c" >&2 + exit 1 +fi echo "[build/c] Done — binaries in $BUILD_DIR" # Build benchmark binaries if --bench flag is passed if [[ "$DO_BENCH" -eq 1 ]]; then echo "[build/bench] Building benchmark binaries..." - make Benchmark_LIGHT Benchmark_FULL + cmake --build "$BUILD_DIR" --config Release --target Benchmark_LIGHT Benchmark_FULL echo "[build/bench] Done — binaries in $BUILD_DIR/src/benchmark/" fi @@ -74,8 +85,6 @@ else echo "[build/go] vet passed" fi -touch "$BUILD_DIR/.built" - # --------------------------------------------------------------------------- # Python extension build (compiles src/python/core.c via pip editable install) # --------------------------------------------------------------------------- @@ -93,4 +102,5 @@ pip install -e "$REPO_ROOT" -q --no-build-isolation deactivate echo "[build/python] Done" +touch "$BUILD_DIR/.built" echo "[build] All done" diff --git a/test/c/CMakeLists.txt b/test/c/CMakeLists.txt index 8b1c5937..4d30c84c 100644 --- a/test/c/CMakeLists.txt +++ b/test/c/CMakeLists.txt @@ -1,54 +1,48 @@ -if (MSVC) - if (NOT BOOST_ROOT) - set (BOOST_ROOT "$ENV{BOOST_ROOT}") - endif() - set (CMAKE_PREFIX_PATH BOOST_ROOT) +if (NOT BOOST_ROOT AND DEFINED ENV{BOOST_ROOT}) + set(BOOST_ROOT "$ENV{BOOST_ROOT}") +endif() +if (BOOST_ROOT) + set(CMAKE_PREFIX_PATH "${BOOST_ROOT};${CMAKE_PREFIX_PATH}") endif() -IF( NOT Boost_FOUND ) - # use multithreaded boost libraries, with -mt suffix - set(Boost_USE_MULTITHREADED ON) - - if (MSVC) - # TODO handle other msvc versions or it will fail find them - set(Boost_COMPILER -vc120) - # use static boost libraries *.lib - set(Boost_USE_STATIC_LIBS ON) - elseif (APPLE) - - # use static boost libraries *.a - set(Boost_USE_STATIC_LIBS ON) - - elseif (UNIX) - # use dynamic boost libraries .dll - set(Boost_USE_STATIC_LIBS OFF) - - endif() - find_package(Boost 1.48.0 COMPONENTS unit_test_framework system filesystem) -ENDIF() - -IF (Boost_FOUND) - message(STATUS "boost header: ${Boost_INCLUDE_DIRS}") - message(STATUS "boost libs : ${Boost_LIBRARIES}") +# The C test suite is required by the build, so fail during configuration when +# any of its Boost components are unavailable instead of omitting Test. +# use multithreaded boost libraries, with -mt suffix +set(Boost_USE_MULTITHREADED ON) - include_directories( ${Boost_INCLUDE_DIR} ) - include_directories(../../src) +if (MSVC) + # use static boost libraries *.lib + set(Boost_USE_STATIC_LIBS ON) +elseif (APPLE) + # use static boost libraries *.a + set(Boost_USE_STATIC_LIBS ON) +elseif (UNIX) + # use dynamic boost libraries .dll + set(Boost_USE_STATIC_LIBS OFF) +endif() - link_directories(${Boost_LIBRARY_DIRS}) - file(GLOB HEADERS "*.h") - if ((NOT MSVC) AND (NOT APPLE)) - ADD_DEFINITIONS(-DBOOST_TEST_DYN_LINK) - endif() - if (NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ") - endif() +# Prefer the package configuration shipped by modern Boost distributions (for +# example Homebrew on macOS), then fall back to CMake's legacy FindBoost module +# for system packages that do not ship BoostConfig.cmake. +find_package(Boost 1.48.0 CONFIG QUIET COMPONENTS unit_test_framework system filesystem) +if (NOT Boost_FOUND) + find_package(Boost 1.48.0 MODULE REQUIRED COMPONENTS unit_test_framework system filesystem) +endif() - add_executable (Test "./test.cpp" ${HEADERS}) - target_link_libraries(Test ${ETHHASH_LIBS}) - target_link_libraries(Test ${Boost_FILESYSTEM_LIBRARIES}) - target_link_libraries(Test ${Boost_SYSTEM_LIBRARIES}) - target_link_libraries(Test ${Boost_UNIT_TEST_FRAMEWORK_LIBRARIES}) +file(GLOB HEADERS "*.h") - enable_testing () - add_test(NAME ethash COMMAND Test) -ENDIF() +add_executable(Test "./test.cpp" ${HEADERS}) +target_compile_features(Test PRIVATE cxx_std_11) +target_include_directories(Test PRIVATE ../../src) +if ((NOT MSVC) AND (NOT APPLE)) + target_compile_definitions(Test PRIVATE BOOST_TEST_DYN_LINK) +endif() +target_link_libraries(Test PRIVATE + ${ETHHASH_LIBS} + Boost::filesystem + Boost::system + Boost::unit_test_framework +) + +enable_testing() +add_test(NAME ethash COMMAND Test) diff --git a/test/c/test.cpp b/test/c/test.cpp index 44e0c385..0a3272fd 100644 --- a/test/c/test.cpp +++ b/test/c/test.cpp @@ -27,8 +27,7 @@ #include using namespace std; -using byte = uint8_t; -using bytes = std::vector; +using bytes = std::vector; namespace fs = boost::filesystem; // Just an alloca "wrapper" to silence uint64_t to size_t conversion warnings in windows @@ -83,7 +82,7 @@ bytes hexStringToBytes(std::string const& _s) for (unsigned i = s; i < _s.size(); i += 2) try { - ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); + ret.push_back((uint8_t)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); } catch (...){ ret.push_back(0); diff --git a/test/c/test.sh b/test/c/test.sh index b0ef7314..47ff48d1 100755 --- a/test/c/test.sh +++ b/test/c/test.sh @@ -27,6 +27,15 @@ if [ ! -f "$BUILD_DIR/.built" ]; then "$REPO_ROOT/build.sh" fi +if [ ! -f "$TEST_BIN" ]; then + TEST_BIN="$(find "$BUILD_DIR/test/c" -type f \( -name Test -o -name Test.exe \) -print 2>/dev/null | head -n 1 || true)" + if [ -z "$TEST_BIN" ]; then + echo "[test/c] Required C test binary is missing under $BUILD_DIR/test/c" >&2 + echo "[test/c] Run 'make rebuild' after installing the required Boost components." >&2 + exit 1 + fi +fi + echo "[test/c] Running $TEST_BIN" # mmap(MAP_SHARED) on NTFS via WSL DrvFs (/mnt/d/...) does not properly flush diff --git a/test/python/test_pyethash.py b/test/python/test_pyethash.py index 625e206d..91eb8e77 100644 --- a/test/python/test_pyethash.py +++ b/test/python/test_pyethash.py @@ -13,14 +13,11 @@ - test_mining_* : mine() commented out """ import hashlib -import os import pytest import pyethash from random import randint from Crypto.Hash import keccak -EXPECTED_DIR = os.path.join(os.path.dirname(__file__), "expected") - def _keccak256(data: bytes) -> bytes: return keccak.new(digest_bits=256, data=data).digest() @@ -68,13 +65,13 @@ def test_mkcache_is_as_expected(cache_epoch0): assert len(cache_epoch0) % pyethash.HASH_BYTES == 0 # Compare SHA-256 fingerprint against stored reference - expected = "396c1ff479b0a02b88bad57fe69a6a4f573b180cc4f108d0d7f431a7d3d666d9" # pre-computed for epoch-0 + expected = "396c1ff479b0a02b88bad57fe69a6a4f573b180cc4f108d0d7f431a7d3d666d9" actual = hashlib.sha256(cache_epoch0).hexdigest() assert actual == expected, ( f"cache_epoch0 SHA-256 mismatch:\n got: {actual}\n expected: {expected}" ) - expected = "fda2c14d3a454243c6f58f74ec60ae854f7d286497757ad32449e42e2dcd0535" # pre-computed for epoch-1 + expected = "fda2c14d3a454243c6f58f74ec60ae854f7d286497757ad32449e42e2dcd0535" raw = pyethash.mkcache_bytes(pyethash.EPOCH_LENGTH) actual = hashlib.sha256(raw).hexdigest() assert actual == expected, ( @@ -137,5 +134,3 @@ def test_get_seedhash(): # Out-of-range block number must raise with pytest.raises(ValueError): pyethash.get_seedhash(pyethash.EPOCH_LENGTH * 2048) - - From 03b790a193db246cf79caec3ae8044a5b7f38bc0 Mon Sep 17 00:00:00 2001 From: ping-ke Date: Tue, 1 Sep 2026 11:19:39 +0800 Subject: [PATCH 2/2] fix: keep C test build on C++11 --- test/c/CMakeLists.txt | 8 ++++++-- test/c/test.cpp | 5 +++-- test/python/test_pyethash.py | 9 +++++++-- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/test/c/CMakeLists.txt b/test/c/CMakeLists.txt index 4d30c84c..9a14a2e8 100644 --- a/test/c/CMakeLists.txt +++ b/test/c/CMakeLists.txt @@ -17,7 +17,7 @@ elseif (APPLE) # use static boost libraries *.a set(Boost_USE_STATIC_LIBS ON) elseif (UNIX) - # use dynamic boost libraries .dll + # use shared boost libraries set(Boost_USE_STATIC_LIBS OFF) endif() @@ -32,7 +32,11 @@ endif() file(GLOB HEADERS "*.h") add_executable(Test "./test.cpp" ${HEADERS}) -target_compile_features(Test PRIVATE cxx_std_11) +set_target_properties(Test PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + CXX_EXTENSIONS NO +) target_include_directories(Test PRIVATE ../../src) if ((NOT MSVC) AND (NOT APPLE)) target_compile_definitions(Test PRIVATE BOOST_TEST_DYN_LINK) diff --git a/test/c/test.cpp b/test/c/test.cpp index 0a3272fd..44e0c385 100644 --- a/test/c/test.cpp +++ b/test/c/test.cpp @@ -27,7 +27,8 @@ #include using namespace std; -using bytes = std::vector; +using byte = uint8_t; +using bytes = std::vector; namespace fs = boost::filesystem; // Just an alloca "wrapper" to silence uint64_t to size_t conversion warnings in windows @@ -82,7 +83,7 @@ bytes hexStringToBytes(std::string const& _s) for (unsigned i = s; i < _s.size(); i += 2) try { - ret.push_back((uint8_t)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); + ret.push_back((byte)(fromHex(_s[i]) * 16 + fromHex(_s[i + 1]))); } catch (...){ ret.push_back(0); diff --git a/test/python/test_pyethash.py b/test/python/test_pyethash.py index 91eb8e77..625e206d 100644 --- a/test/python/test_pyethash.py +++ b/test/python/test_pyethash.py @@ -13,11 +13,14 @@ - test_mining_* : mine() commented out """ import hashlib +import os import pytest import pyethash from random import randint from Crypto.Hash import keccak +EXPECTED_DIR = os.path.join(os.path.dirname(__file__), "expected") + def _keccak256(data: bytes) -> bytes: return keccak.new(digest_bits=256, data=data).digest() @@ -65,13 +68,13 @@ def test_mkcache_is_as_expected(cache_epoch0): assert len(cache_epoch0) % pyethash.HASH_BYTES == 0 # Compare SHA-256 fingerprint against stored reference - expected = "396c1ff479b0a02b88bad57fe69a6a4f573b180cc4f108d0d7f431a7d3d666d9" + expected = "396c1ff479b0a02b88bad57fe69a6a4f573b180cc4f108d0d7f431a7d3d666d9" # pre-computed for epoch-0 actual = hashlib.sha256(cache_epoch0).hexdigest() assert actual == expected, ( f"cache_epoch0 SHA-256 mismatch:\n got: {actual}\n expected: {expected}" ) - expected = "fda2c14d3a454243c6f58f74ec60ae854f7d286497757ad32449e42e2dcd0535" + expected = "fda2c14d3a454243c6f58f74ec60ae854f7d286497757ad32449e42e2dcd0535" # pre-computed for epoch-1 raw = pyethash.mkcache_bytes(pyethash.EPOCH_LENGTH) actual = hashlib.sha256(raw).hexdigest() assert actual == expected, ( @@ -134,3 +137,5 @@ def test_get_seedhash(): # Out-of-range block number must raise with pytest.raises(ValueError): pyethash.get_seedhash(pyethash.EPOCH_LENGTH * 2048) + +