mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 07:14:55 +08:00
[cpp] nostdlib build to be used with WASM and other environments that do not want stdlibc++ linked in.
This commit is contained in:
parent
3927ff25ff
commit
aaca02ad81
@ -3,6 +3,8 @@ project(spine-cpp)
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../flags.cmake)
|
||||
|
||||
option(SPINE_NO_FILE_IO "Disable file I/O operations" OFF)
|
||||
|
||||
include_directories(include)
|
||||
file(GLOB INCLUDES "include/**/*.h")
|
||||
file(GLOB SOURCES "src/**/*.cpp")
|
||||
@ -10,6 +12,15 @@ file(GLOB SOURCES "src/**/*.cpp")
|
||||
add_library(spine-cpp STATIC ${SOURCES} ${INCLUDES})
|
||||
target_include_directories(spine-cpp PUBLIC include)
|
||||
|
||||
if(SPINE_NO_FILE_IO)
|
||||
target_compile_definitions(spine-cpp PRIVATE SPINE_NO_FILE_IO)
|
||||
endif()
|
||||
|
||||
# nostdcpp variant (no C++ standard library)
|
||||
file(GLOB NOSTDCPP_SOURCES ${SOURCES} "src/nostdlib.cpp")
|
||||
add_library(spine-cpp-nostdcpp STATIC ${NOSTDCPP_SOURCES} ${INCLUDES})
|
||||
target_include_directories(spine-cpp-nostdcpp PUBLIC include)
|
||||
|
||||
# Install target
|
||||
install(TARGETS spine-cpp EXPORT spine-cpp_TARGETS DESTINATION dist/lib)
|
||||
install(FILES ${INCLUDES} DESTINATION dist/include)
|
||||
@ -24,4 +35,28 @@ export(
|
||||
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
|
||||
add_executable(headless-test ${CMAKE_CURRENT_SOURCE_DIR}/tests/HeadlessTest.cpp)
|
||||
target_link_libraries(headless-test spine-cpp)
|
||||
|
||||
if(SPINE_NO_FILE_IO)
|
||||
target_compile_definitions(headless-test PRIVATE SPINE_NO_FILE_IO)
|
||||
endif()
|
||||
|
||||
# nostdcpp test executable (no C++ stdlib)
|
||||
add_executable(headless-test-nostdcpp ${CMAKE_CURRENT_SOURCE_DIR}/tests/HeadlessTest.cpp)
|
||||
|
||||
if(MSVC)
|
||||
# On Windows/MSVC, disable default libraries but keep C runtime
|
||||
target_link_libraries(headless-test-nostdcpp spine-cpp-nostdcpp)
|
||||
target_link_options(headless-test-nostdcpp PRIVATE /NODEFAULTLIB)
|
||||
target_link_libraries(headless-test-nostdcpp msvcrt kernel32)
|
||||
else()
|
||||
# Unix/Linux: avoid linking libstdc++ automatically
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
target_link_libraries(headless-test-nostdcpp spine-cpp-nostdcpp)
|
||||
target_link_options(headless-test-nostdcpp PRIVATE -nostdlib++ -lc)
|
||||
else()
|
||||
# GCC: use -nodefaultlibs and link minimal libraries including libgcc for operator new/delete
|
||||
target_link_options(headless-test-nostdcpp PRIVATE -nodefaultlibs)
|
||||
target_link_libraries(headless-test-nostdcpp spine-cpp-nostdcpp -lm -lc -lgcc)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@ -3,11 +3,38 @@ set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Clean only if explicitly requested
|
||||
if [ "$1" = "clean" ]; then
|
||||
# Parse arguments
|
||||
BUILD_TYPE="debug"
|
||||
NOFILEIO=""
|
||||
CLEAN=""
|
||||
|
||||
for arg in "$@"; do
|
||||
case $arg in
|
||||
clean)
|
||||
CLEAN="true"
|
||||
;;
|
||||
release)
|
||||
BUILD_TYPE="release"
|
||||
;;
|
||||
debug)
|
||||
BUILD_TYPE="debug"
|
||||
;;
|
||||
nofileio)
|
||||
NOFILEIO="-DSPINE_NO_FILE_IO=ON"
|
||||
;;
|
||||
*)
|
||||
echo "Unknown argument: $arg"
|
||||
echo "Usage: $0 [clean] [release|debug] [nofileio]"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Clean if requested
|
||||
if [ "$CLEAN" = "true" ]; then
|
||||
rm -rf build
|
||||
fi
|
||||
|
||||
# Always build
|
||||
cmake --preset=debug .
|
||||
cmake --build --preset=debug
|
||||
# Configure and build
|
||||
cmake --preset=$BUILD_TYPE $NOFILEIO .
|
||||
cmake --build --preset=$BUILD_TYPE
|
||||
54
spine-cpp/src/nostdlib.cpp
Normal file
54
spine-cpp/src/nostdlib.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Minimal runtime stubs for nostdlib spine-cpp build
|
||||
*
|
||||
* Provides minimal implementations of C++ runtime symbols required
|
||||
* for spine-cpp to work without the standard library.
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
// ============================================================================
|
||||
// C++ Runtime Stubs
|
||||
// ============================================================================
|
||||
|
||||
// Memory operators - basic malloc/free wrappers
|
||||
// Note: These require a C library that provides malloc/free
|
||||
extern "C" {
|
||||
void* malloc(size_t size);
|
||||
void free(void* ptr);
|
||||
}
|
||||
|
||||
// Memory operators - use malloc/free but with careful implementation
|
||||
// Make them weak so system can override if needed
|
||||
__attribute__((weak)) void* operator new(size_t size) {
|
||||
return malloc(size);
|
||||
}
|
||||
|
||||
__attribute__((weak)) void operator delete(void* ptr) {
|
||||
if (ptr) free(ptr);
|
||||
}
|
||||
|
||||
// Static initialization guards (single-threaded stubs)
|
||||
// Make them weak so system can override if needed
|
||||
extern "C" __attribute__((weak)) int __cxa_guard_acquire(char* guard) {
|
||||
return *guard == 0 ? (*guard = 1, 1) : 0;
|
||||
}
|
||||
|
||||
extern "C" __attribute__((weak)) void __cxa_guard_release(char* guard) {
|
||||
// No-op for single-threaded
|
||||
(void)guard;
|
||||
}
|
||||
|
||||
// Pure virtual function handler
|
||||
extern "C" __attribute__((weak)) void __cxa_pure_virtual() {
|
||||
// In a real implementation, this would abort or throw
|
||||
// For minimal stub, we'll just return (undefined behavior but won't crash)
|
||||
// This should never be called in a correctly written program
|
||||
}
|
||||
|
||||
// Stack protection (for GCC -fstack-protector)
|
||||
// Make it weak so libc can override it in static builds
|
||||
extern "C" __attribute__((weak)) char __stack_chk_guard = 0;
|
||||
extern "C" __attribute__((weak)) void __stack_chk_fail() {
|
||||
// Could call abort() or be no-op for minimal runtime
|
||||
}
|
||||
@ -101,7 +101,7 @@ void DefaultSpineExtension::_free(void *mem, const char *file, int line) {
|
||||
}
|
||||
|
||||
char *DefaultSpineExtension::_readFile(const String &path, int *length) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(SPINE_NO_FILE_IO)
|
||||
char *data;
|
||||
FILE *file = fopen(path.buffer(), "rb");
|
||||
if (!file) return 0;
|
||||
|
||||
82
spine-cpp/test-nostdlib.sh
Executable file
82
spine-cpp/test-nostdlib.sh
Executable file
@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# Build or reuse Docker image
|
||||
IMAGE_NAME="spine-cpp-nostdcpp-test"
|
||||
if ! docker image inspect $IMAGE_NAME >/dev/null 2>&1; then
|
||||
echo "Building Docker image (one-time setup)..."
|
||||
docker build -t $IMAGE_NAME - <<'EOF'
|
||||
FROM ubuntu:22.04
|
||||
RUN apt-get update >/dev/null 2>&1 && \
|
||||
apt-get install -y build-essential cmake ninja-build git file >/dev/null 2>&1 && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
EOF
|
||||
fi
|
||||
|
||||
echo "Running spine-cpp nostdcpp test..."
|
||||
|
||||
# Run Docker container with spine-runtimes directory mounted
|
||||
docker run --rm \
|
||||
-v "$(pwd)/..:/workspace/spine-runtimes" \
|
||||
-w /workspace/spine-runtimes/spine-cpp \
|
||||
$IMAGE_NAME \
|
||||
bash -c "
|
||||
|
||||
# Build everything first
|
||||
echo '=== Building all variants ==='
|
||||
./build.sh clean release >/dev/null 2>&1
|
||||
|
||||
# Try to build static regular executable
|
||||
echo 'Building static regular executable...'
|
||||
g++ -static -o build/headless-test-static build/CMakeFiles/headless-test.dir/tests/HeadlessTest.cpp.o build/libspine-cpp.a >/dev/null 2>&1 || echo 'Static regular build failed'
|
||||
|
||||
# Try to build static nostdcpp executable (multiple approaches)
|
||||
echo 'Building static nostdcpp executable...'
|
||||
|
||||
# Approach 1: Try with -static-libgcc and -static-libstdc++ but no libstdc++
|
||||
if g++ -static -static-libgcc -Wl,--exclude-libs,libstdc++.a -o build/headless-test-nostdcpp-static build/CMakeFiles/headless-test-nostdcpp.dir/tests/HeadlessTest.cpp.o build/libspine-cpp-nostdcpp.a -lm -lc 2>/dev/null; then
|
||||
echo 'SUCCESS: Static nostdcpp built (approach 1)'
|
||||
# Approach 2: Try minimal static linking
|
||||
elif g++ -static -o build/headless-test-nostdcpp-static-minimal build/CMakeFiles/headless-test-nostdcpp.dir/tests/HeadlessTest.cpp.o build/libspine-cpp-nostdcpp.a 2>/dev/null; then
|
||||
echo 'SUCCESS: Static nostdcpp built (approach 2 - minimal)'
|
||||
else
|
||||
echo 'All static nostdcpp approaches failed - static linking may not be practical on this system'
|
||||
fi
|
||||
|
||||
echo ''
|
||||
echo '=== FINAL RESULTS ==='
|
||||
echo ''
|
||||
echo 'File sizes:'
|
||||
for exe in build/headless-test*; do
|
||||
if [ -f \"\$exe\" ]; then
|
||||
ls -lah \"\$exe\" | awk '{printf \"%-30s %s\\n\", \$9, \$5}'
|
||||
fi
|
||||
done
|
||||
|
||||
echo ''
|
||||
echo 'Dependencies:'
|
||||
for exe in build/headless-test*; do
|
||||
if [ -f \"\$exe\" ]; then
|
||||
echo \"\$(basename \$exe):\"
|
||||
ldd \"\$exe\" 2>/dev/null || echo \" (statically linked)\"
|
||||
echo ''
|
||||
fi
|
||||
done
|
||||
|
||||
echo 'Functional test:'
|
||||
if [ -f build/headless-test-nostdcpp ]; then
|
||||
echo 'Testing headless-test-nostdcpp with spineboy...'
|
||||
if OUTPUT=\$(./build/headless-test-nostdcpp ../examples/spineboy/export/spineboy-pro.skel ../examples/spineboy/export/spineboy-pma.atlas idle 2>&1); then
|
||||
echo \"\$OUTPUT\" | head -10
|
||||
echo '... (output truncated)'
|
||||
echo 'SUCCESS: nostdcpp executable works!'
|
||||
else
|
||||
echo 'FAILED: nostdcpp executable failed to run'
|
||||
echo \"Error: \$OUTPUT\"
|
||||
fi
|
||||
else
|
||||
echo 'nostdcpp executable not found'
|
||||
fi
|
||||
"
|
||||
Loading…
x
Reference in New Issue
Block a user