Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions .github/workflows/ap203min.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: AP203Min static ExternalProject

# Builds example/ap203min/ExternalProjectBuild, which builds STEPCode as a
# static-only ExternalProject the same way OpenVSP does, then runs the result.

on:
workflow_dispatch:
push:
pull_request:

jobs:
ap203min:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- name: Ubuntu GCC
os: ubuntu-latest
cc: gcc
cxx: g++
generator: Ninja
- name: Ubuntu Clang
os: ubuntu-latest
cc: clang
cxx: clang++
generator: Ninja
- name: macOS Clang
os: macos-latest
cc: clang
cxx: clang++
generator: Ninja
- name: Windows MSVC Ninja
os: windows-latest
cc: cl
cxx: cl
generator: Ninja
- name: Windows Visual Studio
os: windows-latest
# Empty generator: CMake picks the newest installed Visual Studio.
generator: ""
defaults:
run:
shell: bash
steps:
- name: Setup - CMake and Ninja
uses: lukka/get-cmake@latest

- name: Setup - MSVC
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1

- name: Checkout
uses: actions/checkout@v5

- name: Configure
run: |
# Build outside the checkout: the ExternalProject copies the whole source tree.
BUILD_DIR="$(cd .. && pwd)/ap203min-build"
echo "BUILD_DIR=$BUILD_DIR" >> "$GITHUB_ENV"
args=(-S example/ap203min/ExternalProjectBuild -B "$BUILD_DIR")
if [ -n "${{ matrix.generator }}" ]; then
args+=(-G "${{ matrix.generator }}")
fi
if [ -n "${{ matrix.cc }}" ]; then
args+=(-DCMAKE_C_COMPILER=${{ matrix.cc }} -DCMAKE_CXX_COMPILER=${{ matrix.cxx }})
fi
if [ "${{ matrix.generator }}" = "Ninja" ]; then
args+=(-DCMAKE_BUILD_TYPE=Release)
fi
cmake --version
cmake "${args[@]}"
grep CMAKE_GENERATOR: "$BUILD_DIR/CMakeCache.txt"

- name: Build
run: cmake --build "$BUILD_DIR" --config Release

- name: No shared libraries built
run: |
ls -l "$BUILD_DIR"/sc-install/lib
if ls "$BUILD_DIR"/sc-install/lib | grep -Ei '\.(so|dylib|dll)'; then exit 1; fi
if ls "$BUILD_DIR"/sc-install/bin | grep -Ei '\.dll$'; then exit 1; fi

- name: Test (run, and check for non-system dynamic dependencies)
run: |
cd "$BUILD_DIR"
ctest -C Release -V --output-on-failure
test -s outfile.stp
head -20 outfile.stp
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ endif (POLICY CMP0077)

# SC version
set(SC_VERSION_MAJOR 0)
set(SC_VERSION_MINOR 9)
set(SC_VERSION_MINOR 8)
set(SC_VERSION_PATCH 1)
set(SC_VERSION ${SC_VERSION_MAJOR}.${SC_VERSION_MINOR}.${SC_VERSION_PATCH})

Expand Down
76 changes: 33 additions & 43 deletions example/ap203min/ExternalProjectBuild/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,58 +5,19 @@
# freely copied in part or in full for any purpose. No acknowledgment is required
# for the use of this file.
#
project(AP203Minimum)
cmake_minimum_required(VERSION 3.12)
project(AP203Minimum)

INCLUDE( ExternalProject )

set(CMAKE_MODULE_PATH ${AP203Minimum_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})

# Sets STEPCODE_INSTALL_DIR, STEPCODE_INCLUDE_DIR, STEPCODE_LIBRARIES,
# STEPCODE_SYSTEM_LIBRARIES and STEPCODE_DEFINITIONS.
INCLUDE( External_STEPCode )

#####
# Variables ideally set by FindSTEPCode.cmake

IF(NOT WIN32)
set( STEPCODE_LIBRARIES
${STEPCODE_INSTALL_DIR}/lib/libstepcore.a
${STEPCODE_INSTALL_DIR}/lib/libstepeditor.a
${STEPCODE_INSTALL_DIR}/lib/libstepdai.a
${STEPCODE_INSTALL_DIR}/lib/libsteputils.a
${STEPCODE_INSTALL_DIR}/lib/libsdai_ap203.a
)
ELSE()
set( STEPCODE_LIBRARIES
${STEPCODE_INSTALL_DIR}/lib/libstepcore.lib
${STEPCODE_INSTALL_DIR}/lib/libstepeditor.lib
${STEPCODE_INSTALL_DIR}/lib/libstepdai.lib
${STEPCODE_INSTALL_DIR}/lib/libsteputils.lib
${STEPCODE_INSTALL_DIR}/lib/libsdai_ap203.lib
${STEPCODE_INSTALL_DIR}/lib/libexpress.lib
${STEPCODE_INSTALL_DIR}/lib/libexppp.lib
shlwapi.lib
)
ENDIF()

MESSAGE( STATUS "STEPCODE_INSTALL_DIR: " ${STEPCODE_INSTALL_DIR} )

set( STEPCODE_INCLUDE_DIR
${STEPCODE_INSTALL_DIR}/include/stepcode
${STEPCODE_INSTALL_DIR}/include/stepcode/clstepcore
${STEPCODE_INSTALL_DIR}/include/stepcode/cldai
${STEPCODE_INSTALL_DIR}/include/stepcode/clutils
${STEPCODE_INSTALL_DIR}/include/stepcode/cleditor
${STEPCODE_INSTALL_DIR}/include/schemas/sdai_ap203
)

# End of variables ideally set by FindSTEPCode.cmake
######


include_directories(
${STEPCODE_INCLUDE_DIR}
)

set(SRCS ../ap203min.cpp)

set(HDRS )
Expand All @@ -65,10 +26,39 @@ add_executable( ${PROJECT_NAME} ${SRCS} ${HDRS})

add_dependencies( ${PROJECT_NAME} STEPCODE )

target_link_libraries( ${PROJECT_NAME}
target_include_directories( ${PROJECT_NAME} PRIVATE ${STEPCODE_INCLUDE_DIR} )

target_compile_definitions( ${PROJECT_NAME} PRIVATE ${STEPCODE_DEFINITIONS} )

target_link_libraries( ${PROJECT_NAME}
${STEPCODE_LIBRARIES}
${STEPCODE_SYSTEM_LIBRARIES}
)

#####
# Tests: run the example, and verify it has no non-system dynamic dependencies.

enable_testing()

add_test( NAME run_ap203min COMMAND ${PROJECT_NAME}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )

if( APPLE )
find_program( STATIC_DEPS_TOOL otool )
elseif( WIN32 )
get_filename_component( _cxx_dir "${CMAKE_CXX_COMPILER}" DIRECTORY )
get_filename_component( _linker_dir "${CMAKE_LINKER}" DIRECTORY )
find_program( STATIC_DEPS_TOOL dumpbin HINTS ${_cxx_dir} ${_linker_dir} )
else()
find_program( STATIC_DEPS_TOOL ldd )
endif()

add_test( NAME static_deps_ap203min
COMMAND ${CMAKE_COMMAND}
-DEXE=$<TARGET_FILE:${PROJECT_NAME}>
-DTOOL=${STATIC_DEPS_TOOL}
-P ${AP203Minimum_SOURCE_DIR}/cmake/CheckStaticDeps.cmake )

# Local Variables:
# tab-width: 8
# mode: cmake
Expand Down
128 changes: 128 additions & 0 deletions example/ap203min/ExternalProjectBuild/cmake/CheckStaticDeps.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
#
# Verify that an executable does not dynamically link anything except
# operating system and compiler runtime libraries.
#
# Usage:
# cmake -DEXE=<executable> -DTOOL=<otool|ldd|dumpbin> -P CheckStaticDeps.cmake
#
# The dependency tool's full output is printed as a diagnostic. The check
# fails if any dependency is not a system library, cannot be resolved, or
# looks like a STEPCode library.
#
cmake_minimum_required( VERSION 3.12 )

if( NOT EXE OR NOT EXISTS "${EXE}" )
message( FATAL_ERROR "Executable not found: '${EXE}'" )
endif()
if( NOT TOOL )
message( FATAL_ERROR "No dependency tool (otool, ldd or dumpbin) was found." )
endif()

get_filename_component( _tool_name "${TOOL}" NAME_WE )
string( TOLOWER "${_tool_name}" _tool_name )

if( _tool_name STREQUAL "otool" )
set( _args -L "${EXE}" )
elseif( _tool_name STREQUAL "ldd" )
set( _args "${EXE}" )
elseif( _tool_name STREQUAL "dumpbin" )
set( _args /dependents "${EXE}" )
else()
message( FATAL_ERROR "Unsupported dependency tool: ${TOOL}" )
endif()

execute_process( COMMAND "${TOOL}" ${_args}
OUTPUT_VARIABLE _out
ERROR_VARIABLE _err
RESULT_VARIABLE _res )

list( JOIN _args " " _args_str )
message( "${TOOL} ${_args_str}\n${_out}${_err}" )

if( NOT _res EQUAL 0 )
message( FATAL_ERROR "${TOOL} failed with exit code ${_res}" )
endif()

# Collect (name, resolved path) for each dependency.
set( _deps )
set( _bad )
string( REPLACE "\r" "" _out "${_out}" )
string( REPLACE "\n" ";" _lines "${_out}" )

if( _tool_name STREQUAL "otool" )
# First line is the executable itself; dependencies are indented.
foreach( _line ${_lines} )
if( _line MATCHES "^[ \t]+([^ \t].*) \\(compatibility version" )
set( _path "${CMAKE_MATCH_1}" )
list( APPEND _deps "${_path}" )
if( NOT _path MATCHES "^/usr/lib/" AND NOT _path MATCHES "^/System/Library/" )
list( APPEND _bad "${_path}" )
endif()
endif()
endforeach()

elseif( _tool_name STREQUAL "ldd" )
foreach( _line ${_lines} )
string( STRIP "${_line}" _line )
if( _line STREQUAL "" OR _line MATCHES "statically linked" )
continue()
endif()
list( APPEND _deps "${_line}" )
if( _line MATCHES "not found" )
list( APPEND _bad "${_line}" )
elseif( _line MATCHES "=> (/[^ ]+)" )
set( _path "${CMAKE_MATCH_1}" )
elseif( _line MATCHES "^(/[^ ]+)" )
set( _path "${CMAKE_MATCH_1}" )
else()
# Virtual DSO such as linux-vdso.so.1 has no file.
continue()
endif()
if( NOT _path MATCHES "^/(usr/)?lib(32|64|x32)?/" )
list( APPEND _bad "${_line}" )
endif()
endforeach()

elseif( _tool_name STREQUAL "dumpbin" )
set( _in_deps FALSE )
foreach( _line ${_lines} )
string( STRIP "${_line}" _line )
if( _line MATCHES "has the following dependencies" )
set( _in_deps TRUE )
continue()
endif()
if( NOT _in_deps OR _line STREQUAL "" )
continue()
endif()
if( _line MATCHES "^Summary" OR _line MATCHES "delay load" )
break()
endif()
list( APPEND _deps "${_line}" )
# System DLLs (including the MSVC and UCRT runtimes) live in System32.
file( TO_CMAKE_PATH "$ENV{SystemRoot}/System32/${_line}" _sys )
if( NOT EXISTS "${_sys}" AND NOT _line MATCHES "^(api|ext)-ms-" )
list( APPEND _bad "${_line}" )
endif()
endforeach()
endif()

if( NOT _deps )
message( FATAL_ERROR "Could not parse any dependencies from ${TOOL} output." )
endif()

# Catch STEPCode libraries even if they were somehow installed system-wide.
foreach( _dep ${_deps} )
get_filename_component( _name "${_dep}" NAME )
if( _name MATCHES "(step(core|dai|utils|editor|lazyfile)|sdai_|express|exppp)" )
list( APPEND _bad "${_dep}" )
endif()
endforeach()

list( LENGTH _deps _ndeps )
if( _bad )
list( REMOVE_DUPLICATES _bad )
string( REPLACE ";" "\n " _bad "${_bad}" )
message( FATAL_ERROR "Non-system dynamic dependencies found:\n ${_bad}" )
endif()

message( STATUS "OK: all ${_ndeps} dynamic dependencies are system libraries." )
Loading
Loading