From ca800e8a876cad458726a1d8d2d25feba81f956d Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Thu, 2 Jul 2026 16:44:22 -0500 Subject: [PATCH 01/28] cmake: transition: phase 1 --- .github/workflows/ci.yml | 128 +++++ .gitignore | 15 +- CMakeLists.txt | 489 +++--------------- CMakeModules/FindLLVM.cmake | 201 ------- CMakeModules/FindTee.cmake | 21 - CMakeModules/FindUDUNITS2.cmake | 92 ---- CMakePresets.json | 30 ++ cmake/FindLLVMClang.cmake | 101 ++++ {CMakeModules => cmake}/FindMaven.cmake | 0 cmake/FindUDUNITS2.cmake | 63 +++ cmake/TrickClangLibs.cmake | 95 ++++ cmake/TrickGTest.cmake | 38 ++ cmake/TrickPrograms.cmake | 191 +++++++ cmake/TrickPython.cmake | 59 +++ share/trick/makefiles/config_user_cmake.mk.in | 69 +-- test/build_config/compare_config_user.sh | 98 ++++ 16 files changed, 920 insertions(+), 770 deletions(-) delete mode 100644 CMakeModules/FindLLVM.cmake delete mode 100644 CMakeModules/FindTee.cmake delete mode 100644 CMakeModules/FindUDUNITS2.cmake create mode 100644 CMakePresets.json create mode 100644 cmake/FindLLVMClang.cmake rename {CMakeModules => cmake}/FindMaven.cmake (100%) create mode 100644 cmake/FindUDUNITS2.cmake create mode 100644 cmake/TrickClangLibs.cmake create mode 100644 cmake/TrickGTest.cmake create mode 100644 cmake/TrickPrograms.cmake create mode 100644 cmake/TrickPython.cmake create mode 100755 test/build_config/compare_config_user.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 713a117ff..2bfcfd58f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,6 +40,134 @@ jobs: needs: [style] uses: ./.github/workflows/_test-macos.yml + cmake-configure: + name: CMake Configure (${{ matrix.cfg.os }} ${{ matrix.cfg.tag }}) + needs: [style] + # TODO: Remove always() once style enforcement is stable — ideally builds only run on clean style. + if: always() + runs-on: ubuntu-latest + container: docker://${{ matrix.cfg.image }}:${{ matrix.cfg.tag }} + strategy: + fail-fast: false + matrix: + cfg: + - { os: ubuntu, tag: 24.04, arch: debian, image: ubuntu } + - { os: ubuntu, tag: 26.04, arch: debian, image: ubuntu } + - { os: rockylinux, tag: 9, arch: rhel, image: rockylinux/rockylinux } + include: + - cfg: {} + deps: >- + bison + clang + flex + git + llvm + make + maven + cmake + swig + zip + gdb + conf_pkg: echo package manager already configured + enable_repo: "" + install_cmd: install -y + + #-------- Debian-based Dependencies ---------------- + - cfg: { arch: debian } + pkg_mgr: apt-get + conf_pkg: apt-get update + arch_deps: >- + curl + g++ + libx11-dev + libxml2-dev + libxt-dev + libmotif-common + libmotif-dev + zlib1g-dev + llvm-dev + libclang-dev + libudunits2-dev + libgtest-dev + libgmock-dev + default-jdk + python3-dev + python3-pip + python3-venv + + #-------- RHEL-based Dependencies (all versions) ---------------- + - cfg: { arch: rhel } + pkg_mgr: dnf + conf_pkg: | + dnf -y install epel-release + dnf -y update + dnf install -y 'dnf-command(config-manager)' + arch_deps: >- + clang-devel + diffutils + gcc + gcc-c++ + gtest-devel + gmock-devel + java-21-openjdk-devel + libxml2-devel + llvm-devel + llvm-static + ncurses-devel + openmotif + openmotif-devel + perl + perl-Digest-MD5 + udunits2 + udunits2-devel + which + zlib-devel + python3-devel + + #-------- RHEL 9: gtest lives in crb ---------------- + - cfg: { arch: rhel, tag: 9 } + enable_repo: dnf config-manager --enable crb + + steps: + - name: Set noninteractive mode + run: echo "DEBIAN_FRONTEND=noninteractive" >> "$GITHUB_ENV" + if: matrix.cfg.arch == 'debian' + - name: Update Package Manager + run: | + ${{ matrix.conf_pkg }} + ${{ matrix.enable_repo }} + - name: Install Dependencies + run: > + ${{ matrix.pkg_mgr }} + ${{ matrix.install_cmd }} + ${{ matrix.deps }} + ${{ matrix.arch_deps }} + - name: Checkout repository + uses: actions/checkout@v6 + - name: Compare autoconf vs cmake config_user.mk + run: | + echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" + test/build_config/compare_config_user.sh + + cmake-configure-macos: + name: CMake Configure (macOS) + needs: [style] + # TODO: Remove always() once style enforcement is stable — ideally builds only run on clean style. + if: always() + runs-on: macos-26 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + - name: Install dependencies + run: | + brew update || true + brew upgrade || true + brew install --cask xquartz + brew install udunits openmotif maven + brew install swig llvm + - name: Compare autoconf vs cmake config_user.mk + run: test/build_config/compare_config_user.sh + trickops: needs: [style] uses: ./.github/workflows/_trickops.yml diff --git a/.gitignore b/.gitignore index 9fff8097e..fee3f0d83 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,22 @@ lib_Darwin_* *.gcno coverage.info gmon.out -compile_commands.json Makefile_jsc_dirs +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps +CMakeUserPresets.json + # Autoconf / configure config.status* config.log diff --git a/CMakeLists.txt b/CMakeLists.txt index 73921ff7e..c6eb51151 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,425 +1,86 @@ -cmake_minimum_required(VERSION 3.1) - -# trick is a C/C++ project, but we have some macOS -# configuration to do before CMake searches for compilers -project(trick NONE) -set(TRICK_MAJOR 25) -set(TRICK_MINOR 1) -set(TRICK_TINY 1) -# set TRICK_PRERELEASE TO EMPTY STRING "" ON RELEASE -set(TRICK_PRERELEASE "-beta") - -# On macOS Mojave and Catalina, the compilers in /usr/bin -# are the ones that include the correct C standard library system headers -if(CMAKE_SYSTEM_NAME MATCHES Darwin) - if ( (NOT DEFINED CMAKE_C_COMPILER) AND (NOT DEFINED ENV{CC}) AND (EXISTS /usr/bin/cc) ) - set(CMAKE_C_COMPILER /usr/bin/cc) - endif() - if ( (NOT DEFINED CMAKE_CXX_COMPILER) AND (NOT DEFINED ENV{CXX}) AND (EXISTS /usr/bin/c++) ) - set(CMAKE_CXX_COMPILER /usr/bin/c++) +cmake_minimum_required(VERSION 3.20) + +# Version lives in share/trick/trick_ver.txt — the single source of truth +# also read by bin/trick-version at sim-build time (Makefile.common:28-30). +file(READ ${CMAKE_CURRENT_SOURCE_DIR}/share/trick/trick_ver.txt TR_VER_TXT) +string(REGEX MATCH "current_version *= *\"([0-9]+)\\.([0-9]+)\\.([0-9]+)([^\"]*)\"" _ "${TR_VER_TXT}") +set(TRICK_MAJOR ${CMAKE_MATCH_1}) +set(TRICK_MINOR ${CMAKE_MATCH_2}) +set(TRICK_TINY ${CMAKE_MATCH_3}) +set(TRICK_PRERELEASE ${CMAKE_MATCH_4}) + +# Mirror AC_PROG_CC/AC_PROG_CXX's default search order (gcc/cc, g++/c++) so +# CMAKE_C_COMPILER/CMAKE_CXX_COMPILER matches what ./configure would pick. +# Compiler selection must happen before project() enables the languages; it +# still honors the CC/CXX environment variables exactly like autoconf does. +if(NOT DEFINED ENV{CC} AND NOT CMAKE_C_COMPILER) + find_program(TR_C_COMPILER NAMES gcc cc) + if(TR_C_COMPILER) + set(CMAKE_C_COMPILER "${TR_C_COMPILER}") endif() endif() - - -enable_language(C) -enable_language(CXX) - -#set(CMAKE_VERBOSE_MAKEFILE ON) -set(CMAKE_CXX_STANDARD 11) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(TRICK_FORCE_32BIT OFF CACHE BOOL "Set Trick to compile in 32bit mode") -#set(USE_JAVA ON CACHE BOOL "Use java") -set(USE_ER7_UTILS ON CACHE BOOL "Use er7_utils") -set(UDUNITS2_ROOT "" CACHE STRING "UDUNITS home directory") -set(TRICK_MONGOOSE "0" CACHE STRING "Enable webserver") - -#message("UDUNITS2_ROOT = ${UDUNITS2_ROOT}") - -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) - -if(EXISTS "/etc/redhat-release") - if(TRICK_FORCE_32BIT) - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) - else() - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib64) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib64) +if(NOT DEFINED ENV{CXX} AND NOT CMAKE_CXX_COMPILER) + find_program(TR_CXX_COMPILER NAMES g++ c++) + if(TR_CXX_COMPILER) + set(CMAKE_CXX_COMPILER "${TR_CXX_COMPILER}") endif() -else() - set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) - set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) -endif() - -set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules) -include(UseJava) -find_package(Java REQUIRED) -find_package(Maven REQUIRED) -find_package(BISON REQUIRED) -find_package(FLEX REQUIRED) -find_package(LLVM 3.4 REQUIRED) -find_package(Perl REQUIRED) -find_package(PythonInterp REQUIRED) -find_package(PythonLibs REQUIRED) -find_package(SWIG REQUIRED) -find_package(Tee REQUIRED) -find_package(Threads REQUIRED) -find_package(UDUNITS2 REQUIRED) -find_package(LibXml2 REQUIRED) -find_package(HDF5) -find_package(GSL) - -find_package(X11) -find_package(Motif) - -add_definitions( -DTRICK_VER=${TRICK_MAJOR} ) - -if(USE_ER7_UTILS) - add_definitions( -DUSE_ER7_UTILS_INTEGRATORS) -endif() - -if(GSL_FOUND) - add_definitions( -D_HAVE_GSL) -endif() - -if(USE_MONGOOSE) - add_definitions(-DUSE_MONGOOSE) -endif() - -include_directories( ${CMAKE_BINARY_DIR}/include) -include_directories( ${CMAKE_BINARY_DIR}/include/trick/compat) - -file(COPY bin DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY include DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY libexec DESTINATION ${CMAKE_BINARY_DIR}) -file(COPY share DESTINATION ${CMAKE_BINARY_DIR}) -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include/mongoose) -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/temp_src/io_src) -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/temp_src/lex_yacc) -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/temp_src/mongoose) -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/temp_src/swig) -# copy er7_util header files to build directory -file(GLOB_RECURSE ER7_UTIL_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/trick_source/er7_utils ${CMAKE_CURRENT_SOURCE_DIR}/trick_source/er7_utils/*.hh) -foreach ( infile ${ER7_UTIL_HEADERS} ) - get_filename_component(dir ${infile} DIRECTORY) - file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/trick_source/er7_utils/${infile} DESTINATION "${CMAKE_BINARY_DIR}/include/er7_utils/${dir}") -endforeach(infile) - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/share/trick/makefiles/config_user_cmake.mk.in ${CMAKE_BINARY_DIR}/share/trick/makefiles/config_user.mk) - -############################################################### -# mongoose lib -############################################################### -if(USE_MONGOOSE) -add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/include/mongoose/mongoose.h - COMMAND curl --retry 4 -O https://raw.githubusercontent.com/cesanta/mongoose/6.16/mongoose.h - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/include/mongoose -) - -add_custom_command( - OUTPUT ${CMAKE_BINARY_DIR}/temp_src/mongoose/mongoose.c - COMMAND curl --retry 4 -O https://raw.githubusercontent.com/cesanta/mongoose/6.16/mongoose.c - WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/temp_src/mongoose - DEPENDS ${CMAKE_BINARY_DIR}/include/mongoose/mongoose.h -) - -add_library(mongoose STATIC ${CMAKE_BINARY_DIR}/temp_src/mongoose/mongoose.c) -target_include_directories( mongoose PUBLIC ${CMAKE_BINARY_DIR}/include/mongoose ) endif() -############################################################### -# io_src files -############################################################### - -set( IO_SRC - ${CMAKE_BINARY_DIR}/temp_src/io_src/class_map.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ABM_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_AttributesMap.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_BC635Clock.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_CheckPointAgent.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_CheckPointRestart.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Clock.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_CommandLineArguments.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_DRAscii.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_DRBinary.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_DRHDF5.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_DataRecordDispatcher.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_DataRecordGroup.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_DebugPause.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_EchoJobs.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_EnumAttributesMap.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Environment.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Euler_Cromer_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Euler_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Event.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_EventInstrument.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_EventManager.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_EventProcessor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Executive.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ExecutiveException.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ExternalApplication.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Flag.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_FrameDataRecordGroup.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_FrameLog.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_GetTimeOfDayClock.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_IPPython.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_IPPythonEvent.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ITimer.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_InputProcessor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_InstrumentBase.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_IntegLoopManager.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_IntegLoopScheduler.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_IntegLoopSimObject.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JITEvent.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JITInputFile.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JSONVariableServer.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JSONVariableServerThread.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_JobData.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MM4_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MSConnect.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MSSharedMem.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MSSocket.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MTV.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MalfunctionsTrickView.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Master.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MemoryManager.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessageCout.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessageFile.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessageLCout.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessagePublisher.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessageSubscriber.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessageTCDevice.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MessageThreadedCout.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MonteCarlo.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MonteMonitor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MonteVar.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MultiDtIntegLoopScheduler.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_MultiDtIntegLoopSimObject.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_NL2_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_PlaybackFile.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RK2_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RK4_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RKF45_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RKF78_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RKG4_Integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RealtimeSync.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RemoteShell.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RtiEvent.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RtiExec.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RtiList.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_RtiStager.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_STLInterface.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ScheduledJobQueue.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Scheduler.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Sie.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_SimControlPanel.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_SimObject.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_SimTime.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Slave.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_StripChart.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_TPROCTEClock.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ThreadBase.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ThreadTrigger.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Threads.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Timer.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_TrickView.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_UCFn.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_UdUnits.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Unit.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_UnitTest.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_UnitsMap.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServer.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerListenThread.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerReference.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_VariableServerThread.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_Zeroconf.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_attributes.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_dllist.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_io_alloc.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_lqueue.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_lstack.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_message_type.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_mm_error.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ms_sim_mode.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_n_choose_m.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_parameter_types.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rand_generator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_reference.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_reference_frame.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_regula_falsi.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_sim_mode.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_tc.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_time_offset.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_trick_error_hndlr.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_tsm.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_units_conv.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_value.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_var.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_variable_server_sync_types.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_wave_form.cpp -) - -set( ER7_UTILS_IO_SRC - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_abm4_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_abm4_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_abm4_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_base_integration_group.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_beeman_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_beeman_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_bogus_integration_controls.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_deletable.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_euler_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_euler_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_euler_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_generalized_position_derivative.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrable_object.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integration_controls.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integration_messages.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integration_technique.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrator_constructor_factory.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrator_interface.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrator_result.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrator_result_merger.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_integrator_result_merger_container.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_left_quaternion_functions.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_mm4_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_mm4_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_nl2_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_nl2_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_position_verlet_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_position_verlet_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_priming_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_priming_integration_controls.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_priming_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_priming_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_ratio128.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk2_heun_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk2_heun_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk2_heun_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk2_midpoint_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk2_midpoint_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk2_midpoint_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk4_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk4_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk4_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rk4_second_order_ode_integrator_base.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkf45_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkf45_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkf45_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkf78_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkf78_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkf78_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkg4_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkg4_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_rkg4_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_single_cycle_integration_controls.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_standard_integration_controls.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_state_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_symplectic_euler_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_symplectic_euler_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_time_interface.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_trick_first_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_trick_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_trick_second_order_ode_integrator.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_uint128.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_velocity_verlet_integrator_constructor.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_velocity_verlet_second_order_ode_integrator.cpp -) - -set(ENV{TRICK_ICG_EXCLUDE} ${CMAKE_BINARY_DIR}/include/mongoose) -set(ICG_FLAGS -I${CMAKE_BINARY_DIR}/include -I${CMAKE_BINARY_DIR}/include/trick/compat -I${UDUNITS2_INCLUDES} -DTRICK_VER=${TRICK_MAJOR} -DUSE_ER7_UTILS_INTEGRATORS) -add_custom_command(OUTPUT ${IO_SRC} ${ER7_UTILS_IO_SRC} - COMMAND TRICK_HOME=${CMAKE_BINARY_DIR} TRICK_ICG_EXCLUDE=${CMAKE_BINARY_DIR}/include/mongoose ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/trick-ICG -force -sim_services -m -n -o ${CMAKE_BINARY_DIR}/temp_src/io_src ${ICG_FLAGS} ${CMAKE_BINARY_DIR}/include/trick/files_to_ICG.hh - DEPENDS trick-ICG -) - -add_library( trick STATIC $ $ ${IO_SRC}) -target_include_directories( trick PUBLIC ${UDUNITS2_INCLUDES} ) - -add_library( er7_utils STATIC $ ${ER7_UTILS_IO_SRC}) - -# fake dependency to avoid double ICG -add_dependencies(er7_utils trick) - -############################################################### -# libtrick_pyip.a -############################################################### - -set( TRICK_SWIG_SRC - trick_source/trick_swig/PrimitiveAttributesMap - trick_source/trick_swig/swig_convert_units - trick_source/trick_swig/swig_global_vars -) - -# Generated SWIG files -set( SWIG_SRC - ${CMAKE_BINARY_DIR}/temp_src/swig/sim_services_wrap - ${CMAKE_BINARY_DIR}/temp_src/swig/swig_double_wrap - ${CMAKE_BINARY_DIR}/temp_src/swig/swig_int_wrap - ${CMAKE_BINARY_DIR}/temp_src/swig//swig_ref_wrap -) - -set( SWIG_SRC_BASENAME - sim_services - swig_double - swig_int - swig_ref -) - -set(SWIG_FLAGS -DUSE_ER7_UTILS_INTEGRATORS) -if(GSL_FOUND) - list( APPEND SWIG_FLAGS -D_HAVE_GSL ) -endif() - -if(USE_MONGOOSE) - list( APPEND SWIG_FLAGS -DUSE_MONGOOSE) - list( APPEND IO_SRC - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_WebServer.cpp - ${CMAKE_BINARY_DIR}/temp_src/io_src/io_WebSocketSession.cpp - ) +project(trick VERSION ${TRICK_MAJOR}.${TRICK_MINOR}.${TRICK_TINY} LANGUAGES C CXX) + +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) + +# ────────────────────────────────────────────────────────────────────────── +# Options (Part C, Phase 1, step 1 of CMAKE_MIGRATION_PLAN.md) +# ────────────────────────────────────────────────────────────────────────── +option(TRICK_FORCE_32BIT "Force Trick and sims to compile 32bit" OFF) +option(TRICK_OFFLINE "Compile Trick in offline mode (requires trick-offline/ in the source tree)" OFF) +option(TRICK_USE_JAVA "Build the Trick Java GUIs" ON) +option(TRICK_BUILD_DP "Build the X11/Motif data_products tools" ON) +set(TRICK_CIVETWEB_HOME "" CACHE PATH "CivetWeb root directory (empty = auto-detect/disabled)") +set(HDF5_HOME "" CACHE PATH "HDF5 root directory (empty = auto-detect)") +set(GSL_HOME "" CACHE PATH "GSL root directory (empty = auto-detect)") +set(GTEST_HOME "" CACHE PATH "GoogleTest root directory (empty = auto-detect)") +set(LLVM_HOME "" CACHE PATH "LLVM root directory (empty = auto-detect)") +set(UDUNITS_HOME "" CACHE PATH "UDUnits2 root directory (empty = auto-detect)") +set(PYTHON_VERSION "" CACHE STRING "Suffix for python search, e.g. 3 looks for python3") + +if(TRICK_FORCE_32BIT) + set(TRICK_FORCE_32BIT_MK 1) + add_compile_options(-m32) + add_link_options(-m32) +else() + set(TRICK_FORCE_32BIT_MK 0) endif() -foreach ( infile ${SWIG_SRC_BASENAME} ) - add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/temp_src/swig/${infile}_wrap.cpp - COMMAND ${SWIG_EXECUTABLE} ${SWIG_FLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/trick_source -I${CMAKE_CURRENT_SOURCE_DIR}/include -I${CMAKE_CURRENT_SOURCE_DIR} -c++ -python -includeall -ignoremissing -w201,362,389,451 -o $@ -outdir ${CMAKE_BINARY_DIR}/share/trick/swig ${CMAKE_CURRENT_SOURCE_DIR}/trick_source/trick_swig/${infile}.i - ) -endforeach(infile) - -add_library( trick_pyip STATIC $ ${TRICK_SWIG_SRC} ${SWIG_SRC}) -target_include_directories( trick_pyip PUBLIC ${PYTHON_INCLUDE_DIRS} ) -target_include_directories( trick_pyip PUBLIC ${UDUNITS2_INCLUDES} ) -if(GSL_FOUND) - target_include_directories( trick_pyip PUBLIC ${GSL_INCLUDE_DIRS} ) +if(TRICK_OFFLINE) + if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/trick-offline") + message(FATAL_ERROR "Offline mode requires an uncompressed directory named \"trick-offline\" in TRICK_HOME") + endif() + set(TRICK_OFFLINE_MK 1) +else() + set(TRICK_OFFLINE_MK 0) endif() -target_include_directories( trick_pyip PUBLIC trick_source ) -############################################################### -# libtrickHTTP.a -############################################################### -if(USE_MONGOOSE) -set( TRICKHTTP_SRC - trick_source/web/HttpServer/src/VariableServerSession - trick_source/web/HttpServer/src/VariableServerVariable - trick_source/web/HttpServer/src/WebServer - trick_source/web/HttpServer/src/http_GET_handlers - trick_source/web/HttpServer/src/simpleJSON +# ────────────────────────────────────────────────────────────────────────── +# Detection (no compilation happens in Phase 1 — see A2/D3 in the plan: sims +# consume config_user.mk via the make flow, which is the real product here) +# ────────────────────────────────────────────────────────────────────────── +include(TrickPrograms) +include(FindLLVMClang) +include(TrickClangLibs) +include(TrickPython) +include(FindUDUNITS2) +include(TrickGTest) + +# ────────────────────────────────────────────────────────────────────────── +# config_user.mk (D4: golden-file-tested parity with autoconf's output) +# ────────────────────────────────────────────────────────────────────────── +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/share/trick/makefiles/config_user_cmake.mk.in + ${CMAKE_BINARY_DIR}/share/trick/makefiles/config_user.mk + @ONLY ) -add_library( trickHTTP STATIC ${TRICKHTTP_SRC}) -add_dependencies( trickHTTP mongoose) -endif() -############################################################### -# Other Trick libraries -############################################################### - -add_subdirectory(trick_source/codegen/Interface_Code_Gen) -add_subdirectory(trick_source/er7_utils) -add_subdirectory(trick_source/sim_services) -add_subdirectory(trick_source/trick_utils) -add_subdirectory(trick_source/java) -add_subdirectory(trick_source/data_products) +# Phase 2 adds: add_subdirectory(trick_source/codegen/Interface_Code_Gen) and +# the rest of the native build (ICG, archives, SWIG). Phase 1 stops here. diff --git a/CMakeModules/FindLLVM.cmake b/CMakeModules/FindLLVM.cmake deleted file mode 100644 index 2424012fe..000000000 --- a/CMakeModules/FindLLVM.cmake +++ /dev/null @@ -1,201 +0,0 @@ -# - Find LLVM headers and libraries. -# This module locates LLVM and adapts the llvm-config output for use with -# CMake. -# -# A given list of COMPONENTS is passed to llvm-config. -# -# The following variables are defined: -# LLVM_FOUND - true if LLVM was found -# LLVM_CXXFLAGS - C++ compiler flags for files that include LLVM headers. -# LLVM_HOST_TARGET - Target triple used to configure LLVM. -# LLVM_INCLUDE_DIRS - Directory containing LLVM include files. -# LLVM_LDFLAGS - Linker flags to add when linking against LLVM -# (includes -LLLVM_LIBRARY_DIRS). -# LLVM_LIBRARIES - Full paths to the library files to link against. -# LLVM_LIBRARY_DIRS - Directory containing LLVM libraries. -# LLVM_NATIVE_ARCH - Backend corresponding to LLVM_HOST_TARGET, e.g., -# X86 for x86_64 and i686 hosts. -# LLVM_ROOT_DIR - The root directory of the LLVM installation. -# llvm-config is searched for in ${LLVM_ROOT_DIR}/bin. -# LLVM_VERSION_MAJOR - Major version of LLVM. -# LLVM_VERSION_MINOR - Minor version of LLVM. -# LLVM_VERSION_STRING - Full LLVM version string (e.g. 6.0.0svn). -# LLVM_VERSION_BASE_STRING - Base LLVM version string without git/svn suffix (e.g. 6.0.0). -# -# Note: The variable names were chosen in conformance with the offical CMake -# guidelines, see ${CMAKE_ROOT}/Modules/readme.txt. - -# Try suffixed versions to pick up the newest LLVM install available on Debian -# derivatives. -# We also want an user-specified LLVM_ROOT_DIR to take precedence over the -# system default locations such as /usr/local/bin. Executing find_program() -# multiples times is the approach recommended in the docs. -set(llvm_config_names llvm-config-9.0 llvm-config90 - llvm-config-8.0 llvm-config80 - llvm-config-7.0 llvm-config70 - llvm-config-6.0 llvm-config60 - llvm-config-5.0 llvm-config50 - llvm-config-4.0 llvm-config40 - llvm-config-3.9 llvm-config39 - llvm-config) -find_program(LLVM_CONFIG - NAMES ${llvm_config_names} - PATHS ${LLVM_ROOT_DIR}/bin /usr/local/opt/llvm/bin NO_DEFAULT_PATH - DOC "Path to llvm-config tool.") -find_program(LLVM_CONFIG NAMES ${llvm_config_names}) - -# Prints a warning/failure message depending on the required/quiet flags. Copied -# from FindPackageHandleStandardArgs.cmake because it doesn't seem to be exposed. -macro(_LLVM_FAIL _msg) - if(LLVM_FIND_REQUIRED) - message(FATAL_ERROR "${_msg}") - else() - if(NOT LLVM_FIND_QUIETLY) - message(STATUS "${_msg}") - endif() - endif() -endmacro() - - -if(NOT LLVM_CONFIG) - if(NOT LLVM_FIND_QUIETLY) - message(WARNING "Could not find llvm-config (LLVM >= ${LLVM_FIND_VERSION}). Try manually setting LLVM_CONFIG to the llvm-config executable of the installation to use.") - endif() -else() - macro(llvm_set var flag) - if(LLVM_FIND_QUIETLY) - set(_quiet_arg ERROR_QUIET) - endif() - set(result_code) - execute_process( - COMMAND ${LLVM_CONFIG} --${flag} - RESULT_VARIABLE result_code - OUTPUT_VARIABLE LLVM_${var} - OUTPUT_STRIP_TRAILING_WHITESPACE - ${_quiet_arg} - ) - if(result_code) - _LLVM_FAIL("Failed to execute llvm-config ('${LLVM_CONFIG}', result code: '${result_code})'") - else() - if(${ARGV2}) - file(TO_CMAKE_PATH "${LLVM_${var}}" LLVM_${var}) - endif() - endif() - endmacro() - macro(llvm_set_libs var flag components) - if(LLVM_FIND_QUIETLY) - set(_quiet_arg ERROR_QUIET) - endif() - set(result_code) - execute_process( - COMMAND ${LLVM_CONFIG} --${flag} ${components} - RESULT_VARIABLE result_code - OUTPUT_VARIABLE tmplibs - OUTPUT_STRIP_TRAILING_WHITESPACE - ${_quiet_arg} - ) - if(result_code) - _LLVM_FAIL("Failed to execute llvm-config ('${LLVM_CONFIG}', result code: '${result_code})'") - else() - file(TO_CMAKE_PATH "${tmplibs}" tmplibs) - string(REGEX MATCHALL "${pattern}[^ ]+" LLVM_${var} ${tmplibs}) - endif() - endmacro() - - llvm_set(VERSION_STRING version) - llvm_set(CXXFLAGS cxxflags) - llvm_set(HOST_TARGET host-target) - llvm_set(INCLUDE_DIRS includedir true) - llvm_set(ROOT_DIR prefix true) - - # The LLVM version string _may_ contain a git/svn suffix, so match only the x.y.z part - string(REGEX MATCH "^[0-9]+[.][0-9]+[.][0-9]+" LLVM_VERSION_BASE_STRING "${LLVM_VERSION_STRING}") - - if(NOT ${LLVM_VERSION_STRING} MATCHES "^3\\.4\\..*") - llvm_set(ENABLE_ASSERTIONS assertion-mode) - endif() - - # Versions below 4.0 do not support components debuginfomsf and demangle - if(${LLVM_VERSION_STRING} MATCHES "^3\\..*") - list(REMOVE_ITEM LLVM_FIND_COMPONENTS "debuginfomsf" index) - list(REMOVE_ITEM LLVM_FIND_COMPONENTS "demangle" index) - endif() - # Versions below 6.0 do not support component windowsmanifest - if(${LLVM_VERSION_STRING} MATCHES "^[3-5]\\..*") - list(REMOVE_ITEM LLVM_FIND_COMPONENTS "windowsmanifest" index) - endif() - - llvm_set(LDFLAGS ldflags) - # In LLVM 3.5+, the system library dependencies (e.g. "-lz") are accessed - # using the separate "--system-libs" flag. - if(NOT ${LLVM_VERSION_STRING} MATCHES "^3\\.4\\..*") - llvm_set(SYSTEM_LIBS system-libs) - endif() - string(REPLACE "\n" " " LLVM_LDFLAGS "${LLVM_LDFLAGS} ${LLVM_SYSTEM_LIBS}") - string(STRIP ${LLVM_LDFLAGS} LLVM_LDFLAGS) - llvm_set(LIBRARY_DIRS libdir true) - llvm_set_libs(LIBRARIES libs "${LLVM_FIND_COMPONENTS}") - # LLVM bug: llvm-config --libs tablegen returns -lLLVM-3.8.0 - # but code for it is not in shared library - if("${LLVM_FIND_COMPONENTS}" MATCHES "tablegen") - if (NOT "${LLVM_LIBRARIES}" MATCHES "LLVMTableGen") - set(LLVM_LIBRARIES "${LLVM_LIBRARIES};-lLLVMTableGen") - endif() - endif() - - # Versions below 4.0 do not support llvm-config --cmakedir - if(${LLVM_VERSION_STRING} MATCHES "^3\\..*") - set(LLVM_CMAKEDIR ${LLVM_LIBRARY_DIRS}/cmake/llvm) - else() - llvm_set(CMAKEDIR cmakedir) - endif() - - llvm_set(TARGETS_TO_BUILD targets-built) - string(REGEX MATCHALL "${pattern}[^ ]+" LLVM_TARGETS_TO_BUILD ${LLVM_TARGETS_TO_BUILD}) - - # Parse LLVM_NATIVE_ARCH manually from LLVMConfig.cmake; including it leads to issues like - # https://github.com/ldc-developers/ldc/issues/3079. - if(EXISTS "${LLVM_CMAKEDIR}/LLVMConfig.cmake") - file(STRINGS "${LLVM_CMAKEDIR}/LLVMConfig.cmake" LLVM_NATIVE_ARCH LIMIT_COUNT 1 REGEX "^set\\(LLVM_NATIVE_ARCH (.+)\\)$") - string(REGEX MATCH "set\\(LLVM_NATIVE_ARCH (.+)\\)" LLVM_NATIVE_ARCH "${LLVM_NATIVE_ARCH}") - set(LLVM_NATIVE_ARCH ${CMAKE_MATCH_1}) - message(STATUS "LLVM_NATIVE_ARCH: ${LLVM_NATIVE_ARCH}") - endif() -endif() - -# On CMake builds of LLVM, the output of llvm-config --cxxflags does not -# include -fno-rtti, leading to linker errors. Be sure to add it. -if(NOT MSVC AND (CMAKE_COMPILER_IS_GNUCXX OR (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang"))) - if(NOT ${LLVM_CXXFLAGS} MATCHES "-fno-rtti") - set(LLVM_CXXFLAGS "${LLVM_CXXFLAGS} -fno-rtti") - endif() -endif() - -# Remove some clang-specific flags for gcc. -if(CMAKE_COMPILER_IS_GNUCXX) - string(REPLACE "-Wcovered-switch-default " "" LLVM_CXXFLAGS ${LLVM_CXXFLAGS}) - string(REPLACE "-Wstring-conversion " "" LLVM_CXXFLAGS ${LLVM_CXXFLAGS}) - string(REPLACE "-fcolor-diagnostics " "" LLVM_CXXFLAGS ${LLVM_CXXFLAGS}) - # this requires more recent gcc versions (not supported by 4.9) - string(REPLACE "-Werror=unguarded-availability-new " "" LLVM_CXXFLAGS ${LLVM_CXXFLAGS}) -endif() - -# Remove gcc-specific flags for clang. -if(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang") - string(REPLACE "-Wno-maybe-uninitialized " "" LLVM_CXXFLAGS ${LLVM_CXXFLAGS}) -endif() - -string(REGEX REPLACE "([0-9]+).*" "\\1" LLVM_VERSION_MAJOR "${LLVM_VERSION_STRING}" ) -string(REGEX REPLACE "[0-9]+\\.([0-9]+).*[A-Za-z]*" "\\1" LLVM_VERSION_MINOR "${LLVM_VERSION_STRING}" ) -string(REGEX REPLACE "[0-9]+\\.[0-9]+\\.([0-9]+).*[A-Za-z]*" "\\1" LLVM_VERSION_PATCH "${LLVM_VERSION_STRING}" ) - -if (${LLVM_VERSION_STRING} VERSION_LESS ${LLVM_FIND_VERSION}) - message(FATAL_ERROR "Unsupported LLVM version found ${LLVM_VERSION_STRING}. At least version ${LLVM_FIND_VERSION} is required.") -endif() - -# Use the default CMake facilities for handling QUIET/REQUIRED. -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args(LLVM - REQUIRED_VARS LLVM_ROOT_DIR LLVM_HOST_TARGET - VERSION_VAR LLVM_VERSION_STRING) diff --git a/CMakeModules/FindTee.cmake b/CMakeModules/FindTee.cmake deleted file mode 100644 index 59dddb830..000000000 --- a/CMakeModules/FindTee.cmake +++ /dev/null @@ -1,21 +0,0 @@ - -# FindTee -# -------- -# -# Find tee -# -# This module looks for tee. This module defines the following values: -# -# :: -# -# TEE_EXECUTABLE: the full path to the tee tool. -# TEE_FOUND: True if tee has been found. - -find_program(TEE_EXECUTABLE tee) -mark_as_advanced( TEE_EXECUTABLE ) - -include (FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Tee FOUND_VAR REQUIRED_VARS TEE_EXECUTABLE) -if(Tee_FIND_REQUIRED AND NOT TEE_FOUND) - message(FATAL_ERROR "Could not find tee") -endif() diff --git a/CMakeModules/FindUDUNITS2.cmake b/CMakeModules/FindUDUNITS2.cmake deleted file mode 100644 index d1445a0e1..000000000 --- a/CMakeModules/FindUDUNITS2.cmake +++ /dev/null @@ -1,92 +0,0 @@ -# - Find UDUNITS2 -# Find the native UDUNITS2 includes and library -# -# UDUNITS2_INCLUDES - where to find udunits2.h -# UDUNITS2_LIBRARIES - libraries to link with -# UDUNITS2_FOUND - True if UDUNITS2 was found. - -message(STATUS "UDUNITS2_ROOT = ${UDUNITS2_ROOT}") -if (UDUNITS2_INCLUDES) - # Already in cache, be silent - set (UDUNITS2_FIND_QUIETLY TRUE) -endif (UDUNITS2_INCLUDES) - -find_path (UDUNITS2_INCLUDES udunits2.h - HINTS "${UDUNITS2_ROOT}/include" "$ENV{UDUNITS2_ROOT}/include" - PATH_SUFFIXES "udunits2" - DOC "Path to udunits2.h") - -# UDUNITS2 headers might be in .../include or .../include/udunits2. -# We try both. -if (${UDUNITS2_INCLUDES} MATCHES "udunits2/?$") - string(REGEX REPLACE "/include/udunits2/?$" "/lib" - UDUNITS2_LIB_HINT ${UDUNITS2_INCLUDES}) -else() - string(REGEX REPLACE "/include/?$" "/lib" - UDUNITS2_LIB_HINT ${UDUNITS2_INCLUDES}) -endif() - -find_library (UDUNITS2_LIBRARIES - NAMES udunits2 - HINTS ${UDUNITS2_LIB_HINT}) - -set(UDUNITS2_TEST_SRC " -#include - -int main(int argc, char **argv) { - ut_system *s = ut_read_xml(NULL); - ut_free_system(s); - return 0; -} -") - -if ((NOT UDUNITS2_LIBRARIES) OR (NOT UDUNITS2_INCLUDES)) - message(STATUS "Trying to find UDUNITS-2 using LD_LIBRARY_PATH (we're desperate)...") - - file(TO_CMAKE_PATH "$ENV{LD_LIBRARY_PATH}" LD_LIBRARY_PATH) - - find_library(UDUNITS2_LIBRARIES - NAMES udunits2 - HINTS ${LD_LIBRARY_PATH}) - - if (UDUNITS2_LIBRARIES) - get_filename_component(UDUNITS2_LIB_DIR ${UDUNITS2_LIBRARIES} PATH) - string(REGEX REPLACE "/lib/?$" "/include" - UDUNITS2_H_HINT ${UDUNITS2_LIB_DIR}) - - find_path (UDUNITS2_INCLUDES udunits2.h - HINTS ${UDUNITS2_H_HINT} - PATH_SUFFIXES "udunits2" - DOC "Path to udunits2.h") - endif() -endif() - -include (CheckCSourceRuns) - -set(CMAKE_REQUIRED_INCLUDES ${UDUNITS2_INCLUDES}) -set(CMAKE_REQUIRED_LIBRARIES ${UDUNITS2_LIBRARIES}) -check_c_source_runs("${UDUNITS2_TEST_SRC}" UDUNITS2_WORKS_WITHOUT_EXPAT) - -if(${UDUNITS2_WORKS_WITHOUT_EXPAT}) - #message(STATUS "UDUNITS-2 does not require expat") -else() - find_package(EXPAT REQUIRED) - - set(CMAKE_REQUIRED_INCLUDES ${UDUNITS2_INCLUDES} ${EXPAT_INCLUDE_DIRS}) - set(CMAKE_REQUIRED_LIBRARIES ${UDUNITS2_LIBRARIES} ${EXPAT_LIBRARIES}) - check_c_source_runs("${UDUNITS2_TEST_SRC}" UDUNITS2_WORKS_WITH_EXPAT) - - if(NOT ${UDUNITS2_WORKS_WITH_EXPAT}) - message(FATAL_ERROR "UDUNITS-2 does not seem to work with or without expat") - endif() - - #message(STATUS "UDUNITS-2 requires EXPAT") - set (UDUNITS2_LIBRARIES "${UDUNITS2_LIBRARIES};${EXPAT_LIBRARIES}" CACHE STRING "" FORCE) -endif() - -# handle the QUIETLY and REQUIRED arguments and set UDUNITS2_FOUND to TRUE if -# all listed variables are TRUE -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (UDUNITS2 DEFAULT_MSG UDUNITS2_LIBRARIES UDUNITS2_INCLUDES) - -mark_as_advanced (UDUNITS2_LIBRARIES UDUNITS2_INCLUDES) diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..e9d293680 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,30 @@ +{ + "version": 6, + "cmakeMinimumRequired": { "major": 3, "minor": 20, "patch": 0 }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default", + "description": "Auto-detect all dependencies from the environment", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "macos-homebrew", + "displayName": "macOS (Homebrew)", + "description": "Point detection at Homebrew's LLVM, since Apple's system clang lacks the libraries ICG needs", + "inherits": "default", + "cacheVariables": { + "LLVM_HOME": "/opt/homebrew/opt/llvm" + } + }, + { + "name": "rhel", + "displayName": "RHEL-family (RHEL/Rocky/Oracle Linux)", + "description": "Same detection as default; installs to lib64 automatically per config_Linux.mk's /etc/redhat-release check", + "inherits": "default" + } + ] +} diff --git a/cmake/FindLLVMClang.cmake b/cmake/FindLLVMClang.cmake new file mode 100644 index 000000000..77af818ac --- /dev/null +++ b/cmake/FindLLVMClang.cmake @@ -0,0 +1,101 @@ +# Locate llvm-config and derive the LLVM/clang paths Trick's ICG needs. +# Mirrors autoconf/m4/tr_llvm_home.m4 and configure.ac:198-220. +# +# Honors the LLVM_HOME cache variable (equivalent to --with-llvm=DIR); when +# unset, searches the same fallback directories tr_llvm_home.m4 does, plus +# the Intel-mac homebrew prefix for parity with the old FindLLVM.cmake. +# +# Exports: +# LLVM_CONFIG_EXECUTABLE, LLVM_HOME (prefix), LLVM_LIB_DIR, LLVM_BIN_DIR, +# LLVM_INCLUDE_DIR, LLVM_VERSION_STRING, CLANG_EXECUTABLE, CLANG_VERSION + +if(LLVM_HOME) + find_program(LLVM_CONFIG_EXECUTABLE + NAMES llvm-config + PATHS "${LLVM_HOME}/bin" + NO_DEFAULT_PATH + ) + if(NOT LLVM_CONFIG_EXECUTABLE) + message(FATAL_ERROR "could not find llvm-config") + endif() +else() + find_program(LLVM_CONFIG_EXECUTABLE + NAMES llvm-config + PATHS + /usr/lib64/llvm20/bin + /usr/lib64/llvm/bin + /bin + /usr/bin + /usr/local/bin + /sw/bin + /opt/homebrew/opt/llvm/bin + /usr/local/opt/llvm/bin + NO_DEFAULT_PATH + ) + if(NOT LLVM_CONFIG_EXECUTABLE) + find_program(LLVM_CONFIG_EXECUTABLE NAMES llvm-config) + endif() + if(NOT LLVM_CONFIG_EXECUTABLE) + message(FATAL_ERROR "could not find llvm-config") + endif() + execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --prefix + OUTPUT_VARIABLE LLVM_HOME + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +endif() + +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --libdir + OUTPUT_VARIABLE LLVM_LIB_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --bindir + OUTPUT_VARIABLE LLVM_BIN_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --includedir + OUTPUT_VARIABLE LLVM_INCLUDE_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${LLVM_CONFIG_EXECUTABLE} --version + OUTPUT_VARIABLE LLVM_VERSION_STRING + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +if(NOT EXISTS "${LLVM_INCLUDE_DIR}/clang/Basic/LLVM.h") + message(FATAL_ERROR "could not find clang development headers") +endif() +if(NOT EXISTS "${LLVM_LIB_DIR}/libLLVMSupport.a") + message(FATAL_ERROR "could not find clang library files in ${LLVM_LIB_DIR}") +endif() + +find_program(CLANG_EXECUTABLE + NAMES clang + PATHS "${LLVM_BIN_DIR}" /bin /usr/bin /usr/local/bin /sw/bin + NO_DEFAULT_PATH +) +if(NOT CLANG_EXECUTABLE) + message(FATAL_ERROR "could not find clang") +endif() + +if(LLVM_VERSION_STRING VERSION_LESS "3.4.2") + message(FATAL_ERROR "Trick requires llvm/clang version >= 3.4.2, found ${LLVM_VERSION_STRING}") +endif() + +# autoconf/m4/tr_clang_version.m4 derives CLANG_VERSION from `clang --version` +# rather than trusting llvm-config, since some distros patch clang's reported +# version away from LLVM's. Replicate that here for parity. +execute_process( + COMMAND ${CLANG_EXECUTABLE} --version + OUTPUT_VARIABLE _tr_clang_version_output + OUTPUT_STRIP_TRAILING_WHITESPACE +) +string(REGEX MATCH "version ([0-9]+\\.[0-9]+\\.[0-9]+)" _ "${_tr_clang_version_output}") +set(CLANG_VERSION "${CMAKE_MATCH_1}") +if(CLANG_VERSION AND CLANG_VERSION VERSION_LESS "3.4.2") + message(FATAL_ERROR "Trick requires llvm/clang version >= 3.4.2, found ${CLANG_VERSION}") +endif() diff --git a/CMakeModules/FindMaven.cmake b/cmake/FindMaven.cmake similarity index 100% rename from CMakeModules/FindMaven.cmake rename to cmake/FindMaven.cmake diff --git a/cmake/FindUDUNITS2.cmake b/cmake/FindUDUNITS2.cmake new file mode 100644 index 000000000..b313ec8b1 --- /dev/null +++ b/cmake/FindUDUNITS2.cmake @@ -0,0 +1,63 @@ +# UDUNITS2 detection, mirroring autoconf/m4/tr_udunits_home.m4 and +# configure.ac:260-302 exactly, including the exact rendered +# UDUNITS_INCLUDES / UDUNITS_LDFLAGS / UDUNITS_EXCLUDE strings +# config_user.mk consumes (TRICK_EXCLUDE += :@UDUNITS_EXCLUDE@ requires the +# variable to render empty rather than unset). +# +# Honors the UDUNITS_HOME cache variable (equivalent to --with-udunits=DIR). +# +# Exports: UDUNITS_INCLUDES, UDUNITS_LDFLAGS, UDUNITS_EXCLUDE +# Also defines an imported target Trick::udunits2 for later phases. + +set(UDUNITS_EXCLUDE "") + +if(NOT UDUNITS_HOME) + if(EXISTS "/usr/include/udunits2.h") + set(UDUNITS_INCLUDES "") + set(UDUNITS_LDFLAGS "-ludunits2") + set(_tr_udunits_header "/usr/include/udunits2.h") + elseif(EXISTS "/usr/include/udunits2/udunits2.h") + set(UDUNITS_INCLUDES "-I/usr/include/udunits2") + set(UDUNITS_LDFLAGS "-ludunits2") + set(_tr_udunits_header "/usr/include/udunits2/udunits2.h") + elseif(EXISTS "/opt/homebrew/include/udunits2.h") + set(UDUNITS_HOME "/opt/homebrew") + set(UDUNITS_INCLUDES "-I${UDUNITS_HOME}/include") + set(UDUNITS_LDFLAGS "-L${UDUNITS_HOME}/lib -ludunits2") + set(UDUNITS_EXCLUDE "${UDUNITS_HOME}") + set(_tr_udunits_header "${UDUNITS_HOME}/include/udunits2.h") + else() + message(FATAL_ERROR "could not find udunits2.h") + endif() +else() + set(UDUNITS_EXCLUDE "${UDUNITS_HOME}") + if(EXISTS "${UDUNITS_HOME}/include/udunits2.h") + set(UDUNITS_INCLUDES "-I${UDUNITS_HOME}/include") + set(UDUNITS_LDFLAGS "-Wl,-rpath,${UDUNITS_HOME}/lib -L${UDUNITS_HOME}/lib -ludunits2") + set(_tr_udunits_header "${UDUNITS_HOME}/include/udunits2.h") + elseif(EXISTS "${UDUNITS_HOME}/lib/udunits2.h") + set(UDUNITS_INCLUDES "-I${UDUNITS_HOME}/lib") + set(UDUNITS_LDFLAGS "-Wl,-rpath,${UDUNITS_HOME}/lib -L${UDUNITS_HOME}/lib -ludunits2") + set(_tr_udunits_header "${UDUNITS_HOME}/lib/udunits2.h") + else() + message(FATAL_ERROR "could not find udunits2") + endif() +endif() + +# AC_CHECK_LIB(udunits2, main, ...) — verify the library actually links. +find_library(UDUNITS2_LIBRARY + NAMES udunits2 + HINTS "${UDUNITS_HOME}/lib" +) +if(NOT UDUNITS2_LIBRARY) + message(FATAL_ERROR "could not find libudunits") +endif() + +if(NOT TARGET Trick::udunits2) + add_library(Trick::udunits2 UNKNOWN IMPORTED) + get_filename_component(_tr_udunits_incdir "${_tr_udunits_header}" DIRECTORY) + set_target_properties(Trick::udunits2 PROPERTIES + IMPORTED_LOCATION "${UDUNITS2_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${_tr_udunits_incdir}" + ) +endif() diff --git a/cmake/TrickClangLibs.cmake b/cmake/TrickClangLibs.cmake new file mode 100644 index 000000000..aff24c768 --- /dev/null +++ b/cmake/TrickClangLibs.cmake @@ -0,0 +1,95 @@ +# Implements the ICG_CLANGLIBS selection algorithm from +# autoconf/configure.ac:222-256 exactly, then sanity-checks the result with a +# try_compile against CMakeTestFiles/TestICGLinkedLibs.cpp. +# +# Requires LLVM_LIB_DIR and CLANG_VERSION (from FindLLVMClang.cmake). +# Exports: ICG_CLANGLIBS + +if(NOT DEFINED LLVM_LIB_DIR) + message(FATAL_ERROR "TrickClangLibs.cmake requires LLVM_LIB_DIR (include FindLLVMClang first)") +endif() + +if(CLANG_VERSION AND CLANG_VERSION VERSION_GREATER_EQUAL "18.0.0") + set(_tr_old_clang_libs + "-lclangFrontend -lclangDriver -lclangSerialization -lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST -lclangASTMatchers -lclangAPINotes -lclangLex -lclangBasic" + ) +else() + set(_tr_old_clang_libs + "-lclangFrontend -lclangDriver -lclangSerialization -lclangParse -lclangSema -lclangAnalysis -lclangEdit -lclangAST -lclangLex -lclangBasic" + ) +endif() +set(_tr_new_clang_libs "-lclang-cpp") + +if(EXISTS "${LLVM_LIB_DIR}/libclangFrontend.a" OR EXISTS "${LLVM_LIB_DIR}/libclangFrontend.so") + set(ICG_CLANGLIBS "${_tr_old_clang_libs}") +elseif(EXISTS "${LLVM_LIB_DIR}/libclang-cpp.a" OR EXISTS "${LLVM_LIB_DIR}/libclang-cpp.so") + set(ICG_CLANGLIBS "${_tr_new_clang_libs}") +else() + message(FATAL_ERROR "could not find clang libs in LLVM library: \"${LLVM_LIB_DIR}\"") +endif() + +if(EXISTS "${LLVM_LIB_DIR}/libclangSupport.a") + set(ICG_CLANGLIBS "${ICG_CLANGLIBS} -lclangSupport") +endif() +if(EXISTS "${LLVM_LIB_DIR}/libclangOptions.a") + set(ICG_CLANGLIBS "${ICG_CLANGLIBS} -lclangOptions") +endif() +if(EXISTS "${LLVM_LIB_DIR}/libclangAnalysisLifetimeSafety.a") + set(ICG_CLANGLIBS "${ICG_CLANGLIBS} -lclangAnalysisLifetimeSafety") +endif() + +# Sanity-*link* a real clang/LLVM TU against the derived ICG_CLANGLIBS. This +# is the whole point of TestICGLinkedLibs.cpp: catch a wrong library +# selection at configure time rather than at Phase 2's trick-ICG link step. +# The link recipe mirrors trick_source/codegen/Interface_Code_Gen/Makefile +# exactly (CLANGLIBS/LLVMLDFLAGS/CXXFLAGS construction). +string(REGEX MATCH "^[0-9]+" _tr_llvm_version_major "${LLVM_VERSION_STRING}") + +execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --libs + OUTPUT_VARIABLE _tr_llvm_libs OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --system-libs + OUTPUT_VARIABLE _tr_llvm_system_libs OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags + OUTPUT_VARIABLE _tr_llvm_ldflags OUTPUT_STRIP_TRAILING_WHITESPACE) + +set(_tr_icg_link_libs "${ICG_CLANGLIBS} ${_tr_llvm_libs}") +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(LLVM_VERSION_STRING VERSION_GREATER_EQUAL "3.5") + # Fedora adds -ledit as a system lib, but it isn't installed or required. + string(REPLACE "-ledit" "" _tr_llvm_system_libs_filtered "${_tr_llvm_system_libs}") + set(_tr_icg_link_libs "${_tr_icg_link_libs} ${_tr_llvm_system_libs_filtered}") + endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(_tr_icg_link_libs "${_tr_icg_link_libs} ${_tr_llvm_system_libs}") + if(_tr_llvm_version_major GREATER_EQUAL 16) + set(_tr_icg_link_libs "${_tr_icg_link_libs} -lc++abi -lclang-cpp") + else() + set(_tr_icg_link_libs "${_tr_icg_link_libs} -lc++abi") + endif() +endif() + +if(_tr_llvm_version_major GREATER_EQUAL 16) + set(_tr_icg_cxx_std 17) +elseif(_tr_llvm_version_major GREATER_EQUAL 10) + set(_tr_icg_cxx_std 14) +else() + set(_tr_icg_cxx_std 11) +endif() + +separate_arguments(_tr_icg_link_libs_list NATIVE_COMMAND "-L${LLVM_LIB_DIR} ${_tr_llvm_ldflags} ${_tr_icg_link_libs}") + +try_compile(TR_ICG_CLANG_HEADERS_COMPILE + ${CMAKE_BINARY_DIR}/CMakeFiles/TrickClangLibsCheck + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeTestFiles/TestICGLinkedLibs.cpp + CMAKE_FLAGS + "-DINCLUDE_DIRECTORIES=${LLVM_INCLUDE_DIR}" + COMPILE_DEFINITIONS + -DLIBCLANG_MAJOR=${_tr_llvm_version_major} + LINK_LIBRARIES + ${_tr_icg_link_libs_list} + CXX_STANDARD ${_tr_icg_cxx_std} + OUTPUT_VARIABLE TR_ICG_CLANG_HEADERS_COMPILE_OUTPUT +) +if(NOT TR_ICG_CLANG_HEADERS_COMPILE) + message(FATAL_ERROR "ICG_CLANGLIBS (\"${ICG_CLANGLIBS}\") failed a sanity link against ${LLVM_LIB_DIR}:\n${TR_ICG_CLANG_HEADERS_COMPILE_OUTPUT}") +endif() diff --git a/cmake/TrickGTest.cmake b/cmake/TrickGTest.cmake new file mode 100644 index 000000000..2354eae25 --- /dev/null +++ b/cmake/TrickGTest.cmake @@ -0,0 +1,38 @@ +# GTEST_HOME detection + GTEST_CXXSTD derivation, mirroring +# autoconf/m4/tr_gtest.m4. +# +# Honors the GTEST_HOME cache variable; when unset, auto-detects like +# configure's "--with-gtest not given" branch. +# +# Exports: GTEST_HOME, GTEST_CXXSTD + +if(GTEST_HOME) + if(NOT EXISTS "${GTEST_HOME}/include/gtest") + message(FATAL_ERROR "could not find ${GTEST_HOME}/include/gtest") + endif() +elseif(EXISTS "/usr/include/gtest/gtest.h") + set(GTEST_HOME "/usr") +elseif(EXISTS "/opt/homebrew/include/gtest/gtest.h") + set(GTEST_HOME "/opt/homebrew") +else() + set(GTEST_HOME "") +endif() + +set(GTEST_CXXSTD "") +if(GTEST_HOME) + find_package(PkgConfig QUIET) + if(PKG_CONFIG_FOUND) + pkg_check_modules(TR_GTEST_PC QUIET gtest) + if(TR_GTEST_PC_VERSION) + if(TR_GTEST_PC_VERSION VERSION_GREATER_EQUAL "1.17") + set(GTEST_CXXSTD "-std=c++17") + else() + set(GTEST_CXXSTD "-std=c++14") + endif() + else() + message(WARNING "Could not determine gtest version via pkg-config") + endif() + else() + message(WARNING "pkg-config gtest not found, version detection skipped") + endif() +endif() diff --git a/cmake/TrickPrograms.cmake b/cmake/TrickPrograms.cmake new file mode 100644 index 000000000..11c712c3f --- /dev/null +++ b/cmake/TrickPrograms.cmake @@ -0,0 +1,191 @@ +# Program/library detection mirroring autoconf/configure.ac's non-LLVM, +# non-Python checks (tee, ld, flex, bison, curl, zip, gnuplot, swig, perl + +# modules, zlib, libxml2, threads, X11/Motif, java/maven) plus the small +# "other optional command line arguments" cluster (HDF5, GSL, civetweb). +# See autoconf/configure.ac and autoconf/m4/tr_*.m4 for the macros this +# replicates. + +# ── Required simple tools (autoconf/configure.ac:111-124) ───────────────── +find_program(TEE_EXECUTABLE tee) +if(NOT TEE_EXECUTABLE) + message(FATAL_ERROR "could not find tee") +endif() + +find_program(LD_EXECUTABLE ld) +if(NOT LD_EXECUTABLE) + message(FATAL_ERROR "could not find ld") +endif() + +find_program(CURL_EXECUTABLE curl) +if(NOT CURL_EXECUTABLE) + message(FATAL_ERROR "could not find curl") +endif() + +find_program(ZIP_EXECUTABLE zip) +if(NOT ZIP_EXECUTABLE) + message(FATAL_ERROR "Trick now requires zip but it could not be found. Please install zip. We recommend you use your OS package manager") +endif() + +find_program(GNUPLOT_EXECUTABLE gnuplot) +if(NOT GNUPLOT_EXECUTABLE) + message(NOTICE "could not find gnuplot") +endif() + +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) +# AC_PROG_LEX([noyywrap]) (unlike AC_PATH_PROG(BISON,...)) stores the bare +# program name it matched, not a resolved path — match that exactly. +get_filename_component(FLEX_EXECUTABLE_NAME "${FLEX_EXECUTABLE}" NAME) + +# ── Perl >= 5.14 + required modules (autoconf/configure.ac:121-122,195-196) ─ +find_package(Perl 5.14 REQUIRED) +execute_process( + COMMAND ${PERL_EXECUTABLE} -MText::Balanced -e 1 + RESULT_VARIABLE _tr_perl_text_balanced + OUTPUT_QUIET ERROR_QUIET +) +if(NOT _tr_perl_text_balanced EQUAL 0) + message(FATAL_ERROR "could not find perl modules Text::Balanced") +endif() +execute_process( + COMMAND ${PERL_EXECUTABLE} -MDigest::MD5 -e 1 + RESULT_VARIABLE _tr_perl_digest_md5 + OUTPUT_QUIET ERROR_QUIET +) +if(NOT _tr_perl_digest_md5 EQUAL 0) + message(FATAL_ERROR "could not find perl module Digest::MD5") +endif() + +# ── SWIG >= 3.0 (autoconf/m4/tr_swig_bin.m4) ─────────────────────────────── +find_package(SWIG 3.0 REQUIRED) + +# ── zlib / libxml2 / threads ─────────────────────────────────────────────── +find_package(ZLIB REQUIRED) + +find_package(LibXml2 REQUIRED) +# AC_CHECK_LIB(xml2, main, ...) renders the literal flag, not a resolved path. +set(LIBXML "-lxml2") + +find_package(Threads REQUIRED) +set(PTHREAD_CFLAGS "-pthread") +# AX_PTHREAD renders "-lpthread" on every Unix Trick supports, including +# Darwin (where CMAKE_THREAD_LIBS_INIT is empty because pthread symbols live +# in libSystem) — match its literal output rather than CMake's. +set(PTHREAD_LIBS "-lpthread") + +# ── X11 / Motif (autoconf/m4/tr_xwindows.m4, tr_jsc_dirs.m4) ─────────────── +find_package(X11) +if(X11_FOUND) + set(USE_X_WINDOWS 1) + if(X11_LIBRARY_DIRS) + set(X_LIB_DIR "-L${X11_LIBRARY_DIRS}") + elseif(X11_X11_LIB) + get_filename_component(_tr_x11_libdir "${X11_X11_LIB}" DIRECTORY) + set(X_LIB_DIR "-L${_tr_x11_libdir}") + endif() + if(NOT X11_Xt_FOUND) + message(FATAL_ERROR "could not find libxt development headers") + endif() + + # Motif is only required when the fermi-ware data_products tree exists. + set(MOTIF_HOME "") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/trick_source/data_products/fermi-ware") + if(EXISTS "/usr/include/Xm/Xm.h") + set(MOTIF_HOME "/usr") + elseif(EXISTS "/usr/local/include/Xm/Xm.h") + set(MOTIF_HOME "/usr/local") + elseif(EXISTS "/opt/homebrew/include/Xm/Xm.h") + set(MOTIF_HOME "/opt/homebrew") + else() + message(FATAL_ERROR "could not find Xm/Xm.h") + endif() + endif() +else() + set(USE_X_WINDOWS 0) + set(X_LIB_DIR "") + set(MOTIF_HOME "") +endif() + +# ── Java / Maven (autoconf/m4/tr_java.m4), gated by TRICK_USE_JAVA ───────── +if(TRICK_USE_JAVA) + set(USE_JAVA 1) + find_package(Java 1.8 COMPONENTS Development REQUIRED) + # AX_PROG_JAVA_CC(javac) only verifies "javac" is on PATH and stores the + # literal candidate name, not a resolved path — match that exactly + # rather than substituting Java_JAVAC_EXECUTABLE's absolute path. + set(Java_JAVAC_EXECUTABLE "javac") + if(NOT TRICK_OFFLINE) + find_program(MVN_EXECUTABLE mvn) + if(NOT MVN_EXECUTABLE) + message(FATAL_ERROR "could not find maven") + endif() + else() + set(MVN_EXECUTABLE "") + endif() +else() + set(USE_JAVA 0) + set(Java_JAVAC_EXECUTABLE "") + set(MVN_EXECUTABLE "") +endif() + +# ── HDF5 (autoconf/m4/tr_hdf5_home.m4) ────────────────────────────────────── +if(HDF5_HOME) + if(NOT EXISTS "${HDF5_HOME}/include/hdf5.h") + message(FATAL_ERROR "could not find ${HDF5_HOME}/include/hdf5.h") + endif() +elseif(EXISTS "/usr/include/hdf5.h") + set(HDF5_HOME "/usr") +elseif(EXISTS "/opt/homebrew/include/hdf5.h") + set(HDF5_HOME "/opt/homebrew") +else() + set(HDF5_HOME "") +endif() + +# ── GSL (autoconf/m4/tr_gsl_home.m4) ──────────────────────────────────────── +if(GSL_HOME) + if(NOT EXISTS "${GSL_HOME}/include/gsl") + message(FATAL_ERROR "could not find ${GSL_HOME}/include/gsl") + endif() +elseif(EXISTS "/usr/include/gsl/gsl_rng.h") + set(GSL_HOME "/usr") +elseif(EXISTS "/opt/homebrew/include/gsl/gsl_rng.h") + set(GSL_HOME "/opt/homebrew") +else() + set(GSL_HOME "") +endif() + +# ── civetweb (autoconf/m4/tr_civetweb_home.m4), gated by TRICK_CIVETWEB_HOME ─ +if(TRICK_CIVETWEB_HOME) + if(NOT EXISTS "${TRICK_CIVETWEB_HOME}/include/civetweb.h" OR + NOT EXISTS "${TRICK_CIVETWEB_HOME}/lib/libcivetweb.a") + message(FATAL_ERROR "Could not find all of the civetweb files.") + endif() + set(CIVETWEB_HOME "${TRICK_CIVETWEB_HOME}") + set(USE_CIVETWEB 1) +elseif(EXISTS "/usr/include/civetweb.h") + set(CIVETWEB_HOME "/usr") + set(USE_CIVETWEB 1) +else() + set(CIVETWEB_HOME "") + set(USE_CIVETWEB 0) +endif() + +# ── er7_utils, default enabled (autoconf/m4/tr_er7_utils.m4) ─────────────── +option(TRICK_USE_ER7_UTILS "use er7_utils" ON) +if(TRICK_USE_ER7_UTILS) + set(USE_ER7_UTILS 1) +else() + set(USE_ER7_UTILS 0) +endif() + +# ── Compiler full paths (autoconf/configure.ac:108-109) ──────────────────── +# CMAKE_C/CXX_COMPILER are already resolved to absolute paths by CMake. +set(CC "${CMAKE_C_COMPILER}") +set(CXX "${CMAKE_CXX_COMPILER}") + +# ── GCC version (autoconf/m4/tr_gcc_version.m4), empty for clang/mac ─────── +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set(GCC_VERSION "${CMAKE_CXX_COMPILER_VERSION}") +else() + set(GCC_VERSION "") +endif() diff --git a/cmake/TrickPython.cmake b/cmake/TrickPython.cmake new file mode 100644 index 000000000..6e24621a1 --- /dev/null +++ b/cmake/TrickPython.cmake @@ -0,0 +1,59 @@ +# Python detection for config_user.mk (D5): capture literal python-config +# output strings rather than relying on find_package(Python3) values, since +# sims' historical link lines include platform extras that Python3_LIBRARIES +# does not reproduce. Mirrors autoconf/configure.ac:127-187 exactly, +# including search order and the `--embed` / newline-stripping behavior. +# +# Honors the PYTHON_VERSION cache variable (e.g. "3" looks for python3), +# equivalent to configure's PYTHON_VERSION influential env var. +# +# Exports: PYTHON, PYTHON_CONFIG, PYTHON_CPPFLAGS, PYTHON_LIBS + +find_program(PYTHON + NAMES python${PYTHON_VERSION} python python3 +) +if(NOT PYTHON) + message(FATAL_ERROR "could not find python python or python3. Please install the python development package") +endif() + +execute_process( + COMMAND ${PYTHON} -c "import sys; print(str(sys.version_info[0])+\".\"+str(sys.version_info[1]))" + OUTPUT_VARIABLE PYTHON_MAJORMINOR + OUTPUT_STRIP_TRAILING_WHITESPACE +) +execute_process( + COMMAND ${PYTHON} -c "import sys; print(str(sys.version_info[0]))" + OUTPUT_VARIABLE PYTHON_MAJOR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +find_program(PYTHON_CONFIG + NAMES python${PYTHON_MAJORMINOR}-config python${PYTHON_MAJOR}-config python${PYTHON_VERSION}-config python-config +) +if(NOT PYTHON_CONFIG) + message(FATAL_ERROR "could not find python-config python-config or python-config. Please install the python development package") +endif() + +set(_tr_python_libs_command --ldflags --libs) +if(NOT PYTHON_MAJORMINOR VERSION_LESS "3.8") + list(APPEND _tr_python_libs_command --embed) +endif() + +execute_process( + COMMAND ${PYTHON_CONFIG} --includes + OUTPUT_VARIABLE PYTHON_CPPFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) +string(REPLACE "-I" "-isystem" PYTHON_CPPFLAGS "${PYTHON_CPPFLAGS}") + +execute_process( + COMMAND ${PYTHON_CONFIG} ${_tr_python_libs_command} + OUTPUT_VARIABLE PYTHON_LIBS +) +string(REPLACE "\r" " " PYTHON_LIBS "${PYTHON_LIBS}") +string(REPLACE "\n" " " PYTHON_LIBS "${PYTHON_LIBS}") +string(STRIP "${PYTHON_LIBS}" PYTHON_LIBS) + +# Always empty today (see A1) — kept as a named, substituted variable for +# parity with config_user.mk.in's contract. +set(PYTHON_EXTRA_LIBS "") diff --git a/share/trick/makefiles/config_user_cmake.mk.in b/share/trick/makefiles/config_user_cmake.mk.in index c530d27c6..3d4e2959c 100644 --- a/share/trick/makefiles/config_user_cmake.mk.in +++ b/share/trick/makefiles/config_user_cmake.mk.in @@ -1,64 +1,51 @@ -TRICK_FORCE_32BIT = @TRICK_FORCE_32BIT@ +TRICK_FORCE_32BIT = @TRICK_FORCE_32BIT_MK@ -CC = @CMAKE_C_COMPILER@ -CXX = @CMAKE_CXX_COMPILER@ -LD = @CMAKE_LINKER@ +CC = @CC@ +CXX = @CXX@ +LD = @LD_EXECUTABLE@ PERL = @PERL_EXECUTABLE@ -LEX = @FLEX_EXECUTABLE@ +LEX = @FLEX_EXECUTABLE_NAME@ YACC = @BISON_EXECUTABLE@ SWIG = @SWIG_EXECUTABLE@ -PYTHON = @PYTHON_EXECUTABLE@ +PYTHON = @PYTHON@ CLANG = @CLANG_EXECUTABLE@ TEE = @TEE_EXECUTABLE@ -TRICK_MONGOOSE = @TRICK_MONGOOSE@ +MVN = @MVN_EXECUTABLE@ + USE_JAVA = @USE_JAVA@ JAVAC = @Java_JAVAC_EXECUTABLE@ -USE_X_WINDOWS = @USE_X_WINDOWS@ - -LLVM_HOME = @LLVM_ROOT_DIR@ +TRICK_OFFLINE = @TRICK_OFFLINE_MK@ -ICG_CLANGLIBS = \ - -lclangFrontend \ - -lclangDriver \ - -lclangSerialization \ - -lclangParse \ - -lclangSema \ - -lclangAnalysis \ - -lclangEdit \ - -lclangAST \ - -lclangLex \ - -lclangBasic \ +USE_CIVETWEB = @USE_CIVETWEB@ +CIVETWEB_HOME = @CIVETWEB_HOME@ -PYTHON_INCLUDES = -isystem@PYTHON_INCLUDE_DIRS@ -PYTHON_LIB = @PYTHON_LIBRARIES@ +USE_X_WINDOWS = @USE_X_WINDOWS@ -# Only add udunits include if it is not in /usr/include. -ifneq ("@UDUNITS2_INCLUDES@","/usr/include") -UDUNITS_INCLUDES = -I@UDUNITS2_INCLUDES@ -else -UDUNITS_INCLUDES = -endif +LLVM_HOME = @LLVM_HOME@ +ICG_CLANGLIBS = @ICG_CLANGLIBS@ -UDUNITS_LDFLAGS = @UDUNITS2_LIBRARIES@ -TRICK_EXCLUDE += :@UDUNITS_EXCLUDE@ +PYTHON_INCLUDES = @PYTHON_CPPFLAGS@ +PYTHON_LIB = @PYTHON_LIBS@ @PYTHON_EXTRA_LIBS@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ -LIBXML_INCLUDES = -I@LIBXML2_INCLUDE_DIR@ -LIBXML = @LIBXML2_LIBRARIES@ +X_LIB_DIR = @X_LIB_DIR@ +MOTIF_HOME = @MOTIF_HOME@ -PTHREAD_LIBS = @CMAKE_THREAD_LIBS_INIT@ -GSL_HOME = @GSL_ROOT_DIR@ +UDUNITS_INCLUDES = @UDUNITS_INCLUDES@ +UDUNITS_LDFLAGS = @UDUNITS_LDFLAGS@ +TRICK_EXCLUDE += :@UDUNITS_EXCLUDE@ HDF5 = @HDF5_HOME@ +GSL_HOME = @GSL_HOME@ GTEST_HOME = @GTEST_HOME@ - -PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ -X_LIB_DIR = @X_LIB_DIR@ -MOTIF_HOME = @MOTIF_HOME@ +GTEST_CXXSTD = @GTEST_CXXSTD@ USE_ER7_UTILS = @USE_ER7_UTILS@ +LIBXML = @LIBXML@ -PREFIX ?= @prefix@ +PREFIX ?= @CMAKE_INSTALL_PREFIX@ +TRICK_GCC_VERSION = @GCC_VERSION@ CONFIG_MK = 1 - diff --git a/test/build_config/compare_config_user.sh b/test/build_config/compare_config_user.sh new file mode 100755 index 000000000..8aa34e3d8 --- /dev/null +++ b/test/build_config/compare_config_user.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# Golden-file parity test (Part C, Phase 1, step 6 / D4 in +# CMAKE_MIGRATION_PLAN.md): runs both ./configure and cmake against the same +# tree and diffs the two generated config_user.mk files after normalizing +# away differences that are cosmetic or otherwise known-harmless to +# Makefile.common consumers. Any other difference fails the test. +# +# Usage: test/build_config/compare_config_user.sh [extra cmake -D args...] +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$REPO_ROOT" + +CONFIG_USER_MK="share/trick/makefiles/config_user.mk" +WORKDIR="$(mktemp -d)" + +# ── 1. Run autoconf's ./configure, preserving any pre-existing config_user.mk ── +# configure writes into the source tree; save/restore so this script is +# non-destructive to a developer's existing build state. +HAD_ORIGINAL=0 +if [ -f "$CONFIG_USER_MK" ]; then + HAD_ORIGINAL=1 + cp "$CONFIG_USER_MK" "$WORKDIR/original_config_user.mk" +fi + +restore_original() { + if [ "$HAD_ORIGINAL" -eq 1 ]; then + cp "$WORKDIR/original_config_user.mk" "$CONFIG_USER_MK" + else + rm -f "$CONFIG_USER_MK" + fi +} +trap 'restore_original; rm -rf "$WORKDIR"' EXIT + +./configure >"$WORKDIR/configure.log" 2>&1 || { + echo "./configure failed; see $WORKDIR/configure.log" >&2 + cat "$WORKDIR/configure.log" >&2 + exit 1 +} +cp "$CONFIG_USER_MK" "$WORKDIR/autoconf_config_user.mk" +restore_original + +# ── 2. Run cmake into a scratch build dir ────────────────────────────────── +CMAKE_BUILD_DIR="$WORKDIR/cmake-build" +cmake -S . -B "$CMAKE_BUILD_DIR" "$@" >"$WORKDIR/cmake.log" 2>&1 || { + echo "cmake configure failed; see $WORKDIR/cmake.log" >&2 + cat "$WORKDIR/cmake.log" >&2 + exit 1 +} +CMAKE_GENERATED="$CMAKE_BUILD_DIR/share/trick/makefiles/config_user.mk" +if [ ! -f "$CMAKE_GENERATED" ]; then + echo "cmake did not produce $CMAKE_GENERATED" >&2 + exit 1 +fi +cp "$CMAKE_GENERATED" "$WORKDIR/cmake_config_user.mk" + +# ── 3. Normalize both files ───────────────────────────────────────────────── +# Accepted deltas: +# - PYTHON_LIB: configure's "tr '\r\n' ' '" leaves a trailing space that +# autoconf keeps and CMake's string(STRIP) removes. +# - TRICK_GCC_VERSION: autoconf uses "$CC -dumpfullversion -dumpversion"; +# CMake uses CMAKE_CXX_COMPILER_VERSION. Both resolve to the compiler's +# dotted version triplet on every GCC we support; tolerate a trailing +# ".0" CMake sometimes omits (e.g. "11.4" vs "11.4.0"). +# +# Known NOT-normalized, environment-specific delta (not a bug): on a +# machine with more than one X11 provider installed (e.g. a dev Mac with +# both Homebrew's libx11 formula AND the legacy /usr/X11 selector), +# autoconf's AC_PATH_X and CMake's FindX11 module can each pick a +# different, independently valid installation, producing a different +# X_LIB_DIR value. Every CI image here installs exactly one X11 provider, +# so this does not appear in CI; it is not worth replicating AC_PATH_X's +# legacy search heuristic in CMake to chase it on developer machines. +normalize() { + local infile="$1" + local outfile="$2" + sed -E \ + -e 's/[[:space:]]+$//' \ + -e '/^[[:space:]]*#/d' \ + -e '/^[[:space:]]*$/d' \ + -e 's/^(PYTHON_LIB[[:space:]]*=.*[^[:space:]])[[:space:]]+$/\1/' \ + -e 's/^(TRICK_GCC_VERSION[[:space:]]*=[[:space:]]*[0-9]+\.[0-9]+)$/\1.0/' \ + "$infile" | sort >"$outfile" +} + +normalize "$WORKDIR/autoconf_config_user.mk" "$WORKDIR/autoconf_normalized.mk" +normalize "$WORKDIR/cmake_config_user.mk" "$WORKDIR/cmake_normalized.mk" + +# ── 4. Diff ────────────────────────────────────────────────────────────── +if diff -u "$WORKDIR/autoconf_normalized.mk" "$WORKDIR/cmake_normalized.mk"; then + echo "config_user.mk parity: OK" + exit 0 +else + echo "config_user.mk parity: FAILED (see diff above)" >&2 + echo "autoconf: $WORKDIR/autoconf_config_user.mk" >&2 + echo "cmake: $WORKDIR/cmake_config_user.mk" >&2 + exit 1 +fi From 1a2de5290c5388ae48f92dc3e0427835b58737d3 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Thu, 2 Jul 2026 17:06:43 -0500 Subject: [PATCH 02/28] git-ignore object files in project root; cleanup and dedupe gitignore configuration --- .gitignore | 2 +- test/.gitignore | 2 -- test/SIM_exclusion_mechanisms/models/trickified/.gitignore | 1 - test/SIM_trickified/trickified_project/trickified/.gitignore | 1 - .../trickified_project/trickified/.gitignore | 1 - .../trickified_project/trickified/.gitignore | 1 - .../trickified_project/trickified/.gitignore | 1 - trick_sims/.gitignore | 1 - trick_sims/SIM_balloon/models/graphics/.gitignore | 2 +- trick_sims/SIM_robot/models/graphics/.gitignore | 2 +- trick_sims/SIM_satellite/models/Satellite/graphics/.gitignore | 2 -- trick_sims/SIM_splashdown/models/CrewModuleGraphics/.gitignore | 2 +- trick_sims/SIM_submarine/models/graphics/.gitignore | 2 +- trick_sims/SIM_wheelbot/models/Battery/.gitignore | 1 - trick_source/data_products/.gitignore | 1 - trick_source/data_products/DPX/test/unit_test/.gitignore | 1 - trick_source/sim_services/Clock/test/.gitignore | 1 - trick_source/sim_services/CommandLineArguments/test/.gitignore | 1 - trick_source/sim_services/Executive/test/.gitignore | 1 - trick_source/sim_services/Integrator/test/.gitignore | 1 - trick_source/sim_services/MemoryManager/test/.gitignore | 3 +-- trick_source/sim_services/MonteCarlo/test/.gitignore | 1 - trick_source/sim_services/ScheduledJobQueue/test/.gitignore | 1 - trick_source/sim_services/Timer/test/.gitignore | 1 - trick_source/sim_services/VariableServer/test/.gitignore | 3 +-- trick_source/trick_utils/SAIntegrator/.gitignore | 1 - trick_source/trick_utils/compareFloatingPoint/test/.gitignore | 1 - trick_source/trick_utils/interpolator/test/.gitignore | 1 - trick_source/trick_utils/math/test/.gitignore | 1 - trick_source/trick_utils/optimization/test/.gitignore | 1 - trick_source/trick_utils/trick_adt/test/.gitignore | 3 +-- trick_source/trick_utils/units/test/.gitignore | 1 - trick_source/trick_utils/var_binary_parser/.gitignore | 1 - 33 files changed, 8 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index fee3f0d83..1058d5500 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ # Build artifacts +*.o object_* io_src swig @@ -69,7 +70,6 @@ share/trick/makefiles/config_user.mk # Editor / OS *~ *.swp -*.dox .DS_Store .vscode/ .cache/ diff --git a/test/.gitignore b/test/.gitignore index ca7772f8d..f3d79c4a5 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -27,5 +27,3 @@ build S_sie.json *.ckpnt MonteCarlo_Meta_data_output -*.xml -SIM_mtv/models/test_client/test_client diff --git a/test/SIM_exclusion_mechanisms/models/trickified/.gitignore b/test/SIM_exclusion_mechanisms/models/trickified/.gitignore index 53064b3fe..23cce3683 100644 --- a/test/SIM_exclusion_mechanisms/models/trickified/.gitignore +++ b/test/SIM_exclusion_mechanisms/models/trickified/.gitignore @@ -1,3 +1,2 @@ python -trickified.o full_file diff --git a/test/SIM_trickified/trickified_project/trickified/.gitignore b/test/SIM_trickified/trickified_project/trickified/.gitignore index 7f5440dcd..b4748bbf3 100644 --- a/test/SIM_trickified/trickified_project/trickified/.gitignore +++ b/test/SIM_trickified/trickified_project/trickified/.gitignore @@ -2,4 +2,3 @@ build python trick full_file -*.o diff --git a/test/SIM_trickified_archive/trickified_project/trickified/.gitignore b/test/SIM_trickified_archive/trickified_project/trickified/.gitignore index 1d77349de..987ae75c5 100644 --- a/test/SIM_trickified_archive/trickified_project/trickified/.gitignore +++ b/test/SIM_trickified_archive/trickified_project/trickified/.gitignore @@ -1,5 +1,4 @@ build python trick -*.o S_overrides_trickify.mk diff --git a/test/SIM_trickified_object/trickified_project/trickified/.gitignore b/test/SIM_trickified_object/trickified_project/trickified/.gitignore index 1d77349de..987ae75c5 100644 --- a/test/SIM_trickified_object/trickified_project/trickified/.gitignore +++ b/test/SIM_trickified_object/trickified_project/trickified/.gitignore @@ -1,5 +1,4 @@ build python trick -*.o S_overrides_trickify.mk diff --git a/test/SIM_trickified_shared/trickified_project/trickified/.gitignore b/test/SIM_trickified_shared/trickified_project/trickified/.gitignore index 1d77349de..987ae75c5 100644 --- a/test/SIM_trickified_shared/trickified_project/trickified/.gitignore +++ b/test/SIM_trickified_shared/trickified_project/trickified/.gitignore @@ -1,5 +1,4 @@ build python trick -*.o S_overrides_trickify.mk diff --git a/trick_sims/.gitignore b/trick_sims/.gitignore index 1e6c99f95..fa53acd6c 100644 --- a/trick_sims/.gitignore +++ b/trick_sims/.gitignore @@ -5,7 +5,6 @@ S_run_summary send_hs varserver_log log_* -*init_log.csv* chkpnt_* MONTE_RUN_* .S_library* diff --git a/trick_sims/SIM_balloon/models/graphics/.gitignore b/trick_sims/SIM_balloon/models/graphics/.gitignore index fb2fce41a..f213ce824 100644 --- a/trick_sims/SIM_balloon/models/graphics/.gitignore +++ b/trick_sims/SIM_balloon/models/graphics/.gitignore @@ -1,4 +1,4 @@ -!\makefile +!makefile .classpath .project .settings/ diff --git a/trick_sims/SIM_robot/models/graphics/.gitignore b/trick_sims/SIM_robot/models/graphics/.gitignore index 0a76dd934..c0ae5b4bb 100644 --- a/trick_sims/SIM_robot/models/graphics/.gitignore +++ b/trick_sims/SIM_robot/models/graphics/.gitignore @@ -1 +1 @@ -!\makefile +!makefile diff --git a/trick_sims/SIM_satellite/models/Satellite/graphics/.gitignore b/trick_sims/SIM_satellite/models/Satellite/graphics/.gitignore index 21823e21b..1ec1b7e48 100644 --- a/trick_sims/SIM_satellite/models/Satellite/graphics/.gitignore +++ b/trick_sims/SIM_satellite/models/Satellite/graphics/.gitignore @@ -1,3 +1 @@ -*.o Scene - diff --git a/trick_sims/SIM_splashdown/models/CrewModuleGraphics/.gitignore b/trick_sims/SIM_splashdown/models/CrewModuleGraphics/.gitignore index 0a76dd934..c0ae5b4bb 100644 --- a/trick_sims/SIM_splashdown/models/CrewModuleGraphics/.gitignore +++ b/trick_sims/SIM_splashdown/models/CrewModuleGraphics/.gitignore @@ -1 +1 @@ -!\makefile +!makefile diff --git a/trick_sims/SIM_submarine/models/graphics/.gitignore b/trick_sims/SIM_submarine/models/graphics/.gitignore index 0a76dd934..c0ae5b4bb 100644 --- a/trick_sims/SIM_submarine/models/graphics/.gitignore +++ b/trick_sims/SIM_submarine/models/graphics/.gitignore @@ -1 +1 @@ -!\makefile +!makefile diff --git a/trick_sims/SIM_wheelbot/models/Battery/.gitignore b/trick_sims/SIM_wheelbot/models/Battery/.gitignore index c4f4b0097..d8fbd7cd0 100644 --- a/trick_sims/SIM_wheelbot/models/Battery/.gitignore +++ b/trick_sims/SIM_wheelbot/models/Battery/.gitignore @@ -1,6 +1,5 @@ # Ignore compiled objects and libraries lib obj -*.o test/BatteryTest XMLtestReports diff --git a/trick_source/data_products/.gitignore b/trick_source/data_products/.gitignore index 8826e6b24..92083e8ca 100644 --- a/trick_source/data_products/.gitignore +++ b/trick_source/data_products/.gitignore @@ -1,3 +1,2 @@ fermi-ware -Apps/trkConvert/*.o Apps/trkConvert/trkConvert diff --git a/trick_source/data_products/DPX/test/unit_test/.gitignore b/trick_source/data_products/DPX/test/unit_test/.gitignore index bb6e1d856..d9457e18a 100644 --- a/trick_source/data_products/DPX/test/unit_test/.gitignore +++ b/trick_source/data_products/DPX/test/unit_test/.gitignore @@ -1,4 +1,3 @@ -*.o DPC_test DPM_test DS_test diff --git a/trick_source/sim_services/Clock/test/.gitignore b/trick_source/sim_services/Clock/test/.gitignore index 7aadd5803..fd94e374a 100644 --- a/trick_source/sim_services/Clock/test/.gitignore +++ b/trick_source/sim_services/Clock/test/.gitignore @@ -1,4 +1,3 @@ -*.o BC635Clock_test TPROCTEClock_test GetTimeOfDayClock_test diff --git a/trick_source/sim_services/CommandLineArguments/test/.gitignore b/trick_source/sim_services/CommandLineArguments/test/.gitignore index c9567dcfc..a9083e3c0 100644 --- a/trick_source/sim_services/CommandLineArguments/test/.gitignore +++ b/trick_source/sim_services/CommandLineArguments/test/.gitignore @@ -1,3 +1,2 @@ create_path_test CommandLineArguments_test -*.o diff --git a/trick_source/sim_services/Executive/test/.gitignore b/trick_source/sim_services/Executive/test/.gitignore index c0eac510e..bb1bcd063 100644 --- a/trick_source/sim_services/Executive/test/.gitignore +++ b/trick_source/sim_services/Executive/test/.gitignore @@ -1,2 +1 @@ -*.o Executive_test diff --git a/trick_source/sim_services/Integrator/test/.gitignore b/trick_source/sim_services/Integrator/test/.gitignore index 626aae794..05e5a9c18 100644 --- a/trick_source/sim_services/Integrator/test/.gitignore +++ b/trick_source/sim_services/Integrator/test/.gitignore @@ -1,2 +1 @@ -*.o Integrator_unittest diff --git a/trick_source/sim_services/MemoryManager/test/.gitignore b/trick_source/sim_services/MemoryManager/test/.gitignore index 15d4e8c28..e0124f6a1 100644 --- a/trick_source/sim_services/MemoryManager/test/.gitignore +++ b/trick_source/sim_services/MemoryManager/test/.gitignore @@ -1,4 +1,3 @@ -*.o .icg_no_found Bitfield_tests MM_alloc_deps @@ -21,4 +20,4 @@ MM_write_checkpoint MM_write_checkpoint_hexfloat MM_write_var_unittest MM_stl_checkpoint -MM_stl_restore \ No newline at end of file +MM_stl_restore diff --git a/trick_source/sim_services/MonteCarlo/test/.gitignore b/trick_source/sim_services/MonteCarlo/test/.gitignore index 82c9a9536..a09776546 100644 --- a/trick_source/sim_services/MonteCarlo/test/.gitignore +++ b/trick_source/sim_services/MonteCarlo/test/.gitignore @@ -1,3 +1,2 @@ -*.o MonteCarlo_exceptions MonteCarlo_test diff --git a/trick_source/sim_services/ScheduledJobQueue/test/.gitignore b/trick_source/sim_services/ScheduledJobQueue/test/.gitignore index 098cdd8f1..961d083b6 100644 --- a/trick_source/sim_services/ScheduledJobQueue/test/.gitignore +++ b/trick_source/sim_services/ScheduledJobQueue/test/.gitignore @@ -1,2 +1 @@ -*.o ScheduledJobQueue_test diff --git a/trick_source/sim_services/Timer/test/.gitignore b/trick_source/sim_services/Timer/test/.gitignore index 611d4db12..7b45683f5 100644 --- a/trick_source/sim_services/Timer/test/.gitignore +++ b/trick_source/sim_services/Timer/test/.gitignore @@ -1,2 +1 @@ -*.o ITimer_test diff --git a/trick_source/sim_services/VariableServer/test/.gitignore b/trick_source/sim_services/VariableServer/test/.gitignore index ea339cfdc..9afd9297a 100644 --- a/trick_source/sim_services/VariableServer/test/.gitignore +++ b/trick_source/sim_services/VariableServer/test/.gitignore @@ -1,4 +1,3 @@ lcov_html/* -*.o *_test -*.info \ No newline at end of file +*.info diff --git a/trick_source/trick_utils/SAIntegrator/.gitignore b/trick_source/trick_utils/SAIntegrator/.gitignore index 29f231ccc..9eb1a0efd 100644 --- a/trick_source/trick_utils/SAIntegrator/.gitignore +++ b/trick_source/trick_utils/SAIntegrator/.gitignore @@ -1,4 +1,3 @@ lib/ obj/ -*.o *.dSYM diff --git a/trick_source/trick_utils/compareFloatingPoint/test/.gitignore b/trick_source/trick_utils/compareFloatingPoint/test/.gitignore index da4626dac..d6459089c 100644 --- a/trick_source/trick_utils/compareFloatingPoint/test/.gitignore +++ b/trick_source/trick_utils/compareFloatingPoint/test/.gitignore @@ -1,2 +1 @@ -*.o *_unittest diff --git a/trick_source/trick_utils/interpolator/test/.gitignore b/trick_source/trick_utils/interpolator/test/.gitignore index ad47b65c2..4051fb14e 100644 --- a/trick_source/trick_utils/interpolator/test/.gitignore +++ b/trick_source/trick_utils/interpolator/test/.gitignore @@ -1,2 +1 @@ -*.o Interpolator_unittest diff --git a/trick_source/trick_utils/math/test/.gitignore b/trick_source/trick_utils/math/test/.gitignore index f7b4fe9cf..75407e972 100644 --- a/trick_source/trick_utils/math/test/.gitignore +++ b/trick_source/trick_utils/math/test/.gitignore @@ -1,3 +1,2 @@ UnitTestEulerQuat UnitTestLDLtFactorization -*.o diff --git a/trick_source/trick_utils/optimization/test/.gitignore b/trick_source/trick_utils/optimization/test/.gitignore index 409295011..fcc715e44 100644 --- a/trick_source/trick_utils/optimization/test/.gitignore +++ b/trick_source/trick_utils/optimization/test/.gitignore @@ -1,2 +1 @@ UnitTestPDIPOptimization -*.o diff --git a/trick_source/trick_utils/trick_adt/test/.gitignore b/trick_source/trick_utils/trick_adt/test/.gitignore index 320de4928..491b116a1 100644 --- a/trick_source/trick_utils/trick_adt/test/.gitignore +++ b/trick_source/trick_utils/trick_adt/test/.gitignore @@ -1,3 +1,2 @@ -*.o dllist_unittest -lqueue_unittest \ No newline at end of file +lqueue_unittest diff --git a/trick_source/trick_utils/units/test/.gitignore b/trick_source/trick_utils/units/test/.gitignore index cae4b199f..0ef033958 100644 --- a/trick_source/trick_utils/units/test/.gitignore +++ b/trick_source/trick_utils/units/test/.gitignore @@ -1,3 +1,2 @@ -*.o UnitConvTestSuite UnitTestSuite diff --git a/trick_source/trick_utils/var_binary_parser/.gitignore b/trick_source/trick_utils/var_binary_parser/.gitignore index 9e345c986..9a1296d5f 100644 --- a/trick_source/trick_utils/var_binary_parser/.gitignore +++ b/trick_source/trick_utils/var_binary_parser/.gitignore @@ -1,2 +1 @@ -*.o TEST_var_binary_parser From d03aa43a9108cb1475a49ca964556ce3e74eef1c Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Mon, 6 Jul 2026 10:57:39 -0500 Subject: [PATCH 03/28] cmake: transition: phase 2 --- .github/workflows/ci.yml | 8 +- CMakeLists.txt | 70 ++- cmake/TrickICG.cmake | 97 ++++ cmake/scripts/GenerateIOSrcList.cmake | 50 ++ test/build_config/compare_archives.sh | 99 ++++ .../codegen/Interface_Code_Gen/CMakeLists.txt | 152 ++++-- trick_source/er7_utils/CMakeLists.txt | 143 ++--- trick_source/sim_services/CMakeLists.txt | 500 +++++++++--------- .../InputProcessor/CMakeLists.txt | 17 +- .../sim_services/MemoryManager/CMakeLists.txt | 153 +++--- trick_source/trick_swig/CMakeLists.txt | 78 +++ trick_source/trick_utils/CMakeLists.txt | 36 +- trick_source/trick_utils/comm/CMakeLists.txt | 56 +- .../connection_handlers/CMakeLists.txt | 11 + trick_source/trick_utils/math/CMakeLists.txt | 185 +++---- .../trick_utils/optimization/CMakeLists.txt | 7 + trick_source/trick_utils/units/CMakeLists.txt | 6 +- .../var_binary_parser/CMakeLists.txt | 8 + 18 files changed, 1092 insertions(+), 584 deletions(-) create mode 100644 cmake/TrickICG.cmake create mode 100644 cmake/scripts/GenerateIOSrcList.cmake create mode 100755 test/build_config/compare_archives.sh create mode 100644 trick_source/trick_swig/CMakeLists.txt create mode 100644 trick_source/trick_utils/connection_handlers/CMakeLists.txt create mode 100644 trick_source/trick_utils/optimization/CMakeLists.txt create mode 100644 trick_source/trick_utils/var_binary_parser/CMakeLists.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2bfcfd58f..0efb36000 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,10 +144,12 @@ jobs: ${{ matrix.arch_deps }} - name: Checkout repository uses: actions/checkout@v6 - - name: Compare autoconf vs cmake config_user.mk + - name: Compare autoconf vs cmake config_user.mk (Phase 1) run: | echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" test/build_config/compare_config_user.sh + - name: CMake build + archive parity vs make (Phase 2) + run: test/build_config/compare_archives.sh cmake-configure-macos: name: CMake Configure (macOS) @@ -165,8 +167,10 @@ jobs: brew install --cask xquartz brew install udunits openmotif maven brew install swig llvm - - name: Compare autoconf vs cmake config_user.mk + - name: Compare autoconf vs cmake config_user.mk (Phase 1) run: test/build_config/compare_config_user.sh + - name: CMake build + archive parity vs make (Phase 2) + run: test/build_config/compare_archives.sh trickops: needs: [style] diff --git a/CMakeLists.txt b/CMakeLists.txt index c6eb51151..8f7efcd09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -82,5 +82,71 @@ configure_file( @ONLY ) -# Phase 2 adds: add_subdirectory(trick_source/codegen/Interface_Code_Gen) and -# the rest of the native build (ICG, archives, SWIG). Phase 1 stops here. +# ────────────────────────────────────────────────────────────────────────── +# Phase 2 — core native build: ICG, io_src, archives, SWIG +# ────────────────────────────────────────────────────────────────────────── +find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Embed) +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) + +# Flags shared by every Trick-core compile target, mirroring +# TRICK_SYSTEM_CXXFLAGS/TRICK_CXXFLAGS (Makefile.common:98-197). A handful of +# these (HDF5/GSL/Python includes, USE_CIVETWEB) only apply to specific +# source files in the make build; applying them build-wide here is a +# deliberate simplification (harmless extra -I/-D flags) rather than +# reproducing every per-file override. +add_library(trick_build_flags INTERFACE) +target_compile_features(trick_build_flags INTERFACE cxx_std_17) +target_compile_options(trick_build_flags INTERFACE -fpic) +target_compile_definitions(trick_build_flags INTERFACE + TRICK_VER=${TRICK_MAJOR} + TRICK_MINOR=${TRICK_MINOR} +) +target_include_directories(trick_build_flags INTERFACE + ${CMAKE_SOURCE_DIR}/trick_source + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/include/trick/compat +) +if(UDUNITS_INCLUDES) + separate_arguments(_tr_udunits_includes_list NATIVE_COMMAND "${UDUNITS_INCLUDES}") + target_compile_options(trick_build_flags INTERFACE ${_tr_udunits_includes_list}) +endif() +if(TRICK_USE_ER7_UTILS) + target_compile_definitions(trick_build_flags INTERFACE USE_ER7_UTILS_INTEGRATORS) + if(EXISTS ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/CheckpointHelper) + target_compile_definitions(trick_build_flags INTERFACE USE_ER7_UTILS_CHECKPOINTHELPER) + endif() +endif() +if(TRICK_FORCE_32BIT) + target_compile_options(trick_build_flags INTERFACE -m32) + target_link_options(trick_build_flags INTERFACE -m32) +endif() +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + target_compile_options(trick_build_flags INTERFACE -fcolor-diagnostics) +endif() +if(TRICK_CIVETWEB_HOME) + target_compile_definitions(trick_build_flags INTERFACE USE_CIVETWEB) + target_include_directories(trick_build_flags INTERFACE ${TRICK_CIVETWEB_HOME}/include) +endif() + +add_subdirectory(trick_source/codegen/Interface_Code_Gen) +include(TrickICG) + +add_subdirectory(trick_source/sim_services) +add_subdirectory(trick_source/trick_utils) +if(TRICK_USE_ER7_UTILS) + add_subdirectory(trick_source/er7_utils) +endif() +add_subdirectory(trick_source/trick_swig) + +# libtrick.a = sim_services + trick_utils objects (minus MemoryManager and +# the trick_utils dirs with their own archives) + the non-er7_utils io_src +# objects (A3). +add_library(trick STATIC + $ + $ + ${TRICK_IO_SOURCES} +) +target_link_libraries(trick PUBLIC trick_build_flags) +set_target_properties(trick PROPERTIES ARCHIVE_OUTPUT_NAME trick) +add_dependencies(trick trick_io_src_gen) diff --git a/cmake/TrickICG.cmake b/cmake/TrickICG.cmake new file mode 100644 index 000000000..afd6e9525 --- /dev/null +++ b/cmake/TrickICG.cmake @@ -0,0 +1,97 @@ +# io_src generation for Trick's own core build (Part C, Phase 2, step 3). +# +# Requires trick-ICG (add_subdirectory(trick_source/codegen/Interface_Code_Gen) +# already processed) plus the detection variables from Phase 1. +# +# ICG's output-file placement is dynamic — it discovers headers transitively +# via clang's preprocessor, not just the direct #includes in files_to_ICG.hh +# (confirmed: trick_source/er7_utils/**/io_src/ contain io_ files for headers +# nowhere directly #included). CMake's add_library() needs a static SOURCES +# list at configure time, so this uses the two-pass pattern: a build-time +# custom command runs ICG and writes a small generated .cmake file +# enumerating the real output. CMAKE_CONFIGURE_DEPENDS is set on that file +# for cache-invalidation on later header changes, but CMake only honors +# CONFIGURE_DEPENDS entries that already exist at the *configure* that +# registers them — on a truly clean tree the file doesn't exist yet, so nothing +# auto-triggers a reconfigure once the build-time custom command creates it. +# So on a clean tree this is a three-step bootstrap: +# cmake -S . -B build +# cmake --build build --target trick_io_src_gen # runs ICG, writes the list +# cmake -S . -B build && cmake --build build # reconfigure picks up the +# # real list, then builds +# Once io_src exists, ordinary `cmake --build build` reconfigures/rebuilds +# incrementally as normal (headers touch files_to_ICG.hh's mtime via +# CMAKE_CONFIGURE_DEPENDS below, which DOES work once the file is on disk). + +set(TRICK_IO_SRC_DIR ${CMAKE_BINARY_DIR}/io_src) +set(TRICK_IO_SRC_LIST_FILE ${TRICK_IO_SRC_DIR}/.generated_sources.cmake) + +# ── Flags mirroring TRICK_SYSTEM_CXXFLAGS / TRICK_CXXFLAGS (Makefile.common:98-197) ── +set(_tr_icg_flags + -I${CMAKE_SOURCE_DIR}/trick_source + -I${CMAKE_SOURCE_DIR}/include + -I${CMAKE_SOURCE_DIR}/include/trick/compat + -DTRICK_VER=${TRICK_MAJOR} + -DTRICK_MINOR=${TRICK_MINOR} + -fpic + ${UDUNITS_INCLUDES} + -std=c++17 +) +if(TRICK_USE_ER7_UTILS) + list(APPEND _tr_icg_flags -DUSE_ER7_UTILS_INTEGRATORS) + if(EXISTS ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/CheckpointHelper) + list(APPEND _tr_icg_flags -DUSE_ER7_UTILS_CHECKPOINTHELPER) + endif() +endif() +if(TRICK_FORCE_32BIT) + list(APPEND _tr_icg_flags -m32) +endif() +if(GSL_HOME AND NOT GSL_HOME STREQUAL "/usr/local") + list(APPEND _tr_icg_flags -I${GSL_HOME}/include) +endif() +if(TRICK_CIVETWEB_HOME) + list(APPEND _tr_icg_flags -I${TRICK_CIVETWEB_HOME}/include -DUSE_CIVETWEB) +endif() + +file(MAKE_DIRECTORY ${TRICK_IO_SRC_DIR}) + +add_custom_command( + OUTPUT ${TRICK_IO_SRC_LIST_FILE} + COMMAND ${CMAKE_COMMAND} -E env TRICK_HOME=${CMAKE_SOURCE_DIR} TRICK_EXCLUDE=:${UDUNITS_EXCLUDE} + $ -force -sim_services -m ${_tr_icg_flags} + -o ${TRICK_IO_SRC_DIR} + ${CMAKE_SOURCE_DIR}/include/trick/files_to_ICG.hh + COMMAND ${CMAKE_COMMAND} + -DIO_SRC_DIR=${TRICK_IO_SRC_DIR} + -DER7_UTILS_SRC_DIR=${CMAKE_SOURCE_DIR}/trick_source/er7_utils + -DOUTPUT_FILE=${TRICK_IO_SRC_LIST_FILE} + -P ${CMAKE_SOURCE_DIR}/cmake/scripts/GenerateIOSrcList.cmake + WORKING_DIRECTORY ${TRICK_IO_SRC_DIR} + DEPENDS trick-ICG ${CMAKE_SOURCE_DIR}/include/trick/files_to_ICG.hh + COMMENT "Running trick-ICG over Trick core headers (-sim_services)" + VERBATIM +) +add_custom_target(trick_io_src_gen DEPENDS ${TRICK_IO_SRC_LIST_FILE}) + +if(EXISTS ${TRICK_IO_SRC_LIST_FILE}) + include(${TRICK_IO_SRC_LIST_FILE}) +else() + set(TRICK_IO_SOURCES "") + set(ER7_IO_SOURCES "") + message(WARNING + "io_src has not been generated yet. Run " + "`cmake --build ${CMAKE_BINARY_DIR} --target trick_io_src_gen`, then " + "re-run `cmake -S ${CMAKE_SOURCE_DIR} -B ${CMAKE_BINARY_DIR}` and " + "`cmake --build ${CMAKE_BINARY_DIR}` again so libtrick.a/" + "liber7_utils.a pick up the generated sources (see cmake/TrickICG.cmake).") +endif() +# Re-run configure automatically once trick_io_src_gen produces a fresh list. +set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${TRICK_IO_SRC_LIST_FILE}) +# Conservative net: also reconfigure if any Trick header changes (R7 — this +# is for cache-invalidation only, mirroring make's "rerun whenever headers +# change" behavior; it does not need to be an exact dependency list). +file(GLOB_RECURSE _tr_all_trick_headers CONFIGURE_DEPENDS + ${CMAKE_SOURCE_DIR}/include/trick/*.h + ${CMAKE_SOURCE_DIR}/include/trick/*.hh + ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/*.hh +) diff --git a/cmake/scripts/GenerateIOSrcList.cmake b/cmake/scripts/GenerateIOSrcList.cmake new file mode 100644 index 000000000..ae8824d60 --- /dev/null +++ b/cmake/scripts/GenerateIOSrcList.cmake @@ -0,0 +1,50 @@ +# Run in `cmake -P` script mode after a trick-ICG -sim_services run has +# populated IO_SRC_DIR with a flat set of io_.cpp files (plus +# class_map.cpp). Partitions that flat list into TRICK_IO_SOURCES (bundled +# into libtrick.a) and ER7_IO_SOURCES (bundled into liber7_utils.a), matching +# the historical split PrintAttributes::createIOFileName performs by +# inspecting each header's source path — a distinction lost once ICG is +# invoked with an explicit `-o` (see CMAKE_MIGRATION_PLAN.md Part C, Phase 2 +# risk R1/R7 write-up). Reconstructed here by checking whether a header with +# a matching basename exists anywhere under ER7_UTILS_SRC_DIR; basenames are +# verified unique across the whole tree (no collisions as of this writing). +# +# Required -D arguments: IO_SRC_DIR, ER7_UTILS_SRC_DIR, OUTPUT_FILE + +if(NOT IO_SRC_DIR OR NOT ER7_UTILS_SRC_DIR OR NOT OUTPUT_FILE) + message(FATAL_ERROR "GenerateIOSrcList.cmake requires -DIO_SRC_DIR=, -DER7_UTILS_SRC_DIR=, -DOUTPUT_FILE=") +endif() + +file(GLOB _tr_io_files "${IO_SRC_DIR}/io_*.cpp") +file(GLOB_RECURSE _tr_er7_headers "${ER7_UTILS_SRC_DIR}/*.hh" "${ER7_UTILS_SRC_DIR}/*.h") + +set(_tr_er7_basenames) +foreach(_hdr ${_tr_er7_headers}) + get_filename_component(_stem "${_hdr}" NAME_WE) + list(APPEND _tr_er7_basenames "${_stem}") +endforeach() + +set(_tr_trick_io_sources) +set(_tr_er7_io_sources) +foreach(_cpp ${_tr_io_files}) + get_filename_component(_name "${_cpp}" NAME_WE) + # NOT string(REGEX REPLACE "^io_" ...): CMake's regex replace strips + # repeated leading matches (e.g. turns "io_io_alloc" into "alloc" instead + # of "io_alloc"), silently misrouting io_io_alloc.cpp (from + # include/trick/io_alloc.h) into the er7_utils archive. Every io_*.cpp + # name has exactly one "io_" prefix by construction (PrintAttributes.cpp: + # base_name = "io_" + stem), so a plain substring removal is correct. + string(SUBSTRING "${_name}" 3 -1 _stem) + list(FIND _tr_er7_basenames "${_stem}" _tr_idx) + if(_tr_idx GREATER -1) + list(APPEND _tr_er7_io_sources "${_cpp}") + else() + list(APPEND _tr_trick_io_sources "${_cpp}") + endif() +endforeach() + +file(WRITE "${OUTPUT_FILE}" +"# Auto-generated by GenerateIOSrcList.cmake — do not edit. +set(TRICK_IO_SOURCES ${_tr_trick_io_sources} \"${IO_SRC_DIR}/class_map.cpp\") +set(ER7_IO_SOURCES ${_tr_er7_io_sources}) +") diff --git a/test/build_config/compare_archives.sh b/test/build_config/compare_archives.sh new file mode 100755 index 000000000..3025c4bad --- /dev/null +++ b/test/build_config/compare_archives.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# Archive-membership parity test (Part C, Phase 2, step 6 / R4 in +# CMAKE_MIGRATION_PLAN.md): builds Trick's core libraries with both `make` +# and CMake on the same machine and diffs `ar t` member-basename sets for +# every one of the nine archives listed in A3. This is a mandatory gate — +# archive membership drift breaks sim link lines subtly (TRICK_LIBS in +# Makefile.common:70), often without a build-time error. +# +# Usage: test/build_config/compare_archives.sh +# Requires: a working `./configure` toolchain, and everything Phase 1/2's +# CMake detection needs (same dependencies as ./configure). +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$REPO_ROOT" + +WORKDIR="$(mktemp -d)" +trap 'rm -rf "$WORKDIR"' EXIT + +# Archive name -> path relative to the make build's lib/ dir vs. the cmake +# build tree (paths differ because cmake keeps per-directory build trees; +# see each CMakeLists.txt's ARCHIVE_OUTPUT_NAME/output directory). +ARCHIVES=( + "libtrick.a:libtrick.a" + "liber7_utils.a:trick_source/er7_utils/liber7_utils.a" + "libtrick_mm.a:trick_source/sim_services/MemoryManager/libtrick_mm.a" + "libtrick_pyip.a:trick_source/trick_swig/libtrick_pyip.a" + "libtrick_comm.a:trick_source/trick_utils/comm/libtrick_comm.a" + "libtrick_connection_handlers.a:trick_source/trick_utils/connection_handlers/libtrick_connection_handlers.a" + "libtrick_math.a:trick_source/trick_utils/math/libtrick_math.a" + "libtrick_optimization.a:trick_source/trick_utils/optimization/libtrick_optimization.a" + "libtrick_units.a:trick_source/trick_utils/units/libtrick_units.a" + "libtrick_var_binary_parser.a:trick_source/trick_utils/var_binary_parser/libtrick_var_binary_parser.a" +) + +# ── 1. make build (skipped if lib/ already has a complete build — CI builds +# this once via the existing autotools job and this script re-uses it) ────── +if [ ! -f lib/libtrick.a ] && [ ! -f lib64/libtrick.a ]; then + ./configure >"$WORKDIR/configure.log" 2>&1 + make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" no_dp >"$WORKDIR/make.log" 2>&1 || { + echo "make failed; see $WORKDIR/make.log" >&2 + exit 1 + } +fi +MAKE_LIB_DIR="lib" +[ -d lib64 ] && MAKE_LIB_DIR="lib64" + +# ── 2. cmake build (three-step bootstrap — see cmake/TrickICG.cmake) ─────── +CMAKE_BUILD_DIR="$WORKDIR/cmake-build" +cmake -S . -B "$CMAKE_BUILD_DIR" >"$WORKDIR/cmake_configure1.log" 2>&1 +cmake --build "$CMAKE_BUILD_DIR" --target trick-ICG trick_io_src_gen -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" \ + >"$WORKDIR/cmake_build_icg.log" 2>&1 +cmake -S . -B "$CMAKE_BUILD_DIR" >"$WORKDIR/cmake_configure2.log" 2>&1 +cmake --build "$CMAKE_BUILD_DIR" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" >"$WORKDIR/cmake_build.log" 2>&1 || { + echo "cmake --build failed; see $WORKDIR/cmake_build.log" >&2 + exit 1 +} + +# ── 3. Compare `ar t` member basenames, normalizing away CMake's +# extension-in-object-name convention (Foo.cpp.o vs make's Foo.o) ────────── +normalize_ar() { + ar t "$1" | grep -v '^__\.SYMDEF' | sed -E 's/\.(cpp|cc|c)\.o$/.o/' | sort +} + +FAIL=0 +for pair in "${ARCHIVES[@]}"; do + make_name="${pair%%:*}" + cmake_rel="${pair##*:}" + make_path="$MAKE_LIB_DIR/$make_name" + cmake_path="$CMAKE_BUILD_DIR/$cmake_rel" + + if [ ! -f "$make_path" ]; then + echo "MISSING (make): $make_path" >&2 + FAIL=1 + continue + fi + if [ ! -f "$cmake_path" ]; then + echo "MISSING (cmake): $cmake_path" >&2 + FAIL=1 + continue + fi + + normalize_ar "$make_path" >"$WORKDIR/${make_name}.make.txt" + normalize_ar "$cmake_path" >"$WORKDIR/${make_name}.cmake.txt" + + if diff -u "$WORKDIR/${make_name}.make.txt" "$WORKDIR/${make_name}.cmake.txt" >"$WORKDIR/${make_name}.diff"; then + echo "$make_name: OK ($(wc -l <"$WORKDIR/${make_name}.make.txt") members)" + else + echo "$make_name: MISMATCH" >&2 + cat "$WORKDIR/${make_name}.diff" >&2 + FAIL=1 + fi +done + +if [ "$FAIL" -ne 0 ]; then + echo "Archive parity: FAILED" >&2 + exit 1 +fi +echo "Archive parity: OK (all ${#ARCHIVES[@]} archives match)" diff --git a/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt b/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt index ba656b129..ab25064f8 100644 --- a/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt +++ b/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt @@ -1,62 +1,104 @@ +# trick-ICG: real add_executable, ported from this directory's own Makefile +# (the authority per CMAKE_MIGRATION_PLAN.md Part E.3 — the old CMakeLists.txt +# here belonged to the stale 2019 CMake build and is not trusted). +# +# -DEXTERNAL_BUILD changes PrintAttributes' -sim_services behavior only (see +# PrintAttributes.cpp: printSieClass/printSieEnum/createMapFiles), routing the +# combined class/enum map + SIE resource file into -o's output directory +# instead of hardcoded $TRICK_HOME paths. It has no effect on ordinary +# (non -sim_services) per-sim ICG runs, so it is safe to always define — the +# installed trick-ICG behaves identically for user sims either way. -set ( ICG_SRC - ClassTemplateVisitor - ClassValues - ClassVisitor - CommentSaver - ConstructValues - EnumValues - EnumVisitor - FieldDescription - FieldVisitor - FindTrickICG - HeaderSearchDirs - ICGASTConsumer - PrintAttributes - PrintFileContents10 - PrintFileContentsBase - TranslationUnitVisitor - TypedefVisitor - Utilities - VariableVisitor - main - ../../sim_services/UdUnits/map_trick_units_to_udunits +file(GLOB ICG_SOURCES CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + +add_executable(trick-ICG + ${ICG_SOURCES} + ${CMAKE_SOURCE_DIR}/trick_source/sim_services/UdUnits/map_trick_units_to_udunits.cpp ) -set (ICG_CLANGLIBS - -lclangFrontend - -lclangDriver - -lclangSerialization - -lclangParse - -lclangSema - -lclangAnalysis - -lclangEdit - -lclangAST - -lclangLex - -lclangBasic - ) - -try_compile(haveOldLibs "${CMAKE_BINARY_DIR}" "${PROJECT_SOURCE_DIR}/CMakeTestFiles/TestICGLinkedLibs.cpp" CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${LLVM_INCLUDE_DIRS}") -if(NOT haveOldLibs) - set (ICG_CLANGLIBS - -lclang-cpp - ) +# C++ standard by clang major, mirroring this directory's Makefile:16-24. +if(_tr_llvm_version_major GREATER_EQUAL 16) + set_property(TARGET trick-ICG PROPERTY CXX_STANDARD 17) +elseif(_tr_llvm_version_major GREATER_EQUAL 10) + set_property(TARGET trick-ICG PROPERTY CXX_STANDARD 14) +else() + set_property(TARGET trick-ICG PROPERTY CXX_STANDARD 11) endif() +set_property(TARGET trick-ICG PROPERTY CXX_STANDARD_REQUIRED ON) -add_executable( trick-ICG ${ICG_SRC} ) -target_compile_options( trick-ICG PUBLIC -g -DTRICK_VERSION="${TRICK_MAJOR}.${TRICK_MINOR}.${TRICK_TINY}" -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS) -target_compile_options( trick-ICG PUBLIC -DEXTERNAL_BUILD) -target_compile_options( trick-ICG PUBLIC -DLLVM_HOME="${LLVM_ROOT_DIR}") -target_compile_options( trick-ICG PUBLIC -DLIBCLANG_MAJOR=${LLVM_VERSION_MAJOR} -DLIBCLANG_MINOR=${LLVM_VERSION_MINOR} -DLIBCLANG_PATCHLEVEL=${LLVM_VERSION_PATCH}) -target_include_directories( trick-ICG PUBLIC ${UDUNITS2_INCLUDES} ) -target_include_directories( trick-ICG PUBLIC ${LLVM_INCLUDE_DIRS} ) -set_property(SOURCE trick-ICG APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_BINARY_DIR}/include/mongoose/mongoose.h) -set_target_properties( trick-ICG PROPERTIES CXX_STANDARD 14) - -target_link_libraries( trick-ICG - ${ICG_CLANGLIBS} - ${LLVM_LDFLAGS} - ${LLVM_LIBRARIES} - ${UDUNITS2_LIBRARIES} +target_compile_options(trick-ICG PRIVATE -g -fno-rtti) +target_compile_definitions(trick-ICG PRIVATE + __STDC_CONSTANT_MACROS + __STDC_FORMAT_MACROS + __STDC_LIMIT_MACROS + EXTERNAL_BUILD + LIBCLANG_MAJOR=${_tr_llvm_version_major} ) +string(REGEX MATCH "^[0-9]+\\.([0-9]+)" _tr_llvm_minor_match "${LLVM_VERSION_STRING}") +if(CMAKE_MATCH_1) + target_compile_definitions(trick-ICG PRIVATE LIBCLANG_MINOR=${CMAKE_MATCH_1}) +endif() +string(REGEX MATCH "^[0-9]+\\.[0-9]+\\.([0-9]+)" _tr_llvm_patch_match "${LLVM_VERSION_STRING}") +if(CMAKE_MATCH_1) + target_compile_definitions(trick-ICG PRIVATE LIBCLANG_PATCHLEVEL=${CMAKE_MATCH_1}) +endif() + +target_include_directories(trick-ICG PRIVATE ${LLVM_INCLUDE_DIR}) +separate_arguments(_tr_icg_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(trick-ICG PRIVATE ${_tr_icg_udunits_includes}) +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_compile_definitions(trick-ICG PRIVATE TRICK_GCC_VERSION="${GCC_VERSION}") +endif() + +# Only these two files use these extra per-file defines/includes, matching +# the Makefile's targeted CXXFLAGS overrides (lines 74-76). +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/FieldDescription.cpp PROPERTIES + COMPILE_OPTIONS "-I${CMAKE_SOURCE_DIR}/include" +) +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/HeaderSearchDirs.cpp PROPERTIES + COMPILE_DEFINITIONS "LLVM_HOME=\"${LLVM_HOME}\"" +) +set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/main.cpp PROPERTIES + COMPILE_DEFINITIONS "TRICK_VERSION=\"${TRICK_MAJOR}.${TRICK_MINOR}.${TRICK_TINY}\"" +) + +separate_arguments(_tr_icg_clanglibs_list NATIVE_COMMAND "${ICG_CLANGLIBS}") +execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --libs + OUTPUT_VARIABLE _tr_icg_llvm_libs OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --system-libs + OUTPUT_VARIABLE _tr_icg_llvm_system_libs OUTPUT_STRIP_TRAILING_WHITESPACE) +execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags + OUTPUT_VARIABLE _tr_icg_llvm_ldflags OUTPUT_STRIP_TRAILING_WHITESPACE) +separate_arguments(_tr_icg_llvm_libs_list NATIVE_COMMAND "${_tr_icg_llvm_libs}") +separate_arguments(_tr_icg_llvm_ldflags_list NATIVE_COMMAND "${_tr_icg_llvm_ldflags}") + +separate_arguments(_tr_icg_udunits_ldflags_list NATIVE_COMMAND "${UDUNITS_LDFLAGS}") +target_link_libraries(trick-ICG PRIVATE ${_tr_icg_clanglibs_list} ${_tr_icg_llvm_ldflags_list} ${_tr_icg_llvm_libs_list} ${_tr_icg_udunits_ldflags_list}) +target_link_directories(trick-ICG PRIVATE ${LLVM_LIB_DIR}) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + if(NOT LLVM_VERSION_STRING VERSION_LESS "3.5") + # Fedora adds -ledit as a system lib, but it isn't installed, or required. + string(REPLACE "-ledit" "" _tr_icg_llvm_system_libs "${_tr_icg_llvm_system_libs}") + separate_arguments(_tr_icg_llvm_system_libs_list NATIVE_COMMAND "${_tr_icg_llvm_system_libs}") + target_link_libraries(trick-ICG PRIVATE ${_tr_icg_llvm_system_libs_list}) + endif() +elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + separate_arguments(_tr_icg_llvm_system_libs_list NATIVE_COMMAND "${_tr_icg_llvm_system_libs}") + target_link_libraries(trick-ICG PRIVATE ${_tr_icg_llvm_system_libs_list}) + if(_tr_llvm_version_major GREATER_EQUAL 16) + target_link_libraries(trick-ICG PRIVATE -lc++abi -lclang-cpp) + else() + target_link_libraries(trick-ICG PRIVATE -lc++abi) + endif() + # CMake's own automatic rpath handling already embeds an LC_RPATH for + # LLVM_LIB_DIR here, since we linked against libraries there via + # target_link_directories; the Makefile's explicit install_name_tool step + # is therefore usually redundant on this toolchain (but harmless to skip + # if it fails, e.g. because the rpath is already present). + add_custom_command(TARGET trick-ICG POST_BUILD + COMMAND bash -c "install_name_tool -add_rpath '${LLVM_LIB_DIR}' '$' 2>/dev/null; true" + VERBATIM + ) +endif() diff --git a/trick_source/er7_utils/CMakeLists.txt b/trick_source/er7_utils/CMakeLists.txt index b89cda4f4..42ecf446d 100644 --- a/trick_source/er7_utils/CMakeLists.txt +++ b/trick_source/er7_utils/CMakeLists.txt @@ -1,72 +1,79 @@ - +# liber7_utils.a. Source list cross-checked against a real `make` build's +# lib/liber7_utils.a member set (verified 2026-07-06) — matches exactly. +# io_src objects come from TrickICG.cmake's ER7_IO_SOURCES partition (see +# cmake/scripts/GenerateIOSrcList.cmake). set( ER7_UTILS_SRC - integration/abm4/src/abm4_first_order_ode_integrator - integration/abm4/src/abm4_integrator_constructor - integration/abm4/src/abm4_second_order_ode_integrator - integration/beeman/src/beeman_integrator_constructor - integration/beeman/src/beeman_second_order_ode_integrator - integration/core/src/base_integration_group - integration/core/src/bogus_integration_controls - integration/core/src/first_order_ode_integrator - integration/core/src/integration_controls - integration/core/src/integration_messages - integration/core/src/integrator_constructor - integration/core/src/integrator_constructor_factory - integration/core/src/integrator_result_merger - integration/core/src/integrator_result_merger_container - integration/core/src/left_quaternion_functions - integration/core/src/priming_first_order_ode_integrator - integration/core/src/priming_integration_controls - integration/core/src/priming_integrator_constructor - integration/core/src/priming_second_order_ode_integrator - integration/core/src/second_order_ode_integrator - integration/core/src/single_cycle_integration_controls - integration/core/src/standard_integration_controls - integration/euler/src/euler_first_order_ode_integrator - integration/euler/src/euler_integrator_constructor - integration/euler/src/euler_second_order_ode_integrator - integration/mm4/src/mm4_integrator_constructor - integration/mm4/src/mm4_second_order_ode_integrator - integration/nl2/src/nl2_integrator_constructor - integration/nl2/src/nl2_second_order_ode_integrator - integration/position_verlet/src/position_verlet_integrator_constructor - integration/position_verlet/src/position_verlet_second_order_ode_integrator - integration/rk2_heun/src/rk2_heun_first_order_ode_integrator - integration/rk2_heun/src/rk2_heun_integrator_constructor - integration/rk2_heun/src/rk2_heun_second_order_ode_integrator - integration/rk2_midpoint/src/rk2_midpoint_first_order_ode_integrator - integration/rk2_midpoint/src/rk2_midpoint_integrator_constructor - integration/rk2_midpoint/src/rk2_midpoint_second_order_ode_integrator - integration/rk4/src/rk4_first_order_ode_integrator - integration/rk4/src/rk4_integrator_constructor - integration/rk4/src/rk4_second_order_ode_integrator - integration/rk4/src/rk4_second_order_ode_integrator_base - integration/rkf45/src/rkf45_butcher_tableau - integration/rkf45/src/rkf45_first_order_ode_integrator - integration/rkf45/src/rkf45_integrator_constructor - integration/rkf45/src/rkf45_second_order_ode_integrator - integration/rkf78/src/rkf78_butcher_tableau - integration/rkf78/src/rkf78_first_order_ode_integrator - integration/rkf78/src/rkf78_integrator_constructor - integration/rkf78/src/rkf78_second_order_ode_integrator - integration/rkg4/src/rkg4_butcher_tableau - integration/rkg4/src/rkg4_first_order_ode_integrator - integration/rkg4/src/rkg4_integrator_constructor - integration/rkg4/src/rkg4_second_order_ode_integrator - integration/rkn4/src/rkn4_integrator_constructor - integration/rkn4/src/rkn4_second_order_ode_integrator - integration/symplectic_euler/src/symplectic_euler_integrator_constructor - integration/symplectic_euler/src/symplectic_euler_second_order_ode_integrator - integration/velocity_verlet/src/velocity_verlet_integrator_constructor - integration/velocity_verlet/src/velocity_verlet_second_order_ode_integrator - interface/src/alloc - interface/src/deletable - interface/src/message_handler - math/src/n_choose_m - math/src/ratio128 - math/src/uint128 - trick/integration/src/trick_integrator + integration/abm4/src/abm4_first_order_ode_integrator.cc + integration/abm4/src/abm4_integrator_constructor.cc + integration/abm4/src/abm4_second_order_ode_integrator.cc + integration/beeman/src/beeman_integrator_constructor.cc + integration/beeman/src/beeman_second_order_ode_integrator.cc + integration/core/src/base_integration_group.cc + integration/core/src/bogus_integration_controls.cc + integration/core/src/first_order_ode_integrator.cc + integration/core/src/integration_controls.cc + integration/core/src/integration_messages.cc + integration/core/src/integrator_constructor.cc + integration/core/src/integrator_constructor_factory.cc + integration/core/src/integrator_result_merger.cc + integration/core/src/integrator_result_merger_container.cc + integration/core/src/left_quaternion_functions.cc + integration/core/src/priming_first_order_ode_integrator.cc + integration/core/src/priming_integration_controls.cc + integration/core/src/priming_integrator_constructor.cc + integration/core/src/priming_second_order_ode_integrator.cc + integration/core/src/second_order_ode_integrator.cc + integration/core/src/single_cycle_integration_controls.cc + integration/core/src/standard_integration_controls.cc + integration/euler/src/euler_first_order_ode_integrator.cc + integration/euler/src/euler_integrator_constructor.cc + integration/euler/src/euler_second_order_ode_integrator.cc + integration/mm4/src/mm4_integrator_constructor.cc + integration/mm4/src/mm4_second_order_ode_integrator.cc + integration/nl2/src/nl2_integrator_constructor.cc + integration/nl2/src/nl2_second_order_ode_integrator.cc + integration/position_verlet/src/position_verlet_integrator_constructor.cc + integration/position_verlet/src/position_verlet_second_order_ode_integrator.cc + integration/rk2_heun/src/rk2_heun_first_order_ode_integrator.cc + integration/rk2_heun/src/rk2_heun_integrator_constructor.cc + integration/rk2_heun/src/rk2_heun_second_order_ode_integrator.cc + integration/rk2_midpoint/src/rk2_midpoint_first_order_ode_integrator.cc + integration/rk2_midpoint/src/rk2_midpoint_integrator_constructor.cc + integration/rk2_midpoint/src/rk2_midpoint_second_order_ode_integrator.cc + integration/rk4/src/rk4_first_order_ode_integrator.cc + integration/rk4/src/rk4_integrator_constructor.cc + integration/rk4/src/rk4_second_order_ode_integrator.cc + integration/rk4/src/rk4_second_order_ode_integrator_base.cc + integration/rkf45/src/rkf45_butcher_tableau.cc + integration/rkf45/src/rkf45_first_order_ode_integrator.cc + integration/rkf45/src/rkf45_integrator_constructor.cc + integration/rkf45/src/rkf45_second_order_ode_integrator.cc + integration/rkf78/src/rkf78_butcher_tableau.cc + integration/rkf78/src/rkf78_first_order_ode_integrator.cc + integration/rkf78/src/rkf78_integrator_constructor.cc + integration/rkf78/src/rkf78_second_order_ode_integrator.cc + integration/rkg4/src/rkg4_butcher_tableau.cc + integration/rkg4/src/rkg4_first_order_ode_integrator.cc + integration/rkg4/src/rkg4_integrator_constructor.cc + integration/rkg4/src/rkg4_second_order_ode_integrator.cc + integration/rkn4/src/rkn4_integrator_constructor.cc + integration/rkn4/src/rkn4_second_order_ode_integrator.cc + integration/symplectic_euler/src/symplectic_euler_integrator_constructor.cc + integration/symplectic_euler/src/symplectic_euler_second_order_ode_integrator.cc + integration/velocity_verlet/src/velocity_verlet_integrator_constructor.cc + integration/velocity_verlet/src/velocity_verlet_second_order_ode_integrator.cc + interface/src/alloc.cc + interface/src/deletable.cc + interface/src/message_handler.cc + math/src/n_choose_m.cc + math/src/ratio128.cc + math/src/uint128.cc + trick/integration/src/trick_integrator.cc ) -add_library( er7_utils_objs OBJECT ${ER7_UTILS_SRC} ) +add_library( er7_utils STATIC ${ER7_UTILS_SRC} ${ER7_IO_SOURCES} ) +target_link_libraries( er7_utils PUBLIC trick_build_flags ) +target_compile_options( er7_utils PRIVATE -Wno-unused-parameter ) +set_target_properties( er7_utils PROPERTIES ARCHIVE_OUTPUT_NAME er7_utils) +add_dependencies( er7_utils trick_io_src_gen ) diff --git a/trick_source/sim_services/CMakeLists.txt b/trick_source/sim_services/CMakeLists.txt index ff7eed9e3..0caafb8e1 100644 --- a/trick_source/sim_services/CMakeLists.txt +++ b/trick_source/sim_services/CMakeLists.txt @@ -1,246 +1,274 @@ - -# Sim services C/C++ files +# Sim services C/C++ sources for libtrick.a (top Makefile's SIM_SERV_DIRS +# minus MemoryManager (own archive, liber7_utils split) and InputProcessor +# (goes into libtrick_pyip.a instead) — see A3. List derived by cross +# referencing every non-io_/class_map member of a real `make` build's +# lib/libtrick.a against the source tree (verified 2026-07-06); do not trust +# the 2019 stale CMakeLists.txt list, it predates several renames/additions +# (VariableServerSessionThread -> VariableServerSession, MonteCarloGeneration, +# JSONVariableServerThread, MessageCustomManager, ThreadBase/SysThread). set( SS_SRC - CheckPointAgent/CheckPointAgent - CheckPointAgent/ChkPtParseContext - CheckPointAgent/ClassicCheckPointAgent - CheckPointAgent/PythonPrint - CheckPointRestart/CheckPointRestart - CheckPointRestart/CheckPointRestart_c_intf - CheckPointRestart/next_attr_name - CheckPointRestart/stl_type_name_convert - Clock/BC635Clock - Clock/Clock - Clock/GetTimeOfDayClock - Clock/TPROCTEClock - Clock/clock_c_intf - Collect/collect - CommandLineArguments/CommandLineArguments - CommandLineArguments/command_line_c_intf - DataRecord/DRAscii - DataRecord/DRBinary - DataRecord/DRHDF5 - DataRecord/DataRecordDispatcher - DataRecord/DataRecordGroup - DataRecord/data_record_utilities - DebugPause/DebugPause - DebugPause/DebugPause_c_intf - EchoJobs/EchoJobs - EchoJobs/EchoJobs_c_intf - Environment/Environment - Environment/Environment_c_intf - EventManager/EventInstrument - EventManager/EventManager - EventManager/EventManager_c_intf - EventManager/EventProcessor - Executive/Executive - Executive/ExecutiveException - Executive/Executive_add_depends_on_job - Executive/Executive_add_jobs_to_queue - Executive/Executive_add_scheduled_job_class - Executive/Executive_add_sim_object - Executive/Executive_advance_sim_time - Executive/Executive_c_intf - Executive/Executive_call_default_data - Executive/Executive_call_initialization - Executive/Executive_call_input_processor - Executive/Executive_check_all_job_cycle_times - Executive/Executive_check_all_jobs_handled - Executive/Executive_checkpoint - Executive/Executive_clear_scheduled_queues - Executive/Executive_create_threads - Executive/Executive_fpe_handler - Executive/Executive_freeze - Executive/Executive_freeze_loop - Executive/Executive_get_curr_job - Executive/Executive_get_job - Executive/Executive_get_job_cycle - Executive/Executive_get_process_id - Executive/Executive_get_sim_time - Executive/Executive_init - Executive/Executive_init_freeze_scheduled - Executive/Executive_init_signal_handlers - Executive/Executive_instrument_job - Executive/Executive_isThreadReadyToRun - Executive/Executive_loop - Executive/Executive_loop_multi_thread - Executive/Executive_loop_single_thread - Executive/Executive_post_checkpoint - Executive/Executive_process_sim_args - Executive/Executive_register_scheduler - Executive/Executive_remove_jobs - Executive/Executive_remove_sim_object - Executive/Executive_restart - Executive/Executive_run - Executive/Executive_scheduled_thread_sync - Executive/Executive_set_job_cycle - Executive/Executive_set_job_onoff - Executive/Executive_set_simobject_onoff - Executive/Executive_set_thread_amf_cycle_time - Executive/Executive_set_thread_async_wait - Executive/Executive_set_thread_cpu_affinity - Executive/Executive_set_thread_enabled - Executive/Executive_set_thread_priority - Executive/Executive_set_thread_process_type - Executive/Executive_set_thread_rt_semaphore - Executive/Executive_set_time_tic_value - Executive/Executive_shutdown - Executive/Executive_signal_handler - Executive/Executive_stop - Executive/Executive_terminate - Executive/Executive_thread_sync - Executive/Executive_write_s_job_execution - Executive/Executive_write_s_run_summary - Executive/ThreadTrigger - Executive/Threads - Executive/Threads_child - Executive/Threads_set_amf_cycle_tics - Executive/Threads_set_async_wait - Executive/Threads_set_process_type - Executive/child_handler - Executive/fpe_handler - Executive/sig_hand - ExternalApplications/ExternalApplication - ExternalApplications/ExternalApplicationManager - ExternalApplications/ExternalApplication_c_intf - ExternalApplications/MalfunctionsTrickView - ExternalApplications/MonteMonitor - ExternalApplications/SimControlPanel - ExternalApplications/StripChart - ExternalApplications/TrickView - FrameLog/FrameDataRecordGroup - FrameLog/FrameLog - FrameLog/FrameLog_c_intf - Integrator/src/IntegLoopManager - Integrator/src/IntegLoopScheduler - Integrator/src/IntegLoopSimObject - Integrator/src/MultiDtIntegLoopScheduler - Integrator/src/MultiDtIntegLoopSimObject - Integrator/src/Integrator - Integrator/src/Integrator_C_Intf - Integrator/src/getIntegrator - Integrator/src/regula_falsi - Integrator/src/reset_regula_falsi - JITInputFile/JITEvent - JITInputFile/JITInputFile - JITInputFile/jit_input_file_c_intf - JSONVariableServer/JSONVariableServer - JSONVariableServer/JSONVariableServerSessionThread - MasterSlave/MSSharedMem - MasterSlave/MSSocket - MasterSlave/Master - MasterSlave/Slave - Message/MessageCout - Message/MessageFile - Message/MessageHSFile - Message/MessageCustomFile - Message/MessageLCout - Message/MessagePublisher - Message/MessageSubscriber - Message/MessageTCDevice - Message/MessageThreadedCout - Message/Message_c_intf - Message/PlaybackFile - Message/message_publish_standalone - MonteCarlo/MonteCarlo - MonteCarlo/MonteCarlo_c_intf - MonteCarlo/MonteCarlo_dispatch_run_to_slave - MonteCarlo/MonteCarlo_dryrun - MonteCarlo/MonteCarlo_execute_monte - MonteCarlo/MonteCarlo_funcs - MonteCarlo/MonteCarlo_initialize_sockets - MonteCarlo/MonteCarlo_master - MonteCarlo/MonteCarlo_master_file_io - MonteCarlo/MonteCarlo_master_init - MonteCarlo/MonteCarlo_master_shutdown - MonteCarlo/MonteCarlo_receive_results - MonteCarlo/MonteCarlo_run_queue - MonteCarlo/MonteCarlo_slave - MonteCarlo/MonteCarlo_slave_funcs - MonteCarlo/MonteCarlo_slave_init - MonteCarlo/MonteCarlo_slave_process_run - MonteCarlo/MonteCarlo_spawn_slaves - MonteCarlo/MonteVarCalculated - MonteCarlo/MonteVarFile - MonteCarlo/MonteVarFixed - MonteCarlo/MonteVarRandom - MonteCarlo/StlRandomGenerator - RealtimeInjector/RtiEvent - RealtimeInjector/RtiExec - RealtimeInjector/RtiList - RealtimeInjector/RtiStager - RealtimeSync/RealtimeSync - RealtimeSync/RealtimeSync_c_intf - ScheduledJobQueue/ScheduledJobQueue - ScheduledJobQueue/ScheduledJobQueueInstrument - Scheduler/Scheduler - Sie/AttributesMap - Sie/EnumAttributesMap - Sie/Sie - Sie/sie_c_intf - SimObject/JobData - SimObject/SimObject - SimTime/SimTime - SimTime/SimTime_c_intf - ThreadBase/ThreadBase - Timer/ITimer - Timer/Timer - Timer/it_handler - UdUnits/UdUnits - UdUnits/map_trick_units_to_udunits - UnitTest/UnitTest - UnitTest/UnitTest_c_intf - UnitsMap/UnitsMap - VariableServer/VariableReference - VariableServer/VariableServer - VariableServer/VariableServerListenThread - VariableServer/VariableServerSessionThread - VariableServer/VariableServerSessionThread_commands - VariableServer/VariableServerSessionThread_connect - VariableServer/VariableServerSessionThread_copy_data - VariableServer/VariableServerSessionThread_copy_sim_data - VariableServer/VariableServerSessionThread_create_socket - VariableServer/VariableServerSessionThread_freeze_init - VariableServer/VariableServerSessionThread_loop - VariableServer/VariableServerSessionThread_restart - VariableServer/VariableServerSessionThread_write_data - VariableServer/VariableServerSessionThread_write_stdio - VariableServer/VariableServer_copy_and_write_freeze - VariableServer/VariableServer_copy_and_write_freeze_scheduled - VariableServer/VariableServer_copy_and_write_scheduled - VariableServer/VariableServer_copy_and_write_top - VariableServer/VariableServer_default_data - VariableServer/VariableServer_freeze_init - VariableServer/VariableServer_get_next_freeze_call_time - VariableServer/VariableServer_get_next_sync_call_time - VariableServer/VariableServer_open_additional_servers - VariableServer/VariableServer_init - VariableServer/VariableServer_restart - VariableServer/VariableServer_shutdown - VariableServer/exit_var_thread - VariableServer/var_server_ext - Zeroconf/Zeroconf - mains/master + CheckPointAgent/CheckPointAgent.cpp + CheckPointAgent/ChkPtParseContext.cpp + CheckPointAgent/ClassicCheckPointAgent.cpp + CheckPointAgent/PythonPrint.cpp + CheckPointRestart/CheckPointRestart.cpp + CheckPointRestart/CheckPointRestart_c_intf.cpp + CheckPointRestart/next_attr_name.cpp + CheckPointRestart/stl_type_name_convert.cpp + Clock/Clock.cpp + Clock/GetTimeOfDayClock.cpp + Clock/clock_c_intf.cpp + Collect/collect.cpp + CommandLineArguments/CommandLineArguments.cpp + CommandLineArguments/command_line_c_intf.cpp + DataRecord/DRAscii.cpp + DataRecord/DRBinary.cpp + DataRecord/DRHDF5.cpp + DataRecord/DataRecordDispatcher.cpp + DataRecord/DataRecordGroup.cpp + DataRecord/data_record_utilities.cpp + DebugPause/DebugPause.cpp + DebugPause/DebugPause_c_intf.cpp + EchoJobs/EchoJobs.cpp + EchoJobs/EchoJobs_c_intf.cpp + Environment/Environment.cpp + Environment/Environment_c_intf.cpp + EventManager/EventInstrument.cpp + EventManager/EventManager.cpp + EventManager/EventManager_c_intf.cpp + EventManager/EventProcessor.cpp + Executive/Executive.cpp + Executive/ExecutiveException.cpp + Executive/Executive_add_depends_on_job.cpp + Executive/Executive_add_jobs_to_queue.cpp + Executive/Executive_add_scheduled_job_class.cpp + Executive/Executive_add_sim_object.cpp + Executive/Executive_advance_sim_time.cpp + Executive/Executive_c_intf.cpp + Executive/Executive_call_default_data.cpp + Executive/Executive_call_initialization.cpp + Executive/Executive_call_input_processor.cpp + Executive/Executive_check_all_job_cycle_times.cpp + Executive/Executive_check_all_jobs_handled.cpp + Executive/Executive_checkpoint.cpp + Executive/Executive_clear_scheduled_queues.cpp + Executive/Executive_create_threads.cpp + Executive/Executive_fpe_handler.cpp + Executive/Executive_freeze.cpp + Executive/Executive_freeze_loop.cpp + Executive/Executive_get_curr_job.cpp + Executive/Executive_get_job.cpp + Executive/Executive_get_job_cycle.cpp + Executive/Executive_get_process_id.cpp + Executive/Executive_get_sim_time.cpp + Executive/Executive_init.cpp + Executive/Executive_init_freeze_scheduled.cpp + Executive/Executive_init_signal_handlers.cpp + Executive/Executive_instrument_job.cpp + Executive/Executive_isThreadReadyToRun.cpp + Executive/Executive_loop.cpp + Executive/Executive_loop_multi_thread.cpp + Executive/Executive_loop_single_thread.cpp + Executive/Executive_post_checkpoint.cpp + Executive/Executive_process_sim_args.cpp + Executive/Executive_register_scheduler.cpp + Executive/Executive_remove_jobs.cpp + Executive/Executive_remove_sim_object.cpp + Executive/Executive_restart.cpp + Executive/Executive_run.cpp + Executive/Executive_scheduled_thread_sync.cpp + Executive/Executive_set_job_cycle.cpp + Executive/Executive_set_job_onoff.cpp + Executive/Executive_set_simobject_onoff.cpp + Executive/Executive_set_thread_amf_cycle_time.cpp + Executive/Executive_set_thread_async_wait.cpp + Executive/Executive_set_thread_cpu_affinity.cpp + Executive/Executive_set_thread_enabled.cpp + Executive/Executive_set_thread_priority.cpp + Executive/Executive_set_thread_process_type.cpp + Executive/Executive_set_thread_rt_semaphore.cpp + Executive/Executive_set_time_tic_value.cpp + Executive/Executive_shutdown.cpp + Executive/Executive_signal_handler.cpp + Executive/Executive_stop.cpp + Executive/Executive_terminate.cpp + Executive/Executive_thread_sync.cpp + Executive/Executive_write_s_job_execution.cpp + Executive/Executive_write_s_run_summary.cpp + Executive/ThreadTrigger.cpp + Executive/Threads.cpp + Executive/Threads_child.cpp + Executive/Threads_set_amf_cycle_tics.cpp + Executive/Threads_set_async_wait.cpp + Executive/Threads_set_process_type.cpp + Executive/child_handler.cpp + Executive/fpe_handler.cpp + Executive/sig_hand.cpp + ExternalApplications/ExternalApplication.cpp + ExternalApplications/ExternalApplicationManager.cpp + ExternalApplications/ExternalApplication_c_intf.cpp + ExternalApplications/MalfunctionsTrickView.cpp + ExternalApplications/MonteMonitor.cpp + ExternalApplications/SimControlPanel.cpp + ExternalApplications/StripChart.cpp + ExternalApplications/TrickView.cpp + FrameLog/FrameDataRecordGroup.cpp + FrameLog/FrameLog.cpp + FrameLog/FrameLog_c_intf.cpp + Integrator/src/IntegLoopManager.cpp + Integrator/src/IntegLoopScheduler.cpp + Integrator/src/IntegLoopSimObject.cpp + Integrator/src/Integrator.cpp + Integrator/src/Integrator_C_Intf.cpp + Integrator/src/MultiDtIntegLoopScheduler.cpp + Integrator/src/MultiDtIntegLoopSimObject.cpp + Integrator/src/getIntegrator.cpp + Integrator/src/regula_falsi.c + Integrator/src/reset_regula_falsi.c + JITInputFile/JITEvent.cpp + JITInputFile/JITInputFile.cpp + JITInputFile/jit_input_file_c_intf.cpp + JSONVariableServer/JSONVariableServer.cpp + JSONVariableServer/JSONVariableServerThread.cpp + MasterSlave/MSSharedMem.cpp + MasterSlave/MSSocket.cpp + MasterSlave/Master.cpp + MasterSlave/Slave.cpp + Message/MessageCout.cpp + Message/MessageCustomFile.cpp + Message/MessageCustomManager.cpp + Message/MessageFile.cpp + Message/MessageHSFile.cpp + Message/MessageLCout.cpp + Message/MessagePublisher.cpp + Message/MessageSubscriber.cpp + Message/MessageTCDevice.cpp + Message/MessageThreadedCout.cpp + Message/Message_c_intf.cpp + Message/PlaybackFile.cpp + Message/message_publish_standalone.cpp + MonteCarlo/MonteCarlo.cpp + MonteCarlo/MonteCarlo_c_intf.cpp + MonteCarlo/MonteCarlo_dispatch_run_to_slave.cpp + MonteCarlo/MonteCarlo_dryrun.cpp + MonteCarlo/MonteCarlo_execute_monte.cpp + MonteCarlo/MonteCarlo_funcs.cpp + MonteCarlo/MonteCarlo_initialize_sockets.cpp + MonteCarlo/MonteCarlo_master.cpp + MonteCarlo/MonteCarlo_master_file_io.cpp + MonteCarlo/MonteCarlo_master_init.cpp + MonteCarlo/MonteCarlo_master_shutdown.cpp + MonteCarlo/MonteCarlo_receive_results.cpp + MonteCarlo/MonteCarlo_run_queue.cpp + MonteCarlo/MonteCarlo_slave.cpp + MonteCarlo/MonteCarlo_slave_funcs.cpp + MonteCarlo/MonteCarlo_slave_init.cpp + MonteCarlo/MonteCarlo_slave_process_run.cpp + MonteCarlo/MonteCarlo_spawn_slaves.cpp + MonteCarlo/MonteVarCalculated.cpp + MonteCarlo/MonteVarFile.cpp + MonteCarlo/MonteVarFixed.cpp + MonteCarlo/MonteVarRandom.cpp + MonteCarlo/StlRandomGenerator.cpp + MonteCarloGeneration/mc_master.cc + MonteCarloGeneration/mc_variable.cc + MonteCarloGeneration/mc_variable_file.cc + MonteCarloGeneration/mc_variable_random_normal.cc + MonteCarloGeneration/mc_variable_random_string.cc + MonteCarloGeneration/mc_variable_random_uniform.cc + RealtimeInjector/RtiEvent.cpp + RealtimeInjector/RtiExec.cpp + RealtimeInjector/RtiList.cpp + RealtimeInjector/RtiStager.cpp + RealtimeSync/RealtimeSync.cpp + RealtimeSync/RealtimeSync_c_intf.cpp + ScheduledJobQueue/ScheduledJobQueue.cpp + ScheduledJobQueue/ScheduledJobQueueInstrument.cpp + Scheduler/Scheduler.cpp + Sie/AttributesMap.cpp + Sie/EnumAttributesMap.cpp + Sie/Sie.cpp + Sie/sie_c_intf.cpp + SimObject/JobData.cpp + SimObject/SimObject.cpp + SimTime/SimTime.cpp + SimTime/SimTime_c_intf.cpp + ThreadBase/SysThread.cpp + ThreadBase/ThreadBase.cpp + Timer/ITimer.cpp + Timer/Timer.cpp + Timer/it_handler.cpp + UdUnits/UdUnits.cpp + UdUnits/map_trick_units_to_udunits.cpp + UnitTest/UnitTest.cpp + UnitTest/UnitTest_c_intf.cpp + UnitsMap/UnitsMap.cpp + VariableServer/VariableReference.cpp + VariableServer/VariableServer.cpp + VariableServer/VariableServerListenThread.cpp + VariableServer/VariableServerSession.cpp + VariableServer/VariableServerSessionThread.cpp + VariableServer/VariableServerSessionThread_loop.cpp + VariableServer/VariableServerSession_commands.cpp + VariableServer/VariableServerSession_copy_and_write_modes.cpp + VariableServer/VariableServerSession_copy_sim_data.cpp + VariableServer/VariableServerSession_freeze_init.cpp + VariableServer/VariableServerSession_write_data.cpp + VariableServer/VariableServerSession_write_stdio.cpp + VariableServer/VariableServer_copy_and_write_freeze.cpp + VariableServer/VariableServer_copy_and_write_freeze_scheduled.cpp + VariableServer/VariableServer_copy_and_write_scheduled.cpp + VariableServer/VariableServer_copy_and_write_top.cpp + VariableServer/VariableServer_default_data.cpp + VariableServer/VariableServer_freeze_init.cpp + VariableServer/VariableServer_get_next_freeze_call_time.cpp + VariableServer/VariableServer_get_next_sync_call_time.cpp + VariableServer/VariableServer_init.cpp + VariableServer/VariableServer_open_additional_servers.cpp + VariableServer/VariableServer_restart.cpp + VariableServer/VariableServer_shutdown.cpp + VariableServer/exit_var_thread.cpp + VariableServer/var_server_ext.cpp + Zeroconf/Zeroconf.cpp + mains/master.cpp ) -# Sim services Lex/Yacc files -set( SS_LEX_YACC_SRC - ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.lex - ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.tab +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) +flex_target(trick_ss_input_lexer CheckPointAgent/input_parser.l ${CMAKE_CURRENT_BINARY_DIR}/input_parser.lex.cpp) +bison_target(trick_ss_input_parser CheckPointAgent/input_parser.y ${CMAKE_CURRENT_BINARY_DIR}/input_parser.tab.cpp) +set_source_files_properties(${FLEX_trick_ss_input_lexer_OUTPUTS} PROPERTIES + COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-unused-function;-Wno-sign-compare;-x;c++" ) - -add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.lex.cpp - COMMAND ${FLEX_EXECUTABLE} -d -o ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.lex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CheckPointAgent/input_parser.l - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/CheckPointAgent/input_parser.l +set_source_files_properties(${BISON_trick_ss_input_parser_OUTPUTS} PROPERTIES + COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-unused;-x;c++" ) -add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.tab.cpp ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.tab.hpp - COMMAND ${BISON_EXECUTABLE} -d -o ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/input_parser.tab.cpp ${CMAKE_CURRENT_SOURCE_DIR}/CheckPointAgent/input_parser.y - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/CheckPointAgent/input_parser.y +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set_property(SOURCE ${BISON_trick_ss_input_parser_OUTPUTS} APPEND PROPERTY + COMPILE_OPTIONS "-Wno-parentheses-equality") +endif() + +add_library( sim_services_objs OBJECT + ${SS_SRC} + ${FLEX_trick_ss_input_lexer_OUTPUTS} + ${BISON_trick_ss_input_parser_OUTPUTS} ) +target_link_libraries( sim_services_objs PUBLIC trick_build_flags ) -add_library( sim_services_objs OBJECT ${SS_SRC} ${SS_LEX_YACC_SRC}) -target_include_directories( sim_services_objs PUBLIC ${PYTHON_INCLUDE_DIRS} ) -target_include_directories( sim_services_objs PUBLIC ${UDUNITS2_INCLUDES} ) +# HDF5 support in DataRecord/DRHDF5.cpp (Makefile: DataRecord/Makefile:6-9). +if(HDF5_HOME) + set_property(SOURCE DataRecord/DRHDF5.cpp APPEND PROPERTY COMPILE_DEFINITIONS HDF5) + if(NOT HDF5_HOME STREQUAL "/usr") + set_property(SOURCE DataRecord/DRHDF5.cpp APPEND PROPERTY COMPILE_OPTIONS "-I${HDF5_HOME}/include") + endif() +endif() +# GSL support in MonteCarlo (MonteCarlo/Makefile:6-11). +if(GSL_HOME) + set_property(SOURCE MonteCarlo/MonteCarlo.cpp APPEND PROPERTY COMPILE_DEFINITIONS _HAVE_GSL) + if(NOT GSL_HOME STREQUAL "/usr") + set_property(SOURCE MonteCarlo/MonteCarlo.cpp APPEND PROPERTY COMPILE_OPTIONS "-I${GSL_HOME}/include") + endif() +endif() add_subdirectory(MemoryManager) add_subdirectory(InputProcessor) diff --git a/trick_source/sim_services/InputProcessor/CMakeLists.txt b/trick_source/sim_services/InputProcessor/CMakeLists.txt index f4809b66c..ab6e5c146 100644 --- a/trick_source/sim_services/InputProcessor/CMakeLists.txt +++ b/trick_source/sim_services/InputProcessor/CMakeLists.txt @@ -1,13 +1,12 @@ - set( INPUT_PROCESSOR_SRC - IPPython - IPPythonEvent - InputProcessor - MTV - MTV_c_intf - input_processor_ext + IPPython.cpp + IPPythonEvent.cpp + InputProcessor.cpp + MTV.cpp + MTV_c_intf.cpp + input_processor_ext.cpp ) add_library( input_processor_objs OBJECT ${INPUT_PROCESSOR_SRC}) -target_include_directories( input_processor_objs PUBLIC ${PYTHON_INCLUDE_DIRS} ) - +target_link_libraries( input_processor_objs PUBLIC trick_build_flags ) +target_include_directories( input_processor_objs PRIVATE ${Python3_INCLUDE_DIRS} ) diff --git a/trick_source/sim_services/MemoryManager/CMakeLists.txt b/trick_source/sim_services/MemoryManager/CMakeLists.txt index 19903de0a..36b1c143a 100644 --- a/trick_source/sim_services/MemoryManager/CMakeLists.txt +++ b/trick_source/sim_services/MemoryManager/CMakeLists.txt @@ -1,81 +1,86 @@ set( TRICK_MM_SRC - ADefParseContext - MemoryManager - MemoryManager_C_Intf - MemoryManager_JSON_Intf - MemoryManager_add_attr_info - MemoryManager_add_checkpoint_alloc_dependency - MemoryManager_add_shared_library_symbols - MemoryManager_add_template_name_trans - MemoryManager_add_var - MemoryManager_alloc_depends - MemoryManager_alloc_info_map - MemoryManager_clear_memory - MemoryManager_declare_var - MemoryManager_delete_var - MemoryManager_get_enumerated - MemoryManager_get_size - MemoryManager_get_stl_dependencies - MemoryManager_get_type_attributes - MemoryManager_io_src_intf - MemoryManager_is_alloced - MemoryManager_make_declaration - MemoryManager_make_reference_attr - MemoryManager_map_external_object - MemoryManager_realloc - MemoryManager_ref_allocate - MemoryManager_ref_assignment - MemoryManager_ref_attributes - MemoryManager_ref_dim - MemoryManager_ref_name - MemoryManager_ref_name_from_address - MemoryManager_ref_var - MemoryManager_restore - MemoryManager_restore_stls - MemoryManager_set_checkpointagent - MemoryManager_set_debug_level - MemoryManager_strdup - MemoryManager_write_checkpoint - MemoryManager_write_var - RefParseContext - addr_bitfield - extract_bitfield - extract_unsigned_bitfield - follow_address_path - insert_bitfield - parameter_types - ref_free - ref_to_value - trickTypeCharString - vval - wcs_ext + ADefParseContext.cpp + AttributesUtils.cpp + MemoryManager.cpp + MemoryManager_C_Intf.cpp + MemoryManager_JSON_Intf.cpp + MemoryManager_add_attr_info.cpp + MemoryManager_add_checkpoint_alloc_dependency.cpp + MemoryManager_add_shared_library_symbols.cpp + MemoryManager_add_template_name_trans.cpp + MemoryManager_add_var.cpp + MemoryManager_alloc_depends.cpp + MemoryManager_alloc_info_map.cpp + MemoryManager_clear_memory.cpp + MemoryManager_declare_var.cpp + MemoryManager_delete_var.cpp + MemoryManager_get_attributes_for_address.cpp + MemoryManager_get_enumerated.cpp + MemoryManager_get_size.cpp + MemoryManager_get_stl_dependencies.cpp + MemoryManager_get_type_attributes.cpp + MemoryManager_io_src_intf.cpp + MemoryManager_is_alloced.cpp + MemoryManager_make_declaration.cpp + MemoryManager_make_reference_attr.cpp + MemoryManager_map_external_object.cpp + MemoryManager_realloc.cpp + MemoryManager_ref_allocate.cpp + MemoryManager_ref_assignment.cpp + MemoryManager_ref_attributes.cpp + MemoryManager_ref_dim.cpp + MemoryManager_ref_name.cpp + MemoryManager_ref_name_from_address.cpp + MemoryManager_ref_var.cpp + MemoryManager_restore.cpp + MemoryManager_restore_stls.cpp + MemoryManager_set_checkpointagent.cpp + MemoryManager_set_debug_level.cpp + MemoryManager_strdup.cpp + MemoryManager_write_checkpoint.cpp + MemoryManager_write_var.cpp + RefParseContext.cpp + addr_bitfield.c + extract_bitfield.c + extract_unsigned_bitfield.c + follow_address_path.c + insert_bitfield.c + ref_free.cpp + ref_to_value.c + trickTypeCharString.c + vval.c + wcs_ext.c ) -# Sim services Lex/Yacc files -set( MM_LEX_YACC_SRC - ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.lex - ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.tab - ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.lex - ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.tab -) +find_package(FLEX REQUIRED) +find_package(BISON REQUIRED) -add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.lex.cpp - COMMAND ${FLEX_EXECUTABLE} -d -o ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.lex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/adef_parser.l - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/adef_parser.l -) -add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.tab.cpp ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.tab.hpp - COMMAND ${BISON_EXECUTABLE} -d -o ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/adef_parser.tab.cpp ${CMAKE_CURRENT_SOURCE_DIR}/adef_parser.y - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/adef_parser.y +flex_target(trick_mm_adef_lexer adef_parser.l ${CMAKE_CURRENT_BINARY_DIR}/adef_parser.lex.cpp) +bison_target(trick_mm_adef_parser adef_parser.y ${CMAKE_CURRENT_BINARY_DIR}/adef_parser.tab.cpp) +flex_target(trick_mm_ref_lexer ref_parser.l ${CMAKE_CURRENT_BINARY_DIR}/ref_parser.lex.cpp) +bison_target(trick_mm_ref_parser ref_parser.y ${CMAKE_CURRENT_BINARY_DIR}/ref_parser.tab.cpp) + +add_library( trick_mm STATIC + ${TRICK_MM_SRC} + ${FLEX_trick_mm_adef_lexer_OUTPUTS} + ${BISON_trick_mm_adef_parser_OUTPUTS} + ${FLEX_trick_mm_ref_lexer_OUTPUTS} + ${BISON_trick_mm_ref_parser_OUTPUTS} ) -add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.lex.cpp - COMMAND ${FLEX_EXECUTABLE} -d -o ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.lex.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ref_parser.l - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/ref_parser.l +target_link_libraries( trick_mm PUBLIC trick_build_flags ) +set_target_properties( trick_mm PROPERTIES ARCHIVE_OUTPUT_NAME trick_mm) + +# Match the Makefile's per-generator-output warning suppressions +# (CheckPointAgent/Makefile applies the same pattern; see A3). +set_source_files_properties( + ${FLEX_trick_mm_adef_lexer_OUTPUTS} ${FLEX_trick_mm_ref_lexer_OUTPUTS} + PROPERTIES COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-unused-function;-Wno-sign-compare;-x;c++" ) -add_custom_command( OUTPUT ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.tab.cpp ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.tab.hpp - COMMAND ${BISON_EXECUTABLE} -d -o ${CMAKE_BINARY_DIR}/temp_src/lex_yacc/ref_parser.tab.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ref_parser.y - MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/ref_parser.y +set_source_files_properties( + ${BISON_trick_mm_adef_parser_OUTPUTS} ${BISON_trick_mm_ref_parser_OUTPUTS} + PROPERTIES COMPILE_OPTIONS "-Wno-unused-parameter;-Wno-unused;-x;c++" ) - -add_library( trick_mm STATIC ${TRICK_MM_SRC} ${MM_LEX_YACC_SRC}) -target_include_directories( trick_mm PUBLIC ${UDUNITS2_INCLUDES} ) - +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + set_property(SOURCE ${BISON_trick_mm_adef_parser_OUTPUTS} ${BISON_trick_mm_ref_parser_OUTPUTS} + APPEND PROPERTY COMPILE_OPTIONS "-Wno-parentheses-equality") +endif() diff --git a/trick_source/trick_swig/CMakeLists.txt b/trick_source/trick_swig/CMakeLists.txt new file mode 100644 index 000000000..55a2c0daf --- /dev/null +++ b/trick_source/trick_swig/CMakeLists.txt @@ -0,0 +1,78 @@ +# libtrick_pyip.a: SWIG-generated Trick/Python interface code, ported from +# this directory's Makefile (the authority — see A3/A4). + +find_package(SWIG 3.0 REQUIRED) + +# D3: CMake writes nothing into the source tree — .py wrappers land in the +# build tree; Phase 3's `stage` install target is what assembles a runnable +# TRICK_HOME (including share/trick/swig/*.py) for sim-flow validation. +set(TRICK_SWIG_OUTDIR ${CMAKE_BINARY_DIR}/share/trick/swig) +file(MAKE_DIRECTORY ${TRICK_SWIG_OUTDIR}) + +set(_tr_swig_defs "") +if(HDF5_HOME) + list(APPEND _tr_swig_defs -DHDF5) +endif() +if(GSL_HOME) + list(APPEND _tr_swig_defs -D_HAVE_GSL) +endif() +if(TRICK_USE_ER7_UTILS) + list(APPEND _tr_swig_defs -DUSE_ER7_UTILS_INTEGRATORS) + if(EXISTS ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/CheckpointHelper) + list(APPEND _tr_swig_defs -DUSE_ER7_UTILS_CHECKPOINTHELPER) + endif() +endif() +if(TRICK_CIVETWEB_HOME) + list(APPEND _tr_swig_defs -DUSE_CIVETWEB) +endif() + +function(trick_add_swig_wrap name) + add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${name}_wrap.cpp + COMMAND ${SWIG_EXECUTABLE} ${_tr_swig_defs} + -I${CMAKE_SOURCE_DIR}/trick_source -I${CMAKE_SOURCE_DIR}/include -I${CMAKE_SOURCE_DIR} + -c++ -python -includeall -ignoremissing -w201,362,389,451 + -o ${CMAKE_CURRENT_BINARY_DIR}/${name}_wrap.cpp + -outdir ${TRICK_SWIG_OUTDIR} + ${CMAKE_CURRENT_SOURCE_DIR}/${name}.i + MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${name}.i + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/extra_functions.i ${CMAKE_CURRENT_SOURCE_DIR}/units_attach.i + COMMENT "SWIG ${name}.i" + VERBATIM + ) +endfunction() + +trick_add_swig_wrap(sim_services) +trick_add_swig_wrap(swig_double) +trick_add_swig_wrap(swig_int) +trick_add_swig_wrap(swig_ref) + +add_library( trick_pyip STATIC + ${CMAKE_CURRENT_BINARY_DIR}/sim_services_wrap.cpp + ${CMAKE_CURRENT_BINARY_DIR}/swig_double_wrap.cpp + ${CMAKE_CURRENT_BINARY_DIR}/swig_int_wrap.cpp + ${CMAKE_CURRENT_BINARY_DIR}/swig_ref_wrap.cpp + PrimitiveAttributesMap.cpp + swig_convert_units.cpp + swig_global_vars.cpp + $ +) +target_link_libraries( trick_pyip PUBLIC trick_build_flags ) +target_include_directories( trick_pyip PRIVATE ${Python3_INCLUDE_DIRS} ) +set_target_properties( trick_pyip PROPERTIES ARCHIVE_OUTPUT_NAME trick_pyip) + +# Makefile: trick_swig/Makefile:28-34 — SWIG-generated code triggers warnings +# the rest of the build treats as errors elsewhere; suppress the same set. +set(_tr_swig_wrap_opts -Wno-redundant-decls -Wno-shadow -Wno-unused-parameter -Wno-missing-field-initializers) +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + list(APPEND _tr_swig_wrap_opts -Wno-self-assign -Wno-sometimes-uninitialized) +else() + list(APPEND _tr_swig_wrap_opts -Wno-cast-function-type -Wno-ignored-qualifiers -Wno-delete-non-virtual-dtor) +endif() +set_source_files_properties( + ${CMAKE_CURRENT_BINARY_DIR}/sim_services_wrap.cpp + ${CMAKE_CURRENT_BINARY_DIR}/swig_double_wrap.cpp + ${CMAKE_CURRENT_BINARY_DIR}/swig_int_wrap.cpp + ${CMAKE_CURRENT_BINARY_DIR}/swig_ref_wrap.cpp + PROPERTIES COMPILE_OPTIONS "${_tr_swig_wrap_opts}" +) diff --git a/trick_source/trick_utils/CMakeLists.txt b/trick_source/trick_utils/CMakeLists.txt index d7941c5e9..ae014ef81 100644 --- a/trick_source/trick_utils/CMakeLists.txt +++ b/trick_source/trick_utils/CMakeLists.txt @@ -1,31 +1,31 @@ - - -# Trick utils files that are not in their own library +# Trick utils files that are not in their own library (top Makefile's +# UTILS_DIRS minus the dirs that build a separate archive, A3). set( TRICK_UTILS_SRC + compareFloatingPoint/src/compareFloatingPoint.cpp interpolator/src/Interpolator.cpp - shm/src/tsm_disconnect - shm/src/tsm_init - shm/src/tsm_init_with_lock - shm/src/tsm_reconnect - trick_adt/src/MapStrToPtr - trick_adt/src/bst - trick_adt/src/bubble_sort - trick_adt/src/dllist - trick_adt/src/lqueue - trick_adt/src/lstack - trick_adt/src/record_array - unicode/src/unicode_utils + shm/src/tsm_disconnect.c + shm/src/tsm_init.c + shm/src/tsm_init_with_lock.c + shm/src/tsm_reconnect.c + trick_adt/src/MapStrToPtr.c + trick_adt/src/bst.c + trick_adt/src/bubble_sort.c + trick_adt/src/dllist.c + trick_adt/src/lqueue.c + trick_adt/src/lstack.c + trick_adt/src/record_array.c + unicode/src/unicode_utils.c ) add_library( trick_utils_objs OBJECT ${TRICK_UTILS_SRC} ) +target_link_libraries( trick_utils_objs PUBLIC trick_build_flags ) ############################################################### -# Other Trick libraries +# Other Trick libraries (each packaged into its own archive, A3) ############################################################### add_subdirectory(comm) add_subdirectory(connection_handlers) add_subdirectory(math) +add_subdirectory(optimization) add_subdirectory(units) add_subdirectory(var_binary_parser) - - diff --git a/trick_source/trick_utils/comm/CMakeLists.txt b/trick_source/trick_utils/comm/CMakeLists.txt index 402f5b4bc..5002273ac 100644 --- a/trick_source/trick_utils/comm/CMakeLists.txt +++ b/trick_source/trick_utils/comm/CMakeLists.txt @@ -1,31 +1,33 @@ - set( TRICKCOMM_SRC - src/tc_accept - src/tc_blockio - src/tc_broadcast_conninfo - src/tc_clock_init - src/tc_clock_time - src/tc_connect - src/tc_dev_copy - src/tc_disconnect - src/tc_error - src/tc_init - src/tc_init_mcast_client - src/tc_init_mcast_server - src/tc_init_udp_client - src/tc_init_udp_server - src/tc_isValid - src/tc_listen - src/tc_multiconnect - src/tc_pending - src/tc_read - src/tc_read_byteswap - src/tc_set_blockio - src/tc_write - src/tc_write_byteswap - src/trick_bswap_buffer - src/trick_byteswap - src/trick_error_hndlr + src/tc_accept.c + src/tc_blockio.c + src/tc_broadcast_conninfo.c + src/tc_clock_init.c + src/tc_clock_time.c + src/tc_connect.c + src/tc_dev_copy.c + src/tc_disconnect.c + src/tc_error.c + src/tc_init.c + src/tc_init_mcast_client.c + src/tc_init_mcast_server.c + src/tc_init_udp_client.c + src/tc_init_udp_server.c + src/tc_isValid.c + src/tc_listen.c + src/tc_multiconnect.c + src/tc_pending.c + src/tc_read.c + src/tc_read_byteswap.c + src/tc_set_blockio.c + src/tc_write.c + src/tc_write_byteswap.c + src/trick_bswap_buffer.c + src/trick_bswap_single_parameter.c + src/trick_byteswap.c + src/trick_error_hndlr.c ) add_library( trick_comm STATIC ${TRICKCOMM_SRC}) +target_link_libraries( trick_comm PUBLIC trick_build_flags ) +set_target_properties( trick_comm PROPERTIES ARCHIVE_OUTPUT_NAME trick_comm) diff --git a/trick_source/trick_utils/connection_handlers/CMakeLists.txt b/trick_source/trick_utils/connection_handlers/CMakeLists.txt new file mode 100644 index 000000000..c007465ff --- /dev/null +++ b/trick_source/trick_utils/connection_handlers/CMakeLists.txt @@ -0,0 +1,11 @@ +set( TRICK_CONNECTION_HANDLERS_SRC + ClientConnection.cpp + MulticastGroup.cpp + TCPClientListener.cpp + TCPConnection.cpp + UDPConnection.cpp +) + +add_library( trick_connection_handlers STATIC ${TRICK_CONNECTION_HANDLERS_SRC}) +target_link_libraries( trick_connection_handlers PUBLIC trick_build_flags ) +set_target_properties( trick_connection_handlers PROPERTIES ARCHIVE_OUTPUT_NAME trick_connection_handlers) diff --git a/trick_source/trick_utils/math/CMakeLists.txt b/trick_source/trick_utils/math/CMakeLists.txt index ef40c4e40..2c56fe062 100644 --- a/trick_source/trick_utils/math/CMakeLists.txt +++ b/trick_source/trick_utils/math/CMakeLists.txt @@ -1,96 +1,101 @@ - set( TRICKMATH_SRC - src/LUD_inv - src/LUT_inv - src/LU_bksb - src/LU_dcmp - src/RodriguesRotation - src/dLU_Choleski - src/dLU_solver - src/dS_function - src/deuler_123 - src/deuler_123_quat - src/deuler_132 - src/deuler_132_quat - src/deuler_213 - src/deuler_213_quat - src/deuler_231 - src/deuler_231_quat - src/deuler_312 - src/deuler_312_quat - src/deuler_321 - src/deuler_321_quat - src/dm_add - src/dm_copy - src/dm_ident - src/dm_init - src/dm_invert - src/dm_invert_symm - src/dm_orthonormal - src/dm_print - src/dm_scale - src/dm_sub - src/dm_trans - src/dmtxm - src/dmtxmt - src/dmtxv - src/dmxm - src/dmxmt - src/dmxv - src/drandom_gaussian - src/dsingle_axis_rot - src/dv_add - src/dv_copy - src/dv_cross - src/dv_dot - src/dv_init - src/dv_mag - src/dv_norm - src/dv_print - src/dv_scale - src/dv_skew - src/dv_store - src/dv_sub - src/dvxm - src/dvxv_add - src/dvxv_sub - src/eigen_hh_red - src/eigen_jacobi - src/eigen_jacobi_4 - src/eigen_ql - src/euler_matrix - src/euler_quat - src/gauss_rnd_bell - src/gauss_rnd_pseudo - src/mat_copy - src/mat_permute - src/mat_print - src/mat_to_quat - src/mat_trans - src/matxmat - src/matxtrans - src/matxvec - src/quat_mult - src/quat_norm - src/quat_norm_integ - src/quat_to_mat - src/rand_num - src/roundoff - src/tm_print_error - src/transxmat - src/transxtrans - src/transxvec - src/trick_gsl_rand - src/trns_fnct_1o - src/trns_fnct_2o - src/uniform_rnd_1 - src/uniform_rnd_triple - src/vec_print - src/wave_form + src/LUD_inv.c + src/LUT_inv.c + src/LU_bksb.c + src/LU_dcmp.c + src/RodriguesRotation.c + src/dLDLt_solver.c + src/dLU_Choleski.c + src/dLU_solver.c + src/dS_function.c + src/deuler_123.c + src/deuler_123_quat.c + src/deuler_132.c + src/deuler_132_quat.c + src/deuler_213.c + src/deuler_213_quat.c + src/deuler_231.c + src/deuler_231_quat.c + src/deuler_312.c + src/deuler_312_quat.c + src/deuler_321.c + src/deuler_321_quat.c + src/dm_add.c + src/dm_copy.c + src/dm_ident.c + src/dm_init.c + src/dm_invert.c + src/dm_invert_symm.c + src/dm_orthonormal.c + src/dm_print.c + src/dm_scale.c + src/dm_sub.c + src/dm_trans.c + src/dmtxm.c + src/dmtxmt.c + src/dmtxv.c + src/dmxm.c + src/dmxmt.c + src/dmxv.c + src/drandom_gaussian.c + src/dsingle_axis_rot.c + src/dv_add.c + src/dv_copy.c + src/dv_cross.c + src/dv_dot.c + src/dv_init.c + src/dv_mag.c + src/dv_norm.c + src/dv_print.c + src/dv_scale.c + src/dv_skew.c + src/dv_store.c + src/dv_sub.c + src/dvxm.c + src/dvxv_add.c + src/dvxv_sub.c + src/eigen_hh_red.c + src/eigen_jacobi.c + src/eigen_jacobi_4.c + src/eigen_ql.c + src/euler_matrix.c + src/euler_quat.c + src/gauss_rnd_bell.c + src/gauss_rnd_pseudo.c + src/mat_copy.c + src/mat_permute.c + src/mat_print.c + src/mat_to_quat.c + src/mat_trans.c + src/matxmat.c + src/matxtrans.c + src/matxvec.c + src/quat_mult.c + src/quat_norm.c + src/quat_norm_integ.c + src/quat_to_mat.c + src/rand_num.c + src/roundoff.c + src/tm_print_error.c + src/transxmat.c + src/transxtrans.c + src/transxvec.c + src/trick_gsl_rand.c + src/trns_fnct_1o.c + src/trns_fnct_2o.c + src/uniform_rnd_1.c + src/uniform_rnd_triple.c + src/vec_print.c + src/wave_form.c ) add_library( trick_math STATIC ${TRICKMATH_SRC}) +target_link_libraries( trick_math PUBLIC trick_build_flags ) +set_target_properties( trick_math PROPERTIES ARCHIVE_OUTPUT_NAME trick_math) -if(GSL_FOUND) - target_include_directories( trick_math PUBLIC ${GSL_INCLUDE_DIRS} ) +if(GSL_HOME AND NOT GSL_HOME STREQUAL "/usr/local") + target_include_directories( trick_math PRIVATE ${GSL_HOME}/include ) +endif() +if(GSL_HOME) + target_compile_definitions( trick_math PRIVATE _HAVE_GSL ) endif() diff --git a/trick_source/trick_utils/optimization/CMakeLists.txt b/trick_source/trick_utils/optimization/CMakeLists.txt new file mode 100644 index 000000000..40872fd8c --- /dev/null +++ b/trick_source/trick_utils/optimization/CMakeLists.txt @@ -0,0 +1,7 @@ +set( TRICK_OPTIMIZATION_SRC + src/dPDIP_solver.c +) + +add_library( trick_optimization STATIC ${TRICK_OPTIMIZATION_SRC}) +target_link_libraries( trick_optimization PUBLIC trick_build_flags ) +set_target_properties( trick_optimization PROPERTIES ARCHIVE_OUTPUT_NAME trick_optimization) diff --git a/trick_source/trick_utils/units/CMakeLists.txt b/trick_source/trick_utils/units/CMakeLists.txt index 613529c7f..dbdf9971f 100644 --- a/trick_source/trick_utils/units/CMakeLists.txt +++ b/trick_source/trick_utils/units/CMakeLists.txt @@ -1,9 +1,9 @@ - set( TRICK_UNITS_SRC src/UCFn.cpp src/Unit.cpp - src/units_conv + src/units_conv.c ) add_library( trick_units STATIC ${TRICK_UNITS_SRC}) - +target_link_libraries( trick_units PUBLIC trick_build_flags ) +set_target_properties( trick_units PROPERTIES ARCHIVE_OUTPUT_NAME trick_units) diff --git a/trick_source/trick_utils/var_binary_parser/CMakeLists.txt b/trick_source/trick_utils/var_binary_parser/CMakeLists.txt new file mode 100644 index 000000000..460b3d6e4 --- /dev/null +++ b/trick_source/trick_utils/var_binary_parser/CMakeLists.txt @@ -0,0 +1,8 @@ +set( TRICK_VAR_BINARY_PARSER_SRC + src/var_binary_parser.cc +) + +add_library( trick_var_binary_parser STATIC ${TRICK_VAR_BINARY_PARSER_SRC}) +target_include_directories( trick_var_binary_parser PRIVATE ${CMAKE_SOURCE_DIR}/include ) +target_link_libraries( trick_var_binary_parser PUBLIC trick_build_flags ) +set_target_properties( trick_var_binary_parser PROPERTIES ARCHIVE_OUTPUT_NAME trick_var_binary_parser) From c15286655476f3f03f26fa1adf23b9937d2ec9fa Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Mon, 6 Jul 2026 11:19:43 -0500 Subject: [PATCH 04/28] cmake: transition: phase 1: fix configuration mismatches from CI failures --- cmake/TrickPrograms.cmake | 34 ++++++++++++++++++++++-- cmake/TrickPython.cmake | 6 ++++- test/build_config/compare_config_user.sh | 24 ++++++++--------- trick_source/trick_swig/CMakeLists.txt | 11 ++++++-- 4 files changed, 58 insertions(+), 17 deletions(-) diff --git a/cmake/TrickPrograms.cmake b/cmake/TrickPrograms.cmake index 11c712c3f..35fbaaf60 100644 --- a/cmake/TrickPrograms.cmake +++ b/cmake/TrickPrograms.cmake @@ -57,7 +57,25 @@ if(NOT _tr_perl_digest_md5 EQUAL 0) endif() # ── SWIG >= 3.0 (autoconf/m4/tr_swig_bin.m4) ─────────────────────────────── -find_package(SWIG 3.0 REQUIRED) +# tr_swig_bin.m4 -> AX_PKG_SWIG -> AC_PATH_PROG([SWIG], [swig]) only ever +# searches for the bare name "swig". find_package(SWIG)'s own executable +# search can instead land on a version-suffixed binary (e.g. swig4.0) when +# both exist on PATH, which mismatches config_user.mk's SWIG value even +# though the version requirement is equally satisfied either way. +find_program(SWIG_EXECUTABLE NAMES swig) +if(NOT SWIG_EXECUTABLE) + message(FATAL_ERROR "could not find swig") +endif() +execute_process( + COMMAND ${SWIG_EXECUTABLE} -version + OUTPUT_VARIABLE _tr_swig_version_output + OUTPUT_STRIP_TRAILING_WHITESPACE +) +string(REGEX MATCH "SWIG Version ([0-9]+\\.[0-9]+\\.[0-9]+)" _ "${_tr_swig_version_output}") +if(NOT CMAKE_MATCH_1 OR CMAKE_MATCH_1 VERSION_LESS "3.0") + message(FATAL_ERROR "Trick requires SWIG version >= 3.0, found: ${CMAKE_MATCH_1}") +endif() +set(SWIG_VERSION "${CMAKE_MATCH_1}") # ── zlib / libxml2 / threads ─────────────────────────────────────────────── find_package(ZLIB REQUIRED) @@ -77,10 +95,22 @@ set(PTHREAD_LIBS "-lpthread") find_package(X11) if(X11_FOUND) set(USE_X_WINDOWS 1) + set(_tr_x11_libdir "") if(X11_LIBRARY_DIRS) - set(X_LIB_DIR "-L${X11_LIBRARY_DIRS}") + set(_tr_x11_libdir "${X11_LIBRARY_DIRS}") elseif(X11_X11_LIB) get_filename_component(_tr_x11_libdir "${X11_X11_LIB}" DIRECTORY) + endif() + # AC_PATH_XTRA (tr_xwindows.m4:12) only sets X_LIB_DIR when $x_libraries + # is non-empty, which autoconf itself leaves empty whenever the compiler + # already finds X11 on its default library search path (the common case + # on Linux distros and Homebrew, where X11 libs live in + # /usr/lib/, /usr/lib64, or /opt/homebrew/lib alongside + # everything else). CMake's FindX11 always returns an absolute dir + # regardless, so replicate autoconf's "only if nonstandard" behavior by + # checking against the compiler's own implicit link directories. + set(X_LIB_DIR "") + if(_tr_x11_libdir AND NOT _tr_x11_libdir IN_LIST CMAKE_C_IMPLICIT_LINK_DIRECTORIES) set(X_LIB_DIR "-L${_tr_x11_libdir}") endif() if(NOT X11_Xt_FOUND) diff --git a/cmake/TrickPython.cmake b/cmake/TrickPython.cmake index 6e24621a1..444251b28 100644 --- a/cmake/TrickPython.cmake +++ b/cmake/TrickPython.cmake @@ -50,9 +50,13 @@ execute_process( COMMAND ${PYTHON_CONFIG} ${_tr_python_libs_command} OUTPUT_VARIABLE PYTHON_LIBS ) +# configure.ac only does `tr '\r\n' ' '` on python-config's raw output — no +# further stripping — so a leading/trailing space python-config itself emits +# is preserved in config_user.mk. Don't string(STRIP ...) here: it produced a +# byte-for-byte parity mismatch against autoconf's output on RHEL-family +# pythons (see test/build_config/compare_config_user.sh CI failures). string(REPLACE "\r" " " PYTHON_LIBS "${PYTHON_LIBS}") string(REPLACE "\n" " " PYTHON_LIBS "${PYTHON_LIBS}") -string(STRIP "${PYTHON_LIBS}" PYTHON_LIBS) # Always empty today (see A1) — kept as a named, substituted variable for # parity with config_user.mk.in's contract. diff --git a/test/build_config/compare_config_user.sh b/test/build_config/compare_config_user.sh index 8aa34e3d8..f3f5cf797 100755 --- a/test/build_config/compare_config_user.sh +++ b/test/build_config/compare_config_user.sh @@ -56,21 +56,21 @@ cp "$CMAKE_GENERATED" "$WORKDIR/cmake_config_user.mk" # ── 3. Normalize both files ───────────────────────────────────────────────── # Accepted deltas: -# - PYTHON_LIB: configure's "tr '\r\n' ' '" leaves a trailing space that -# autoconf keeps and CMake's string(STRIP) removes. # - TRICK_GCC_VERSION: autoconf uses "$CC -dumpfullversion -dumpversion"; # CMake uses CMAKE_CXX_COMPILER_VERSION. Both resolve to the compiler's # dotted version triplet on every GCC we support; tolerate a trailing # ".0" CMake sometimes omits (e.g. "11.4" vs "11.4.0"). -# -# Known NOT-normalized, environment-specific delta (not a bug): on a -# machine with more than one X11 provider installed (e.g. a dev Mac with -# both Homebrew's libx11 formula AND the legacy /usr/X11 selector), -# autoconf's AC_PATH_X and CMake's FindX11 module can each pick a -# different, independently valid installation, producing a different -# X_LIB_DIR value. Every CI image here installs exactly one X11 provider, -# so this does not appear in CI; it is not worth replicating AC_PATH_X's -# legacy search heuristic in CMake to chase it on developer machines. +# - X_LIB_DIR: confirmed in CI (not just hypothetical dev machines) on the +# macOS runner, which has more than one valid X11 provider installed +# (Homebrew's + Xquartz's /usr/X11); autoconf's AC_PATH_X and CMake's +# FindX11 module each independently pick a different, equally valid +# installation. Both resolve to a linkable libX11, so the value itself +# (not just its presence/absence) is ignored here rather than replicating +# AC_PATH_X's legacy search-order heuristic in CMake. On single-X11- +# provider platforms this value should now match exactly (see +# cmake/TrickPrograms.cmake's IMPLICIT_LINK_DIRECTORIES check) — this +# normalization is a safety net for the multi-provider case, not a mask +# for a real bug. normalize() { local infile="$1" local outfile="$2" @@ -78,8 +78,8 @@ normalize() { -e 's/[[:space:]]+$//' \ -e '/^[[:space:]]*#/d' \ -e '/^[[:space:]]*$/d' \ - -e 's/^(PYTHON_LIB[[:space:]]*=.*[^[:space:]])[[:space:]]+$/\1/' \ -e 's/^(TRICK_GCC_VERSION[[:space:]]*=[[:space:]]*[0-9]+\.[0-9]+)$/\1.0/' \ + -e 's/^X_LIB_DIR[[:space:]]*=.*/X_LIB_DIR = /' \ "$infile" | sort >"$outfile" } diff --git a/trick_source/trick_swig/CMakeLists.txt b/trick_source/trick_swig/CMakeLists.txt index 55a2c0daf..da2768da1 100644 --- a/trick_source/trick_swig/CMakeLists.txt +++ b/trick_source/trick_swig/CMakeLists.txt @@ -1,7 +1,14 @@ # libtrick_pyip.a: SWIG-generated Trick/Python interface code, ported from # this directory's Makefile (the authority — see A3/A4). - -find_package(SWIG 3.0 REQUIRED) +# +# SWIG_EXECUTABLE/SWIG_VERSION come from cmake/TrickPrograms.cmake's own +# find_program(NAMES swig) — do NOT find_package(SWIG) here, it re-runs +# FindSWIG.cmake's own executable search and can override SWIG_EXECUTABLE +# with a version-suffixed binary (e.g. swig4.0), mismatching config_user.mk's +# SWIG value (see CMAKE_MIGRATION_PLAN.md Phase 1/2 CI parity fixes). +if(NOT SWIG_EXECUTABLE) + message(FATAL_ERROR "SWIG_EXECUTABLE not set — include(TrickPrograms) first") +endif() # D3: CMake writes nothing into the source tree — .py wrappers land in the # build tree; Phase 3's `stage` install target is what assembles a runnable From f0ad2c5df675b7ca29fb4bcaed03c6f9c64a887e Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Mon, 6 Jul 2026 15:04:02 -0500 Subject: [PATCH 05/28] cmake: transition: phase 3 --- .github/workflows/ci.yml | 8 ++ .gitignore | 1 + CMakeLists.txt | 6 ++ cmake/TrickInstall.cmake | 114 ++++++++++++++++++++++++++ cmake/TrickTest.cmake | 83 +++++++++++++++++++ share/trick/makefiles/Makefile.common | 17 ---- test/build_config/run_ctest.sh | 30 +++++++ test/build_config/run_sim_flow.sh | 43 ++++++++++ 8 files changed, 285 insertions(+), 17 deletions(-) create mode 100644 cmake/TrickInstall.cmake create mode 100644 cmake/TrickTest.cmake create mode 100755 test/build_config/run_ctest.sh create mode 100755 test/build_config/run_sim_flow.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0efb36000..dffca82c9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -150,6 +150,10 @@ jobs: test/build_config/compare_config_user.sh - name: CMake build + archive parity vs make (Phase 2) run: test/build_config/compare_archives.sh + - name: CMake install + CTest (Phase 3) + run: | + echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" + test/build_config/run_ctest.sh 'unit|sims' cmake-configure-macos: name: CMake Configure (macOS) @@ -171,6 +175,10 @@ jobs: run: test/build_config/compare_config_user.sh - name: CMake build + archive parity vs make (Phase 2) run: test/build_config/compare_archives.sh + - name: CMake install + sim-flow CTest (Phase 3) + # gtest is not installed on the macOS runner, so run only the trick-CP + # sim-flow acceptance tests (LABEL sims), not the gtest unit suite. + run: test/build_config/run_ctest.sh sims trickops: needs: [style] diff --git a/.gitignore b/.gitignore index 1058d5500..b7137f17e 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ gmon.out Makefile_jsc_dirs # CMake +build*/ CMakeLists.txt.user CMakeCache.txt CMakeFiles diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f7efcd09..e05532c55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,3 +150,9 @@ add_library(trick STATIC target_link_libraries(trick PUBLIC trick_build_flags) set_target_properties(trick PROPERTIES ARCHIVE_OUTPUT_NAME trick) add_dependencies(trick trick_io_src_gen) + +# ────────────────────────────────────────────────────────────────────────── +# Phase 3 — install rules, stage target, CTest +# ────────────────────────────────────────────────────────────────────────── +include(TrickInstall) +include(TrickTest) diff --git a/cmake/TrickInstall.cmake b/cmake/TrickInstall.cmake new file mode 100644 index 000000000..57337b905 --- /dev/null +++ b/cmake/TrickInstall.cmake @@ -0,0 +1,114 @@ +# Phase 3 — install rules that assemble a working TRICK_HOME. +# +# The installed tree is the real product (D2/D3): sims are built by make against +# it, forever. Do NOT trust the top-level Makefile's `install` target as a spec — +# it is unmaintained and possibly broken. The authority is "what trick-CP needs to +# build and run a sim against the prefix" (Part E rule 4). The source tree is +# already TRICK_HOME-shaped (D3), so the install reproduces its bin/ include/ +# libexec/ share/ layout plus the built archives and the *generated* config_user.mk +# and swig *.py wrappers — never the copies a prior make/configure left in-source. + +# ── D6: library subdir (lib64 iff redhat-release && x86_64 && !32bit; else lib; +# Darwin always lib) — mirror config_Linux.mk exactly, do NOT use +# GNUInstallDirs (the perl flow hardcodes lib/lib64 discovery). +if(TRICK_FORCE_32BIT) + set(TRICK_LIB_SUBDIR lib) +elseif(NOT APPLE AND EXISTS "/etc/redhat-release" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + set(TRICK_LIB_SUBDIR lib64) +else() + set(TRICK_LIB_SUBDIR lib) +endif() +message(STATUS "Trick library subdir: ${TRICK_LIB_SUBDIR}") + +# ── Static archives → ${TRICK_LIB_SUBDIR} (the A3 nine + var_binary_parser). +set(TRICK_INSTALL_ARCHIVES + trick + trick_mm + trick_comm + trick_connection_handlers + trick_math + trick_optimization + trick_units + trick_var_binary_parser + trick_pyip +) +if(TRICK_USE_ER7_UTILS) + list(APPEND TRICK_INSTALL_ARCHIVES er7_utils) +endif() +install(TARGETS ${TRICK_INSTALL_ARCHIVES} + ARCHIVE DESTINATION ${TRICK_LIB_SUBDIR} +) + +# ── bin/: the built trick-ICG, plus every checked-in perl driver. USE_SOURCE_ +# PERMISSIONS keeps the +x bit. trick-ICG is excluded from the source +# copy because the build target supplies it (a stale in-source binary +# may be present on a dev tree). +install(TARGETS trick-ICG RUNTIME DESTINATION bin) +install(DIRECTORY ${CMAKE_SOURCE_DIR}/bin/ + DESTINATION bin + USE_SOURCE_PERMISSIONS + PATTERN "trick-ICG" EXCLUDE +) + +# ── include/: Trick headers (incl. compat) verbatim; er7_utils headers rehomed +# from trick_source/er7_utils/**/*.hh to include/er7_utils/... . +install(DIRECTORY ${CMAKE_SOURCE_DIR}/include/trick DESTINATION include) +if(TRICK_USE_ER7_UTILS) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/ + DESTINATION include/er7_utils + FILES_MATCHING PATTERN "*.hh" + ) +endif() + +# ── libexec/trick: perl helpers (configuration_processor, make_makefile_*, pm/, +# ...) verbatim. Java jars land here too but are a Phase 4 add. +install(DIRECTORY ${CMAKE_SOURCE_DIR}/libexec/trick + DESTINATION libexec + USE_SOURCE_PERMISSIONS +) + +# ── share/trick: static content from source (makefiles, sim_objects, pymods, +# trickops, swig *.i and the checked-in *.py, ...). Exclude the generated +# artifacts so a dirty dev tree can't ship stale copies — the build-tree +# versions are installed just below. config_user.mk is generated by +# configure/CMake; the generated swig wrappers are sim_services/swig_*.py. +install(DIRECTORY ${CMAKE_SOURCE_DIR}/share/trick/ + DESTINATION share/trick + USE_SOURCE_PERMISSIONS + PATTERN "config_user.mk" EXCLUDE + PATTERN "sim_services.py" EXCLUDE + PATTERN "swig_double.py" EXCLUDE + PATTERN "swig_int.py" EXCLUDE + PATTERN "swig_ref.py" EXCLUDE +) +# Generated config_user.mk (D4 parity contract) and swig wrappers, from the build +# tree. The swig *.py are add_custom_command side effects (not a target output), +# so they are globbed at install time — they exist once the build has run. +install(FILES ${CMAKE_BINARY_DIR}/share/trick/makefiles/config_user.mk + DESTINATION share/trick/makefiles +) +install(DIRECTORY ${CMAKE_BINARY_DIR}/share/trick/swig/ + DESTINATION share/trick/swig + FILES_MATCHING PATTERN "*.py" +) + +# ── share/man, share/doc: man pages and any prebuilt docs (best-effort). +install(DIRECTORY ${CMAKE_SOURCE_DIR}/share/man DESTINATION share) +if(EXISTS ${CMAKE_SOURCE_DIR}/share/doc) + install(DIRECTORY ${CMAKE_SOURCE_DIR}/share/doc DESTINATION share) +endif() + +# ── stage target: `cmake --build build --target stage` gives a runnable TRICK_HOME +# at ${CMAKE_BINARY_DIR}/stage for sim-flow validation and CTest (D3). It depends +# on the build targets so the install always reflects a fresh build. +set(TRICK_STAGE_DIR ${CMAKE_BINARY_DIR}/stage) +add_custom_target(stage + COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix ${TRICK_STAGE_DIR} + COMMENT "Staging a runnable TRICK_HOME into ${TRICK_STAGE_DIR}" + VERBATIM +) +add_dependencies(stage + ${TRICK_INSTALL_ARCHIVES} + trick-ICG + trick_io_src_gen +) diff --git a/cmake/TrickTest.cmake b/cmake/TrickTest.cmake new file mode 100644 index 000000000..760586ac7 --- /dev/null +++ b/cmake/TrickTest.cmake @@ -0,0 +1,83 @@ +# Phase 3 — CTest wiring. +# +# Two kinds of tests, both run against a staged TRICK_HOME (never the source tree, +# never a make build): +# +# LABEL unit — the per-directory gtest suites under trick_source/**/test. Each +# has a hand-written Makefile that `include`s Makefile.common; that +# file force-computes TRICK_HOME from its own location, but the +# assignment is plain (not `override`), so a `make TRICK_HOME=` +# command-line override wins and redirects config_user.mk, the +# archives (TRICK_LIB_DIR), headers, and trick-ICG to the stage. +# +# LABEL sims — the universal acceptance test: trick-CP builds and runs a real sim +# against the stage (test/build_config/run_sim_flow.sh). +# +# A CTest fixture stages the install first (cmake --install into ${TRICK_STAGE_DIR}), +# so `ctest` works after a plain `cmake --build`. The build itself must be complete +# before ctest — ctest does not compile the archives. + +include(CTest) + +if(NOT BUILD_TESTING) + return() +endif() + +if(NOT DEFINED TRICK_STAGE_DIR) + set(TRICK_STAGE_DIR ${CMAKE_BINARY_DIR}/stage) +endif() + +# ── Setup fixture: install the build tree into the stage prefix. +add_test(NAME stage_install + COMMAND ${CMAKE_COMMAND} --install ${CMAKE_BINARY_DIR} --prefix ${TRICK_STAGE_DIR} +) +set_tests_properties(stage_install PROPERTIES FIXTURES_SETUP trick_stage) + +# ── Unit tests: mirror the top-level Makefile's UNIT_TEST_DIRS (sim_services/*/test +# and trick_utils/*/test), with the same %Integrator/test filter (Makefile:137). +file(GLOB _tr_unit_test_dirs + ${CMAKE_SOURCE_DIR}/trick_source/sim_services/*/test + ${CMAKE_SOURCE_DIR}/trick_source/trick_utils/*/test +) +foreach(_dir ${_tr_unit_test_dirs}) + if(NOT EXISTS ${_dir}/Makefile) + continue() + endif() + if(_dir MATCHES "Integrator/test$") + continue() # filtered out of releases by the make build (Makefile:137) + endif() + # ut_, e.g. ut_MemoryManager + get_filename_component(_parent ${_dir} DIRECTORY) + get_filename_component(_name ${_parent} NAME) + add_test(NAME ut_${_name} + COMMAND make -C ${_dir} test TRICK_HOME=${TRICK_STAGE_DIR} + ) + set_tests_properties(ut_${_name} PROPERTIES + LABELS unit + FIXTURES_REQUIRED trick_stage + TIMEOUT 900 + ) +endforeach() + +# ── Sim-flow acceptance tests (LABEL sims): trick-CP builds + runs real sims from +# the stage. One data-recording/checkpoint sim and one STL sim, matching the +# plan's Phase 3 step 5 ("SIM_cannon_analytic plus one data-recording/STL sim" — +# cannon_analytic isn't in this checkout; the demo/STL sims exercise the same +# trick-CP -> ICG -> SWIG -> compile -> run path). +set(_tr_sim_flow_script ${CMAKE_SOURCE_DIR}/test/build_config/run_sim_flow.sh) +function(trick_add_sim_flow_test name sim_dir) + if(NOT EXISTS ${CMAKE_SOURCE_DIR}/${sim_dir}/S_define) + return() + endif() + add_test(NAME ${name} + COMMAND ${_tr_sim_flow_script} ${TRICK_STAGE_DIR} ${CMAKE_SOURCE_DIR}/${sim_dir} + ) + set_tests_properties(${name} PROPERTIES + LABELS sims + FIXTURES_REQUIRED trick_stage + TIMEOUT 1800 + ) +endfunction() + +trick_add_sim_flow_test(sim_demo_sdefine test/SIM_demo_sdefine) +trick_add_sim_flow_test(sim_stls test/SIM_stls) diff --git a/share/trick/makefiles/Makefile.common b/share/trick/makefiles/Makefile.common index 4db13de63..ead94fd04 100644 --- a/share/trick/makefiles/Makefile.common +++ b/share/trick/makefiles/Makefile.common @@ -106,16 +106,6 @@ ifeq ($(HAVE_ZEROCONF),1) TRICK_SYSTEM_CXXFLAGS += -DHAVE_ZEROCONF endif -ifeq ($(USE_ER7_UTILS),ON) - ER7_UTILS_HOME := $(TRICK_HOME)/trick_source/er7_utils - TRICK_SYSTEM_CXXFLAGS += -DUSE_ER7_UTILS_INTEGRATORS - TRICK_LIBS += -ler7_utils - ifneq ($(wildcard ${ER7_UTILS_HOME}/CheckpointHelper),) - USE_ER7_UTILS_CHECKPOINTHELPER = 1 - TRICK_SYSTEM_CXXFLAGS += -DUSE_ER7_UTILS_CHECKPOINTHELPER - endif -endif -# older test, remove when cmake is only build system ifeq ($(USE_ER7_UTILS), 1) ER7_UTILS_HOME := $(TRICK_HOME)/trick_source/er7_utils TRICK_SYSTEM_CXXFLAGS += -DUSE_ER7_UTILS_INTEGRATORS @@ -126,13 +116,6 @@ ifeq ($(USE_ER7_UTILS), 1) endif endif -ifeq ($(TRICK_FORCE_32BIT),ON) - TRICK_ICGFLAGS += -m32 - TRICK_SYSTEM_CXXFLAGS += -m32 - TRICK_SYSTEM_LDFLAGS += -m32 - LD_PARTIAL += -melf_i386 -endif -# older test, remove when cmake is only build system ifeq ($(TRICK_FORCE_32BIT), 1) TRICK_ICGFLAGS += -m32 TRICK_SYSTEM_CXXFLAGS += -m32 diff --git a/test/build_config/run_ctest.sh b/test/build_config/run_ctest.sh new file mode 100755 index 000000000..893e5a5df --- /dev/null +++ b/test/build_config/run_ctest.sh @@ -0,0 +1,30 @@ +#!/usr/bin/env bash +# Phase 3 install + CTest gate (CMAKE_MIGRATION_PLAN.md Part C, Phase 3): does the +# full three-step CMake build (see cmake/TrickICG.cmake), stages a runnable +# TRICK_HOME with `cmake --build ... --target stage`, then runs CTest. The unit +# tests build mini-sims through the real trick-CP flow against the stage, which +# doubles as install/interface validation; the sim-flow tests are the universal +# acceptance test (a real sim built and run by trick-CP from the installed prefix). +# +# Usage: test/build_config/run_ctest.sh [ctest-label-regex] (default: unit|sims) +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$REPO_ROOT" + +LABELS="${1:-unit|sims}" +BUILD_DIR="${TRICK_CMAKE_BUILD_DIR:-$REPO_ROOT/build}" +NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + +# Three-step io_src bootstrap (a nonexistent-at-first-configure generated list +# does not wire itself into CMAKE_CONFIGURE_DEPENDS — see cmake/TrickICG.cmake). +cmake -S . -B "$BUILD_DIR" +cmake --build "$BUILD_DIR" --target trick-ICG trick_io_src_gen -j"$NPROC" +cmake -S . -B "$BUILD_DIR" +cmake --build "$BUILD_DIR" -j"$NPROC" + +# Stage a runnable TRICK_HOME (build/stage) — the fixture in cmake/TrickTest.cmake +# also does this, but doing it explicitly here surfaces install errors directly. +cmake --build "$BUILD_DIR" --target stage -j"$NPROC" + +ctest --test-dir "$BUILD_DIR" --output-on-failure -L "$LABELS" diff --git a/test/build_config/run_sim_flow.sh b/test/build_config/run_sim_flow.sh new file mode 100755 index 000000000..3a7fb439e --- /dev/null +++ b/test/build_config/run_sim_flow.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# +# Universal acceptance test (CMAKE_MIGRATION_PLAN.md A2/Part E rule 4): a sim built +# with trick-CP against a CMake-installed Trick must compile and run. This drives +# the real installed product interface — trick-CP -> Makefile.common -> config_user.mk +# -> trick-ICG/SWIG — against a staged/installed TRICK_HOME, exactly as an end user +# would. +# +# Usage: run_sim_flow.sh [run_input] +# TRICK_HOME a CMake-installed (or staged) Trick prefix +# sim_dir path to a sim directory containing an S_define +# run_input input file to run, relative to sim_dir (default RUN_test/input.py) +# +set -euo pipefail + +if [ $# -lt 2 ]; then + echo "usage: $0 [run_input]" >&2 + exit 2 +fi + +export TRICK_HOME="$(cd "$1" && pwd)" +SIM_DIR="$(cd "$2" && pwd)" +RUN_INPUT="${3:-RUN_test/input.py}" + +export PATH="${TRICK_HOME}/bin:${PATH}" + +echo "=== sim-flow: TRICK_HOME=${TRICK_HOME}" +echo "=== sim-flow: building ${SIM_DIR} with trick-CP" +cd "${SIM_DIR}" +# Clean any stale build so this genuinely exercises ICG/SWIG/compile against the +# installed Trick, not leftover artifacts from another build system. +trick-CP + +TRICK_HOST_CPU="$(trick-gte TRICK_HOST_CPU)" +SIM_EXE="S_main_${TRICK_HOST_CPU}.exe" +if [ ! -x "${SIM_DIR}/${SIM_EXE}" ]; then + echo "sim-flow FAILED: ${SIM_EXE} was not produced" >&2 + exit 1 +fi + +echo "=== sim-flow: running ${SIM_EXE} ${RUN_INPUT}" +"${SIM_DIR}/${SIM_EXE}" "${RUN_INPUT}" +echo "=== sim-flow: OK" From 5432b725439febe14e5b51b0682ddaacbb28a56d Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Mon, 6 Jul 2026 15:52:00 -0500 Subject: [PATCH 06/28] cmake: transition: phase 2: Fixes to globs and GSL flags --- CMakeLists.txt | 24 ++++++++++++---- cmake/TrickICG.cmake | 29 ++++++++++++++------ trick_source/sim_services/CMakeLists.txt | 13 ++++----- trick_source/trick_utils/math/CMakeLists.txt | 10 ++----- 4 files changed, 48 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e05532c55..86388a269 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,11 +90,14 @@ find_package(FLEX REQUIRED) find_package(BISON REQUIRED) # Flags shared by every Trick-core compile target, mirroring -# TRICK_SYSTEM_CXXFLAGS/TRICK_CXXFLAGS (Makefile.common:98-197). A handful of -# these (HDF5/GSL/Python includes, USE_CIVETWEB) only apply to specific -# source files in the make build; applying them build-wide here is a -# deliberate simplification (harmless extra -I/-D flags) rather than -# reproducing every per-file override. +# TRICK_SYSTEM_CXXFLAGS/TRICK_CXXFLAGS (Makefile.common:98-197). HDF5's +# -DHDF5/-I only apply to DataRecord/DRHDF5.cpp in the make build too (that +# file's own Makefile-local override — see sim_services/CMakeLists.txt), so +# it stays per-file here rather than global. GSL is different: Makefile.common +# itself puts -D_HAVE_GSL/-I on TRICK_SYSTEM_CXXFLAGS globally (every compiled +# file inherits it); MonteCarlo/Makefile's own local-looking GSL override is +# actually dead code (gated on a $(HAVE_GSL) variable nothing ever sets), so +# GSL flags belong here, build-wide, not scoped to one file. add_library(trick_build_flags INTERFACE) target_compile_features(trick_build_flags INTERFACE cxx_std_17) target_compile_options(trick_build_flags INTERFACE -fpic) @@ -128,6 +131,17 @@ if(TRICK_CIVETWEB_HOME) target_compile_definitions(trick_build_flags INTERFACE USE_CIVETWEB) target_include_directories(trick_build_flags INTERFACE ${TRICK_CIVETWEB_HOME}/include) endif() +if(GSL_HOME) + target_compile_definitions(trick_build_flags INTERFACE _HAVE_GSL) + # Makefile.common:164-177's nested ifneq excludes both /usr and + # /usr/local (the latter for e.g. Intel-mac Homebrew, whose default + # prefix is /usr/local rather than Apple Silicon's /opt/homebrew) from + # needing an explicit -I, since both are already on the compiler's + # default include search path. + if(NOT GSL_HOME STREQUAL "/usr" AND NOT GSL_HOME STREQUAL "/usr/local") + target_include_directories(trick_build_flags INTERFACE ${GSL_HOME}/include) + endif() +endif() add_subdirectory(trick_source/codegen/Interface_Code_Gen) include(TrickICG) diff --git a/cmake/TrickICG.cmake b/cmake/TrickICG.cmake index afd6e9525..df71af1ee 100644 --- a/cmake/TrickICG.cmake +++ b/cmake/TrickICG.cmake @@ -26,6 +26,25 @@ set(TRICK_IO_SRC_DIR ${CMAKE_BINARY_DIR}/io_src) set(TRICK_IO_SRC_LIST_FILE ${TRICK_IO_SRC_DIR}/.generated_sources.cmake) +# Conservative net (R7): a glob of every header ICG might transitively pull +# in. This does two distinct jobs, both needed: +# - Passed to the custom command's DEPENDS below, so a *content* edit to any +# header already in this set (e.g. adding a checkpointable member to +# include/trick/Executive.hh) touches its mtime and makes the *next* +# `cmake --build` (no reconfigure needed) re-run trick-ICG and regenerate +# the stale io_*.cpp — this is the half that actually matters day to day. +# - CONFIGURE_DEPENDS additionally forces a reconfigure when the *set* of +# matching files changes (a header added/removed), so newly-created +# headers get picked up too. +# It does not need to be an exact dependency list — same tradeoff as ICG's +# own conservative header-set discovery — but it must actually be used +# somewhere to do anything at all. +file(GLOB_RECURSE _tr_all_trick_headers CONFIGURE_DEPENDS + ${CMAKE_SOURCE_DIR}/include/trick/*.h + ${CMAKE_SOURCE_DIR}/include/trick/*.hh + ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/*.hh +) + # ── Flags mirroring TRICK_SYSTEM_CXXFLAGS / TRICK_CXXFLAGS (Makefile.common:98-197) ── set(_tr_icg_flags -I${CMAKE_SOURCE_DIR}/trick_source @@ -67,7 +86,7 @@ add_custom_command( -DOUTPUT_FILE=${TRICK_IO_SRC_LIST_FILE} -P ${CMAKE_SOURCE_DIR}/cmake/scripts/GenerateIOSrcList.cmake WORKING_DIRECTORY ${TRICK_IO_SRC_DIR} - DEPENDS trick-ICG ${CMAKE_SOURCE_DIR}/include/trick/files_to_ICG.hh + DEPENDS trick-ICG ${CMAKE_SOURCE_DIR}/include/trick/files_to_ICG.hh ${_tr_all_trick_headers} COMMENT "Running trick-ICG over Trick core headers (-sim_services)" VERBATIM ) @@ -87,11 +106,3 @@ else() endif() # Re-run configure automatically once trick_io_src_gen produces a fresh list. set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${TRICK_IO_SRC_LIST_FILE}) -# Conservative net: also reconfigure if any Trick header changes (R7 — this -# is for cache-invalidation only, mirroring make's "rerun whenever headers -# change" behavior; it does not need to be an exact dependency list). -file(GLOB_RECURSE _tr_all_trick_headers CONFIGURE_DEPENDS - ${CMAKE_SOURCE_DIR}/include/trick/*.h - ${CMAKE_SOURCE_DIR}/include/trick/*.hh - ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/*.hh -) diff --git a/trick_source/sim_services/CMakeLists.txt b/trick_source/sim_services/CMakeLists.txt index 0caafb8e1..a2bbba9f3 100644 --- a/trick_source/sim_services/CMakeLists.txt +++ b/trick_source/sim_services/CMakeLists.txt @@ -262,13 +262,12 @@ if(HDF5_HOME) set_property(SOURCE DataRecord/DRHDF5.cpp APPEND PROPERTY COMPILE_OPTIONS "-I${HDF5_HOME}/include") endif() endif() -# GSL support in MonteCarlo (MonteCarlo/Makefile:6-11). -if(GSL_HOME) - set_property(SOURCE MonteCarlo/MonteCarlo.cpp APPEND PROPERTY COMPILE_DEFINITIONS _HAVE_GSL) - if(NOT GSL_HOME STREQUAL "/usr") - set_property(SOURCE MonteCarlo/MonteCarlo.cpp APPEND PROPERTY COMPILE_OPTIONS "-I${GSL_HOME}/include") - endif() -endif() +# GSL support for MonteCarlo.cpp comes from trick_build_flags (root +# CMakeLists.txt), not a per-file override here: MonteCarlo/Makefile's own +# GSL block is gated on $(HAVE_GSL), a variable nothing in the config system +# ever sets — dead code. The _HAVE_GSL define MonteCarlo.cpp (and +# include/trick/rand_generator.h) actually see in the real build comes +# entirely from Makefile.common's global TRICK_SYSTEM_CXXFLAGS. add_subdirectory(MemoryManager) add_subdirectory(InputProcessor) diff --git a/trick_source/trick_utils/math/CMakeLists.txt b/trick_source/trick_utils/math/CMakeLists.txt index 2c56fe062..0f3045791 100644 --- a/trick_source/trick_utils/math/CMakeLists.txt +++ b/trick_source/trick_utils/math/CMakeLists.txt @@ -92,10 +92,6 @@ set( TRICKMATH_SRC add_library( trick_math STATIC ${TRICKMATH_SRC}) target_link_libraries( trick_math PUBLIC trick_build_flags ) set_target_properties( trick_math PROPERTIES ARCHIVE_OUTPUT_NAME trick_math) - -if(GSL_HOME AND NOT GSL_HOME STREQUAL "/usr/local") - target_include_directories( trick_math PRIVATE ${GSL_HOME}/include ) -endif() -if(GSL_HOME) - target_compile_definitions( trick_math PRIVATE _HAVE_GSL ) -endif() +# GSL flags (when GSL_HOME is set) come from trick_build_flags — Makefile.common +# puts them on TRICK_SYSTEM_CXXFLAGS globally, and math/Makefile has no local +# override, so it only ever gets them that way in the real build too. From 781a7e72773f75876ea7b3b16a6c6205ee803cf9 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 7 Jul 2026 11:37:36 -0500 Subject: [PATCH 07/28] cmake: transition: phase 4 --- CMakeLists.txt | 24 +- cmake/TrickInstall.cmake | 20 +- .../Apps/ExternalPrograms/CMakeLists.txt | 13 + .../data_products/Apps/Trk2csv/CMakeLists.txt | 20 ++ trick_source/data_products/CMakeLists.txt | 21 +- .../DPX/APPS/GXPLOT/CMakeLists.txt | 70 ++++-- trick_source/data_products/DPX/CMakeLists.txt | 12 +- .../data_products/DPX/DPC/CMakeLists.txt | 40 ++-- .../data_products/DPX/DPM/CMakeLists.txt | 54 +++-- .../DPX/DPV/UTILS/CMakeLists.txt | 11 +- .../data_products/EQParse/CMakeLists.txt | 38 +-- trick_source/data_products/Log/CMakeLists.txt | 54 +++-- trick_source/data_products/Var/CMakeLists.txt | 5 +- .../data_products/units/CMakeLists.txt | 21 +- trick_source/java/CMakeLists.txt | 226 ++++-------------- trick_source/web/CivetServer/CMakeLists.txt | 23 ++ 16 files changed, 354 insertions(+), 298 deletions(-) create mode 100644 trick_source/data_products/Apps/ExternalPrograms/CMakeLists.txt create mode 100644 trick_source/data_products/Apps/Trk2csv/CMakeLists.txt create mode 100644 trick_source/web/CivetServer/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 86388a269..1d1dab931 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,9 +127,14 @@ endif() if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") target_compile_options(trick_build_flags INTERFACE -fcolor-diagnostics) endif() -if(TRICK_CIVETWEB_HOME) +# USE_CIVETWEB/CIVETWEB_HOME (not the raw TRICK_CIVETWEB_HOME cache option) +# are what cmake/TrickPrograms.cmake actually resolved detection to — an +# unset TRICK_CIVETWEB_HOME can still auto-detect civetweb.h at /usr and set +# USE_CIVETWEB=1, matching config_user.mk, so this must gate on the same +# variables config_user.mk uses (see A1/tr_civetweb_home.m4). +if(USE_CIVETWEB) target_compile_definitions(trick_build_flags INTERFACE USE_CIVETWEB) - target_include_directories(trick_build_flags INTERFACE ${TRICK_CIVETWEB_HOME}/include) + target_include_directories(trick_build_flags INTERFACE ${CIVETWEB_HOME}/include) endif() if(GSL_HOME) target_compile_definitions(trick_build_flags INTERFACE _HAVE_GSL) @@ -165,6 +170,21 @@ target_link_libraries(trick PUBLIC trick_build_flags) set_target_properties(trick PROPERTIES ARCHIVE_OUTPUT_NAME trick) add_dependencies(trick trick_io_src_gen) +# ────────────────────────────────────────────────────────────────────────── +# Phase 4 — optional components: civetweb, Java GUIs, data_products +# ────────────────────────────────────────────────────────────────────────── +if(USE_CIVETWEB) + add_subdirectory(trick_source/web/CivetServer) +endif() + +if(TRICK_USE_JAVA) + add_subdirectory(trick_source/java) +endif() + +if(TRICK_BUILD_DP) + add_subdirectory(trick_source/data_products) +endif() + # ────────────────────────────────────────────────────────────────────────── # Phase 3 — install rules, stage target, CTest # ────────────────────────────────────────────────────────────────────────── diff --git a/cmake/TrickInstall.cmake b/cmake/TrickInstall.cmake index 57337b905..e31b8ab0e 100644 --- a/cmake/TrickInstall.cmake +++ b/cmake/TrickInstall.cmake @@ -35,6 +35,9 @@ set(TRICK_INSTALL_ARCHIVES if(TRICK_USE_ER7_UTILS) list(APPEND TRICK_INSTALL_ARCHIVES er7_utils) endif() +if(USE_CIVETWEB) + list(APPEND TRICK_INSTALL_ARCHIVES trickCivet) +endif() install(TARGETS ${TRICK_INSTALL_ARCHIVES} ARCHIVE DESTINATION ${TRICK_LIB_SUBDIR} ) @@ -61,12 +64,21 @@ if(TRICK_USE_ER7_UTILS) endif() # ── libexec/trick: perl helpers (configuration_processor, make_makefile_*, pm/, -# ...) verbatim. Java jars land here too but are a Phase 4 add. +# ...) verbatim. install(DIRECTORY ${CMAKE_SOURCE_DIR}/libexec/trick DESTINATION libexec USE_SOURCE_PERMISSIONS ) +# ── libexec/trick/java/build: Trick's Java GUI jars, built to the build tree +# by trick_source/java/CMakeLists.txt (D3 — never written into source). +if(TRICK_USE_JAVA) + install(DIRECTORY ${CMAKE_BINARY_DIR}/libexec/trick/java/build/ + DESTINATION libexec/trick/java/build + FILES_MATCHING PATTERN "*.jar" + ) +endif() + # ── share/trick: static content from source (makefiles, sim_objects, pymods, # trickops, swig *.i and the checked-in *.py, ...). Exclude the generated # artifacts so a dirty dev tree can't ship stale copies — the build-tree @@ -112,3 +124,9 @@ add_dependencies(stage trick-ICG trick_io_src_gen ) +if(TRICK_USE_JAVA) + add_dependencies(stage trick-java) +endif() +if(TRICK_BUILD_DP) + add_dependencies(stage trick-data-products) +endif() diff --git a/trick_source/data_products/Apps/ExternalPrograms/CMakeLists.txt b/trick_source/data_products/Apps/ExternalPrograms/CMakeLists.txt new file mode 100644 index 000000000..0e6910605 --- /dev/null +++ b/trick_source/data_products/Apps/ExternalPrograms/CMakeLists.txt @@ -0,0 +1,13 @@ +# DP "external program" plugin .so's — ported from this directory's own +# makefile (the authority). Only dp_test.so and dp_subtract.so are actually +# built by `make all` (OBJECTS0's other targets, e.g. dp_lvlh_attitude.so, +# are real rules but nothing in the makefile's dependency graph reaches +# them — `objects: $(OBJECTS1)` where OBJECTS1 = dp_test.so dp_subtract.so +# only; the fuller OBJECTS0 list is commented out). No stale 2019 +# CMakeLists.txt existed for this directory. +add_library(dp_test MODULE dp_test.c) +add_library(dp_subtract MODULE dp_subtract.c) +set_target_properties(dp_test dp_subtract PROPERTIES + PREFIX "" + SUFFIX ".so" +) diff --git a/trick_source/data_products/Apps/Trk2csv/CMakeLists.txt b/trick_source/data_products/Apps/Trk2csv/CMakeLists.txt new file mode 100644 index 000000000..6c556cef5 --- /dev/null +++ b/trick_source/data_products/Apps/Trk2csv/CMakeLists.txt @@ -0,0 +1,20 @@ +# trick-trk2ascii — ported from this directory's own makefile (the +# authority). No stale 2019 CMakeLists.txt existed for this directory. +# +# The makefile's link line is `-L../../lib_ -llog -lvar -L$(TRICK_LIB_DIR) +# -ltrick_units`: -L search paths are cumulative in link order, so +# `-ltrick_units` actually resolves against data_products/units's own +# libtrick_units.a (the first -L given) rather than trick_utils/units' +# same-named archive at TRICK_LIB_DIR — confirmed by nm against a real +# make build's liblog.a/libtrick_units.a in trick_source/data_products/ +# lib_Darwin_25 (TrickBinary.cpp calls map_trick_units_to_udunits(), which +# only data_products/units/map_trick_units_to_udunits.cpp defines; the core +# trick_utils/units archive does not). Link dp_units, not trick_units. +add_executable(trick-trk2ascii trk2ascii.cpp) +target_include_directories(trick-trk2ascii PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../..) +target_link_libraries(trick-trk2ascii PRIVATE dp_log dp_var dp_units) +if(APPLE) + target_link_libraries(trick-trk2ascii PRIVATE -lc++abi) +endif() + +install(TARGETS trick-trk2ascii RUNTIME DESTINATION bin) diff --git a/trick_source/data_products/CMakeLists.txt b/trick_source/data_products/CMakeLists.txt index 63b3380a7..6fa3ff8f9 100644 --- a/trick_source/data_products/CMakeLists.txt +++ b/trick_source/data_products/CMakeLists.txt @@ -1,11 +1,28 @@ - +# data_products: ported from this directory's own makefile (the authority). +# LIBDIRS always build; fermi-ware only exists on some checkouts (this one +# doesn't have it — matches DPX's FXPLOT gate below); APPDIRS = DPX, +# Apps/Trk2csv, Apps/ExternalPrograms. add_subdirectory(Var) add_subdirectory(Log) add_subdirectory(EQParse) add_subdirectory(units) -if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/fermi-ware/CMakeLists.txt ) +if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/fermi-ware/CMakeLists.txt) add_subdirectory(fermi-ware) endif() add_subdirectory(DPX) +add_subdirectory(Apps/Trk2csv) +add_subdirectory(Apps/ExternalPrograms) + +# Umbrella target for the Phase 3 `stage` install target to depend on. +add_custom_target(trick-data-products) +add_dependencies(trick-data-products + dp_var dp_log dp_eqparse dp_units + DPM DPC dpv_utils + trick-trk2ascii + dp_test dp_subtract +) +if(USE_X_WINDOWS) + add_dependencies(trick-data-products trick-gxplot) +endif() diff --git a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt index 68dc33136..477461eef 100644 --- a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt @@ -1,22 +1,56 @@ +# trick-gxplot — ported from this directory's own makefile (the authority). +# parse_format.c was missing from the stale 2019 source list (Part A4: the +# stale files are hints, not the authority) — without it gp_view.cpp's +# parse_format() calls fail to link. +# +# HDF5_LIB / -lc++abi / DPC listed twice mirror the makefile's own ALL_LIBS + +# trailing ${CONTROLLER_LIBS} -lDPV (a real circular DPC<->DPV dependency the +# make link line works around by repeating -lDPC after -lDPV). +# +# -ltrick_units resolves against data_products/units's own libtrick_units.a, +# not trick_utils/units' same-named archive — see the comment in +# ../../Apps/Trk2csv/CMakeLists.txt for the -L-search-order evidence. Link +# dp_units, not trick_units. +set(GXPLOT_SRC + parse_format.c + gp_view.cpp + gp_colors.cpp + gp_utilities.cpp + gp_version.cpp + gxplot.cpp + gp_view_curve_node.cpp + gp_view_plot_node.cpp + gp_view_page_node.cpp + gp_view_product_node.cpp +) -set( GXPLOT_SRC - gp_colors - gp_utilities - gp_version - gp_view - gp_view_curve_node - gp_view_page_node - gp_view_plot_node - gp_view_product_node - gxplot +add_executable(trick-gxplot ${GXPLOT_SRC}) +target_include_directories(trick-gxplot PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_SOURCE_DIR}/include + ${X11_X11_INCLUDE_PATH} + ${LIBXML2_INCLUDE_DIR} ) +separate_arguments(_tr_gxplot_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_includes}) -add_executable( trick-gxplot ${GXPLOT_SRC}) -target_include_directories( trick-gxplot PUBLIC ${X11_X11_INCLUDE_PATH} ) -target_include_directories( trick-gxplot PUBLIC ${LIBXML2_INCLUDE_DIR} ) -target_include_directories( trick-gxplot PUBLIC ../.. ) -target_link_libraries( trick-gxplot DPC DPM dp_log dp_var dpv_utils dp_eqparse dp_units - -ldl - ${X11_Xt_LIB} ${X11_X11_LIB} - ${UDUNITS2_LIBRARIES} ${LIBXML2_LIBRARIES}) +target_link_libraries(trick-gxplot PRIVATE + DPC DPM dp_log dp_var dp_eqparse dp_units + dpv_utils + ${X11_Xt_LIB} ${X11_X11_LIB} + ${LIBXML2_LIBRARIES} + DPC dpv_utils + dl +) +separate_arguments(_tr_gxplot_udunits_ldflags NATIVE_COMMAND "${UDUNITS_LDFLAGS}") +target_link_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_ldflags}) +if(HDF5_HOME) + target_link_libraries(trick-gxplot PRIVATE -L${HDF5_HOME}/lib -lhdf5_hl -lhdf5 -lsz) +endif() +if(APPLE) + target_link_libraries(trick-gxplot PRIVATE -lc++abi) +else() + target_link_libraries(trick-gxplot PRIVATE rt) +endif() +install(TARGETS trick-gxplot RUNTIME DESTINATION bin) diff --git a/trick_source/data_products/DPX/CMakeLists.txt b/trick_source/data_products/DPX/CMakeLists.txt index 1209cad47..cf9e3d845 100644 --- a/trick_source/data_products/DPX/CMakeLists.txt +++ b/trick_source/data_products/DPX/CMakeLists.txt @@ -1,8 +1,12 @@ - +# ported from this directory's own makefile: LIBDIRS always build; APPDIRS +# (GXPLOT, and FXPLOT if fermi-ware/ exists) only when USE_X_WINDOWS. add_subdirectory(DPM) add_subdirectory(DPC) add_subdirectory(DPV/UTILS) -add_subdirectory(APPS/GXPLOT) -if( EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../fermi-ware/CMakeLists.txt ) - add_subdirectory(APPS/FXPLOT) + +if(USE_X_WINDOWS) + add_subdirectory(APPS/GXPLOT) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../fermi-ware/CMakeLists.txt) + add_subdirectory(APPS/FXPLOT) + endif() endif() diff --git a/trick_source/data_products/DPX/DPC/CMakeLists.txt b/trick_source/data_products/DPX/DPC/CMakeLists.txt index fa9985d08..95491fa0d 100644 --- a/trick_source/data_products/DPX/DPC/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPC/CMakeLists.txt @@ -1,20 +1,24 @@ - -set( DPC_SRC - DPC_TimeCstrDataStream - DPC_UnitConvDataStream - DPC_datastream_supplier - DPC_delta_curve - DPC_delta_plot - DPC_page - DPC_plot - DPC_product - DPC_standard_plot - DPC_std_curve - DPC_table +# libDPC.a — ported from this directory's own makefile (the authority). +set(DPC_SRC + DPC_TimeCstrDataStream.cpp + DPC_UnitConvDataStream.cpp + DPC_datastream_supplier.cpp + DPC_delta_curve.cpp + DPC_delta_plot.cpp + DPC_page.cpp + DPC_plot.cpp + DPC_product.cpp + DPC_standard_plot.cpp + DPC_std_curve.cpp + DPC_table.cpp ) -add_library( DPC STATIC ${DPC_SRC}) -target_include_directories( DPC PUBLIC ${LIBXML2_INCLUDE_DIR} ) -target_include_directories( DPC PUBLIC ${UDUNITS2_INCLUDES} ) -target_include_directories( DPC PUBLIC .. ) - +add_library(DPC STATIC ${DPC_SRC}) +target_include_directories(DPC PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_SOURCE_DIR}/include + ${LIBXML2_INCLUDE_DIR} +) +separate_arguments(_tr_dpc_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(DPC PRIVATE ${_tr_dpc_udunits_includes}) diff --git a/trick_source/data_products/DPX/DPM/CMakeLists.txt b/trick_source/data_products/DPX/DPM/CMakeLists.txt index dae9c46bf..ff6d94578 100644 --- a/trick_source/data_products/DPX/DPM/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPM/CMakeLists.txt @@ -1,27 +1,31 @@ - -set( DPM_SRC - DPM_attribute - DPM_axis - DPM_column - DPM_component - DPM_curve - DPM_extfn - DPM_inputs - DPM_measurement - DPM_outputs - DPM_page - DPM_parse_tree - DPM_product - DPM_relation - DPM_run - DPM_session - DPM_table - DPM_time_constraints - DPM_var +# libDPM.a — ported from this directory's own makefile (the authority). +set(DPM_SRC + DPM_attribute.cpp + DPM_axis.cpp + DPM_column.cpp + DPM_component.cpp + DPM_curve.cpp + DPM_extfn.cpp + DPM_inputs.cpp + DPM_measurement.cpp + DPM_outputs.cpp + DPM_page.cpp + DPM_parse_tree.cpp + DPM_product.cpp + DPM_relation.cpp + DPM_run.cpp + DPM_session.cpp + DPM_table.cpp + DPM_time_constraints.cpp + DPM_var.cpp ) -add_library( DPM STATIC ${DPM_SRC}) -target_include_directories( DPM PUBLIC ${LIBXML2_INCLUDE_DIR} ) -target_include_directories( DPM PUBLIC ${UDUNITS2_INCLUDES} ) -target_include_directories( DPM PUBLIC .. ) - +add_library(DPM STATIC ${DPM_SRC}) +target_include_directories(DPM PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_SOURCE_DIR}/include + ${LIBXML2_INCLUDE_DIR} +) +separate_arguments(_tr_dpm_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(DPM PRIVATE ${_tr_dpm_udunits_includes}) diff --git a/trick_source/data_products/DPX/DPV/UTILS/CMakeLists.txt b/trick_source/data_products/DPX/DPV/UTILS/CMakeLists.txt index b5a1f7cba..78e59fa7d 100644 --- a/trick_source/data_products/DPX/DPV/UTILS/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPV/UTILS/CMakeLists.txt @@ -1,7 +1,4 @@ - -set( DPV_UTILS_SRC - DPV_textbuffer -) - -add_library( dpv_utils STATIC ${DPV_UTILS_SRC}) - +# libDPV.a — ported from this directory's own makefile (the authority). +add_library(dpv_utils STATIC DPV_textbuffer.cpp) +target_include_directories(dpv_utils PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../..) +set_target_properties(dpv_utils PROPERTIES ARCHIVE_OUTPUT_NAME DPV) diff --git a/trick_source/data_products/EQParse/CMakeLists.txt b/trick_source/data_products/EQParse/CMakeLists.txt index 54b79a046..3aa6846cf 100644 --- a/trick_source/data_products/EQParse/CMakeLists.txt +++ b/trick_source/data_products/EQParse/CMakeLists.txt @@ -1,19 +1,23 @@ - -set ( DP_EQPARSE_SRC - eqparse - eqparse_chkvalid - eqparse_error - eqparse_evaluate - eqparse_fillno - eqparse_funcsub - eqparse_math - eqparse_negcheck - eqparse_operatorcheck - eqparse_postfix - eqparse_stack - eqparse_takeinput - eqparse_test +# libeqparse.a — ported from this directory's own makefile (the authority). +# eqparse_test.c is a standalone smoke-test executable's source, NOT part of +# the library (makefile: E_C_SRC = $(filter-out eqparse_test.c, $(wildcard +# *.c))) — the stale 2019 CMakeLists.txt included it in the library, which +# is wrong (see CMAKE_MIGRATION_PLAN.md Part A4: the stale files are hints, +# not the authority). +set(DP_EQPARSE_SRC + eqparse.c + eqparse_chkvalid.c + eqparse_error.c + eqparse_evaluate.c + eqparse_fillno.c + eqparse_funcsub.c + eqparse_math.c + eqparse_negcheck.c + eqparse_operatorcheck.c + eqparse_postfix.c + eqparse_stack.c + eqparse_takeinput.c ) -add_library( dp_eqparse STATIC ${DP_EQPARSE_SRC}) - +add_library(dp_eqparse STATIC ${DP_EQPARSE_SRC}) +set_target_properties(dp_eqparse PROPERTIES ARCHIVE_OUTPUT_NAME eqparse) diff --git a/trick_source/data_products/Log/CMakeLists.txt b/trick_source/data_products/Log/CMakeLists.txt index 9e12cbed8..fc3333ace 100644 --- a/trick_source/data_products/Log/CMakeLists.txt +++ b/trick_source/data_products/Log/CMakeLists.txt @@ -1,24 +1,36 @@ - -# need to add TrickHDF5 if HDF5 found -set ( DP_LOG_SRC - Csv - DataStream - DataStreamFactory - DataStreamGroup - Delta - ExternalProgram - MatLab - MatLab4 - TrickBinary - log - multiLog - parseLogHeader - trick_byteswap +# liblog.a — ported from this directory's own makefile (the authority). +# TrickHDF5.cpp/DataStreamFactory.cpp's extra -DHDF5/-I only apply when HDF5 +# was detected (makefile:24-28), mirrored below with per-file overrides. +set(DP_LOG_SRC + Csv.cpp + DataStream.cpp + DataStreamFactory.cpp + DataStreamGroup.cpp + Delta.cpp + ExternalProgram.cpp + MatLab.cpp + MatLab4.cpp + TrickBinary.cpp + log.cpp + multiLog.cpp + parseLogHeader.cpp + trick_byteswap.cpp ) -# TrickHDF5 - -add_library( dp_log STATIC ${DP_LOG_SRC}) -target_include_directories( dp_log PUBLIC .. ) -target_include_directories( dp_log PUBLIC ${UDUNITS2_INCLUDES} ) +if(HDF5_HOME) + list(APPEND DP_LOG_SRC TrickHDF5.cpp) +endif() +add_library(dp_log STATIC ${DP_LOG_SRC}) +target_include_directories(dp_log PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/.. + ${CMAKE_SOURCE_DIR}/include +) +separate_arguments(_tr_dp_log_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(dp_log PRIVATE ${_tr_dp_log_udunits_includes}) +set_target_properties(dp_log PROPERTIES ARCHIVE_OUTPUT_NAME log) +if(HDF5_HOME) + set_source_files_properties(TrickHDF5.cpp DataStreamFactory.cpp PROPERTIES + COMPILE_OPTIONS "-I${HDF5_HOME}/include;-DHDF5" + ) +endif() diff --git a/trick_source/data_products/Var/CMakeLists.txt b/trick_source/data_products/Var/CMakeLists.txt index d78198f0d..ea148a945 100644 --- a/trick_source/data_products/Var/CMakeLists.txt +++ b/trick_source/data_products/Var/CMakeLists.txt @@ -1 +1,4 @@ -add_library( dp_var STATIC var ) +# libvar.a — ported from this directory's own makefile (the authority). +add_library(dp_var STATIC var.cpp) +target_include_directories(dp_var PRIVATE ${CMAKE_SOURCE_DIR}/include) +set_target_properties(dp_var PROPERTIES ARCHIVE_OUTPUT_NAME var) diff --git a/trick_source/data_products/units/CMakeLists.txt b/trick_source/data_products/units/CMakeLists.txt index 8f55c7a00..679f52d6b 100644 --- a/trick_source/data_products/units/CMakeLists.txt +++ b/trick_source/data_products/units/CMakeLists.txt @@ -1,10 +1,13 @@ - -set ( DP_UNITS_SRC - init_units_system - map_trick_units_to_udunits - units_conv +# libtrick_units.a (data_products' own copy — unrelated to and not linked +# against trick_source/trick_utils/units' same-named archive; see this +# directory's makefile, whose output nothing else in the make flow +# references). Built anyway to match `make all`'s artifact set. +add_library(dp_units STATIC + init_units_system.cpp + map_trick_units_to_udunits.cpp + units_conv.c ) - -add_library( dp_units STATIC ${DP_UNITS_SRC}) -target_include_directories( dp_units PUBLIC ${UDUNITS2_INCLUDES} ) - +target_include_directories(dp_units PRIVATE ${CMAKE_SOURCE_DIR}/include) +separate_arguments(_tr_dp_units_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(dp_units PRIVATE ${_tr_dp_units_udunits_includes}) +set_target_properties(dp_units PROPERTIES ARCHIVE_OUTPUT_NAME trick_units) diff --git a/trick_source/java/CMakeLists.txt b/trick_source/java/CMakeLists.txt index 997b621b5..923308092 100644 --- a/trick_source/java/CMakeLists.txt +++ b/trick_source/java/CMakeLists.txt @@ -1,176 +1,56 @@ +# Trick's Java GUIs (trickview, simcontrol, dp, sie, ...), ported from this +# directory's own Makefile (top-level Makefile:242-274) and this project's +# pom.xml, which already has a "cmake" Maven profile +# (cmaketrue) that +# redirects the build output directory to -DbuildDirectory, matching the +# stale 2019 CMakeLists.txt's invocation — salvaged from there, but as a +# real add_custom_command with dependency tracking instead of a +# hand-maintained ~150-file source DEPENDS list, which had already drifted +# from the source tree once (see the sim_services/trick_utils source-list +# note in the Phase 2 status writeup). +# +# TRICK_OFFLINE copies prebuilt jars from trick-offline/ instead of invoking +# mvn (top-level Makefile:245-272); mirrored here rather than deferring to +# `mvn -o` since offline mode's jars are pre-built releases, not merely a +# local Maven cache. -############################################################### -# Build Java -############################################################### +set(TRICK_JAVA_BUILD_DIR ${CMAKE_BINARY_DIR}/libexec/trick/java/build) +if(NOT TRICK_OFFLINE) + file(GLOB_RECURSE TRICK_JAVA_SOURCES CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/src/main/java/*.java + ) -set(JAVA_JARS - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/MTV.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/TrickView.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/JXPlot.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/QP.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/DP.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/Sie.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/Dre.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/SimControl.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/SimSniffer.jar - ${CMAKE_BINARY_DIR}/libexec/trick/java/build/MM.jar -) - -add_custom_target(java ALL DEPENDS ${JAVA_JARS}) - -add_custom_command( - OUTPUT - ${JAVA_JARS} - COMMAND COMMAND - ${MAVEN_EXECUTABLE} -q package -Dcmake=true -DbuildDirectory=${CMAKE_BINARY_DIR}/libexec/trick/java/build - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - DEPENDS - src/main/java/trick/dre/DreApplication.java - src/main/java/trick/sniffer/SimulationSniffer.java - src/main/java/trick/sniffer/SimulationInformation.java - src/main/java/trick/sniffer/SimulationListener.java - src/main/java/trick/sniffer/SimSnifferApplication.java - src/main/java/trick/simcontrol/utils/SimControlActionController.java - src/main/java/trick/simcontrol/utils/SimState.java - src/main/java/trick/simcontrol/SimControlApplication.java - src/main/java/trick/test/Client.java - src/main/java/trick/tv/TVApplication.java - src/main/java/trick/tv/StripChart.java - src/main/java/trick/tv/TVVariableTree.java - src/main/java/trick/tv/StripChartManager.java - src/main/java/trick/tv/DoubleComboBox.java - src/main/java/trick/tv/TVBean.java - src/main/java/trick/tv/TVDouble.java - src/main/java/trick/tv/TVInteger.java - src/main/java/trick/tv/TrickViewFluent.java - src/main/java/trick/tv/VariableTable.java - src/main/java/trick/tv/TVLong.java - src/main/java/trick/tv/TVEnumeration.java - src/main/java/trick/tv/TVShort.java - src/main/java/trick/tv/TVByte.java - src/main/java/trick/tv/TVBoolean.java - src/main/java/trick/tv/TVString.java - src/main/java/trick/tv/TVFloat.java - src/main/java/trick/common/ui/panels/ConnectionStatusBar.java - src/main/java/trick/common/ui/panels/FindBar.java - src/main/java/trick/common/ui/panels/SmallTrickIconLabel.java - src/main/java/trick/common/ui/panels/AnimationPlayer.java - src/main/java/trick/common/ui/panels/DataPanel.java - src/main/java/trick/common/ui/panels/ListPanel.java - src/main/java/trick/common/ui/panels/DynamicTree.java - src/main/java/trick/common/ui/UIUtils.java - src/main/java/trick/common/ui/components/DoubleJSlider.java - src/main/java/trick/common/ui/components/NumberTextField.java - src/main/java/trick/common/ui/components/CommonTreeNode.java - src/main/java/trick/common/ui/components/FontChooser.java - src/main/java/trick/common/ui/TrickFileFilter.java - src/main/java/trick/common/utils/vs/VSFloat.java - src/main/java/trick/common/utils/vs/VariableListener.java - src/main/java/trick/common/utils/vs/VSShort.java - src/main/java/trick/common/utils/vs/VSString.java - src/main/java/trick/common/utils/vs/VariableTracker.java - src/main/java/trick/common/utils/vs/VSBoolean.java - src/main/java/trick/common/utils/vs/Variable.java - src/main/java/trick/common/utils/vs/VariableServerFluent.java - src/main/java/trick/common/utils/vs/VSDouble.java - src/main/java/trick/common/utils/vs/VSInteger.java - src/main/java/trick/common/utils/vs/VSValue.java - src/main/java/trick/common/utils/vs/VSLong.java - src/main/java/trick/common/utils/vs/VSByte.java - src/main/java/trick/common/utils/SortedListModel.java - src/main/java/trick/common/utils/UnitType.java - src/main/java/trick/common/utils/DataReader.java - src/main/java/trick/common/utils/XMLCreator.java - src/main/java/trick/common/utils/ErrorChecker.java - src/main/java/trick/common/utils/UnitInfixExpression.java - src/main/java/trick/common/utils/CSVDataReader.java - src/main/java/trick/common/utils/LogVar.java - src/main/java/trick/common/utils/VariableServerConnection.java - src/main/java/trick/common/utils/BinaryDataReader.java - src/main/java/trick/common/utils/LogHeaderReader.java - src/main/java/trick/common/utils/TrickColors.java - src/main/java/trick/common/TrickApplication.java - src/main/java/trick/common/RunTimeTrickApplication.java - src/main/java/trick/vc/VariableCounter.java - src/main/java/trick/dataproducts/plot/JXPlotApplication.java - src/main/java/trick/dataproducts/plot/utils/TrickChart.java - src/main/java/trick/dataproducts/plot/utils/TrickChartControlPanel.java - src/main/java/trick/dataproducts/plot/utils/TrickXYPlot.java - src/main/java/trick/dataproducts/plot/utils/TrickChartFrame.java - src/main/java/trick/dataproducts/plot/utils/TrickXYSeries.java - src/main/java/trick/dataproducts/plot/utils/PlotUtils.java - src/main/java/trick/dataproducts/plot/utils/TrickChartPanel.java - src/main/java/trick/dataproducts/plot/utils/TrickChartTheme.java - src/main/java/trick/dataproducts/plot/utils/TrickFrame.java - src/main/java/trick/dataproducts/plot/utils/TrickXYLineAndShapeRenderer.java - src/main/java/trick/dataproducts/plot/utils/editor/DefaultLogAxisEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickTitleEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickNumberAxisEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickChartEditorManager.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickChartEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickPolarPlotEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickPlotEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickChartEditorFactory.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickValueAxisEditor.java - src/main/java/trick/dataproducts/plot/utils/editor/TrickAxisEditor.java - src/main/java/trick/dataproducts/plot/utils/TrickTableFrame.java - src/main/java/trick/dataproducts/utils/FileTreeNode.java - src/main/java/trick/dataproducts/utils/SessionRunTransferHandler.java - src/main/java/trick/dataproducts/utils/SessionXMLCreator.java - src/main/java/trick/dataproducts/utils/SessionRun.java - src/main/java/trick/dataproducts/utils/SimRunTree.java - src/main/java/trick/dataproducts/utils/SimDPTree.java - src/main/java/trick/dataproducts/utils/Session.java - src/main/java/trick/dataproducts/utils/SimRunDPTree.java - src/main/java/trick/dataproducts/utils/SessionDomParser.java - src/main/java/trick/dataproducts/utils/FileTreePanel.java - src/main/java/trick/dataproducts/trickqp/TrickQPApplication.java - src/main/java/trick/dataproducts/trickqp/utils/ProductTable.java - src/main/java/trick/dataproducts/trickqp/utils/TrickQPActionController.java - src/main/java/trick/dataproducts/trickqp/utils/ProductDataPanel.java - src/main/java/trick/dataproducts/trickqp/utils/QPRemoteCallInterface.java - src/main/java/trick/dataproducts/trickqp/utils/ProductVar.java - src/main/java/trick/dataproducts/trickqp/utils/ProductVarcase.java - src/main/java/trick/dataproducts/trickqp/utils/ProductMeasurement.java - src/main/java/trick/dataproducts/trickqp/utils/Product.java - src/main/java/trick/dataproducts/trickqp/utils/ProductColumn.java - src/main/java/trick/dataproducts/trickqp/utils/ProductPlot.java - src/main/java/trick/dataproducts/trickqp/utils/DataTransferHandler.java - src/main/java/trick/dataproducts/trickqp/utils/ProductCurve.java - src/main/java/trick/dataproducts/trickqp/utils/ProductPage.java - src/main/java/trick/dataproducts/trickqp/utils/ProductTree.java - src/main/java/trick/dataproducts/trickqp/utils/CommonProduct.java - src/main/java/trick/dataproducts/trickqp/utils/ProductExternalFunction.java - src/main/java/trick/dataproducts/trickqp/utils/QPRemoteCallInterfaceImpl.java - src/main/java/trick/dataproducts/trickqp/utils/ProductXMLCreator.java - src/main/java/trick/dataproducts/trickqp/utils/ProductDomParser.java - src/main/java/trick/dataproducts/trickqp/utils/ProductAxis.java - src/main/java/trick/dataproducts/trickqp/utils/VarListPanel.java - src/main/java/trick/dataproducts/DataProductsApplication.java - src/main/java/trick/dataproducts/trickdp/TrickDPApplication.java - src/main/java/trick/dataproducts/trickdp/utils/DPRemoteCallInterfaceImpl.java - src/main/java/trick/dataproducts/trickdp/utils/DPRemoteCallInterface.java - src/main/java/trick/dataproducts/trickdp/utils/TrickDPActionController.java - src/main/java/trick/dataproducts/trickdp/utils/PDFBooklet.java - src/main/java/trick/Template.java - src/main/java/trick/sie/utils/TreeModelExclusionFilter.java - src/main/java/trick/sie/utils/TreeModelSortingFilter.java - src/main/java/trick/sie/utils/SearchListener.java - src/main/java/trick/sie/utils/SieEnumeration.java - src/main/java/trick/sie/utils/VariableList.java - src/main/java/trick/sie/utils/Searcher.java - src/main/java/trick/sie/utils/SearchPanel.java - src/main/java/trick/sie/utils/SieTree.java - src/main/java/trick/sie/utils/SieTemplate.java - src/main/java/trick/sie/utils/SieVariableTree.java - src/main/java/trick/sie/utils/SieTreeModel.java - src/main/java/trick/sie/utils/TreeModelFilter.java - src/main/java/trick/sie/utils/SieResourceDomParser.java - src/main/java/trick/sie/SieApplication.java - src/main/java/trick/montemonitor/Slave.java - src/main/java/trick/montemonitor/MonteMonitorApplication.java - src/main/java/trick/mtv/MtvApp.java - src/main/java/trick/mtv/MtvView.java -) - + add_custom_command( + OUTPUT ${TRICK_JAVA_BUILD_DIR}/.stamp + COMMAND ${MVN_EXECUTABLE} -q package -Dcmake=true + -DbuildDirectory=${TRICK_JAVA_BUILD_DIR} + -Dmaven.wagon.http.retryHandler.count=15 + COMMAND ${CMAKE_COMMAND} -E touch ${TRICK_JAVA_BUILD_DIR}/.stamp + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + DEPENDS ${TRICK_JAVA_SOURCES} ${CMAKE_CURRENT_SOURCE_DIR}/pom.xml + COMMENT "Building Trick Java GUIs (mvn package)" + VERBATIM + ) + add_custom_target(trick-java ALL DEPENDS ${TRICK_JAVA_BUILD_DIR}/.stamp) +else() + set(TRICK_JAVA_OFFLINE_JARS + DP Dre JXPlot MM MTV QP Sie SimControl SimSniffer TrickView + trick-java-${TRICK_MAJOR}.${TRICK_MINOR}.${TRICK_TINY} + ) + set(TRICK_JAVA_OFFLINE_TARGETS "") + foreach(_tr_jar ${TRICK_JAVA_OFFLINE_JARS}) + set(_tr_src ${CMAKE_SOURCE_DIR}/trick-offline/${_tr_jar}.jar) + set(_tr_dst ${TRICK_JAVA_BUILD_DIR}/${_tr_jar}.jar) + add_custom_command( + OUTPUT ${_tr_dst} + COMMAND ${CMAKE_COMMAND} -E make_directory ${TRICK_JAVA_BUILD_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${_tr_src} ${_tr_dst} + DEPENDS ${_tr_src} + COMMENT "Copying offline jar ${_tr_jar}.jar" + VERBATIM + ) + list(APPEND TRICK_JAVA_OFFLINE_TARGETS ${_tr_dst}) + endforeach() + add_custom_target(trick-java ALL DEPENDS ${TRICK_JAVA_OFFLINE_TARGETS}) +endif() diff --git a/trick_source/web/CivetServer/CMakeLists.txt b/trick_source/web/CivetServer/CMakeLists.txt new file mode 100644 index 000000000..f6ab97a3c --- /dev/null +++ b/trick_source/web/CivetServer/CMakeLists.txt @@ -0,0 +1,23 @@ +# libtrickCivet.a — Trick's native civetweb variable-server webserver, +# ported from this directory's own makefile (the authority). Only added to +# the build when USE_CIVETWEB is set (Phase 4, gated on TRICK_CIVETWEB_HOME +# detection in cmake/TrickPrograms.cmake). +# +# trick/MyCivetServer.hh (and this whole feature) is compiled out entirely +# unless USE_CIVETWEB is defined, mirroring Makefile.common:162-167's +# TRICK_SYSTEM_CXXFLAGS += -DUSE_CIVETWEB — trick_build_flags already adds +# that definition globally when USE_CIVETWEB is on (root CMakeLists.txt). + +add_library(trickCivet STATIC + src/MyCivetServer.cpp + src/VariableServerSession.cpp + src/VariableServerVariable.cpp + src/http_GET_handlers.cpp + src/simpleJSON.cpp +) +target_include_directories(trickCivet PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CIVETWEB_HOME}/include +) +target_link_libraries(trickCivet PUBLIC trick_build_flags) +set_target_properties(trickCivet PROPERTIES ARCHIVE_OUTPUT_NAME trickCivet) From 449a336bcaf9ba744ecc6b23a37d681077d56d46 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 7 Jul 2026 12:38:25 -0500 Subject: [PATCH 08/28] cmake: transition: fix CommandLineArguments test that assumed an in-source build --- .gitignore | 2 ++ .../CommandLineArguments/test/Makefile | 22 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index b7137f17e..2f4f910ac 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,8 @@ lib_Darwin_* coverage.info gmon.out Makefile_jsc_dirs +.extracted +__.SYMDEF # CMake build*/ diff --git a/trick_source/sim_services/CommandLineArguments/test/Makefile b/trick_source/sim_services/CommandLineArguments/test/Makefile index d8fc2b26a..ed7498bac 100644 --- a/trick_source/sim_services/CommandLineArguments/test/Makefile +++ b/trick_source/sim_services/CommandLineArguments/test/Makefile @@ -14,8 +14,22 @@ TRICK_SYSTEM_CXXFLAGS := $(subst -isystem,-I,$(TRICK_SYSTEM_CXXFLAGS)) TRICK_CXXFLAGS += -I$(GTEST_HOME)/include -I$(TRICK_HOME)/include -g ${TRICK_SYSTEM_CXXFLAGS} ${TRICK_TEST_FLAGS} # so it seems like there's some weirdness linking in the bitfield objects since they are C -MM_OBJECTS = $(TRICK_HOME)/trick_source/sim_services/MemoryManager/object_${TRICK_HOST_CPU}/extract_bitfield.o \ - $(TRICK_HOME)/trick_source/sim_services/MemoryManager/object_${TRICK_HOST_CPU}/extract_unsigned_bitfield.o +# +# Extracted from libtrick_mm.a rather than read from MemoryManager's +# source-tree object_$(TRICK_HOST_CPU)/ dir: that path only exists after an +# in-source `make` build of Trick itself, but this test's own TRICK_HOME can +# also be an out-of-source CMake install (cmake/TrickTest.cmake overrides +# TRICK_HOME to the staged prefix, which only ships the archive, not +# per-object build artifacts). Extracting from the archive works under both +# build systems, and tolerates their differing object basenames +# (extract_bitfield.o vs extract_bitfield.c.o). +MM_OBJDIR = mm_bitfield_objs +MM_OBJECTS = $(MM_OBJDIR)/.extracted + +$(MM_OBJDIR)/.extracted: ${TRICK_LIB_DIR}/libtrick_mm.a + mkdir -p $(MM_OBJDIR) + cd $(MM_OBJDIR) && ar x $(abspath $<) $$(ar t $(abspath $<) | grep -E '^extract_(unsigned_)?bitfield') + touch $@ #TRICK_LIBS = -L${TRICK_LIB_DIR} -ltrick_mm -ltrick_units -ltrick -ltrick_mm -ltrick_units -ltrick -ltrick_pyip -ltrick_connection_handlers -ltrick_comm -ltrick_mm -ltrick_units -ltrick -ltrick_mm TRICK_LIB_NAMES = trick_mm trick_units trick trick_pyip trick_connection_handlers trick_comm @@ -44,13 +58,13 @@ test: $(TESTS) clean : rm -f $(TESTS) *.o *.gcno *.gcda - rm -rf a pre_existing_output_dir/a ../a + rm -rf a pre_existing_output_dir/a ../a $(MM_OBJDIR) create_path_test.o : create_path_test.cpp $(TRICK_CXX) $(TRICK_CXXFLAGS) -c $< create_path_test : create_path_test.o $(MM_OBJECTS) - $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) -o $@ $^ -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) -o $@ create_path_test.o $(MM_OBJDIR)/*.o -L${TRICK_HOME}/lib_${TRICK_HOST_CPU} $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) CommandLineArguments_test.o : CommandLineArguments_test.cpp $(TRICK_CXX) $(TRICK_CXXFLAGS) -c $< From c92c896c9733353c579cf328099c81ebbc344d31 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 7 Jul 2026 15:47:52 -0500 Subject: [PATCH 09/28] cmake: transition: all sim tests --- cmake/TrickTest.cmake | 81 ++++++++++++++++++- .../data_products/DPX/test/unit_test/makefile | 56 +++++++++---- .../sim_services/Integrator/test/Makefile | 51 +++++++----- trickops.py | 51 ++++++++++-- 4 files changed, 193 insertions(+), 46 deletions(-) diff --git a/cmake/TrickTest.cmake b/cmake/TrickTest.cmake index 760586ac7..1ce5f5926 100644 --- a/cmake/TrickTest.cmake +++ b/cmake/TrickTest.cmake @@ -34,7 +34,10 @@ add_test(NAME stage_install set_tests_properties(stage_install PROPERTIES FIXTURES_SETUP trick_stage) # ── Unit tests: mirror the top-level Makefile's UNIT_TEST_DIRS (sim_services/*/test -# and trick_utils/*/test), with the same %Integrator/test filter (Makefile:137). +# and trick_utils/*/test), with the same %Integrator/test filter — conditional on +# USE_ER7_UTILS, exactly like Makefile:136-138 (`ifeq ($(USE_ER7_UTILS), 0)`), not +# unconditional. This checkout defaults TRICK_USE_ER7_UTILS=ON, so Integrator/test +# is normally INCLUDED, matching `make unit_test`'s real dir count. file(GLOB _tr_unit_test_dirs ${CMAKE_SOURCE_DIR}/trick_source/sim_services/*/test ${CMAKE_SOURCE_DIR}/trick_source/trick_utils/*/test @@ -43,8 +46,8 @@ foreach(_dir ${_tr_unit_test_dirs}) if(NOT EXISTS ${_dir}/Makefile) continue() endif() - if(_dir MATCHES "Integrator/test$") - continue() # filtered out of releases by the make build (Makefile:137) + if(NOT TRICK_USE_ER7_UTILS AND _dir MATCHES "Integrator/test$") + continue() # filtered out when ER7 utils are off (Makefile:136-138) endif() # ut_, e.g. ut_MemoryManager get_filename_component(_parent ${_dir} DIRECTORY) @@ -59,6 +62,53 @@ foreach(_dir ${_tr_unit_test_dirs}) ) endforeach() +# ── DPX (data_products) unit tests: same LABEL unit as above, but its own +# makefile needs extra overrides (see that makefile's header comment) +# because CMake builds each data_products library into its own target +# output directory instead of one shared lib_/ dir. TRICK_SRC_HOME +# keeps headers resolving against the real source tree even though +# TRICK_HOME below is the staged install. +if(TRICK_BUILD_DP) + set(_tr_dpx_unit_test_dir ${CMAKE_SOURCE_DIR}/trick_source/data_products/DPX/test/unit_test) + if(EXISTS ${_tr_dpx_unit_test_dir}/makefile) + add_test(NAME ut_DataProducts + COMMAND make -C ${_tr_dpx_unit_test_dir} test + TRICK_HOME=${TRICK_STAGE_DIR} + TRICK_SRC_HOME=${CMAKE_SOURCE_DIR} + LIB_LOG_DIR=${CMAKE_BINARY_DIR}/trick_source/data_products/Log + LIB_VAR_DIR=${CMAKE_BINARY_DIR}/trick_source/data_products/Var + LIB_EQPARSE_DIR=${CMAKE_BINARY_DIR}/trick_source/data_products/EQParse + LIB_UNITS_DIR=${CMAKE_BINARY_DIR}/trick_source/data_products/units + LIB_DPM_DIR=${CMAKE_BINARY_DIR}/trick_source/data_products/DPX/DPM + LIB_DPC_DIR=${CMAKE_BINARY_DIR}/trick_source/data_products/DPX/DPC + ) + set_tests_properties(ut_DataProducts PROPERTIES + LABELS unit + FIXTURES_REQUIRED trick_stage + TIMEOUT 900 + ) + endif() +endif() + +# ── Java unit tests (JUnit, via `mvn test`). Unlike the C++ gtest suites, Maven +# resolves everything relative to trick_source/java itself, so no TRICK_HOME +# path-shape juggling is needed; -Dcmake=false matches the top-level +# Makefile's `test:` target (Makefile in trick_source/java) and writes +# surefire reports/compiled test classes under libexec/trick/java/build in +# the source tree — test output, not a product-build write (D3 is about the +# product build, same class of exception already noted for the C++ gtest +# suites' own **/test artifacts). +if(TRICK_USE_JAVA) + add_test(NAME ut_java + COMMAND ${MVN_EXECUTABLE} test -Dcmake=false -Dmaven.wagon.http.retryHandler.count=15 + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/trick_source/java + ) + set_tests_properties(ut_java PROPERTIES + LABELS unit + TIMEOUT 900 + ) +endif() + # ── Sim-flow acceptance tests (LABEL sims): trick-CP builds + runs real sims from # the stage. One data-recording/checkpoint sim and one STL sim, matching the # plan's Phase 3 step 5 ("SIM_cannon_analytic plus one data-recording/STL sim" — @@ -81,3 +131,28 @@ endfunction() trick_add_sim_flow_test(sim_demo_sdefine test/SIM_demo_sdefine) trick_add_sim_flow_test(sim_stls test/SIM_stls) + +# ── Full sim suite (LABEL sims_full): the same trickops.py/test_sims.yml suite +# `make sim_test` runs (test_overrides.mk), covering all ~52 sims in +# test_sims.yml — the two lightweight sim_demo_sdefine/sim_stls tests above are +# a fast subset/smoke-check of the same trick-CP path, not a substitute for +# this. trickops.py's --trick_dir (added alongside this CTest wiring) points +# build_cmd at /bin/trick-CP, which self-derives TRICK_HOME from its own +# location (bin/trick-CP:13, dirname($trick_bin)) — no TRICK_HOME env needed. +# --trick_top_level stays the source tree, since the test/SIM_* sim +# directories test_sims.yml references only exist there, not in the stage. +if(EXISTS ${CMAKE_SOURCE_DIR}/test_sims.yml) + find_package(Python3 COMPONENTS Interpreter) + add_test(NAME sims_full + COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/trickops.py + --trick_top_level ${CMAKE_SOURCE_DIR} + --trick_dir ${TRICK_STAGE_DIR} + --config_file test_sims.yml + --quiet + ) + set_tests_properties(sims_full PROPERTIES + LABELS sims_full + FIXTURES_REQUIRED trick_stage + TIMEOUT 3600 + ) +endif() diff --git a/trick_source/data_products/DPX/test/unit_test/makefile b/trick_source/data_products/DPX/test/unit_test/makefile index 364e79e54..e9ae007b2 100644 --- a/trick_source/data_products/DPX/test/unit_test/makefile +++ b/trick_source/data_products/DPX/test/unit_test/makefile @@ -5,16 +5,24 @@ RM = rm -rf CC = gcc CPP = g++ -DPX_DIR = ${TRICK_HOME}/trick_source/data_products/DPX -DS_DIR = ${TRICK_HOME}/trick_source/data_products +# TRICK_SRC_HOME is separate from TRICK_HOME so this test can run with +# TRICK_HOME pointed at a staged (out-of-source) CMake install while still +# finding data_products' headers, which only exist in the source tree — a +# CMake build compiles in place from source without copying headers into +# the build tree. Defaults to TRICK_HOME, preserving in-source `make` +# behavior exactly (TRICK_HOME already is the source root there). +TRICK_SRC_HOME ?= ${TRICK_HOME} + +DPX_DIR = ${TRICK_SRC_HOME}/trick_source/data_products/DPX +DS_DIR = ${TRICK_SRC_HOME}/trick_source/data_products INCDIRS = -I$(GTEST_HOME)/include \ -I/usr/include/libxml2 \ -I/usr/X11R6/include \ -I${TRICK_HOME}/include \ - -I${TRICK_HOME}/trick_source/data_products \ - -I${TRICK_HOME}/trick_source/data_products/Apps/FPlot \ - -I${TRICK_HOME}/trick_source/data_products/DPX \ + -I${DS_DIR} \ + -I${DS_DIR}/Apps/FPlot \ + -I${DPX_DIR} \ ${MOTIF_INCDIR} CFLAGS = -g -Wall -Wextra ${INCDIRS} $(UDUNITS_INCLUDES) @@ -23,6 +31,19 @@ LIB_DPX_DIR = ${DPX_DIR}/lib_${TRICK_HOST_CPU} LIB_DS_DIR = ${DS_DIR}/lib_${TRICK_HOST_CPU} # Save number plus first decimal place. +# Per-library search dirs, each independently overridable and defaulting to +# the shared lib_$(TRICK_HOST_CPU) dirs above (identical to historical +# behavior under in-source `make`). A CMake-driven test run overrides these +# individually, since CMake builds each library into its own target output +# directory (DPM/libDPM.a, DPC/libDPC.a, Log/liblog.a, ...) rather than one +# shared directory. +LIB_LOG_DIR = ${LIB_DS_DIR} +LIB_VAR_DIR = ${LIB_DS_DIR} +LIB_EQPARSE_DIR = ${LIB_DS_DIR} +LIB_UNITS_DIR = ${LIB_DS_DIR} +LIB_DPM_DIR = ${LIB_DPX_DIR} +LIB_DPC_DIR = ${LIB_DPX_DIR} + ifeq ($(TRICK_HOST_TYPE), Linux) LIBRTDEF = -lrt else @@ -42,13 +63,15 @@ endif GTEST_LIBS = -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtest_main -DP_LIBS = -L${TRICK_HOME}/trick_source/data_products/lib_${TRICK_HOST_CPU} \ - -llog -lvar -leqparse -ltrick_units +DP_LIBS = -L${LIB_LOG_DIR} -llog \ + -L${LIB_VAR_DIR} -lvar \ + -L${LIB_EQPARSE_DIR} -leqparse \ + -L${LIB_UNITS_DIR} -ltrick_units MODEL_LIBS = -lxml2 \ - -L${DPX_DIR}/lib_${TRICK_HOST_CPU} -lDPM \ - -L${TRICK_HOME}/trick_source/data_products/lib_${TRICK_HOST_CPU} -ltrick_units \ + -L${LIB_DPM_DIR} -lDPM \ + -L${LIB_UNITS_DIR} -ltrick_units \ ${GTEST_LIBS} -lpthread -ludunits2 ${UDUNITS_LDFLAGS} -CONTROLLER_LIBS = -lDPC \ +CONTROLLER_LIBS = -L${LIB_DPC_DIR} -lDPC \ ${MODEL_LIBS} ${DP_LIBS} \ ${XLIBS} ${LIBRTDEF} \ -L/usr/lib64 -L/usr/lib -lz ${HDF5_LIB} @@ -76,27 +99,26 @@ test : $(TESTS) ./DPM_test --gtest_output=xml:${TRICK_HOME}/trick_test/DataProducts_M.xml ./DS_test --gtest_output=xml:${TRICK_HOME}/trick_test/DataStream.xml -DPM_test: DPM_test.o ${LIB_DPX_DIR}/libDPM.a +DPM_test: DPM_test.o ${LIB_DPM_DIR}/libDPM.a @echo "===== Making DPM_test =====" ${CPP} -o $@ DPM_test.o ${MODEL_LIBS} -DPC_test: DPC_test.o test_view.o ${LIB_DPX_DIR}/libDPM.a ${LIB_DPX_DIR}/libDPC.a +DPC_test: DPC_test.o test_view.o ${LIB_DPM_DIR}/libDPM.a ${LIB_DPC_DIR}/libDPC.a @echo "===== Making DPC_test =====" ${CPP} -o $@ DPC_test.o test_view.o ${CONTROLLER_LIBS} -DS_test: DS_test.o ${LIB_DS_DIR}/liblog.a - @echo "===== Making DS_test ======" +DS_test: DS_test.o ${LIB_LOG_DIR}/liblog.a + @echo "===== Making DS_test ======" ${CPP} -o $@ DS_test.o ${DS_LIBS} -${LIB_DPX_DIR}/libDPM.a: +${LIB_DPM_DIR}/libDPM.a: @echo "===== Making libDPM.a =====" $(MAKE) -C ${DPX_DIR}/DPM -${LIB_DPX_DIR}/libDPC.a: +${LIB_DPC_DIR}/libDPC.a: @echo "===== Making libDPC.a =====" $(MAKE) -C ${DPX_DIR}/DPC clean: ${RM} *~ ${RM} $(TESTS) *.o - diff --git a/trick_source/sim_services/Integrator/test/Makefile b/trick_source/sim_services/Integrator/test/Makefile index 0896036ff..976425475 100644 --- a/trick_source/sim_services/Integrator/test/Makefile +++ b/trick_source/sim_services/Integrator/test/Makefile @@ -25,23 +25,34 @@ TRICK_EXEC_LINK_LIBS += -L${GTEST_HOME}/lib64 -L${GTEST_HOME}/lib -lgtest -lgtes # created to the list. TESTS = Integrator_unittest -OTHER_OBJECTS = \ - ../../include/object_${TRICK_HOST_CPU}/io_ABM_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_Euler_Cromer_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_Euler_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_MM4_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_NL2_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_RK2_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_RK4_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_RKF45_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_RKF78_Integrator.o \ - ../../include/object_${TRICK_HOST_CPU}/io_RKG4_Integrator.o - -OTHER_OBJECTS += \ -${TRICK_HOME}/trick_source/er7_utils/integration/*/object_${TRICK_HOST_CPU}/*.o \ -${TRICK_HOME}/trick_source/er7_utils/interface/object_${TRICK_HOST_CPU}/*.o \ -${TRICK_HOME}/trick_source/er7_utils/trick/integration/object_${TRICK_HOST_CPU}/*.o +# These force-include individual .o files rather than relying on -ltrick/ +# -ler7_utils archive linking: these are ICG-registration objects whose +# static initializers (type introspection) nothing in this test directly +# calls, so a normal `-lname` archive link — which only pulls in members +# that satisfy an unresolved symbol reference — silently drops them. +# +# Extracted from libtrick.a / liber7_utils.a instead of read from +# component-level object_$(TRICK_HOST_CPU)/ dirs: those source-tree paths +# only exist after an in-source `make` build, but this test's own +# TRICK_HOME can also be an out-of-source CMake install (same pattern/ +# rationale as CommandLineArguments/test/Makefile's MM_OBJECTS). All of +# liber7_utils.a's members originate from exactly the integration/*, +# interface/, and trick/integration/ subtrees this Makefile used to +# wildcard-glob directly, so extracting the whole archive reproduces that +# scope exactly. +INTEGRATOR_OBJDIR = integrator_objs +ER7_OBJDIR = er7_utils_objs +OTHER_OBJECTS = $(INTEGRATOR_OBJDIR)/.extracted $(ER7_OBJDIR)/.extracted + +$(INTEGRATOR_OBJDIR)/.extracted: ${TRICK_LIB_DIR}/libtrick.a + mkdir -p $(INTEGRATOR_OBJDIR) + cd $(INTEGRATOR_OBJDIR) && ar x $(abspath $<) $$(ar t $(abspath $<) | grep -E '^io_(ABM_Integrator|Euler_Cromer_Integrator|Euler_Integrator|Integrator|MM4_Integrator|NL2_Integrator|RK2_Integrator|RK4_Integrator|RKF45_Integrator|RKF78_Integrator|RKG4_Integrator)\.') + touch $@ + +$(ER7_OBJDIR)/.extracted: ${TRICK_LIB_DIR}/liber7_utils.a + mkdir -p $(ER7_OBJDIR) + cd $(ER7_OBJDIR) && ar x $(abspath $<) + touch $@ # House-keeping build targets. @@ -52,10 +63,10 @@ test: $(TESTS) clean : rm -f $(TESTS) *.o - rm -rf io_src xml + rm -rf io_src xml $(INTEGRATOR_OBJDIR) $(ER7_OBJDIR) Integrator_unittest.o : Integrator_unittest.cc $(TRICK_CXX) $(TRICK_CPPFLAGS) -c $< -Integrator_unittest : Integrator_unittest.o - $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) -o $@ $^ $(OTHER_OBJECTS) $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) +Integrator_unittest : Integrator_unittest.o $(OTHER_OBJECTS) + $(TRICK_CXX) $(TRICK_SYSTEM_LDFLAGS) -o $@ Integrator_unittest.o $(INTEGRATOR_OBJDIR)/*.o $(ER7_OBJDIR)/*.o $(TRICK_LIBS) $(TRICK_EXEC_LINK_LIBS) diff --git a/trickops.py b/trickops.py index cbe466e89..a944d7cb8 100644 --- a/trickops.py +++ b/trickops.py @@ -1,5 +1,6 @@ -import sys +import argparse import os +import sys thisdir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) sys.path.append(os.path.join(thisdir,"share/trick/trickops")) @@ -10,15 +11,31 @@ max_retries = 5 class SimTestWorkflow(TrickWorkflow): - def __init__( self, quiet, trick_top_level, cpus, config_file): + def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): self.cpus = cpus # Create the trick_test directory if it doesn't already exist if not os.path.exists(trick_top_level + "/trick_test"): os.makedirs(trick_top_level + "/trick_test") + # trick_dir defaults to trick_top_level (historical behavior: sims and + # the installed Trick used by bin/trick-CP live in the same in-source + # tree). A CMake-driven test run passes them separately: sim + # directories (test/SIM_*) only exist in the source tree, but the + # installed product (bin/trick-CP, libexec/, share/) may be a staged + # out-of-source CMake install instead. + if trick_dir is None: + trick_dir = trick_top_level + # Base Class initialize, this creates internal management structures - TrickWorkflow.__init__(self, project_top_level=(trick_top_level), log_dir=(trick_top_level +'/trickops_logs/'), - trick_dir=trick_top_level, config_file=(trick_top_level + "/" + config_file), cpus=self.cpus, quiet=quiet) + TrickWorkflow.__init__( + self, + project_top_level=(trick_top_level), + log_dir=(trick_top_level + "/trickops_logs/"), + trick_dir=trick_dir, + config_file=(trick_top_level + "/" + config_file), + cpus=self.cpus, + quiet=quiet, + ) def run( self ): build_jobs = self.get_jobs(kind='build') @@ -85,7 +102,21 @@ def run( self ): if __name__ == "__main__": parser = argparse.ArgumentParser(description='Build, run, and compare all test sims for Trick', formatter_class=argparse.ArgumentDefaultsHelpFormatter) - parser.add_argument( "--trick_top_level", type=str, help="Path to TRICK_HOME", default=thisdir) + parser.add_argument( + "--trick_top_level", + type=str, + help="Path to the project tree containing the sim " + "directories referenced by --config_file", + default=thisdir, + ) + parser.add_argument( + "--trick_dir", + type=str, + help="Path to the installed/staged Trick (bin/trick-CP, " + "libexec/, share/) used to build the sims. Defaults to --trick_top_level (historical in-source " + "behavior, where sims and the Trick install share one tree).", + default=None, + ) parser.add_argument( "--quiet", action="store_true", help="Suppress progress bars (automatically set to True if environment variable CI is present).") parser.add_argument( "--cpus", type=int, default=(os.cpu_count() if os.cpu_count() is not None else 8), help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " @@ -94,4 +125,12 @@ def run( self ): myargs = parser.parse_args() should_be_quiet = myargs.quiet or os.getenv('CI') is not None - sys.exit(SimTestWorkflow(quiet=should_be_quiet, trick_top_level=myargs.trick_top_level, cpus=myargs.cpus, config_file=myargs.config_file).run()) + sys.exit( + SimTestWorkflow( + quiet=should_be_quiet, + trick_top_level=myargs.trick_top_level, + trick_dir=myargs.trick_dir, + cpus=myargs.cpus, + config_file=myargs.config_file, + ).run() + ) From 0e6023b5fbde35dde61497dac83f6d9d97ca465f Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 7 Jul 2026 16:00:25 -0500 Subject: [PATCH 10/28] cmake: transition: more ci logging --- test/build_config/compare_archives.sh | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/build_config/compare_archives.sh b/test/build_config/compare_archives.sh index 3025c4bad..51537dc7b 100755 --- a/test/build_config/compare_archives.sh +++ b/test/build_config/compare_archives.sh @@ -38,7 +38,8 @@ ARCHIVES=( if [ ! -f lib/libtrick.a ] && [ ! -f lib64/libtrick.a ]; then ./configure >"$WORKDIR/configure.log" 2>&1 make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" no_dp >"$WORKDIR/make.log" 2>&1 || { - echo "make failed; see $WORKDIR/make.log" >&2 + echo "make failed; last 200 lines of $WORKDIR/make.log (deleted on exit, so printed here):" >&2 + tail -n 200 "$WORKDIR/make.log" >&2 exit 1 } fi @@ -52,7 +53,14 @@ cmake --build "$CMAKE_BUILD_DIR" --target trick-ICG trick_io_src_gen -j"$(getcon >"$WORKDIR/cmake_build_icg.log" 2>&1 cmake -S . -B "$CMAKE_BUILD_DIR" >"$WORKDIR/cmake_configure2.log" 2>&1 cmake --build "$CMAKE_BUILD_DIR" -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" >"$WORKDIR/cmake_build.log" 2>&1 || { - echo "cmake --build failed; see $WORKDIR/cmake_build.log" >&2 + # WORKDIR is deleted by the EXIT trap before CI can capture it as an artifact, + # so a bare "see $WORKDIR/cmake_build.log" pointer is useless in CI output — + # print the actual failure here. Full build logs routinely exceed a few + # hundred lines (parallel -j output interleaves per-file compiler + # invocations); tail -n 300 keeps the pointer close enough to the real + # error line(s) without flooding the CI log. + echo "cmake --build failed; last 300 lines of the build log:" >&2 + tail -n 300 "$WORKDIR/cmake_build.log" >&2 exit 1 } From 5bc5c722044de2962c1ba6f788d7c19bc7fbf8f8 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 7 Jul 2026 16:03:38 -0500 Subject: [PATCH 11/28] cmake: transition: ruff format --- trickops.py | 197 ++++++++++++++++++++++++++++++++-------------------- 1 file changed, 123 insertions(+), 74 deletions(-) diff --git a/trickops.py b/trickops.py index a944d7cb8..aa9326035 100644 --- a/trickops.py +++ b/trickops.py @@ -3,19 +3,20 @@ import sys thisdir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.path.join(thisdir,"share/trick/trickops")) +sys.path.append(os.path.join(thisdir, "share/trick/trickops")) -from TrickWorkflow import * -from WorkflowCommon import Job +from TrickWorkflow import TrickWorkflow # noqa: E402 (needs the sys.path.append above) +from WorkflowCommon import Job # noqa: E402 max_retries = 5 + class SimTestWorkflow(TrickWorkflow): def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): self.cpus = cpus # Create the trick_test directory if it doesn't already exist if not os.path.exists(trick_top_level + "/trick_test"): - os.makedirs(trick_top_level + "/trick_test") + os.makedirs(trick_top_level + "/trick_test") # trick_dir defaults to trick_top_level (historical behavior: sims and # the installed Trick used by bin/trick-CP live in the same in-source @@ -36,72 +37,107 @@ def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): cpus=self.cpus, quiet=quiet, ) - def run( self ): - build_jobs = self.get_jobs(kind='build') - - # This is awful but I can't think of another way around it - # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux - # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? - import signal - run_names = ["Run test/SIM_test_varserv RUN_test/err1_test.py", "Run test/SIM_test_varserv RUN_test/err2_test.py"] - for job in [job for job in self.get_jobs(kind='run') if job.name in run_names]: - job._expected_exit_status = signal.SIGUSR1.value - - # Several test sims have runs that require ordering via phases: - # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run - # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs - # - SIM_test_varserver has 3 runs that cannot be concurrent - # - SIM_mc_generation generates runs and then runs them - phases = [-1, 0, 1, 2, 3] - - analysis_jobs = self.get_jobs(kind='analyze') - if platform == "darwin": - for job in build_jobs: - if job.name == "Build test/SIM_trickified_shared" : - print("REMOVING JOB: " + job.name) - build_jobs.remove(job) - builds_status = self.execute_jobs(build_jobs, max_concurrent=self.cpus, header='Executing all sim builds.') - - jobs = build_jobs - - run_status = 0 - for phase in phases: - run_jobs = self.get_jobs(kind='run', phase=phase) - if platform == "darwin": - for job in run_jobs: - if job.name == "Run test/SIM_trickified_shared RUN_test/unit_test.py" : - print("REMOVING JOB: " + job.name) - run_jobs.remove(job) - this_status = self.execute_jobs(run_jobs, max_concurrent=self.cpus, header="Executing phase " + str(phase) + " runs.", job_timeout=1000) - run_status = run_status or this_status - jobs += run_jobs - - comparison_result = self.compare() - analysis_status = self.execute_jobs(analysis_jobs, max_concurrent=self.cpus, header='Executing all analysis.') - - self.report() # Print Verbose report - self.status_summary() # Print a Succinct summary - - # Dump failing logs - for job in jobs: - if job.get_status() == Job.Status.FAILED or job.get_status() == Job.Status.TIMEOUT: - print ("*"*120) - if job.get_status() == Job.Status.FAILED: - header = "Failing job: " + job.name - else: - header = "Timed out job: " + job.name - - numspaces = int((120 - 20 - len(header))/2 -2) - print("*"*10, " "*numspaces, header, " "*numspaces, "*"*10,) - print ("*"*120) - print(open(job.log_file, "r").read()) - print ("*"*120, "\n\n\n") - - return (builds_status or run_status or len(self.config_errors) > 0 or comparison_result or analysis_status) + + def run(self): + build_jobs = self.get_jobs(kind="build") + + # This is awful but I can't think of another way around it + # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux + # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? + import signal + + run_names = [ + "Run test/SIM_test_varserv RUN_test/err1_test.py", + "Run test/SIM_test_varserv RUN_test/err2_test.py", + ] + for job in [job for job in self.get_jobs(kind="run") if job.name in run_names]: + job._expected_exit_status = signal.SIGUSR1.value + + # Several test sims have runs that require ordering via phases: + # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run + # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs + # - SIM_test_varserver has 3 runs that cannot be concurrent + # - SIM_mc_generation generates runs and then runs them + phases = [-1, 0, 1, 2, 3] + + analysis_jobs = self.get_jobs(kind="analyze") + if self.platform == "darwin": + for job in build_jobs: + if job.name == "Build test/SIM_trickified_shared": + print("REMOVING JOB: " + job.name) + build_jobs.remove(job) + builds_status = self.execute_jobs( + build_jobs, max_concurrent=self.cpus, header="Executing all sim builds." + ) + + jobs = build_jobs + + run_status = 0 + for phase in phases: + run_jobs = self.get_jobs(kind="run", phase=phase) + if self.platform == "darwin": + for job in run_jobs: + if ( + job.name + == "Run test/SIM_trickified_shared RUN_test/unit_test.py" + ): + print("REMOVING JOB: " + job.name) + run_jobs.remove(job) + this_status = self.execute_jobs( + run_jobs, + max_concurrent=self.cpus, + header="Executing phase " + str(phase) + " runs.", + job_timeout=1000, + ) + run_status = run_status or this_status + jobs += run_jobs + + comparison_result = self.compare() + analysis_status = self.execute_jobs( + analysis_jobs, max_concurrent=self.cpus, header="Executing all analysis." + ) + + self.report() # Print Verbose report + self.status_summary() # Print a Succinct summary + + # Dump failing logs + for job in jobs: + if ( + job.get_status() == Job.Status.FAILED + or job.get_status() == Job.Status.TIMEOUT + ): + print("*" * 120) + if job.get_status() == Job.Status.FAILED: + header = "Failing job: " + job.name + else: + header = "Timed out job: " + job.name + + numspaces = int((120 - 20 - len(header)) / 2 - 2) + print( + "*" * 10, + " " * numspaces, + header, + " " * numspaces, + "*" * 10, + ) + print("*" * 120) + print(open(job.log_file, "r").read()) + print("*" * 120, "\n\n\n") + + return ( + builds_status + or run_status + or len(self.config_errors) > 0 + or comparison_result + or analysis_status + ) + if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Build, run, and compare all test sims for Trick', - formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser = argparse.ArgumentParser( + description="Build, run, and compare all test sims for Trick", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) parser.add_argument( "--trick_top_level", type=str, @@ -117,14 +153,27 @@ def run( self ): "behavior, where sims and the Trick install share one tree).", default=None, ) - parser.add_argument( "--quiet", action="store_true", help="Suppress progress bars (automatically set to True if environment variable CI is present).") - parser.add_argument( "--cpus", type=int, default=(os.cpu_count() if os.cpu_count() is not None else 8), - help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " - "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.") - parser.add_argument( "--config_file", type=str, help="Run configuration file to use, relative to trick_top_level", default="test_sims.yml") + parser.add_argument( + "--quiet", + action="store_true", + help="Suppress progress bars (automatically set to True if environment variable CI is present).", + ) + parser.add_argument( + "--cpus", + type=int, + default=(os.cpu_count() if os.cpu_count() is not None else 8), + help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " + "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.", + ) + parser.add_argument( + "--config_file", + type=str, + help="Run configuration file to use, relative to trick_top_level", + default="test_sims.yml", + ) myargs = parser.parse_args() - should_be_quiet = myargs.quiet or os.getenv('CI') is not None + should_be_quiet = myargs.quiet or os.getenv("CI") is not None sys.exit( SimTestWorkflow( quiet=should_be_quiet, From 9c9146d7279eeda3f4deccc2e81cca37d97baaf9 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 7 Jul 2026 16:41:25 -0500 Subject: [PATCH 12/28] cmake: transition: fix errors found in ci --- .github/workflows/ci.yml | 10 ++--- test/build_config/run_ctest.sh | 12 +++++- .../DPX/APPS/GXPLOT/CMakeLists.txt | 38 +++++++++++++------ 3 files changed, 40 insertions(+), 20 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dffca82c9..4a1e12ae1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -153,7 +153,7 @@ jobs: - name: CMake install + CTest (Phase 3) run: | echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" - test/build_config/run_ctest.sh 'unit|sims' + test/build_config/run_ctest.sh '^(unit|sims)$' cmake-configure-macos: name: CMake Configure (macOS) @@ -169,16 +169,14 @@ jobs: brew update || true brew upgrade || true brew install --cask xquartz - brew install udunits openmotif maven + brew install udunits openmotif maven googletest brew install swig llvm - name: Compare autoconf vs cmake config_user.mk (Phase 1) run: test/build_config/compare_config_user.sh - name: CMake build + archive parity vs make (Phase 2) run: test/build_config/compare_archives.sh - - name: CMake install + sim-flow CTest (Phase 3) - # gtest is not installed on the macOS runner, so run only the trick-CP - # sim-flow acceptance tests (LABEL sims), not the gtest unit suite. - run: test/build_config/run_ctest.sh sims + - name: CMake install + CTest (Phase 3) + run: test/build_config/run_ctest.sh '^(unit|sims)$' trickops: needs: [style] diff --git a/test/build_config/run_ctest.sh b/test/build_config/run_ctest.sh index 893e5a5df..d941d894e 100755 --- a/test/build_config/run_ctest.sh +++ b/test/build_config/run_ctest.sh @@ -6,13 +6,21 @@ # doubles as install/interface validation; the sim-flow tests are the universal # acceptance test (a real sim built and run by trick-CP from the installed prefix). # -# Usage: test/build_config/run_ctest.sh [ctest-label-regex] (default: unit|sims) +# Usage: test/build_config/run_ctest.sh [ctest-label-regex] (default: ^(unit|sims)$) +# +# The regex is matched by `ctest -L`, which does an unanchored substring +# search, not an exact match — an unanchored "sims" would also select the +# "sims_full" label (the full ~52-sim test_sims.yml suite via trickops.py, +# cmake/TrickTest.cmake), which needs Python deps (PyYAML, psutil — see +# share/trick/trickops/requirements.txt) this script's caller does not +# install. Callers that actually want sims_full must request it explicitly +# (e.g. `run_ctest.sh '^sims_full$'`) after installing those deps themselves. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" cd "$REPO_ROOT" -LABELS="${1:-unit|sims}" +LABELS="${1:-^(unit|sims)$}" BUILD_DIR="${TRICK_CMAKE_BUILD_DIR:-$REPO_ROOT/build}" NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" diff --git a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt index 477461eef..d20567253 100644 --- a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt @@ -34,23 +34,37 @@ target_include_directories(trick-gxplot PRIVATE separate_arguments(_tr_gxplot_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") target_compile_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_includes}) -target_link_libraries(trick-gxplot PRIVATE - DPC DPM dp_log dp_var dp_eqparse dp_units - dpv_utils - ${X11_Xt_LIB} ${X11_X11_LIB} - ${LIBXML2_LIBRARIES} - DPC dpv_utils - dl -) +# Link order matters here: GNU ld (default on Linux) resolves symbols in a +# single left-to-right pass, so a library must appear *after* whatever +# consumes its symbols. This mirrors the makefile's ALL_LIBS + final link +# line order exactly (CONTROLLER_LIBS MODEL_LIBS DP_LIBS HDF5_LIB -ldl +# UDUNITS_LDFLAGS XLIBS LIBRTDEF CONTROLLER_LIBS -lDPV) — a prior version of +# this file used a separate target_link_options() call for UDUNITS_LDFLAGS, +# which does not guarantee it lands after the DPC/DPM/dp_units archives that +# actually need it, and silently linked fine on macOS (ld64's linker is far +# more forgiving of ordering) while failing with "undefined reference to +# `ut_parse'" etc. on Linux. separate_arguments(_tr_gxplot_udunits_ldflags NATIVE_COMMAND "${UDUNITS_LDFLAGS}") -target_link_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_ldflags}) +set(_tr_gxplot_libs + DPC + DPM + dp_log dp_var dp_eqparse dp_units +) if(HDF5_HOME) - target_link_libraries(trick-gxplot PRIVATE -L${HDF5_HOME}/lib -lhdf5_hl -lhdf5 -lsz) + list(APPEND _tr_gxplot_libs -L${HDF5_HOME}/lib -lhdf5_hl -lhdf5 -lsz) endif() +list(APPEND _tr_gxplot_libs + dl + ${_tr_gxplot_udunits_ldflags} + ${X11_Xt_LIB} ${X11_X11_LIB} + ${LIBXML2_LIBRARIES} +) if(APPLE) - target_link_libraries(trick-gxplot PRIVATE -lc++abi) + list(APPEND _tr_gxplot_libs -lc++abi) else() - target_link_libraries(trick-gxplot PRIVATE rt) + list(APPEND _tr_gxplot_libs rt) endif() +list(APPEND _tr_gxplot_libs DPC dpv_utils) +target_link_libraries(trick-gxplot PRIVATE ${_tr_gxplot_libs}) install(TARGETS trick-gxplot RUNTIME DESTINATION bin) From b829142e025ad80db45baec0be431cfd406915bd Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Wed, 8 Jul 2026 10:29:46 -0500 Subject: [PATCH 13/28] cmake: transition: fix civetweb/GSL/-m32 gating --- .github/workflows/ci.yml | 62 +++++++++++++++++++++++--- CMakeLists.txt | 8 +++- CMakePresets.json | 2 +- cmake/TrickICG.cmake | 31 ++++++++++--- cmake/TrickTest.cmake | 55 +++++++++-------------- test/build_config/run_ctest.sh | 38 ---------------- trick_source/trick_swig/CMakeLists.txt | 6 ++- 7 files changed, 115 insertions(+), 87 deletions(-) delete mode 100755 test/build_config/run_ctest.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a1e12ae1..88ef8b462 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,10 +90,14 @@ jobs: libudunits2-dev libgtest-dev libgmock-dev + libgsl-dev + libhdf5-dev default-jdk python3-dev python3-pip python3-venv + python3-yaml + python3-psutil #-------- RHEL-based Dependencies (all versions) ---------------- - cfg: { arch: rhel } @@ -109,6 +113,8 @@ jobs: gcc-c++ gtest-devel gmock-devel + gsl-devel + hdf5-devel java-21-openjdk-devel libxml2-devel llvm-devel @@ -123,6 +129,8 @@ jobs: which zlib-devel python3-devel + python3-pyyaml + python3-psutil #-------- RHEL 9: gtest lives in crb ---------------- - cfg: { arch: rhel, tag: 9 } @@ -144,16 +152,31 @@ jobs: ${{ matrix.arch_deps }} - name: Checkout repository uses: actions/checkout@v6 + # A dedicated step: GITHUB_ENV writes only take effect in *subsequent* + # steps, so setting JAVA_HOME inline in the same run block as a script + # would leave that script itself without it. + - name: Set JAVA_HOME + run: echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" - name: Compare autoconf vs cmake config_user.mk (Phase 1) - run: | - echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" - test/build_config/compare_config_user.sh + run: test/build_config/compare_config_user.sh - name: CMake build + archive parity vs make (Phase 2) run: test/build_config/compare_archives.sh - name: CMake install + CTest (Phase 3) run: | - echo "JAVA_HOME=$(dirname $(dirname $(readlink -f $(which java))))" >> "$GITHUB_ENV" - test/build_config/run_ctest.sh '^(unit|sims)$' + NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + # Three-step io_src bootstrap: a nonexistent-at-first-configure + # generated list does not wire itself into CMAKE_CONFIGURE_DEPENDS + # (see cmake/TrickICG.cmake) — the first `trick_io_src_gen` build + # writes it, then a reconfigure picks it up before the real build. + cmake -S . -B build + cmake --build build --target trick-ICG trick_io_src_gen -j"$NPROC" + cmake -S . -B build + cmake --build build -j"$NPROC" + # Stage a runnable TRICK_HOME (build/stage) — the stage_install + # fixture in cmake/TrickTest.cmake also does this, but doing it + # explicitly here surfaces install errors directly. + cmake --build build --target stage -j"$NPROC" + ctest --test-dir build --output-on-failure -L '^(unit|sims)$' cmake-configure-macos: name: CMake Configure (macOS) @@ -169,14 +192,39 @@ jobs: brew update || true brew upgrade || true brew install --cask xquartz - brew install udunits openmotif maven googletest + brew install udunits openmotif maven googletest gsl hdf5 brew install swig llvm + # sims (LABEL sims, exercised by the Phase 3 step below) drives + # trickops.py, which needs PyYAML/psutil — it's the only sims test now + # (replaced the two-sim smoke subset), so installing these is no longer + # optional for this job. Installed into the same Python3 `cmake` resolves + # at configure time (find_package(Python3 ...) in TrickTest.cmake) and + # bakes as an absolute path into the generated ctest command — a venv's + # activate script in a later step would not reach that baked-in path, so + # this installs directly rather than isolating in a venv like the + # autotools test-macos job above does. + - name: Install trickops Python dependencies + run: pip3 install --break-system-packages -r share/trick/trickops/requirements.txt - name: Compare autoconf vs cmake config_user.mk (Phase 1) run: test/build_config/compare_config_user.sh - name: CMake build + archive parity vs make (Phase 2) run: test/build_config/compare_archives.sh - name: CMake install + CTest (Phase 3) - run: test/build_config/run_ctest.sh '^(unit|sims)$' + run: | + NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" + # Three-step io_src bootstrap: a nonexistent-at-first-configure + # generated list does not wire itself into CMAKE_CONFIGURE_DEPENDS + # (see cmake/TrickICG.cmake) — the first `trick_io_src_gen` build + # writes it, then a reconfigure picks it up before the real build. + cmake -S . -B build + cmake --build build --target trick-ICG trick_io_src_gen -j"$NPROC" + cmake -S . -B build + cmake --build build -j"$NPROC" + # Stage a runnable TRICK_HOME (build/stage) — the stage_install + # fixture in cmake/TrickTest.cmake also does this, but doing it + # explicitly here surfaces install errors directly. + cmake --build build --target stage -j"$NPROC" + ctest --test-dir build --output-on-failure -L '^(unit|sims)$' trickops: needs: [style] diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d1dab931..4caa374fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,10 +45,14 @@ set(LLVM_HOME "" CACHE PATH "LLVM root directory (empty = auto-detect)") set(UDUNITS_HOME "" CACHE PATH "UDUnits2 root directory (empty = auto-detect)") set(PYTHON_VERSION "" CACHE STRING "Suffix for python search, e.g. 3 looks for python3") +# -m32 propagates through the trick_build_flags INTERFACE target below, NOT +# directory-wide: Makefile.common:119-122 only puts -m32 on TRICK_ICGFLAGS/ +# TRICK_SYSTEM_CXXFLAGS/TRICK_SYSTEM_LDFLAGS (flags for sim/library code), +# never on the trick-ICG tool's own compile — a 32-bit ICG can't link the +# 64-bit LLVM archives — and data_products' own -m32 gate +# (TRICK_DP_FORCE_32BIT) is dead upstream, so it never builds 32-bit either. if(TRICK_FORCE_32BIT) set(TRICK_FORCE_32BIT_MK 1) - add_compile_options(-m32) - add_link_options(-m32) else() set(TRICK_FORCE_32BIT_MK 0) endif() diff --git a/CMakePresets.json b/CMakePresets.json index e9d293680..d3faf57cc 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,5 +1,5 @@ { - "version": 6, + "version": 3, "cmakeMinimumRequired": { "major": 3, "minor": 20, "patch": 0 }, "configurePresets": [ { diff --git a/cmake/TrickICG.cmake b/cmake/TrickICG.cmake index df71af1ee..226e6644d 100644 --- a/cmake/TrickICG.cmake +++ b/cmake/TrickICG.cmake @@ -65,18 +65,39 @@ endif() if(TRICK_FORCE_32BIT) list(APPEND _tr_icg_flags -m32) endif() -if(GSL_HOME AND NOT GSL_HOME STREQUAL "/usr/local") - list(APPEND _tr_icg_flags -I${GSL_HOME}/include) +# GSL: Makefile.common:147-159 puts -D_HAVE_GSL on TRICK_SYSTEM_CXXFLAGS +# whenever GSL_HOME is set (the make ICG pass inherits it), and only adds the +# -I when GSL_HOME is neither /usr nor /usr/local (both already on the +# compiler's default include path). +if(GSL_HOME) + list(APPEND _tr_icg_flags -D_HAVE_GSL) + if(NOT GSL_HOME STREQUAL "/usr" AND NOT GSL_HOME STREQUAL "/usr/local") + list(APPEND _tr_icg_flags -I${GSL_HOME}/include) + endif() endif() -if(TRICK_CIVETWEB_HOME) - list(APPEND _tr_icg_flags -I${TRICK_CIVETWEB_HOME}/include -DUSE_CIVETWEB) +# Gate on USE_CIVETWEB/CIVETWEB_HOME (what TrickPrograms.cmake resolved +# detection to), not the raw TRICK_CIVETWEB_HOME cache option: civetweb.h at +# /usr auto-detects to USE_CIVETWEB=1 with TRICK_CIVETWEB_HOME unset, and the +# make build's ICG pass gets -DUSE_CIVETWEB via TRICK_SYSTEM_CXXFLAGS in that +# case too (Makefile.common:162-167) — files_to_ICG.hh has an +# #ifdef USE_CIVETWEB block, so a mismatch silently drops the civetweb +# io_*.cpp from libtrick.a. +if(USE_CIVETWEB) + list(APPEND _tr_icg_flags -I${CIVETWEB_HOME}/include -DUSE_CIVETWEB) endif() file(MAKE_DIRECTORY ${TRICK_IO_SRC_DIR}) +# Makefile.common:167 also adds CIVETWEB_HOME to TRICK_SYSTEM_ICG_EXCLUDE +# (exported at :72), keeping ICG out of civetweb's own headers. +set(_tr_icg_env TRICK_HOME=${CMAKE_SOURCE_DIR} TRICK_EXCLUDE=:${UDUNITS_EXCLUDE}) +if(USE_CIVETWEB) + list(APPEND _tr_icg_env TRICK_SYSTEM_ICG_EXCLUDE=${CIVETWEB_HOME}) +endif() + add_custom_command( OUTPUT ${TRICK_IO_SRC_LIST_FILE} - COMMAND ${CMAKE_COMMAND} -E env TRICK_HOME=${CMAKE_SOURCE_DIR} TRICK_EXCLUDE=:${UDUNITS_EXCLUDE} + COMMAND ${CMAKE_COMMAND} -E env ${_tr_icg_env} $ -force -sim_services -m ${_tr_icg_flags} -o ${TRICK_IO_SRC_DIR} ${CMAKE_SOURCE_DIR}/include/trick/files_to_ICG.hh diff --git a/cmake/TrickTest.cmake b/cmake/TrickTest.cmake index 1ce5f5926..44c70dec2 100644 --- a/cmake/TrickTest.cmake +++ b/cmake/TrickTest.cmake @@ -10,8 +10,13 @@ # command-line override wins and redirects config_user.mk, the # archives (TRICK_LIB_DIR), headers, and trick-ICG to the stage. # -# LABEL sims — the universal acceptance test: trick-CP builds and runs a real sim -# against the stage (test/build_config/run_sim_flow.sh). +# LABEL sims — the universal acceptance test: trick-CP builds and runs real sims +# against the stage. The sole test carrying this label is `sims` — the +# full ~52-sim test_sims.yml suite via trickops.py, the same one +# `make sim_test` runs. It replaced the earlier two-sim smoke subset; +# there is no lighter-weight sims test anymore. It requires the +# trickops Python deps (share/trick/trickops/requirements.txt) to be +# installed by the caller. # # A CTest fixture stages the install first (cmake --install into ${TRICK_STAGE_DIR}), # so `ctest` works after a plain `cmake --build`. The build itself must be complete @@ -98,7 +103,10 @@ endif() # the source tree — test output, not a product-build write (D3 is about the # product build, same class of exception already noted for the C++ gtest # suites' own **/test artifacts). -if(TRICK_USE_JAVA) +# MVN_EXECUTABLE is empty under TRICK_OFFLINE (offline jars are prebuilt +# copies; maven isn't required or detected), so gate on it too rather than +# registering a test with an empty command. +if(TRICK_USE_JAVA AND MVN_EXECUTABLE) add_test(NAME ut_java COMMAND ${MVN_EXECUTABLE} test -Dcmake=false -Dmaven.wagon.http.retryHandler.count=15 WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/trick_source/java @@ -109,49 +117,30 @@ if(TRICK_USE_JAVA) ) endif() -# ── Sim-flow acceptance tests (LABEL sims): trick-CP builds + runs real sims from -# the stage. One data-recording/checkpoint sim and one STL sim, matching the -# plan's Phase 3 step 5 ("SIM_cannon_analytic plus one data-recording/STL sim" — -# cannon_analytic isn't in this checkout; the demo/STL sims exercise the same -# trick-CP -> ICG -> SWIG -> compile -> run path). -set(_tr_sim_flow_script ${CMAKE_SOURCE_DIR}/test/build_config/run_sim_flow.sh) -function(trick_add_sim_flow_test name sim_dir) - if(NOT EXISTS ${CMAKE_SOURCE_DIR}/${sim_dir}/S_define) - return() - endif() - add_test(NAME ${name} - COMMAND ${_tr_sim_flow_script} ${TRICK_STAGE_DIR} ${CMAKE_SOURCE_DIR}/${sim_dir} - ) - set_tests_properties(${name} PROPERTIES - LABELS sims - FIXTURES_REQUIRED trick_stage - TIMEOUT 1800 - ) -endfunction() - -trick_add_sim_flow_test(sim_demo_sdefine test/SIM_demo_sdefine) -trick_add_sim_flow_test(sim_stls test/SIM_stls) - -# ── Full sim suite (LABEL sims_full): the same trickops.py/test_sims.yml suite +# ── Full sim suite (LABEL sims): the same trickops.py/test_sims.yml suite # `make sim_test` runs (test_overrides.mk), covering all ~52 sims in -# test_sims.yml — the two lightweight sim_demo_sdefine/sim_stls tests above are -# a fast subset/smoke-check of the same trick-CP path, not a substitute for -# this. trickops.py's --trick_dir (added alongside this CTest wiring) points +# test_sims.yml. This is now the *only* sims-acceptance test — it replaced the +# earlier two-sim smoke subset (sim_demo_sdefine/sim_stls via +# test/build_config/run_sim_flow.sh), which exercised the identical trick-CP -> +# ICG -> SWIG -> compile -> run path as a strict subset of what this suite +# already covers. run_sim_flow.sh itself is left in place for ad hoc manual +# single-sim verification (Part E rule 4); it's just no longer wired into CTest. +# trickops.py's --trick_dir (added alongside this CTest wiring) points # build_cmd at /bin/trick-CP, which self-derives TRICK_HOME from its own # location (bin/trick-CP:13, dirname($trick_bin)) — no TRICK_HOME env needed. # --trick_top_level stays the source tree, since the test/SIM_* sim # directories test_sims.yml references only exist there, not in the stage. if(EXISTS ${CMAKE_SOURCE_DIR}/test_sims.yml) find_package(Python3 COMPONENTS Interpreter) - add_test(NAME sims_full + add_test(NAME sims COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/trickops.py --trick_top_level ${CMAKE_SOURCE_DIR} --trick_dir ${TRICK_STAGE_DIR} --config_file test_sims.yml --quiet ) - set_tests_properties(sims_full PROPERTIES - LABELS sims_full + set_tests_properties(sims PROPERTIES + LABELS sims FIXTURES_REQUIRED trick_stage TIMEOUT 3600 ) diff --git a/test/build_config/run_ctest.sh b/test/build_config/run_ctest.sh deleted file mode 100755 index d941d894e..000000000 --- a/test/build_config/run_ctest.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# Phase 3 install + CTest gate (CMAKE_MIGRATION_PLAN.md Part C, Phase 3): does the -# full three-step CMake build (see cmake/TrickICG.cmake), stages a runnable -# TRICK_HOME with `cmake --build ... --target stage`, then runs CTest. The unit -# tests build mini-sims through the real trick-CP flow against the stage, which -# doubles as install/interface validation; the sim-flow tests are the universal -# acceptance test (a real sim built and run by trick-CP from the installed prefix). -# -# Usage: test/build_config/run_ctest.sh [ctest-label-regex] (default: ^(unit|sims)$) -# -# The regex is matched by `ctest -L`, which does an unanchored substring -# search, not an exact match — an unanchored "sims" would also select the -# "sims_full" label (the full ~52-sim test_sims.yml suite via trickops.py, -# cmake/TrickTest.cmake), which needs Python deps (PyYAML, psutil — see -# share/trick/trickops/requirements.txt) this script's caller does not -# install. Callers that actually want sims_full must request it explicitly -# (e.g. `run_ctest.sh '^sims_full$'`) after installing those deps themselves. -set -euo pipefail - -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -cd "$REPO_ROOT" - -LABELS="${1:-^(unit|sims)$}" -BUILD_DIR="${TRICK_CMAKE_BUILD_DIR:-$REPO_ROOT/build}" -NPROC="$(getconf _NPROCESSORS_ONLN 2>/dev/null || echo 4)" - -# Three-step io_src bootstrap (a nonexistent-at-first-configure generated list -# does not wire itself into CMAKE_CONFIGURE_DEPENDS — see cmake/TrickICG.cmake). -cmake -S . -B "$BUILD_DIR" -cmake --build "$BUILD_DIR" --target trick-ICG trick_io_src_gen -j"$NPROC" -cmake -S . -B "$BUILD_DIR" -cmake --build "$BUILD_DIR" -j"$NPROC" - -# Stage a runnable TRICK_HOME (build/stage) — the fixture in cmake/TrickTest.cmake -# also does this, but doing it explicitly here surfaces install errors directly. -cmake --build "$BUILD_DIR" --target stage -j"$NPROC" - -ctest --test-dir "$BUILD_DIR" --output-on-failure -L "$LABELS" diff --git a/trick_source/trick_swig/CMakeLists.txt b/trick_source/trick_swig/CMakeLists.txt index da2768da1..91e0d59e3 100644 --- a/trick_source/trick_swig/CMakeLists.txt +++ b/trick_source/trick_swig/CMakeLists.txt @@ -29,7 +29,11 @@ if(TRICK_USE_ER7_UTILS) list(APPEND _tr_swig_defs -DUSE_ER7_UTILS_CHECKPOINTHELPER) endif() endif() -if(TRICK_CIVETWEB_HOME) +# Gate on USE_CIVETWEB (detection result), not the raw TRICK_CIVETWEB_HOME +# cache option — trick_swig/Makefile:57 gates SWIG_DEFS on +# `ifeq ($(USE_CIVETWEB), 1)`, which is also 1 when civetweb.h auto-detects +# at /usr with no --with-civetweb/TRICK_CIVETWEB_HOME given. +if(USE_CIVETWEB) list(APPEND _tr_swig_defs -DUSE_CIVETWEB) endif() From e5a09b01cb42bc3f574c0a9753edb9d39f13cb00 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Wed, 8 Jul 2026 15:55:41 -0500 Subject: [PATCH 14/28] cmake: transition: fix HDF5 flag pass through --- trick_source/trick_swig/CMakeLists.txt | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/trick_source/trick_swig/CMakeLists.txt b/trick_source/trick_swig/CMakeLists.txt index 91e0d59e3..33cf86ff2 100644 --- a/trick_source/trick_swig/CMakeLists.txt +++ b/trick_source/trick_swig/CMakeLists.txt @@ -72,6 +72,21 @@ target_link_libraries( trick_pyip PUBLIC trick_build_flags ) target_include_directories( trick_pyip PRIVATE ${Python3_INCLUDE_DIRS} ) set_target_properties( trick_pyip PROPERTIES ARCHIVE_OUTPUT_NAME trick_pyip) +# HDF5: -DHDF5 must reach the wrapper *compile*, not just SWIG. sim_services.i +# guards `#include "trick/DRHDF5.hh"` behind `#ifdef HDF5` — SWIG runs with +# -DHDF5 (via _tr_swig_defs above) so it emits code referencing Trick::DRHDF5, +# but if the generated wrapper is then compiled without -DHDF5 that same +# #ifdef skips the include and the class is undeclared ("no type named +# 'DRHDF5'"). Mirrors trick_swig/Makefile:4-5,38-42 (TRICK_CXXFLAGS += -DHDF5, +# plus -I$(HDF5)/include when HDF5 != /usr, since -DHDF5 then also activates +# DRHDF5.hh's own `#include "hdf5.h"`). +if(HDF5_HOME) + target_compile_definitions( trick_pyip PRIVATE HDF5 ) + if(NOT HDF5_HOME STREQUAL "/usr") + target_include_directories( trick_pyip PRIVATE ${HDF5_HOME}/include ) + endif() +endif() + # Makefile: trick_swig/Makefile:28-34 — SWIG-generated code triggers warnings # the rest of the build treats as errors elsewhere; suppress the same set. set(_tr_swig_wrap_opts -Wno-redundant-decls -Wno-shadow -Wno-unused-parameter -Wno-missing-field-initializers) From 3805216ffec5d544210da383da72b9b0e653af1c Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Wed, 8 Jul 2026 16:28:46 -0500 Subject: [PATCH 15/28] cmake: transition: use modern cmake features --- CMakeLists.txt | 13 ++- .../DPX/APPS/FXPLOT/CMakeLists.txt | 91 +++++++++++++++---- .../DPX/APPS/GXPLOT/CMakeLists.txt | 9 +- .../data_products/DPX/DPC/CMakeLists.txt | 4 +- .../data_products/DPX/DPM/CMakeLists.txt | 5 +- 5 files changed, 95 insertions(+), 27 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4caa374fe..304807f02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,7 @@ -cmake_minimum_required(VERSION 3.20) +# Range form: 3.20 is the supported floor (oldest distro CMake we target); the +# upper bound opts every policy introduced through 4.3 into its NEW behavior +# when built with a CMake that new, without raising the floor contributors need. +cmake_minimum_required(VERSION 3.20...4.3) # Version lives in share/trick/trick_ver.txt — the single source of truth # also read by bin/trick-version at sim-build time (Makefile.common:28-30). @@ -104,7 +107,13 @@ find_package(BISON REQUIRED) # GSL flags belong here, build-wide, not scoped to one file. add_library(trick_build_flags INTERFACE) target_compile_features(trick_build_flags INTERFACE cxx_std_17) -target_compile_options(trick_build_flags INTERFACE -fpic) +# Position-independent code via the CMake property rather than a literal -fpic, +# so the compiler-appropriate flag is emitted. INTERFACE_POSITION_INDEPENDENT_CODE +# propagates POSITION_INDEPENDENT_CODE=ON to every target that links +# trick_build_flags (all the core archives + trick_pyip). Sims compiled by the +# make flow use -fpic; -fPIC is a compatible superset, and archive membership +# (the ar-t parity gate) is unaffected by the flag. +set_target_properties(trick_build_flags PROPERTIES INTERFACE_POSITION_INDEPENDENT_CODE ON) target_compile_definitions(trick_build_flags INTERFACE TRICK_VER=${TRICK_MAJOR} TRICK_MINOR=${TRICK_MINOR} diff --git a/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt index 787e80865..efc2fa05d 100644 --- a/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt @@ -1,23 +1,76 @@ +# trick-fxplot — the Motif/fermi-ware plotter, ported from this directory's own +# makefile (the authority; the 2019 stale CMakeLists.txt is untrusted per +# CMAKE_MIGRATION_PLAN.md A4). Sibling of GXPLOT (../GXPLOT/CMakeLists.txt) — +# same structure and the same GNU-ld single-pass link-order care. +# +# UNVERIFIED: fermi-ware ("fermi-ware/") is a NASA-internal tree absent from +# this checkout, so this whole target is gated (data_products/DPX/CMakeLists.txt +# only add_subdirectory's APPS/FXPLOT when fermi-ware/CMakeLists.txt exists) and +# has never been built here. The `fermi` link target below is the name the +# makefile implies (it links fermi-ware's `libfermi.a`); whoever restores +# fermi-ware must ensure its CMakeLists provides a target producing libfermi.a +# (ARCHIVE_OUTPUT_NAME fermi). Motif has no X11::Xm imported target (Motif is +# not core X11), so it stays on MOTIF_HOME, matching the makefile. +set(FXPLOT_SRC + parse_format.c + post_dialog.c + fermi_view.cpp + fxplot.cpp + curve_view_node.cpp + plot_view_node.cpp + table_view_node.cpp + page_view_node.cpp + product_view_node.cpp +) -set( FXPLOT_SRC - curve_view_node - fermi_view - fxplot - page_view_node - parse_format - plot_view_node - post_dialog - product_view_node - table_view_node +add_executable(trick-fxplot ${FXPLOT_SRC}) +target_include_directories(trick-fxplot PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../.. + ${CMAKE_CURRENT_SOURCE_DIR}/../../.. + ${CMAKE_SOURCE_DIR}/include + ${CMAKE_SOURCE_DIR}/trick_source/data_products/fermi-ware ) +if(MOTIF_HOME) + target_include_directories(trick-fxplot PRIVATE ${MOTIF_HOME}/include) +endif() +separate_arguments(_tr_fxplot_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") +target_compile_options(trick-fxplot PRIVATE ${_tr_fxplot_udunits_includes}) -add_executable( trick-fxplot ${FXPLOT_SRC}) -target_include_directories( trick-fxplot PUBLIC ${X11_X11_INCLUDE_PATH} ) -target_include_directories( trick-fxplot PUBLIC ${LIBXML2_INCLUDE_DIR} ) -target_include_directories( trick-fxplot PUBLIC ../.. ) -target_include_directories( trick-fxplot PUBLIC ../../../fermi-ware ) -target_link_libraries( trick-fxplot DPC DPM dp_log dp_var dpv_utils fermiware dp_eqparse dp_units - -ldl - ${X11_Xt_LIB} ${X11_X11_LIB} ${MOTIF_LIBRARIES} - ${UDUNITS2_LIBRARIES} ${LIBXML2_LIBRARIES}) +# Link order matters (GNU ld resolves left-to-right in one pass, so a library +# must follow whatever consumes its symbols). Mirrors the makefile's final link +# line: OBJECT_FILES then ALL_LIBS (DPX_LIBS FERMI_WARE_LIB DP_LIBS +# TRICK_UNIT_LIBS LIBXML HDF5_LIB -ldl UDUNITS_LDFLAGS) then XLIBS (Motif -lXm +# -lXt -lX11) then LIBRTDEF (-lrt on Linux). See ../GXPLOT/CMakeLists.txt for +# the same reasoning and the macOS-vs-Linux linker-strictness note. +separate_arguments(_tr_fxplot_udunits_ldflags NATIVE_COMMAND "${UDUNITS_LDFLAGS}") +set(_tr_fxplot_libs + DPC + DPM + fermi + dp_log dp_var dp_eqparse dp_units + LibXml2::LibXml2 +) +if(HDF5_HOME) + list(APPEND _tr_fxplot_libs -L${HDF5_HOME}/lib -lhdf5_hl -lhdf5 -lsz) +endif() +list(APPEND _tr_fxplot_libs + dl + ${_tr_fxplot_udunits_ldflags} +) +# Motif (-lXm) has no imported target; link it from MOTIF_HOME like the makefile. +if(MOTIF_HOME AND NOT MOTIF_HOME STREQUAL "/usr") + list(APPEND _tr_fxplot_libs -L${MOTIF_HOME}/lib) +endif() +list(APPEND _tr_fxplot_libs + -lXm + X11::Xt + X11::X11 +) +if(APPLE) + list(APPEND _tr_fxplot_libs -lc++abi) +else() + list(APPEND _tr_fxplot_libs rt) +endif() +target_link_libraries(trick-fxplot PRIVATE ${_tr_fxplot_libs}) +install(TARGETS trick-fxplot RUNTIME DESTINATION bin) diff --git a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt index d20567253..158071c6e 100644 --- a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt @@ -25,11 +25,12 @@ set(GXPLOT_SRC ) add_executable(trick-gxplot ${GXPLOT_SRC}) +# X11/LibXml2 include dirs come from the X11::X11 / LibXml2::LibXml2 imported +# targets linked below (their INTERFACE_INCLUDE_DIRECTORIES), so they are not +# repeated here. target_include_directories(trick-gxplot PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_SOURCE_DIR}/include - ${X11_X11_INCLUDE_PATH} - ${LIBXML2_INCLUDE_DIR} ) separate_arguments(_tr_gxplot_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") target_compile_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_includes}) @@ -56,8 +57,8 @@ endif() list(APPEND _tr_gxplot_libs dl ${_tr_gxplot_udunits_ldflags} - ${X11_Xt_LIB} ${X11_X11_LIB} - ${LIBXML2_LIBRARIES} + X11::Xt X11::X11 + LibXml2::LibXml2 ) if(APPLE) list(APPEND _tr_gxplot_libs -lc++abi) diff --git a/trick_source/data_products/DPX/DPC/CMakeLists.txt b/trick_source/data_products/DPX/DPC/CMakeLists.txt index 95491fa0d..88082e4ff 100644 --- a/trick_source/data_products/DPX/DPC/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPC/CMakeLists.txt @@ -18,7 +18,9 @@ target_include_directories(DPC PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_SOURCE_DIR}/include - ${LIBXML2_INCLUDE_DIR} ) +# PRIVATE: libxml2 headers are a compile-only need (static archive → no link +# propagation); LibXml2::LibXml2 supplies the include dirs. +target_link_libraries(DPC PRIVATE LibXml2::LibXml2) separate_arguments(_tr_dpc_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") target_compile_options(DPC PRIVATE ${_tr_dpc_udunits_includes}) diff --git a/trick_source/data_products/DPX/DPM/CMakeLists.txt b/trick_source/data_products/DPX/DPM/CMakeLists.txt index ff6d94578..1e0cf721d 100644 --- a/trick_source/data_products/DPX/DPM/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPM/CMakeLists.txt @@ -25,7 +25,10 @@ target_include_directories(DPM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_SOURCE_DIR}/include - ${LIBXML2_INCLUDE_DIR} ) +# PRIVATE: libxml2 headers are a compile-only need of DPM's own sources; a +# static archive propagates no link, so this just supplies the include dirs +# (via LibXml2::LibXml2's INTERFACE_INCLUDE_DIRECTORIES). +target_link_libraries(DPM PRIVATE LibXml2::LibXml2) separate_arguments(_tr_dpm_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") target_compile_options(DPM PRIVATE ${_tr_dpm_udunits_includes}) From 0e78e7ae179e8812a45b2effc63845e1604a1b10 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Wed, 8 Jul 2026 16:39:49 -0500 Subject: [PATCH 16/28] cmake: transition: move cmake file under cmake/ --- .../TestICGLinkedLibs.cpp | 23 ++++++++----------- cmake/TrickClangLibs.cmake | 4 ++-- 2 files changed, 12 insertions(+), 15 deletions(-) rename {CMakeTestFiles => cmake}/TestICGLinkedLibs.cpp (95%) diff --git a/CMakeTestFiles/TestICGLinkedLibs.cpp b/cmake/TestICGLinkedLibs.cpp similarity index 95% rename from CMakeTestFiles/TestICGLinkedLibs.cpp rename to cmake/TestICGLinkedLibs.cpp index 33ce68915..94828b497 100644 --- a/CMakeTestFiles/TestICGLinkedLibs.cpp +++ b/cmake/TestICGLinkedLibs.cpp @@ -1,3 +1,12 @@ +#include "clang/Basic/Builtins.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Parse/ParseAST.h" // `llvm/Support/Host.h` is deprecated in favour of `llvm/TargetParser/Host.h` since clang 17 #if LIBCLANG_MAJOR > 16 #include "llvm/TargetParser/Host.h" @@ -7,16 +16,4 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" -#include "clang/Basic/Builtins.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/Basic/TargetOptions.h" -#include "clang/Basic/TargetInfo.h" -#include "clang/Basic/FileManager.h" -#include "clang/Lex/Preprocessor.h" -#include "clang/Lex/PreprocessorOptions.h" -#include "clang/Basic/Diagnostic.h" -#include "clang/Parse/ParseAST.h" - -int main() { - return 0; -} \ No newline at end of file +int main() { return 0; } diff --git a/cmake/TrickClangLibs.cmake b/cmake/TrickClangLibs.cmake index aff24c768..72adc32b7 100644 --- a/cmake/TrickClangLibs.cmake +++ b/cmake/TrickClangLibs.cmake @@ -1,6 +1,6 @@ # Implements the ICG_CLANGLIBS selection algorithm from # autoconf/configure.ac:222-256 exactly, then sanity-checks the result with a -# try_compile against CMakeTestFiles/TestICGLinkedLibs.cpp. +# try_compile against TestICGLinkedLibs.cpp. # # Requires LLVM_LIB_DIR and CLANG_VERSION (from FindLLVMClang.cmake). # Exports: ICG_CLANGLIBS @@ -80,7 +80,7 @@ separate_arguments(_tr_icg_link_libs_list NATIVE_COMMAND "-L${LLVM_LIB_DIR} ${_t try_compile(TR_ICG_CLANG_HEADERS_COMPILE ${CMAKE_BINARY_DIR}/CMakeFiles/TrickClangLibsCheck - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeTestFiles/TestICGLinkedLibs.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestICGLinkedLibs.cpp CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${LLVM_INCLUDE_DIR}" COMPILE_DEFINITIONS From 08dcab181fed347399fdd821ec37f2c8feadded2 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 14 Jul 2026 16:18:46 -0500 Subject: [PATCH 17/28] cmake: transition: remove dead code --- CMakeLists.txt | 3 --- cmake/FindMaven.cmake | 19 ------------------- cmake/TrickTest.cmake | 1 - 3 files changed, 23 deletions(-) delete mode 100644 cmake/FindMaven.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 304807f02..d9d827800 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -93,8 +93,6 @@ configure_file( # Phase 2 — core native build: ICG, io_src, archives, SWIG # ────────────────────────────────────────────────────────────────────────── find_package(Python3 REQUIRED COMPONENTS Interpreter Development.Embed) -find_package(FLEX REQUIRED) -find_package(BISON REQUIRED) # Flags shared by every Trick-core compile target, mirroring # TRICK_SYSTEM_CXXFLAGS/TRICK_CXXFLAGS (Makefile.common:98-197). HDF5's @@ -180,7 +178,6 @@ add_library(trick STATIC ${TRICK_IO_SOURCES} ) target_link_libraries(trick PUBLIC trick_build_flags) -set_target_properties(trick PROPERTIES ARCHIVE_OUTPUT_NAME trick) add_dependencies(trick trick_io_src_gen) # ────────────────────────────────────────────────────────────────────────── diff --git a/cmake/FindMaven.cmake b/cmake/FindMaven.cmake deleted file mode 100644 index 890982426..000000000 --- a/cmake/FindMaven.cmake +++ /dev/null @@ -1,19 +0,0 @@ - -# FindMaven -# -------- -# -# Find mvn -# -# This module looks for mvn. This module defines the following values: -# -# :: -# -# MAVEN_EXECUTABLE: the full path to the mvn tool. -# MAVEN_FOUND: True if mvn has been found. - -find_program(MAVEN_EXECUTABLE mvn) -include (FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(Maven FOUND_VAR MAVEN_FOUND REQUIRED_VARS MAVEN_EXECUTABLE) -if(MAVEN_FIND_REQUIRED AND NOT MAVEN_FOUND) - message(FATAL_ERROR "Could not find mvn") -endif() diff --git a/cmake/TrickTest.cmake b/cmake/TrickTest.cmake index 44c70dec2..825fc46b3 100644 --- a/cmake/TrickTest.cmake +++ b/cmake/TrickTest.cmake @@ -131,7 +131,6 @@ endif() # --trick_top_level stays the source tree, since the test/SIM_* sim # directories test_sims.yml references only exist there, not in the stage. if(EXISTS ${CMAKE_SOURCE_DIR}/test_sims.yml) - find_package(Python3 COMPONENTS Interpreter) add_test(NAME sims COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/trickops.py --trick_top_level ${CMAKE_SOURCE_DIR} From 52a62708478218c77bed9fbef1876fb8aaf1e964 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 14 Jul 2026 16:27:07 -0500 Subject: [PATCH 18/28] cmake: transition: Simplify udunits2 import --- CMakeLists.txt | 5 +---- trick_source/codegen/Interface_Code_Gen/CMakeLists.txt | 10 ++++++---- .../data_products/DPX/APPS/FXPLOT/CMakeLists.txt | 9 +++++---- .../data_products/DPX/APPS/GXPLOT/CMakeLists.txt | 9 +++++---- trick_source/data_products/DPX/DPC/CMakeLists.txt | 4 +--- trick_source/data_products/DPX/DPM/CMakeLists.txt | 4 +--- trick_source/data_products/Log/CMakeLists.txt | 3 +-- trick_source/data_products/units/CMakeLists.txt | 3 +-- 8 files changed, 21 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d9d827800..efa6a9b34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,10 +121,7 @@ target_include_directories(trick_build_flags INTERFACE ${CMAKE_SOURCE_DIR}/include ${CMAKE_SOURCE_DIR}/include/trick/compat ) -if(UDUNITS_INCLUDES) - separate_arguments(_tr_udunits_includes_list NATIVE_COMMAND "${UDUNITS_INCLUDES}") - target_compile_options(trick_build_flags INTERFACE ${_tr_udunits_includes_list}) -endif() +target_link_libraries(trick_build_flags INTERFACE Trick::udunits2) if(TRICK_USE_ER7_UTILS) target_compile_definitions(trick_build_flags INTERFACE USE_ER7_UTILS_INTEGRATORS) if(EXISTS ${CMAKE_SOURCE_DIR}/trick_source/er7_utils/CheckpointHelper) diff --git a/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt b/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt index ab25064f8..cc1b37f3d 100644 --- a/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt +++ b/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt @@ -44,8 +44,6 @@ if(CMAKE_MATCH_1) endif() target_include_directories(trick-ICG PRIVATE ${LLVM_INCLUDE_DIR}) -separate_arguments(_tr_icg_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(trick-ICG PRIVATE ${_tr_icg_udunits_includes}) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") target_compile_definitions(trick-ICG PRIVATE TRICK_GCC_VERSION="${GCC_VERSION}") @@ -73,8 +71,7 @@ execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags separate_arguments(_tr_icg_llvm_libs_list NATIVE_COMMAND "${_tr_icg_llvm_libs}") separate_arguments(_tr_icg_llvm_ldflags_list NATIVE_COMMAND "${_tr_icg_llvm_ldflags}") -separate_arguments(_tr_icg_udunits_ldflags_list NATIVE_COMMAND "${UDUNITS_LDFLAGS}") -target_link_libraries(trick-ICG PRIVATE ${_tr_icg_clanglibs_list} ${_tr_icg_llvm_ldflags_list} ${_tr_icg_llvm_libs_list} ${_tr_icg_udunits_ldflags_list}) +target_link_libraries(trick-ICG PRIVATE ${_tr_icg_clanglibs_list} ${_tr_icg_llvm_ldflags_list} ${_tr_icg_llvm_libs_list} Trick::udunits2) target_link_directories(trick-ICG PRIVATE ${LLVM_LIB_DIR}) if(CMAKE_SYSTEM_NAME STREQUAL "Linux") @@ -102,3 +99,8 @@ elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") VERBATIM ) endif() + +# UDUNITS_HOME may be a non-system prefix; keep the build-time rpath to +# libudunits2 in the installed binary (CMake drops build rpath at install by +# default) instead of the makefile's literal -Wl,-rpath. +set_target_properties(trick-ICG PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) diff --git a/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt index efc2fa05d..cb629156d 100644 --- a/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt @@ -33,8 +33,6 @@ target_include_directories(trick-fxplot PRIVATE if(MOTIF_HOME) target_include_directories(trick-fxplot PRIVATE ${MOTIF_HOME}/include) endif() -separate_arguments(_tr_fxplot_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(trick-fxplot PRIVATE ${_tr_fxplot_udunits_includes}) # Link order matters (GNU ld resolves left-to-right in one pass, so a library # must follow whatever consumes its symbols). Mirrors the makefile's final link @@ -42,7 +40,6 @@ target_compile_options(trick-fxplot PRIVATE ${_tr_fxplot_udunits_includes}) # TRICK_UNIT_LIBS LIBXML HDF5_LIB -ldl UDUNITS_LDFLAGS) then XLIBS (Motif -lXm # -lXt -lX11) then LIBRTDEF (-lrt on Linux). See ../GXPLOT/CMakeLists.txt for # the same reasoning and the macOS-vs-Linux linker-strictness note. -separate_arguments(_tr_fxplot_udunits_ldflags NATIVE_COMMAND "${UDUNITS_LDFLAGS}") set(_tr_fxplot_libs DPC DPM @@ -55,7 +52,7 @@ if(HDF5_HOME) endif() list(APPEND _tr_fxplot_libs dl - ${_tr_fxplot_udunits_ldflags} + Trick::udunits2 ) # Motif (-lXm) has no imported target; link it from MOTIF_HOME like the makefile. if(MOTIF_HOME AND NOT MOTIF_HOME STREQUAL "/usr") @@ -72,5 +69,9 @@ else() list(APPEND _tr_fxplot_libs rt) endif() target_link_libraries(trick-fxplot PRIVATE ${_tr_fxplot_libs}) +# UDUNITS_HOME may be a non-system prefix; keep the build-time rpath to +# libudunits2 in the installed binary (CMake drops build rpath at install by +# default) instead of the makefile's literal -Wl,-rpath. +set_target_properties(trick-fxplot PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) install(TARGETS trick-fxplot RUNTIME DESTINATION bin) diff --git a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt index 158071c6e..e8e6f2c1a 100644 --- a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt @@ -32,8 +32,6 @@ target_include_directories(trick-gxplot PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_SOURCE_DIR}/include ) -separate_arguments(_tr_gxplot_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_includes}) # Link order matters here: GNU ld (default on Linux) resolves symbols in a # single left-to-right pass, so a library must appear *after* whatever @@ -45,7 +43,6 @@ target_compile_options(trick-gxplot PRIVATE ${_tr_gxplot_udunits_includes}) # actually need it, and silently linked fine on macOS (ld64's linker is far # more forgiving of ordering) while failing with "undefined reference to # `ut_parse'" etc. on Linux. -separate_arguments(_tr_gxplot_udunits_ldflags NATIVE_COMMAND "${UDUNITS_LDFLAGS}") set(_tr_gxplot_libs DPC DPM @@ -56,7 +53,7 @@ if(HDF5_HOME) endif() list(APPEND _tr_gxplot_libs dl - ${_tr_gxplot_udunits_ldflags} + Trick::udunits2 X11::Xt X11::X11 LibXml2::LibXml2 ) @@ -67,5 +64,9 @@ else() endif() list(APPEND _tr_gxplot_libs DPC dpv_utils) target_link_libraries(trick-gxplot PRIVATE ${_tr_gxplot_libs}) +# UDUNITS_HOME may be a non-system prefix; keep the build-time rpath to +# libudunits2 in the installed binary (CMake drops build rpath at install by +# default) instead of the makefile's literal -Wl,-rpath. +set_target_properties(trick-gxplot PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) install(TARGETS trick-gxplot RUNTIME DESTINATION bin) diff --git a/trick_source/data_products/DPX/DPC/CMakeLists.txt b/trick_source/data_products/DPX/DPC/CMakeLists.txt index 88082e4ff..f65f33542 100644 --- a/trick_source/data_products/DPX/DPC/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPC/CMakeLists.txt @@ -21,6 +21,4 @@ target_include_directories(DPC PRIVATE ) # PRIVATE: libxml2 headers are a compile-only need (static archive → no link # propagation); LibXml2::LibXml2 supplies the include dirs. -target_link_libraries(DPC PRIVATE LibXml2::LibXml2) -separate_arguments(_tr_dpc_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(DPC PRIVATE ${_tr_dpc_udunits_includes}) +target_link_libraries(DPC PRIVATE LibXml2::LibXml2 Trick::udunits2) diff --git a/trick_source/data_products/DPX/DPM/CMakeLists.txt b/trick_source/data_products/DPX/DPM/CMakeLists.txt index 1e0cf721d..3713f2bf7 100644 --- a/trick_source/data_products/DPX/DPM/CMakeLists.txt +++ b/trick_source/data_products/DPX/DPM/CMakeLists.txt @@ -29,6 +29,4 @@ target_include_directories(DPM PRIVATE # PRIVATE: libxml2 headers are a compile-only need of DPM's own sources; a # static archive propagates no link, so this just supplies the include dirs # (via LibXml2::LibXml2's INTERFACE_INCLUDE_DIRECTORIES). -target_link_libraries(DPM PRIVATE LibXml2::LibXml2) -separate_arguments(_tr_dpm_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(DPM PRIVATE ${_tr_dpm_udunits_includes}) +target_link_libraries(DPM PRIVATE LibXml2::LibXml2 Trick::udunits2) diff --git a/trick_source/data_products/Log/CMakeLists.txt b/trick_source/data_products/Log/CMakeLists.txt index fc3333ace..65f709d10 100644 --- a/trick_source/data_products/Log/CMakeLists.txt +++ b/trick_source/data_products/Log/CMakeLists.txt @@ -25,8 +25,7 @@ target_include_directories(dp_log PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_SOURCE_DIR}/include ) -separate_arguments(_tr_dp_log_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(dp_log PRIVATE ${_tr_dp_log_udunits_includes}) +target_link_libraries(dp_log PRIVATE Trick::udunits2) set_target_properties(dp_log PROPERTIES ARCHIVE_OUTPUT_NAME log) if(HDF5_HOME) diff --git a/trick_source/data_products/units/CMakeLists.txt b/trick_source/data_products/units/CMakeLists.txt index 679f52d6b..bf66c5b40 100644 --- a/trick_source/data_products/units/CMakeLists.txt +++ b/trick_source/data_products/units/CMakeLists.txt @@ -8,6 +8,5 @@ add_library(dp_units STATIC units_conv.c ) target_include_directories(dp_units PRIVATE ${CMAKE_SOURCE_DIR}/include) -separate_arguments(_tr_dp_units_udunits_includes NATIVE_COMMAND "${UDUNITS_INCLUDES}") -target_compile_options(dp_units PRIVATE ${_tr_dp_units_udunits_includes}) +target_link_libraries(dp_units PRIVATE Trick::udunits2) set_target_properties(dp_units PROPERTIES ARCHIVE_OUTPUT_NAME trick_units) From 704bc101146f28b9ad5d9a50ed64c7903ff5f77e Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 14 Jul 2026 16:34:36 -0500 Subject: [PATCH 19/28] cmake: transition: Simplify hdf5 import --- cmake/TrickPrograms.cmake | 16 ++++++++++++++++ .../data_products/DPX/APPS/FXPLOT/CMakeLists.txt | 2 +- .../data_products/DPX/APPS/GXPLOT/CMakeLists.txt | 2 +- trick_source/data_products/Log/CMakeLists.txt | 5 +++++ trick_source/sim_services/CMakeLists.txt | 3 +++ trick_source/trick_swig/CMakeLists.txt | 4 +--- 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/cmake/TrickPrograms.cmake b/cmake/TrickPrograms.cmake index 35fbaaf60..b79b9a9ee 100644 --- a/cmake/TrickPrograms.cmake +++ b/cmake/TrickPrograms.cmake @@ -171,6 +171,22 @@ else() set(HDF5_HOME "") endif() +# trick::hdf5: derived from HDF5_HOME (not find_package(HDF5), which can +# resolve a different install than the ladder above — e.g. via HDF5_ROOT or +# pkg-config — and silently diverge from the HDF5_HOME baked into +# config_user.mk for sims). Mirrors the make build's literal link line +# (-L$(HDF5_HOME)/lib -lhdf5_hl -lhdf5 -lsz); -lsz has no FindHDF5 equivalent. +# No -DHDF5 here: that define is deliberately per-file/per-target scoped to +# match the Makefiles (see sim_services/CMakeLists.txt, data_products/Log). +if(HDF5_HOME) + add_library(trick::hdf5 INTERFACE IMPORTED) + target_link_directories(trick::hdf5 INTERFACE ${HDF5_HOME}/lib) + target_link_libraries(trick::hdf5 INTERFACE hdf5_hl hdf5 sz) + if(NOT HDF5_HOME STREQUAL "/usr") + target_include_directories(trick::hdf5 INTERFACE ${HDF5_HOME}/include) + endif() +endif() + # ── GSL (autoconf/m4/tr_gsl_home.m4) ──────────────────────────────────────── if(GSL_HOME) if(NOT EXISTS "${GSL_HOME}/include/gsl") diff --git a/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt index cb629156d..8c67cfd52 100644 --- a/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/FXPLOT/CMakeLists.txt @@ -48,7 +48,7 @@ set(_tr_fxplot_libs LibXml2::LibXml2 ) if(HDF5_HOME) - list(APPEND _tr_fxplot_libs -L${HDF5_HOME}/lib -lhdf5_hl -lhdf5 -lsz) + list(APPEND _tr_fxplot_libs trick::hdf5) endif() list(APPEND _tr_fxplot_libs dl diff --git a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt index e8e6f2c1a..7900251ed 100644 --- a/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt +++ b/trick_source/data_products/DPX/APPS/GXPLOT/CMakeLists.txt @@ -49,7 +49,7 @@ set(_tr_gxplot_libs dp_log dp_var dp_eqparse dp_units ) if(HDF5_HOME) - list(APPEND _tr_gxplot_libs -L${HDF5_HOME}/lib -lhdf5_hl -lhdf5 -lsz) + list(APPEND _tr_gxplot_libs trick::hdf5) endif() list(APPEND _tr_gxplot_libs dl diff --git a/trick_source/data_products/Log/CMakeLists.txt b/trick_source/data_products/Log/CMakeLists.txt index 65f709d10..fc45070ee 100644 --- a/trick_source/data_products/Log/CMakeLists.txt +++ b/trick_source/data_products/Log/CMakeLists.txt @@ -28,6 +28,11 @@ target_include_directories(dp_log PRIVATE target_link_libraries(dp_log PRIVATE Trick::udunits2) set_target_properties(dp_log PROPERTIES ARCHIVE_OUTPUT_NAME log) +# Deliberately per-source rather than target_link_libraries(... trick::hdf5): +# source properties can't consume a target's usage requirements, and this +# scoping must match the Makefile's file-local override exactly (link-time +# HDF5 libs come from trick::hdf5 on the FXPLOT/GXPLOT executables that link +# dp_log). if(HDF5_HOME) set_source_files_properties(TrickHDF5.cpp DataStreamFactory.cpp PROPERTIES COMPILE_OPTIONS "-I${HDF5_HOME}/include;-DHDF5" diff --git a/trick_source/sim_services/CMakeLists.txt b/trick_source/sim_services/CMakeLists.txt index a2bbba9f3..c6ef9e58e 100644 --- a/trick_source/sim_services/CMakeLists.txt +++ b/trick_source/sim_services/CMakeLists.txt @@ -256,6 +256,9 @@ add_library( sim_services_objs OBJECT target_link_libraries( sim_services_objs PUBLIC trick_build_flags ) # HDF5 support in DataRecord/DRHDF5.cpp (Makefile: DataRecord/Makefile:6-9). +# Deliberately per-source rather than target_link_libraries(... trick::hdf5): +# source properties can't consume a target's usage requirements, and this +# object's -I scoping must match the Makefile's file-local override exactly. if(HDF5_HOME) set_property(SOURCE DataRecord/DRHDF5.cpp APPEND PROPERTY COMPILE_DEFINITIONS HDF5) if(NOT HDF5_HOME STREQUAL "/usr") diff --git a/trick_source/trick_swig/CMakeLists.txt b/trick_source/trick_swig/CMakeLists.txt index 33cf86ff2..f430baa22 100644 --- a/trick_source/trick_swig/CMakeLists.txt +++ b/trick_source/trick_swig/CMakeLists.txt @@ -82,9 +82,7 @@ set_target_properties( trick_pyip PROPERTIES ARCHIVE_OUTPUT_NAME trick_pyip) # DRHDF5.hh's own `#include "hdf5.h"`). if(HDF5_HOME) target_compile_definitions( trick_pyip PRIVATE HDF5 ) - if(NOT HDF5_HOME STREQUAL "/usr") - target_include_directories( trick_pyip PRIVATE ${HDF5_HOME}/include ) - endif() + target_link_libraries( trick_pyip PRIVATE trick::hdf5 ) endif() # Makefile: trick_swig/Makefile:28-34 — SWIG-generated code triggers warnings From 625cc8a081f285f04de3adec9464e1c295140d73 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Tue, 14 Jul 2026 16:47:58 -0500 Subject: [PATCH 20/28] cmake: transition: Consolidate ICG flags into TrickClangLibs.cmake --- cmake/TrickClangLibs.cmake | 27 +++++++++++------ .../codegen/Interface_Code_Gen/CMakeLists.txt | 30 +++++++------------ 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/cmake/TrickClangLibs.cmake b/cmake/TrickClangLibs.cmake index 72adc32b7..ba89c50ad 100644 --- a/cmake/TrickClangLibs.cmake +++ b/cmake/TrickClangLibs.cmake @@ -3,7 +3,10 @@ # try_compile against TestICGLinkedLibs.cpp. # # Requires LLVM_LIB_DIR and CLANG_VERSION (from FindLLVMClang.cmake). -# Exports: ICG_CLANGLIBS +# Exports: ICG_CLANGLIBS, TR_LLVM_LIBS, TR_LLVM_SYSTEM_LIBS, TR_LLVM_LDFLAGS +# (llvm-config --libs/--system-libs/--ldflags, the latter pre-filtered per +# platform — see below). trick_source/codegen/Interface_Code_Gen/CMakeLists.txt +# links trick-ICG from these instead of re-running llvm-config itself. if(NOT DEFINED LLVM_LIB_DIR) message(FATAL_ERROR "TrickClangLibs.cmake requires LLVM_LIB_DIR (include FindLLVMClang first)") @@ -46,21 +49,27 @@ endif() string(REGEX MATCH "^[0-9]+" _tr_llvm_version_major "${LLVM_VERSION_STRING}") execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --libs - OUTPUT_VARIABLE _tr_llvm_libs OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_VARIABLE TR_LLVM_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --system-libs - OUTPUT_VARIABLE _tr_llvm_system_libs OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_VARIABLE _tr_llvm_system_libs_raw OUTPUT_STRIP_TRAILING_WHITESPACE) execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags - OUTPUT_VARIABLE _tr_llvm_ldflags OUTPUT_STRIP_TRAILING_WHITESPACE) + OUTPUT_VARIABLE TR_LLVM_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE) -set(_tr_icg_link_libs "${ICG_CLANGLIBS} ${_tr_llvm_libs}") +# TR_LLVM_SYSTEM_LIBS: --system-libs filtered per platform, computed once +# here and reused by both this file's sanity try_compile below and +# Interface_Code_Gen/CMakeLists.txt's trick-ICG link. +set(TR_LLVM_SYSTEM_LIBS "") if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(LLVM_VERSION_STRING VERSION_GREATER_EQUAL "3.5") # Fedora adds -ledit as a system lib, but it isn't installed or required. - string(REPLACE "-ledit" "" _tr_llvm_system_libs_filtered "${_tr_llvm_system_libs}") - set(_tr_icg_link_libs "${_tr_icg_link_libs} ${_tr_llvm_system_libs_filtered}") + string(REPLACE "-ledit" "" TR_LLVM_SYSTEM_LIBS "${_tr_llvm_system_libs_raw}") endif() elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - set(_tr_icg_link_libs "${_tr_icg_link_libs} ${_tr_llvm_system_libs}") + set(TR_LLVM_SYSTEM_LIBS "${_tr_llvm_system_libs_raw}") +endif() + +set(_tr_icg_link_libs "${ICG_CLANGLIBS} ${TR_LLVM_LIBS} ${TR_LLVM_SYSTEM_LIBS}") +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") if(_tr_llvm_version_major GREATER_EQUAL 16) set(_tr_icg_link_libs "${_tr_icg_link_libs} -lc++abi -lclang-cpp") else() @@ -76,7 +85,7 @@ else() set(_tr_icg_cxx_std 11) endif() -separate_arguments(_tr_icg_link_libs_list NATIVE_COMMAND "-L${LLVM_LIB_DIR} ${_tr_llvm_ldflags} ${_tr_icg_link_libs}") +separate_arguments(_tr_icg_link_libs_list NATIVE_COMMAND "-L${LLVM_LIB_DIR} ${TR_LLVM_LDFLAGS} ${_tr_icg_link_libs}") try_compile(TR_ICG_CLANG_HEADERS_COMPILE ${CMAKE_BINARY_DIR}/CMakeFiles/TrickClangLibsCheck diff --git a/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt b/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt index cc1b37f3d..b7bb676fa 100644 --- a/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt +++ b/trick_source/codegen/Interface_Code_Gen/CMakeLists.txt @@ -61,29 +61,21 @@ set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/main.cpp PROPERTIES COMPILE_DEFINITIONS "TRICK_VERSION=\"${TRICK_MAJOR}.${TRICK_MINOR}.${TRICK_TINY}\"" ) +# TR_LLVM_LIBS/TR_LLVM_SYSTEM_LIBS/TR_LLVM_LDFLAGS come from +# cmake/TrickClangLibs.cmake (included at the top level before this +# subdirectory), which already ran `llvm-config --libs/--system-libs/--ldflags` +# and filtered --system-libs per platform (e.g. stripping Fedora's spurious +# -ledit on Linux) for its own sanity try_compile — reuse them here instead of +# invoking llvm-config and re-deriving the same filter a second time. separate_arguments(_tr_icg_clanglibs_list NATIVE_COMMAND "${ICG_CLANGLIBS}") -execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --libs - OUTPUT_VARIABLE _tr_icg_llvm_libs OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --system-libs - OUTPUT_VARIABLE _tr_icg_llvm_system_libs OUTPUT_STRIP_TRAILING_WHITESPACE) -execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags - OUTPUT_VARIABLE _tr_icg_llvm_ldflags OUTPUT_STRIP_TRAILING_WHITESPACE) -separate_arguments(_tr_icg_llvm_libs_list NATIVE_COMMAND "${_tr_icg_llvm_libs}") -separate_arguments(_tr_icg_llvm_ldflags_list NATIVE_COMMAND "${_tr_icg_llvm_ldflags}") +separate_arguments(_tr_icg_llvm_libs_list NATIVE_COMMAND "${TR_LLVM_LIBS}") +separate_arguments(_tr_icg_llvm_ldflags_list NATIVE_COMMAND "${TR_LLVM_LDFLAGS}") +separate_arguments(_tr_icg_llvm_system_libs_list NATIVE_COMMAND "${TR_LLVM_SYSTEM_LIBS}") -target_link_libraries(trick-ICG PRIVATE ${_tr_icg_clanglibs_list} ${_tr_icg_llvm_ldflags_list} ${_tr_icg_llvm_libs_list} Trick::udunits2) +target_link_libraries(trick-ICG PRIVATE ${_tr_icg_clanglibs_list} ${_tr_icg_llvm_ldflags_list} ${_tr_icg_llvm_libs_list} ${_tr_icg_llvm_system_libs_list} Trick::udunits2) target_link_directories(trick-ICG PRIVATE ${LLVM_LIB_DIR}) -if(CMAKE_SYSTEM_NAME STREQUAL "Linux") - if(NOT LLVM_VERSION_STRING VERSION_LESS "3.5") - # Fedora adds -ledit as a system lib, but it isn't installed, or required. - string(REPLACE "-ledit" "" _tr_icg_llvm_system_libs "${_tr_icg_llvm_system_libs}") - separate_arguments(_tr_icg_llvm_system_libs_list NATIVE_COMMAND "${_tr_icg_llvm_system_libs}") - target_link_libraries(trick-ICG PRIVATE ${_tr_icg_llvm_system_libs_list}) - endif() -elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - separate_arguments(_tr_icg_llvm_system_libs_list NATIVE_COMMAND "${_tr_icg_llvm_system_libs}") - target_link_libraries(trick-ICG PRIVATE ${_tr_icg_llvm_system_libs_list}) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") if(_tr_llvm_version_major GREATER_EQUAL 16) target_link_libraries(trick-ICG PRIVATE -lc++abi -lclang-cpp) else() From abe3ef4b63b5217fc8a6d342c08087be0a6b6f0b Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Thu, 16 Jul 2026 14:52:20 -0500 Subject: [PATCH 21/28] cmake: transition: fail configure if standard library mismatch --- cmake/TestICGLinkedLibs.cpp | 25 +++++++++++++++++++++++-- cmake/TrickClangLibs.cmake | 25 ++++++++++++++++++++++++- 2 files changed, 47 insertions(+), 3 deletions(-) diff --git a/cmake/TestICGLinkedLibs.cpp b/cmake/TestICGLinkedLibs.cpp index 94828b497..0d67b1f0d 100644 --- a/cmake/TestICGLinkedLibs.cpp +++ b/cmake/TestICGLinkedLibs.cpp @@ -7,13 +7,34 @@ #include "clang/Lex/Preprocessor.h" #include "clang/Lex/PreprocessorOptions.h" #include "clang/Parse/ParseAST.h" +#include "llvm/Support/CommandLine.h" // `llvm/Support/Host.h` is deprecated in favour of `llvm/TargetParser/Host.h` since clang 17 #if LIBCLANG_MAJOR > 16 #include "llvm/TargetParser/Host.h" #else #include "llvm/Support/Host.h" #endif -#include "llvm/Support/CommandLine.h" #include "llvm/Support/raw_ostream.h" -int main() { return 0; } +#include + +// This TU is link-tested (not run) by cmake/TrickClangLibs.cmake to verify the +// selected C++ compiler can actually link the LLVM/Clang archives trick-ICG +// needs. main() must therefore *reference a std::string-returning LLVM symbol*, +// not just return: an empty main() links even when the compiler's C++ standard +// library (libc++ vs libstdc++) disagrees with the one LLVM was built against, +// because it pulls no library symbols — which lets a mismatched compiler slip +// past configure and only fail when trick-ICG itself is linked. +// +// llvm::sys::getDefaultTargetTriple() returns std::string and exists in every +// supported LLVM version, so the compiler emits a reference to *its* standard +// library's mangling of it (e.g. the libstdc++ [abi:cxx11] symbol under gcc vs +// the libc++ std::__1 symbol under a libc++ clang). That reference only +// resolves when the archives were built with the same standard library, so a +// mismatch turns into a link failure here — on any platform, without hardcoding +// per-OS/per-compiler assumptions. +int main() +{ + std::string triple = llvm::sys::getDefaultTargetTriple(); + return triple.empty() ? 1 : 0; +} diff --git a/cmake/TrickClangLibs.cmake b/cmake/TrickClangLibs.cmake index ba89c50ad..92a433cae 100644 --- a/cmake/TrickClangLibs.cmake +++ b/cmake/TrickClangLibs.cmake @@ -100,5 +100,28 @@ try_compile(TR_ICG_CLANG_HEADERS_COMPILE OUTPUT_VARIABLE TR_ICG_CLANG_HEADERS_COMPILE_OUTPUT ) if(NOT TR_ICG_CLANG_HEADERS_COMPILE) - message(FATAL_ERROR "ICG_CLANGLIBS (\"${ICG_CLANGLIBS}\") failed a sanity link against ${LLVM_LIB_DIR}:\n${TR_ICG_CLANG_HEADERS_COMPILE_OUTPUT}") + message(FATAL_ERROR +"trick-ICG cannot be linked against the LLVM/Clang libraries in + ${LLVM_LIB_DIR} +using + ${CMAKE_CXX_COMPILER} (${CMAKE_CXX_COMPILER_ID} ${CMAKE_CXX_COMPILER_VERSION}). + +The usual cause is a C++ standard-library (ABI) mismatch: the LLVM/Clang +archives are built against one standard library (libc++ or libstdc++) and the +selected C++ compiler defaults to the other, so the std::string / std::* symbols +the archives reference cannot be resolved. The compiler and LLVM must agree on +the standard library. + * macOS: Homebrew/system LLVM is built against libc++. Build with a libc++ + Clang -- the default 'c++'/Apple Clang, or Homebrew clang. GNU gcc/g++ uses + libstdc++ and has no working libc++ mode, so it cannot link these archives; + unset CC/CXX or set them to clang/clang++. + * Linux: distro LLVM is built against libstdc++; gcc and the system clang both + default to libstdc++, so either works. Avoid clang -stdlib=libc++ unless your + LLVM was also built that way. +Alternatively, point -DLLVM_HOME at an LLVM built with the same standard library +as your compiler. + +ICG_CLANGLIBS was: \"${ICG_CLANGLIBS}\" +Sanity-link output: +${TR_ICG_CLANG_HEADERS_COMPILE_OUTPUT}") endif() From 9dba3a78089d5ae73fdd77432507bed290fd64ef Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Fri, 28 Aug 2026 23:09:27 -0500 Subject: [PATCH 22/28] fix trickops python warnings and modernize to python v3.10 style --- share/trick/trickops/TrickWorkflow.py | 14 +- share/trick/trickops/WorkflowCommon.py | 20 +-- share/trick/trickops/send_hs.py | 6 +- trickops.py | 180 +++++++++++++------------ 4 files changed, 111 insertions(+), 109 deletions(-) diff --git a/share/trick/trickops/TrickWorkflow.py b/share/trick/trickops/TrickWorkflow.py index 7941ac1f2..5fe65650f 100644 --- a/share/trick/trickops/TrickWorkflow.py +++ b/share/trick/trickops/TrickWorkflow.py @@ -1707,7 +1707,7 @@ def start(self): Start this Simulation job. Attempts a connection to the sim variable server in another thread after calling the base class Job() start() method """ - super(SingleRun, self).start() + super().start() self._connected = False # Finding a sim via PID can take several seconds. @@ -1766,13 +1766,13 @@ def connect(): thread.start() def get_status_string_line_count(self): - return super(SingleRun, self).get_status_string_line_count() + 1 + return super().get_status_string_line_count() + 1 def _not_started_string(self): - return super(SingleRun, self)._not_started_string() + "\n" + return super()._not_started_string() + "\n" def _running_string(self): - elapsed_time = super(SingleRun, self)._running_string() + elapsed_time = super()._running_string() if self._connected: return ( @@ -1782,13 +1782,13 @@ def _running_string(self): return elapsed_time + "\n" + create_progress_bar(0, "Connecting") def _success_string(self): - text = super(SingleRun, self)._success_string() + text = super()._success_string() if self._connected: text += self._connected_string() return text + "\n" + self._success_progress_bar def _failed_string(self): - text = super(SingleRun, self)._failed_string() + text = super()._failed_string() if self._connected: text += self._connected_string() return text + "\n" + self._failed_progress_bar @@ -1798,7 +1798,7 @@ def die(self): self._variable_server.close() except: pass - super(SingleRun, self).die() + super().die() def __del__(self): try: diff --git a/share/trick/trickops/WorkflowCommon.py b/share/trick/trickops/WorkflowCommon.py index 71e446fea..51523dd69 100644 --- a/share/trick/trickops/WorkflowCommon.py +++ b/share/trick/trickops/WorkflowCommon.py @@ -226,7 +226,7 @@ def unixify_string(string): # CLASSES -class Job(object): +class Job: """ Manages a given command intended to be run as a subprocess and provides methods for getting status information. More specific types of Jobs should inherit from @@ -523,34 +523,28 @@ def __init__(self, name, command, log_file, size): The expected number size in bytes of the log_file when the build is complete. Used to estimate progress. """ - super(FileSizeJob, self).__init__(name, command, log_file) + super().__init__(name, command, log_file) self.size = size def get_status_string_line_count(self): - return super(FileSizeJob, self).get_status_string_line_count() + 1 + return super().get_status_string_line_count() + 1 def _not_started_string(self): - return super(FileSizeJob, self)._not_started_string() + "\n" + return super()._not_started_string() + "\n" def _running_string(self): progress = os.path.getsize(self._log_file.name) / float(self.size) return ( - super(FileSizeJob, self)._running_string() + super()._running_string() + "\n" + create_progress_bar(progress, "{0:.1f}%".format(100 * progress)) ) def _success_string(self): - return ( - super(FileSizeJob, self)._success_string() - + "\n" - + self._success_progress_bar - ) + return super()._success_string() + "\n" + self._success_progress_bar def _failed_string(self): - return ( - super(FileSizeJob, self)._failed_string() + "\n" + self._failed_progress_bar - ) + return super()._failed_string() + "\n" + self._failed_progress_bar class WorkflowCommon: diff --git a/share/trick/trickops/send_hs.py b/share/trick/trickops/send_hs.py index e885a046f..8f8992d1f 100644 --- a/share/trick/trickops/send_hs.py +++ b/share/trick/trickops/send_hs.py @@ -12,7 +12,7 @@ # I do not like adding globals to "production code" just to facilitate a testing mechanism, but # I don't know of any cleaner way way to do this. -Jordan 12/2024 this_trick = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../..')) -class send_hs(object): +class send_hs: """ Utility class for parsing simulation diagnostic data at the end of a Trick-generated send_hs output file. @@ -145,8 +145,8 @@ def _attempt_hs_match(self, name, text): float or None Value of name if found, else: None """ - name = name.replace('(', '\(').replace(')', '\)') - m = re.match(name+': +([-]?[0-9]*\.?[0-9]+)', text.strip()) + name = name.replace('(', r'\(').replace(')', r'\)') + m = re.match(name + r': +([-]?[0-9]*\.?[0-9]+)', text.strip()) if m: return(float(m.group(1))) else: diff --git a/trickops.py b/trickops.py index aa9326035..ca8819e9e 100644 --- a/trickops.py +++ b/trickops.py @@ -1,22 +1,50 @@ import argparse import os +import signal import sys +from pathlib import Path -thisdir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.path.join(thisdir, "share/trick/trickops")) +sys.path.append(str(Path(__file__).resolve().parent / "share" / "trick" / "trickops")) -from TrickWorkflow import TrickWorkflow # noqa: E402 (needs the sys.path.append above) -from WorkflowCommon import Job # noqa: E402 +from TrickWorkflow import TrickWorkflow +from WorkflowCommon import Job -max_retries = 5 +THIS_DIR = Path(__file__).resolve().parent + +# Runs ordered by phase: some test sims depend on artifacts from an earlier +# phase (SIM_stls / SIM_checkpoint_data_recording read checkpoints written by a +# prior run; SIM_test_varserver has runs that cannot be concurrent; +# SIM_mc_generation generates runs and then executes them). +PHASES = (-1, 0, 1, 2, 3) + +# SIM_test_varserv's err1/err2 runs are expected to exit with SIGUSR1. The exit +# code differs Mac vs Linux, so it can't be hardcoded in the yaml; tag them here. +VARSERV_SIGUSR1_RUNS = frozenset( + { + "Run test/SIM_test_varserv RUN_test/err1_test.py", + "Run test/SIM_test_varserv RUN_test/err2_test.py", + } +) + +# Jobs skipped on macOS (SIM_trickified_shared does not build/run there). +DARWIN_SKIP_BUILDS = frozenset({"Build test/SIM_trickified_shared"}) +DARWIN_SKIP_RUNS = frozenset({"Run test/SIM_trickified_shared RUN_test/unit_test.py"}) class SimTestWorkflow(TrickWorkflow): - def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): + def __init__( + self, + quiet: bool, + trick_top_level: str | Path, + cpus: int, + config_file: str, + trick_dir: str | Path | None = None, + ) -> None: self.cpus = cpus + project_root = Path(trick_top_level) + # Create the trick_test directory if it doesn't already exist - if not os.path.exists(trick_top_level + "/trick_test"): - os.makedirs(trick_top_level + "/trick_test") + (project_root / "trick_test").mkdir(exist_ok=True) # trick_dir defaults to trick_top_level (historical behavior: sims and # the installed Trick used by bin/trick-CP live in the same in-source @@ -25,68 +53,52 @@ def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): # installed product (bin/trick-CP, libexec/, share/) may be a staged # out-of-source CMake install instead. if trick_dir is None: - trick_dir = trick_top_level + trick_dir = project_root # Base Class initialize, this creates internal management structures - TrickWorkflow.__init__( - self, - project_top_level=(trick_top_level), - log_dir=(trick_top_level + "/trickops_logs/"), - trick_dir=trick_dir, - config_file=(trick_top_level + "/" + config_file), + super().__init__( + project_top_level=str(project_root), + log_dir=str(project_root / "trickops_logs"), + trick_dir=str(trick_dir), + config_file=str(project_root / config_file), cpus=self.cpus, quiet=quiet, ) - def run(self): - build_jobs = self.get_jobs(kind="build") - - # This is awful but I can't think of another way around it - # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux - # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? - import signal - - run_names = [ - "Run test/SIM_test_varserv RUN_test/err1_test.py", - "Run test/SIM_test_varserv RUN_test/err2_test.py", - ] - for job in [job for job in self.get_jobs(kind="run") if job.name in run_names]: - job._expected_exit_status = signal.SIGUSR1.value + def _skip_darwin(self, jobs: list, skip_names: frozenset) -> list: + """Return jobs with the macOS-excluded ones filtered out (announcing each).""" + if self.platform != "darwin": + return jobs + kept = [] + for job in jobs: + if job.name in skip_names: + print(f"REMOVING JOB: {job.name}") + else: + kept.append(job) + return kept + + def run(self) -> int: + build_jobs = self._skip_darwin(self.get_jobs(kind="build"), DARWIN_SKIP_BUILDS) + analysis_jobs = self.get_jobs(kind="analyze") - # Several test sims have runs that require ordering via phases: - # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run - # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs - # - SIM_test_varserver has 3 runs that cannot be concurrent - # - SIM_mc_generation generates runs and then runs them - phases = [-1, 0, 1, 2, 3] + for job in self.get_jobs(kind="run"): + if job.name in VARSERV_SIGUSR1_RUNS: + job._expected_exit_status = signal.SIGUSR1.value - analysis_jobs = self.get_jobs(kind="analyze") - if self.platform == "darwin": - for job in build_jobs: - if job.name == "Build test/SIM_trickified_shared": - print("REMOVING JOB: " + job.name) - build_jobs.remove(job) builds_status = self.execute_jobs( build_jobs, max_concurrent=self.cpus, header="Executing all sim builds." ) - jobs = build_jobs - + jobs = list(build_jobs) run_status = 0 - for phase in phases: - run_jobs = self.get_jobs(kind="run", phase=phase) - if self.platform == "darwin": - for job in run_jobs: - if ( - job.name - == "Run test/SIM_trickified_shared RUN_test/unit_test.py" - ): - print("REMOVING JOB: " + job.name) - run_jobs.remove(job) + for phase in PHASES: + run_jobs = self._skip_darwin( + self.get_jobs(kind="run", phase=phase), DARWIN_SKIP_RUNS + ) this_status = self.execute_jobs( run_jobs, max_concurrent=self.cpus, - header="Executing phase " + str(phase) + " runs.", + header=f"Executing phase {phase} runs.", job_timeout=1000, ) run_status = run_status or this_status @@ -102,26 +114,17 @@ def run(self): # Dump failing logs for job in jobs: - if ( - job.get_status() == Job.Status.FAILED - or job.get_status() == Job.Status.TIMEOUT - ): - print("*" * 120) - if job.get_status() == Job.Status.FAILED: - header = "Failing job: " + job.name - else: - header = "Timed out job: " + job.name - - numspaces = int((120 - 20 - len(header)) / 2 - 2) - print( - "*" * 10, - " " * numspaces, - header, - " " * numspaces, - "*" * 10, + status = job.get_status() + if status in (Job.Status.FAILED, Job.Status.TIMEOUT): + header = ( + f"Failing job: {job.name}" + if status == Job.Status.FAILED + else f"Timed out job: {job.name}" ) print("*" * 120) - print(open(job.log_file, "r").read()) + print(f" {header} ".center(120, "*")) + print("*" * 120) + print(Path(job.log_file).read_text()) print("*" * 120, "\n\n\n") return ( @@ -133,7 +136,7 @@ def run(self): ) -if __name__ == "__main__": +def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="Build, run, and compare all test sims for Trick", formatter_class=argparse.ArgumentDefaultsHelpFormatter, @@ -143,7 +146,7 @@ def run(self): type=str, help="Path to the project tree containing the sim " "directories referenced by --config_file", - default=thisdir, + default=str(THIS_DIR), ) parser.add_argument( "--trick_dir", @@ -161,7 +164,7 @@ def run(self): parser.add_argument( "--cpus", type=int, - default=(os.cpu_count() if os.cpu_count() is not None else 8), + default=os.cpu_count() or 8, help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.", ) @@ -171,15 +174,20 @@ def run(self): help="Run configuration file to use, relative to trick_top_level", default="test_sims.yml", ) + return parser.parse_args() - myargs = parser.parse_args() - should_be_quiet = myargs.quiet or os.getenv("CI") is not None - sys.exit( - SimTestWorkflow( - quiet=should_be_quiet, - trick_top_level=myargs.trick_top_level, - trick_dir=myargs.trick_dir, - cpus=myargs.cpus, - config_file=myargs.config_file, - ).run() - ) + +def main() -> int: + args = parse_args() + quiet = args.quiet or os.getenv("CI") is not None + return SimTestWorkflow( + quiet=quiet, + trick_top_level=args.trick_top_level, + trick_dir=args.trick_dir, + cpus=args.cpus, + config_file=args.config_file, + ).run() + + +if __name__ == "__main__": + raise SystemExit(main()) From f8ed10d69f95449cc1e6965e6e05ae316055db79 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Thu, 16 Jul 2026 15:55:13 -0500 Subject: [PATCH 23/28] Ruff format --- share/trick/trickops/WorkflowCommon.py | 1 - share/trick/trickops/send_hs.py | 112 ++++++++++++++----------- 2 files changed, 61 insertions(+), 52 deletions(-) diff --git a/share/trick/trickops/WorkflowCommon.py b/share/trick/trickops/WorkflowCommon.py index 51523dd69..efb648c18 100644 --- a/share/trick/trickops/WorkflowCommon.py +++ b/share/trick/trickops/WorkflowCommon.py @@ -748,7 +748,6 @@ def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None) # status output is enabled. Otherwise, it will be called # directly. See below. def execute(stdscr=None): - # stdscr is passed via curses.wrapper if stdscr: # Turn off the cursor. Not all terminals may support diff --git a/share/trick/trickops/send_hs.py b/share/trick/trickops/send_hs.py index 8f8992d1f..e7c95892a 100644 --- a/share/trick/trickops/send_hs.py +++ b/share/trick/trickops/send_hs.py @@ -11,12 +11,17 @@ # https://note.nkmk.me/en/python-script-file-path/ # I do not like adding globals to "production code" just to facilitate a testing mechanism, but # I don't know of any cleaner way way to do this. -Jordan 12/2024 -this_trick = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../..')) +this_trick = os.path.normpath( + os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../..") +) + + class send_hs: """ Utility class for parsing simulation diagnostic data at the end of a Trick-generated send_hs output file. """ + def __init__(self, hs_file=None): """ Initialize this instance. @@ -30,8 +35,8 @@ def __init__(self, hs_file=None): """ self.hs_file = hs_file self.num_lines = None - self._diagnostics = {} # Internal dict of diagnostic keys and values - self._missing_diagnostics = [] # List of diagnostics we failed to find + self._diagnostics = {} # Internal dict of diagnostic keys and values + self._missing_diagnostics = [] # List of diagnostics we failed to find for k in self.keys(): self._diagnostics[k] = None @@ -53,7 +58,7 @@ def is_valid(self): True if all expected internal members were parsed. False if any member wasn't found. """ - self._missing_diagnostics = [] # Reset the internal list + self._missing_diagnostics = [] # Reset the internal list for d in self._diagnostics: if self._diagnostics[d] is None: self._missing_diagnostics.append(d) @@ -63,7 +68,7 @@ def is_valid(self): return True def missing_diagnostics(self): - ''' + """ Check for validity and return a list of any missing diagnostics that we were unable to find in the send_hs output @@ -75,12 +80,12 @@ def missing_diagnostics(self): ------- list All diagnotics that were unable to be parsed - ''' + """ self.is_valid() return self._missing_diagnostics def parse(self, hs_file=None): - ''' + """ Parse the content of self.hs_file and assign internal variables for each field found if hs_file is given, overwrite self.hs_file with it's value @@ -102,30 +107,32 @@ def parse(self, hs_file=None): ------ RuntimeError If no send_hs file was provided at construction or when calling this function - ''' + """ if hs_file: self.hs_file = hs_file if not self.hs_file or not os.path.isfile(self.hs_file): - raise RuntimeError(f"send_hs file: '{self.hs_file}' cannot be read." - " You must provide a valid send_hs output file.") + raise RuntimeError( + f"send_hs file: '{self.hs_file}' cannot be read." + " You must provide a valid send_hs output file." + ) self.num_lines = 0 with open(self.hs_file) as fp: - # Efficiency guard - we don't do an re pattern match until we've starting parsing the diagnostic msg - start_reading_diagnostics = False - for line in fp: - self.num_lines += 1 - # NOTE this "attempt match" approach is less efficient but it should be - # robust to a future ordering change of the lines from Trick's output. - if start_reading_diagnostics: - for d in self._diagnostics: - if self._diagnostics[d] is None: - self._diagnostics[d] = self._attempt_hs_match(d, line) - # This text precedes the diagnostics output, use it as the trigger to start parsing - if 'REALTIME SHUTDOWN STATS:' in line: - start_reading_diagnostics = True - continue + # Efficiency guard - we don't do an re pattern match until we've starting parsing the diagnostic msg + start_reading_diagnostics = False + for line in fp: + self.num_lines += 1 + # NOTE this "attempt match" approach is less efficient but it should be + # robust to a future ordering change of the lines from Trick's output. + if start_reading_diagnostics: + for d in self._diagnostics: + if self._diagnostics[d] is None: + self._diagnostics[d] = self._attempt_hs_match(d, line) + # This text precedes the diagnostics output, use it as the trigger to start parsing + if "REALTIME SHUTDOWN STATS:" in line: + start_reading_diagnostics = True + continue # Set validity status immediately after parsing self.is_valid() @@ -145,12 +152,12 @@ def _attempt_hs_match(self, name, text): float or None Value of name if found, else: None """ - name = name.replace('(', r'\(').replace(')', r'\)') - m = re.match(name + r': +([-]?[0-9]*\.?[0-9]+)', text.strip()) + name = name.replace("(", r"\(").replace(")", r"\)") + m = re.match(name + r": +([-]?[0-9]*\.?[0-9]+)", text.strip()) if m: - return(float(m.group(1))) + return float(m.group(1)) else: - return(None) + return None def keys(self) -> list: """ @@ -167,24 +174,25 @@ def keys(self) -> list: list All possible diagnostic names that get() can accept """ - return (['ACTUAL INIT TIME', - 'ACTUAL ELAPSED TIME', - 'SIMULATION START TIME', - 'SIMULATION STOP TIME', - 'SIMULATION ELAPSED TIME', - 'USER CPU TIME USED', - 'SYSTEM CPU TIME USED', - 'SIMULATION / CPU TIME', - 'INITIALIZATION USER CPU TIME', - 'INITIALIZATION SYSTEM CPU TIME', - 'SIMULATION RAM USAGE', - 'VOLUNTARY CONTEXT SWITCHES (INIT)', - 'INVOLUNTARY CONTEXT SWITCHES (INIT)', - 'VOLUNTARY CONTEXT SWITCHES (RUN)', - 'INVOLUNTARY CONTEXT SWITCHES (RUN)' - ]) - - def get_diagnostic (self, name: str) -> float: + return [ + "ACTUAL INIT TIME", + "ACTUAL ELAPSED TIME", + "SIMULATION START TIME", + "SIMULATION STOP TIME", + "SIMULATION ELAPSED TIME", + "USER CPU TIME USED", + "SYSTEM CPU TIME USED", + "SIMULATION / CPU TIME", + "INITIALIZATION USER CPU TIME", + "INITIALIZATION SYSTEM CPU TIME", + "SIMULATION RAM USAGE", + "VOLUNTARY CONTEXT SWITCHES (INIT)", + "INVOLUNTARY CONTEXT SWITCHES (INIT)", + "VOLUNTARY CONTEXT SWITCHES (RUN)", + "INVOLUNTARY CONTEXT SWITCHES (RUN)", + ] + + def get_diagnostic(self, name: str) -> float: """ Get a diagnostic value by it's name or partial name The first name matched in the self._diagnostics dict will be returned @@ -213,8 +221,10 @@ def get_diagnostic (self, name: str) -> float: for d in self._diagnostics: if name in d: - return(self._diagnostics[d]) - raise LookupError(f"Unable to get diagnostic '{name}'. Is it spelled correctly?") + return self._diagnostics[d] + raise LookupError( + f"Unable to get diagnostic '{name}'. Is it spelled correctly?" + ) def get(self, name: str) -> float: """ @@ -235,10 +245,10 @@ def get(self, name: str) -> float: return self.get_diagnostic(name) @property - def diagnostics (self) -> dict: + def diagnostics(self) -> dict: return dict(self._diagnostics) - def get_diagnostics (self) -> dict: + def get_diagnostics(self) -> dict: """ Get all diagnostics stored in internal self._diagnostics dictionary @@ -251,4 +261,4 @@ def get_diagnostics (self) -> dict: dict A copy of the internal self._diagnostics dictionary """ - return (self.diagnostics) + return self.diagnostics From f285aa5e6e890bed03ae826ae04bdd1d908ae64d Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Thu, 16 Jul 2026 16:01:23 -0500 Subject: [PATCH 24/28] Ruff lint fixes --- share/trick/trickops/WorkflowCommon.py | 4 ---- share/trick/trickops/send_hs.py | 4 ++-- trickops.py | 10 ++++------ 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/share/trick/trickops/WorkflowCommon.py b/share/trick/trickops/WorkflowCommon.py index efb648c18..0d4083754 100644 --- a/share/trick/trickops/WorkflowCommon.py +++ b/share/trick/trickops/WorkflowCommon.py @@ -5,24 +5,20 @@ subprocesses to get progress bars and curses display logic for free! """ -import argparse import collections import curses import datetime import logging import multiprocessing import os -import pdb import platform import re import signal import socket -import stat import subprocess import sys import textwrap import time -from contextlib import contextmanager from pathlib import Path from ColorStr import ColorStr diff --git a/share/trick/trickops/send_hs.py b/share/trick/trickops/send_hs.py index e7c95892a..eeb20c86b 100644 --- a/share/trick/trickops/send_hs.py +++ b/share/trick/trickops/send_hs.py @@ -1,5 +1,5 @@ -import re, os -import pdb +import os +import re # This global is the result of hours of frustration and debugging. This is only used by doctest # but appears to be the only solution to the problem of __file__ not being an absolute path in diff --git a/trickops.py b/trickops.py index ca8819e9e..92903aa5f 100644 --- a/trickops.py +++ b/trickops.py @@ -19,12 +19,10 @@ # SIM_test_varserv's err1/err2 runs are expected to exit with SIGUSR1. The exit # code differs Mac vs Linux, so it can't be hardcoded in the yaml; tag them here. -VARSERV_SIGUSR1_RUNS = frozenset( - { - "Run test/SIM_test_varserv RUN_test/err1_test.py", - "Run test/SIM_test_varserv RUN_test/err2_test.py", - } -) +VARSERV_SIGUSR1_RUNS = frozenset({ + "Run test/SIM_test_varserv RUN_test/err1_test.py", + "Run test/SIM_test_varserv RUN_test/err2_test.py", +}) # Jobs skipped on macOS (SIM_trickified_shared does not build/run there). DARWIN_SKIP_BUILDS = frozenset({"Build test/SIM_trickified_shared"}) From acc1a246c2db86811aa443d7b82d7d7fb49eb2b7 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Fri, 17 Jul 2026 17:07:19 -0500 Subject: [PATCH 25/28] Support python version installed on RHEL 8 (3.6) --- trickops.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/trickops.py b/trickops.py index 92903aa5f..3795eab5b 100644 --- a/trickops.py +++ b/trickops.py @@ -3,6 +3,7 @@ import signal import sys from pathlib import Path +from typing import Optional, Union sys.path.append(str(Path(__file__).resolve().parent / "share" / "trick" / "trickops")) @@ -33,10 +34,10 @@ class SimTestWorkflow(TrickWorkflow): def __init__( self, quiet: bool, - trick_top_level: str | Path, + trick_top_level: Union[str, Path], cpus: int, config_file: str, - trick_dir: str | Path | None = None, + trick_dir: Optional[Union[str, Path]] = None, ) -> None: self.cpus = cpus project_root = Path(trick_top_level) From 8a1106ff8b24cb799b4b74c6e87d79cd0d69a8e3 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Fri, 17 Jul 2026 17:41:49 -0500 Subject: [PATCH 26/28] Revert python script changes to only ones needed for cmake compatibility --- share/trick/trickops/TrickWorkflow.py | 1086 +++++++++--------------- share/trick/trickops/WorkflowCommon.py | 384 +++------ share/trick/trickops/send_hs.py | 118 ++- trickops.py | 248 +++--- 4 files changed, 696 insertions(+), 1140 deletions(-) diff --git a/share/trick/trickops/TrickWorkflow.py b/share/trick/trickops/TrickWorkflow.py index 5fe65650f..ab4399564 100644 --- a/share/trick/trickops/TrickWorkflow.py +++ b/share/trick/trickops/TrickWorkflow.py @@ -7,32 +7,16 @@ PyYAML # For reading input yml files psutil # For child process acquisition """ - -import copy -import hashlib -import inspect -import os -import re -import socket -import subprocess -import sys -import threading -import time - +import os, sys, threading, socket, abc, time, re, copy, subprocess, hashlib, inspect from TrickWorkflowYamlVerifier import * # TODO revisit this import - Jordan -from WorkflowCommon import * +from WorkflowCommon import * +import pprint # Import Trick natively supported python variable server utilities -sys.path.append( - os.path.abspath( - os.path.join( - os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: 0))), - "../pymods", - ) - ) -) - +sys.path.append(os.path.abspath(os.path.join(os.path.dirname( + os.path.abspath(inspect.getsourcefile(lambda:0))), '../pymods'))) from trick import variable_server +import pdb # This global is the result of hours of frustration and debugging. This is only used by doctest # but appears to be the only solution to the problem of __file__ not being an absolute path in @@ -47,10 +31,7 @@ # given for any function that prints, because we make use of color printing extensively in this # module via the tprint function, and doctest is not well suited for handling color output. # - Sincerely, a quite ornery Dan Jordan 4/2021 -this_trick = os.path.normpath( - os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../..") -) - +this_trick = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../..')) class TrickWorkflow(WorkflowCommon): """ @@ -95,14 +76,11 @@ class TrickWorkflow(WorkflowCommon): # Note that runs, comparisons, and analysis management classes and/or jobs also # support the report() method """ - # These are static so Run and Sim classes can access them when needed # TODO: It would be nice to consolidate and read this from .yaml_requirements.txt # -Jordan 12/2022 - allowed_phase_range = {"min": -1000, "max": 1000} - all_possible_phases = range( - allowed_phase_range["min"], allowed_phase_range["max"] + 1 - ) + allowed_phase_range = {'min': -1000, 'max': 1000} + all_possible_phases = range(allowed_phase_range['min'], allowed_phase_range['max']+1) def listify_phase(phase=None): """ @@ -131,28 +109,21 @@ def listify_phase(phase=None): """ # Handle type of phase given phases = [] # To be populated with all valid phases requested by user - if phase is None: + if (phase is None): phases = list(TrickWorkflow.all_possible_phases) elif isinstance(phase, int) and phase in TrickWorkflow.all_possible_phases: phases = [phase] - elif ( - (isinstance(phase, list) or isinstance(phase, range)) - and all(isinstance(p, int) for p in phase) - and all(p in TrickWorkflow.all_possible_phases for p in phase) - ): + elif ( (isinstance(phase, list) or isinstance(phase, range)) and + all(isinstance(p, int) for p in phase) and + all(p in TrickWorkflow.all_possible_phases for p in phase)): phases = list(phase) # Cast to list to cover range case else: - msg = ( - "ERROR: Given phase %s in listify_given_phase() must be an int," - " list of ints, or range() within the bounds [%s, %s]" - % ( - phase, - TrickWorkflow.allowed_phase_range["min"], - TrickWorkflow.allowed_phase_range["max"], - ) - ) + msg =("ERROR: Given phase %s in listify_given_phase() must be an int," + " list of ints, or range() within the bounds [%s, %s]" % (phase, + TrickWorkflow.allowed_phase_range['min'], + TrickWorkflow.allowed_phase_range['max'])) raise RuntimeError(msg) - return phases + return(phases) def _find_range_string(string): """ @@ -171,22 +142,18 @@ def _find_range_string(string): RuntimeError If more than one range string found """ - pattern = r"\[\d+-\d+\]" + pattern = "\[\d+-\d+\]" if (len(re.findall(pattern, string))) > 1: - msg = ( - "ERROR: [min-max] pattern found more than once in %s. Only one instance is" - " supported." % (string) - ) - raise RuntimeError(msg) + msg = ("ERROR: [min-max] pattern found more than once in %s. Only one instance is" + " supported." % (string)) + raise RuntimeError(msg) m = re.search(pattern, string) if m: - return m.group(0) + return m.group(0) else: - return None + return None - def __init__( - self, project_top_level, log_dir, trick_dir, config_file, cpus=3, quiet=False - ): + def __init__(self, project_top_level, log_dir, trick_dir, config_file, cpus=3, quiet=False): """ Initialize this instance. @@ -209,25 +176,21 @@ def __init__( when true which is useful for running in a CI system where stdin isn't available """ - super().__init__( - project_top_level=project_top_level, log_dir=log_dir, quiet=quiet - ) - self.config_errors = [] # Contains errors in setup of management classes - self.compare_errors = False # True if comparison errors were found - self.sims = [] # list of Sim() instances, filled out from config file + super().__init__(project_top_level=project_top_level, log_dir=log_dir, quiet=quiet) + self.config_errors = [] # Contains errors in setup of management classes + self.compare_errors = False # True if comparison errors were found + self.sims = [] # list of Sim() instances, filled out from config file self.config_file = config_file # path to yml config - self.cpus = cpus # Number of CPUs this workflow should use when running + self.cpus = cpus # Number of CPUs this workflow should use when running self.yaml_verifier = TrickWorkflowYamlVerifier(self.config_file) # If not found in the config file, these defaults are used self.trick_dir = trick_dir self.trick_host_cpu = self.get_trick_host_cpu() - self.env = "" + self.env = '' self.config = self.yaml_verifier.verify() - self.parsing_errors = ( - self.yaml_verifier.parsing_errors - ) # All errors found during parsing + self.parsing_errors = self.yaml_verifier.parsing_errors # All errors found during parsing for e in self.parsing_errors: - tprint(e, "DARK_RED") + tprint(e, 'DARK_RED') self._populate_sims() def _populate_sims(self): @@ -239,127 +202,87 @@ def _populate_sims(self): from self.config so we can create all management classes at the sim and run levels without worry. """ - def cprint(msg, color): self.config_errors.append(msg) tprint(msg, color) - - self.env = self.config["globals"]["env"] - all_sim_paths = [] # Keep a list of all paths for uniqueness check + self.env = self.config['globals']['env'] + all_sim_paths = [] # Keep a list of all paths for uniqueness check for s in self.config: - if not str(s).startswith("SIM"): # Ignore everything not starting with SIM - continue - if not os.path.exists( - os.path.join(self.project_top_level, self.config[s]["path"]) - ): - cprint( - "ERROR: %s's 'path' %s not found. Continuing but skipping this entire entry from %s." - % (s, self.config[s]["path"], self.config_file), - "DARK_RED", - ) - continue - if self.config[s]["path"] in all_sim_paths: - cprint( - "ERROR: %s's 'path' is not unique in %s. Continuing but ignoring this sim." - % (s, self.config_file), - "DARK_RED", - ) - continue - # Add the CPU format string (won't fail if there is no CPU placeholder) - self.config[s]["binary"] = self.config[s]["binary"].format( - cpu=self.trick_host_cpu - ) - # Add the full path to the build command - trick_CP = os.path.join(self.trick_dir, "bin/trick-CP") - if self.config[s]["build_args"]: - trick_CP += " " + self.config[s]["build_args"] - thisSim = TrickWorkflow.Sim( - name=s, - sim_dir=self.config[s]["path"], - description=self.config[s]["description"], - labels=self.config[s]["labels"], - prebuild_cmd=self.env, - build_cmd=trick_CP, - cpus=self.cpus, - size=self.config[s]["size"], - phase=self.config[s]["phase"], - log_dir=self.log_dir, - ) - all_sim_paths.append(self.config[s]["path"]) - - all_run_paths = [] # Keep a list of all paths for uniqueness check - for r in self.config[s]["runs"]: - just_RUN = r.split()[0] # Drop arguments after RUN../...py - just_RUN_dir = os.path.dirname( - just_RUN - ) # Drop path before RUN../..py - # TODO: This check will break generated runs, do we even need to check this at all? - Jordan 2022 - # if not os.path.exists(os.path.join(self.project_top_level, self.config[s]['path'], just_RUN)): - # cprint("ERROR: %s's 'run' path %s not found. Continuing but skipping this run " - # "from %s." % (s, just_RUN, self.config_file), 'DARK_RED') - # continue - if ( - just_RUN_dir in all_run_paths - and self.config[s]["parallel_safety"] == "strict" - ): - cprint( - "ERROR: %s's run directory %s is not unique in %s. With setting " - "parallel_safety: strict, you cannot have the same RUN directory listed " - "more than once per sim. Continuing but skipping this run." - % (s, r, self.config_file), - "DARK_RED", - ) - continue - - thisRun = TrickWorkflow.Run( - sim_dir=self.config[s]["path"], - input_file=r, - binary=self.config[s]["binary"], - prerun_cmd=self.env, - returns=self.config[s]["runs"][r]["returns"], - valgrind_flags=self.config[s]["runs"][r]["valgrind"], - phase=self.config[s]["runs"][r]["phase"], - log_dir=self.log_dir, - ) - - # The check for list allows all other non-list types in the yaml file, - # allowing groups to define their own comparison methodology - if isinstance(self.config[s]["runs"][r]["compare"], list): - for cmp in self.config[s]["runs"][r]["compare"]: - lhs, rhs = [s.strip() for s in cmp.split(" vs.")] - thisRun.add_comparison(test_data=lhs, baseline_data=rhs) - - if self.config[s]["runs"][r]["analyze"] is not None: - thisRun.add_analysis(cmd=self.config[s]["runs"][r]["analyze"]) - - theseRuns = [] - try: - theseRuns = ( - thisRun.multiply() - ) # If [from-to] notation used, expand runs - except RuntimeError as e: - msg = ( - "ERROR: Unable to multiply run %s in sim %s. Ignoring this run. " - "Check for bad [min-max] syntax in run keys in %s and try again.\n %s" - % (r, s, self.config_file, e) - ) - cprint(msg, "DARK_RED") - - for r in theseRuns: - thisSim.add_run(r) # Add Run/Runs to this Sim() - - all_run_paths.append( - just_RUN_dir - ) # Keep track of all runs to check parallel safety - - self.sims.append(thisSim) # Add Sim() to internal list + if not str(s).startswith('SIM'): # Ignore everything not starting with SIM + continue + if (not os.path.exists(os.path.join(self.project_top_level, self.config[s]['path'])) ): + cprint("ERROR: %s's 'path' %s not found. Continuing but skipping this entire entry from %s." + % (s, self.config[s]['path'], self.config_file), 'DARK_RED') + continue + if self.config[s]['path'] in all_sim_paths: + cprint("ERROR: %s's 'path' is not unique in %s. Continuing but ignoring this sim." + % (s, self.config_file), 'DARK_RED') + continue + # Add the CPU format string (won't fail if there is no CPU placeholder) + self.config[s]['binary'] = self.config[s]['binary'].format(cpu=self.trick_host_cpu) + # Add the full path to the build command + trick_CP=os.path.join(self.trick_dir, "bin/trick-CP") + if self.config[s]['build_args']: + trick_CP+=(' ' + self.config[s]['build_args']) + thisSim = TrickWorkflow.Sim(name=s, sim_dir=self.config[s]['path'], + description=self.config[s]['description'], labels=self.config[s]['labels'], + prebuild_cmd=self.env, build_cmd=trick_CP, + cpus=self.cpus, size=self.config[s]['size'], phase=self.config[s]['phase'], + log_dir=self.log_dir) + all_sim_paths.append(self.config[s]['path']) + + all_run_paths = [] # Keep a list of all paths for uniqueness check + for r in self.config[s]['runs']: + just_RUN = r.split()[0] # Drop arguments after RUN../...py + just_RUN_dir = os.path.dirname(just_RUN) # Drop path before RUN../..py + # TODO: This check will break generated runs, do we even need to check this at all? - Jordan 2022 + #if not os.path.exists(os.path.join(self.project_top_level, self.config[s]['path'], just_RUN)): + # cprint("ERROR: %s's 'run' path %s not found. Continuing but skipping this run " + # "from %s." % (s, just_RUN, self.config_file), 'DARK_RED') + # continue + if just_RUN_dir in all_run_paths and self.config[s]['parallel_safety'] == 'strict': + cprint("ERROR: %s's run directory %s is not unique in %s. With setting " + "parallel_safety: strict, you cannot have the same RUN directory listed " + "more than once per sim. Continuing but skipping this run." % + (s, r, self.config_file), 'DARK_RED') + continue + + thisRun = TrickWorkflow.Run(sim_dir=self.config[s]['path'], input_file=r, + binary= self.config[s]['binary'], prerun_cmd=self.env, + returns=self.config[s]['runs'][r]['returns'], + valgrind_flags=self.config[s]['runs'][r]['valgrind'], + phase=self.config[s]['runs'][r]['phase'], log_dir=self.log_dir) + + # The check for list allows all other non-list types in the yaml file, + # allowing groups to define their own comparison methodology + if isinstance(self.config[s]['runs'][r]['compare'], list): + for cmp in self.config[s]['runs'][r]['compare']: + lhs, rhs = [ s.strip() for s in cmp.split(' vs.') ] + thisRun.add_comparison(test_data=lhs, baseline_data=rhs) + + if self.config[s]['runs'][r]['analyze'] is not None: + thisRun.add_analysis(cmd=self.config[s]['runs'][r]['analyze']) + + theseRuns = [] + try: + theseRuns = thisRun.multiply() # If [from-to] notation used, expand runs + except RuntimeError as e: + msg = ("ERROR: Unable to multiply run %s in sim %s. Ignoring this run. " + "Check for bad [min-max] syntax in run keys in %s and try again.\n %s" + % (r, s, self.config_file, e) ) + cprint(msg, 'DARK_RED') + + for r in theseRuns: + thisSim.add_run(r) # Add Run/Runs to this Sim() + + all_run_paths.append(just_RUN_dir) # Keep track of all runs to check parallel safety + + self.sims.append(thisSim) # Add Sim() to internal list if len(self.sims) < 1: # At minimum, one valid SIM structure must exist - msg = ( - "ERROR: After validating config file, there is insufficient information to continue." + msg = ("ERROR: After validating config file, there is insufficient information to continue." " Check the syntax in config file %s and try again." - % (self.config_file) - ) - cprint(msg, "DARK_RED") + % (self.config_file) ) + cprint(msg, 'DARK_RED') self._cleanup() raise RuntimeError(msg) @@ -374,10 +297,10 @@ def create_test_suite(self): >>> tw.create_test_suite() """ - self.get_jobs(kind="build") - self.get_jobs(kind="run") - self.get_jobs(kind="analysis") - self.get_jobs(kind="valgrind") + self.get_jobs(kind='build') + self.get_jobs(kind='run') + self.get_jobs(kind='analysis') + self.get_jobs(kind='valgrind') def get_trick_host_cpu(self): """ @@ -392,17 +315,12 @@ def get_trick_host_cpu(self): str or None TRICK_HOST_CPU or None if it cannot be determined """ - gte_cmd = [os.path.join(self.trick_dir, "bin/trick-gte"), "TRICK_HOST_CPU"] - result = run_subprocess( - gte_cmd, m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE - ) + gte_cmd = [os.path.join(self.trick_dir, "bin/trick-gte" ), "TRICK_HOST_CPU"] + result = run_subprocess(gte_cmd, m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE) self.trick_host_cpu = result.stdout.strip() - if self.trick_host_cpu == None or self.trick_host_cpu == "": - tprint( - "ERROR: Unable to determine TRICK_HOST_CPU, you may encounter problems " - "if you continue... ", - "DARK_RED", - ) + if self.trick_host_cpu == None or self.trick_host_cpu == '': + tprint("ERROR: Unable to determine TRICK_HOST_CPU, you may encounter problems " + "if you continue... ", 'DARK_RED') self.trick_host_cpu = None return self.trick_host_cpu @@ -432,9 +350,7 @@ def get_sim(self, identifier): If identifier is not a str """ if type(identifier) != str: - raise TypeError( - "get_sim() only accepts a Sim name or Sim path from project top level" - ) + raise TypeError('get_sim() only accepts a Sim name or Sim path from project top level') for sim in self.sims: if sim.sim_dir == identifier or sim.name == identifier: return sim @@ -475,15 +391,14 @@ def get_sims(self, labels=None): elif type(labels) == list: ls = [str(l) for l in labels] else: - raise TypeError( - "get_sims() only accepts a label string or list of label strings" - ) + raise TypeError('get_sims() only accepts a label string or list of label strings') for sim in self.sims: if all(l in sim.labels for l in ls): sims_found.append(sim) return sims_found - def report(self, indent=""): + + def report(self, indent=''): """ Recursively report all internal information >>> tw = TrickWorkflow(project_top_level=this_trick, log_dir='/tmp/', trick_dir=this_trick, config_file=os.path.join(this_trick,"share/trick/trickops/tests/trick_sims.yml")) @@ -495,90 +410,41 @@ def status_summary(self): Print a summary of all jobs executed, and return 'SUCCESS' if all were successful, 'FAILURE' if any job was not successful """ - all_builds = self.get_jobs(kind="build") - all_runs = self.get_jobs(kind="run") - all_analysis = self.get_jobs(kind="analysis") + all_builds = self.get_jobs(kind='build') + all_runs = self.get_jobs(kind='run') + all_analysis = self.get_jobs(kind='analysis') all_comparisons = self.get_comparisons() - all_valgrind = self.get_jobs(kind="valgrind") - - executed_builds = [ - build - for build in all_builds - if build.get_status() != Job.Status.NOT_STARTED - ] - executed_runs = [ - run for run in all_runs if run.get_status() != Job.Status.NOT_STARTED - ] - executed_analysis = [ - a for a in all_analysis if a.get_status() != Job.Status.NOT_STARTED - ] - executed_comparisons = [ - c for c in all_comparisons if c.status != Job.Status.NOT_STARTED - ] - executed_valgrind = [ - v for v in all_valgrind if v.get_status() != Job.Status.NOT_STARTED - ] - - ok_builds = [ - build - for build in executed_builds - if build.get_status() == Job.Status.SUCCESS - ] - ok_runs = [ - run for run in executed_runs if run.get_status() == Job.Status.SUCCESS - ] - ok_analysis = [ - a for a in executed_analysis if a.get_status() == Job.Status.SUCCESS - ] - ok_comparisons = [ - c for c in executed_comparisons if c.status == Job.Status.SUCCESS - ] - ok_valgrind = [ - v for v in executed_valgrind if v.get_status() == Job.Status.SUCCESS - ] - - tprint("SUMMARY:") + all_valgrind = self.get_jobs(kind='valgrind') + + executed_builds = [ build for build in all_builds if build.get_status() != Job.Status.NOT_STARTED ] + executed_runs = [ run for run in all_runs if run.get_status() != Job.Status.NOT_STARTED ] + executed_analysis = [ a for a in all_analysis if a.get_status() != Job.Status.NOT_STARTED ] + executed_comparisons = [ c for c in all_comparisons if c.status != Job.Status.NOT_STARTED ] + executed_valgrind = [ v for v in all_valgrind if v.get_status() != Job.Status.NOT_STARTED ] + + ok_builds = [ build for build in executed_builds if build.get_status() == Job.Status.SUCCESS ] + ok_runs = [ run for run in executed_runs if run.get_status() == Job.Status.SUCCESS ] + ok_analysis = [ a for a in executed_analysis if a.get_status() == Job.Status.SUCCESS ] + ok_comparisons = [ c for c in executed_comparisons if c.status == Job.Status.SUCCESS ] + ok_valgrind = [ v for v in executed_valgrind if v.get_status() == Job.Status.SUCCESS ] + + tprint( "SUMMARY:" ) if executed_builds: - tprint( - " {0} out of {1} builds succeeded".format( - len(ok_builds), len(executed_builds) - ) - ) + tprint( " {0} out of {1} builds succeeded".format(len(ok_builds),len(executed_builds))) if executed_runs: - tprint( - " {0} out of {1} runs succeeded".format( - len(ok_runs), len(executed_runs) - ) - ) + tprint( " {0} out of {1} runs succeeded".format(len(ok_runs),len(executed_runs))) if executed_analysis: - tprint( - " {0} out of {1} analyses succeeded".format( - len(ok_analysis), len(executed_analysis) - ) - ) + tprint( " {0} out of {1} analyses succeeded".format(len(ok_analysis),len(executed_analysis))) if executed_comparisons: - tprint( - " {0} out of {1} comparisons succeeded".format( - len(ok_comparisons), len(executed_comparisons) - ) - ) + tprint( " {0} out of {1} comparisons succeeded".format(len(ok_comparisons),len(executed_comparisons))) if executed_valgrind: - tprint( - " {0} out of {1} valgrind runs succeeded".format( - len(ok_valgrind), len(executed_valgrind) - ) - ) - - if ( - executed_builds == ok_builds - and executed_runs == ok_runs - and executed_analysis == ok_analysis - and executed_comparisons == ok_comparisons - and executed_valgrind == ok_valgrind - ): - return "SUCCESS" + tprint( " {0} out of {1} valgrind runs succeeded".format(len(ok_valgrind),len(executed_valgrind))) + + if (executed_builds == ok_builds and executed_runs == ok_runs and executed_analysis == ok_analysis + and executed_comparisons == ok_comparisons and executed_valgrind == ok_valgrind) : + return 'SUCCESS' else: - return "FAILURE" + return 'FAILURE' def compare(self): """ @@ -624,27 +490,25 @@ def get_jobs(self, kind, phase=None): phases = TrickWorkflow.listify_phase(phase) jobs = [] - if kind == "build" or kind == "builds": - jobs = [sim.get_build_job() for sim in self.sims if sim.phase in phases] - elif kind == "run" or kind == "runs": - for sim in self.sims: - jobs += sim.get_run_jobs(kind="normal", phase=phases) - elif kind == "valgrind" or kind == "valgrinds": - for sim in self.sims: - jobs += sim.get_run_jobs(kind="valgrind", phase=phases) - elif kind == "analysis" or kind == "analyses" or kind == "analyze": - for sim in self.sims: - jobs += sim.get_analysis_jobs(phase=phases) + if kind == 'build' or kind == 'builds': + jobs = [ sim.get_build_job() for sim in self.sims if sim.phase in phases ] + elif kind == 'run' or kind == 'runs': + for sim in self.sims: + jobs += sim.get_run_jobs(kind='normal', phase=phases) + elif kind == 'valgrind' or kind == 'valgrinds': + for sim in self.sims: + jobs += sim.get_run_jobs(kind='valgrind', phase=phases) + elif kind == 'analysis' or kind == 'analyses' or kind == 'analyze': + for sim in self.sims: + jobs += sim.get_analysis_jobs(phase=phases) else: - raise TypeError( - "get_jobs() only accepts kinds: build, run, valgrind, analysis" - ) + raise TypeError('get_jobs() only accepts kinds: build, run, valgrind, analysis') # If these jobs are of type SingleRun and self.quiet is True, tell the jobs to # skip the variable server connection logic for job in jobs: if self.quiet and isinstance(job, SingleRun): job.set_use_var_server(False) - return jobs + return (jobs) def get_comparisons(self): """ @@ -660,7 +524,7 @@ def get_comparisons(self): list List of Comparison objects """ - return [c for sim in self.sims for run in sim.runs for c in run.comparisons] + return ([ c for sim in self.sims for run in sim.runs for c in run.comparisons ]) def get_unique_comparison_dirs(self): """ @@ -683,11 +547,11 @@ def get_unique_comparison_dirs(self): """ all_cmp_dirnames = [] # list of tuples ( test_dir, baseline_dir ) for sim in self.sims: - for run in sim.runs: - for cmp in run.comparisons: - all_cmp_dirnames.append(cmp.get_dirnames()) + for run in sim.runs: + for cmp in run.comparisons: + all_cmp_dirnames.append(cmp.get_dirnames()) # Reduce full list to unique list - return list(set(all_cmp_dirnames)) + return(list(set(all_cmp_dirnames))) def get_koviz_report_job(self, test_dir, baseline_dir, pres=None): """ @@ -716,44 +580,28 @@ def get_koviz_report_job(self, test_dir, baseline_dir, pres=None): (Job() instance for the run directories given or None if error encountered, error details or None if successful) """ - if os.system(self.env + " which koviz > /dev/null 2>&1") != 0: + if os.system(self.env + ' which koviz > /dev/null 2>&1') != 0: msg = "ERROR: koviz is not found in PATH. Returning None in get_koviz_report_job()" - tprint(msg, "DARK_RED") + tprint (msg, 'DARK_RED') return None, msg dirs = [test_dir, baseline_dir] for dir in dirs: - if not os.path.exists(dir): - msg = ( - "ERROR: %s not found, Returning None in get_koviz_report_job()" - % (dir) - ) - tprint(msg, "DARK_RED") - return None, msg - cmd = self.env + " koviz -platform offscreen -a" + if not os.path.exists(dir): + msg = "ERROR: %s not found, Returning None in get_koviz_report_job()" % (dir) + tprint(msg, 'DARK_RED') + return None, msg + cmd = (self.env + " koviz -platform offscreen -a") if pres: - cmd += " -pres %s " % pres - cmd += " -pdf %s %s %s" % ( - os.path.join( - self.log_dir, - (unixify_string(test_dir) + "_vs_" + unixify_string(baseline_dir)) - + ".pdf", - ), - test_dir, - baseline_dir, - ) - name = "koviz report %s vs. %s" % (test_dir, baseline_dir) - return ( - Job( - name=name, - command=cmd, - log_file=os.path.join(self.log_dir, "." + unixify_string(name)), - expected_exit_status=0, - ), - None, - ) + cmd+= (" -pres %s " % pres ) + cmd+= (" -pdf %s %s %s" % (os.path.join(self.log_dir, + (unixify_string(test_dir)+'_vs_' + unixify_string(baseline_dir)) + '.pdf'), + test_dir, baseline_dir) ) + name='koviz report %s vs. %s' % (test_dir, baseline_dir) + return(Job(name=name, command=cmd, log_file=os.path.join(self.log_dir,"."+unixify_string(name)), + expected_exit_status=0), None) def get_koviz_report_jobs(self): - """ + ''' Loop over all runs for all sims and generate a koviz pdf report job for each unique run directory comparison found. @@ -770,22 +618,18 @@ def get_koviz_report_jobs(self): ------- Tuple List of 'koviz' commands executed, List of errors - """ + ''' koviz_jobs = [] koviz_errors = [] - all_cmp_dirnames = ( - self.get_unique_comparison_dirs() - ) # List of (test_dir, baseline_dir) tuples + all_cmp_dirnames = self.get_unique_comparison_dirs() # List of (test_dir, baseline_dir) tuples # Generate koviz reports for the given test_dir, baseline_dir pairs - for test_dir, baseline_dir in all_cmp_dirnames: - if test_dir and baseline_dir: # Will be None if dir is empty - job, error = self.get_koviz_report_job( - test_dir, baseline_dir, pres="error" - ) - if job: - koviz_jobs.append(job) - if error: - koviz_errors.append(error) + for (test_dir, baseline_dir) in all_cmp_dirnames: + if test_dir and baseline_dir: # Will be None if dir is empty + job, error = self.get_koviz_report_job(test_dir, baseline_dir, pres='error') + if job: + koviz_jobs.append(job) + if error: + koviz_errors.append(error) return koviz_jobs, koviz_errors class Sim(object): @@ -794,20 +638,8 @@ class Sim(object): key in the dict read will become a single instance of this management class stored in the TrickWorkflow.sims list. """ - - def __init__( - self, - name, - sim_dir, - description=None, - labels=[], - prebuild_cmd="", - build_cmd="trick-CP", - cpus=3, - size=2200000, - phase=0, - log_dir="/tmp", - ): + def __init__(self, name, sim_dir, description=None, labels=[], prebuild_cmd='', + build_cmd='trick-CP', cpus=3, size=2200000, phase=0, log_dir='/tmp'): """ Initialize this instance. @@ -835,24 +667,22 @@ def __init__( log_dir: str Directory in which log files will be written """ - self.name = name # Name of sim - self.sim_dir = sim_dir # Path to sim directory wrt to top level of project - self.description = description # Description of sim - self.labels = ( - labels # Options list of user-specified labels associated w/ this sim - ) - self.prebuild_cmd = prebuild_cmd # Optional string to execute in shell immediately before building - self.build_cmd = build_cmd # Build command for sim - self.cpus = cpus # Number of CPUs to use in build - self.size = size # Estimated size of successful build output in bytes - self.phase = phase # Phase associated with this sim build - self.log_dir = log_dir # Directory for which log file should be written - self.build_job = None # Contains Build Job instance - self.runs = [] # List of normal Run instances - self.valgrind_runs = [] # List of valgrind Run instances - self.printer = ColorStr() # Color printer utility - - def get_build_job(self): + self.name = name # Name of sim + self.sim_dir = sim_dir # Path to sim directory wrt to top level of project + self.description = description # Description of sim + self.labels = labels # Options list of user-specified labels associated w/ this sim + self.prebuild_cmd = prebuild_cmd # Optional string to execute in shell immediately before building + self.build_cmd = build_cmd # Build command for sim + self.cpus = cpus # Number of CPUs to use in build + self.size = size # Estimated size of successful build output in bytes + self.phase = phase # Phase associated with this sim build + self.log_dir = log_dir # Directory for which log file should be written + self.build_job = None # Contains Build Job instance + self.runs = [] # List of normal Run instances + self.valgrind_runs = [] # List of valgrind Run instances + self.printer = ColorStr() # Color printer utility + + def get_build_job( self): """ Create the FileSizeJob(Job) instance if not already created and return it for this Sim @@ -867,21 +697,15 @@ def get_build_job(self): """ if not self.build_job: - name = "Build " + self.sim_dir - self.build_job = FileSizeJob( - name=name, - command=( - "%s cd %s && export MAKEFLAGS=-j%d && %s" - % (self.prebuild_cmd, self.sim_dir, self.cpus, self.build_cmd) - ), - log_file=os.path.join( - self.log_dir, unixify_string(self.sim_dir) + "_build.txt" - ), - size=self.size, - ) - return self.build_job - - def get_run_jobs(self, kind="normal", phase=None): + name = 'Build ' + self.sim_dir + self.build_job = FileSizeJob(name=name, + command=("%s cd %s && export MAKEFLAGS=-j%d && %s" % + (self.prebuild_cmd, self.sim_dir, self.cpus, self.build_cmd)), + log_file=os.path.join(self.log_dir, unixify_string(self.sim_dir)+'_build.txt'), + size=self.size ) + return (self.build_job) + + def get_run_jobs( self, kind='normal', phase=None): """ Collect all SingleRun() instances and return them for all sims subject to the filtering paramters kind and phase @@ -906,15 +730,13 @@ def get_run_jobs(self, kind="normal", phase=None): # Transform given phase into a list phases = TrickWorkflow.listify_phase(phase) all_jobs = [] - if kind == "valgrind": - all_jobs = [ - r.get_run_job() for r in self.valgrind_runs if r.phase in phases - ] + if (kind == 'valgrind'): + all_jobs = [r.get_run_job() for r in self.valgrind_runs if r.phase in phases] else: all_jobs = [r.get_run_job() for r in self.runs if r.phase in phases] - return all_jobs + return (all_jobs) - def get_analysis_jobs(self, phase=None): + def get_analysis_jobs( self, phase=None): """ Collect all Job() instances for all analysis across all sim runs and valgrind runs @@ -935,11 +757,7 @@ def get_analysis_jobs(self, phase=None): """ # Transform given phase into a list phases = TrickWorkflow.listify_phase(phase) - return [ - r.analysis - for r in (self.runs + self.valgrind_runs) - if (r.analysis and r.phase in phases) - ] + return ([r.analysis for r in (self.runs + self.valgrind_runs) if (r.analysis and r.phase in phases) ]) def get_run(self, input_file): """ @@ -966,13 +784,11 @@ def get_run(self, input_file): If input is not a str """ if type(input_file) != str: - raise TypeError( - "get_run() only accepts the unique key representing the entire input to" - ' the sim binary. Ex: "RUN_test/input.py --flags-too"' - ) + raise TypeError('get_run() only accepts the unique key representing the entire input to' + ' the sim binary. Ex: "RUN_test/input.py --flags-too"') for run in self.runs: if run.input_file == input_file: - return run + return run return None def get_runs(self): @@ -1008,7 +824,7 @@ def get_phase(self): """ return self.phase - def set_phase(self, phase): + def set_phase( self, phase): """ Set the phase member variable. Phase is an integer in the range(TrickWorkflow.allowed_phase_range['min'], TrickWorkflow.allowed_phase_range['max']) which can be used to order sim builds when a workflow cannot @@ -1026,24 +842,15 @@ def set_phase(self, phase): phase : int phase to change to """ - if ( - not isinstance(phase, int) - or phase < TrickWorkflow.allowed_phase_range["min"] - or phase > TrickWorkflow.allowed_phase_range["max"] - ): - msg = ( - "ERROR: set_phase() for SIM %s must be an integer between [%s, %s]" - % ( - self.name, - TrickWorkflow.allowed_phase_range["min"], - TrickWorkflow.allowed_phase_range["max"], - ) - ) + if (not isinstance(phase, int) or phase < TrickWorkflow.allowed_phase_range['min'] or + phase > TrickWorkflow.allowed_phase_range['max']): + msg =("ERROR: set_phase() for SIM %s must be an integer between [%s, %s]" + % (self.name, TrickWorkflow.allowed_phase_range['min'], TrickWorkflow.allowed_phase_range['max'])) raise RuntimeError(msg) else: self.phase = phase - def add_run(self, run): + def add_run( self, run): """ Append a new Run() instance to the internal run lists. Appends to valgrind list if run.valgrind_flags is not None, appends to normal run list otherwise @@ -1057,12 +864,12 @@ def add_run(self, run): run : Run() Instance to add """ - if run.valgrind_flags: + if (run.valgrind_flags): self.valgrind_runs.append(run) else: self.runs.append(run) - def pop_run(self, input_file): + def pop_run( self, input_file): """ Remove a run by its unique self.input_file value @@ -1082,10 +889,10 @@ def pop_run(self, input_file): Instance in this sim's runs list matching self.input_file """ for i, run in enumerate(self.runs): - if run.input_file == input_file: - return self.runs.pop(i) + if run.input_file == input_file: + return self.runs.pop(i) - def compare(self): + def compare( self): """ Run compare() on all runs for this sim @@ -1100,7 +907,7 @@ def compare(self): """ return any([r.compare() for r in self.runs]) - def report(self, indent=""): + def report(self, indent=''): """ Report this sims information verbosely, ignoring members that are None @@ -1111,49 +918,31 @@ def report(self, indent=""): """ if self.name: - tprint(indent + "Name: " + self.name) + tprint(indent + "Name: " + self.name) else: - tprint(indent + self.sim_dir) + tprint(indent + self.sim_dir) if self.description: - tprint(indent + "Description: " + str(self.description)) + tprint(indent + "Description: " + str(self.description)) if self.labels: - tprint(indent + "Labels: " + str(", ".join(self.labels))) - tprint( - indent - + " Build: %-20s %-60s " - % ( - self.build_job._translate_status() - if self.build_job - else printer.colorstr("NOT RUN", "DARK_YELLOW"), - self.sim_dir, - ) - ) + tprint(indent + "Labels: " + str(', '.join(self.labels))) + tprint(indent + " Build: %-20s %-60s " %(self.build_job._translate_status() + if self.build_job else printer.colorstr('NOT RUN', 'DARK_YELLOW'), self.sim_dir)) if self.runs: - tprint(indent + " Runs:") - for run in self.runs: - run.report(indent=indent + " ") + tprint(indent + " Runs:") + for run in self.runs: + run.report(indent=indent+' ') if self.valgrind_runs: - tprint(indent + " Valgrind Runs:") - for run in self.valgrind_runs: - run.report(indent=indent + " ") + tprint(indent + " Valgrind Runs:") + for run in self.valgrind_runs: + run.report(indent=indent+' ') class Run(object): """ Management class for run content read from yml config file. Each key in the runs: sub-dict will become a single instance of this management class """ - - def __init__( - self, - sim_dir, - input_file, - binary, - prerun_cmd="", - returns=0, - valgrind_flags=None, - phase=0, - log_dir="/tmp/", - ): + def __init__(self, sim_dir, input_file, binary, prerun_cmd = '', returns=0, valgrind_flags=None, + phase=0, log_dir='/tmp/'): """ Initialize this instance. @@ -1181,23 +970,23 @@ def __init__( log_dir : str Directory in which log files will be written """ - self.sim_dir = sim_dir # Path to sim directory wrt to top level of project for this run + self.sim_dir = sim_dir # Path to sim directory wrt to top level of project for this run self.prerun_cmd = prerun_cmd # Optional string to execute in shell immediately before running (env) self.input_file = input_file # Full RUN.../input.py --any-flags --as-well, relative to sim_dir - self.returns = returns # Expected exit code on success for this run + self.returns = returns # Expected exit code on success for this run self.valgrind_flags = valgrind_flags # If not None, this run is to be run in valgrind w/ these flags - self.phase = phase # Phase associated with this run - self.log_dir = log_dir # Dir where all logged output will go - self.just_input = self.input_file.split(" ")[0] # Strip flags if any + self.phase = phase # Phase associated with this run + self.log_dir = log_dir # Dir where all logged output will go + self.just_input = self.input_file.split(' ')[0] # Strip flags if any # Derive Just the "RUN_something" part of run_dir_path self.just_run_dir = os.path.dirname(self.just_input) # Derive Path to run directory wrt to top level of project self.run_dir_path = os.path.join(self.sim_dir, self.just_run_dir) # Populated later - self.binary = binary # Name of binary - self.run_job = None # SingleRun Job instance for this run - self.comparisons = [] # List of comparison objects associated with this run - self.analysis = None # Job instance of after-run-completes custom analysis + self.binary = binary # Name of binary + self.run_job = None # SingleRun Job instance for this run + self.comparisons = [] # List of comparison objects associated with this run + self.analysis = None # Job instance of after-run-completes custom analysis def add_comparison(self, test_data, baseline_data): """ @@ -1213,8 +1002,8 @@ def add_comparison(self, test_data, baseline_data): baseline_dir : str path to file containing baseline logged data """ - comparison = TrickWorkflow.Comparison(test_data, baseline_data) - self.comparisons.append(comparison) + comparison = TrickWorkflow.Comparison( test_data, baseline_data ); + self.comparisons.append( comparison ) def add_analysis(self, cmd): """ @@ -1229,23 +1018,12 @@ def add_analysis(self, cmd): literal string representing command to execute post-run """ if self.analysis: - tprint( - "WARNING: Overwriting analysis definition for %s's %s" - % (self.sim_dir, self.input_file), - "DARK_YELLOW", - ) - logfile = ( - os.path.join(self.log_dir, unixify_string(self.sim_dir)) - + "_" - + unixify_string(self.input_file) - + "_analysis.txt" - ) - self.analysis = Job( - name=textwrap.shorten(cmd, width=90), - command=self.prerun_cmd + " " + cmd, - log_file=logfile, - expected_exit_status=0, - ) + tprint("WARNING: Overwriting analysis definition for %s's %s" % (self.sim_dir, + self.input_file), 'DARK_YELLOW') + logfile = (os.path.join(self.log_dir, unixify_string(self.sim_dir)) + +'_'+ unixify_string(self.input_file) + '_analysis.txt') + self.analysis = Job(name=textwrap.shorten(cmd, width=90), command=self.prerun_cmd + " " +cmd, + log_file=logfile, expected_exit_status=0) def get_phase(self): """ @@ -1258,7 +1036,7 @@ def get_phase(self): """ return self.phase - def set_phase(self, phase): + def set_phase( self, phase): """ Set the phase member variable. Phase is an integer in the range(TrickWorkflow.allowed_phase_range['min'], TrickWorkflow.allowed_phase_range['max']) which can be used to order sim runs when a workflow cannot @@ -1279,25 +1057,16 @@ def set_phase(self, phase): # TODO: This is very similar to Sim.set_phase() in functionality but I didn't want to create # a base class just to support this single instance of reducing code duplication # -Jordan 12/2022 - if ( - not isinstance(phase, int) - or phase < TrickWorkflow.allowed_phase_range["min"] - or phase > TrickWorkflow.allowed_phase_range["max"] - ): - msg = ( - "ERROR: set_phase() for %s/%s must be an integer between [%s, %s]" - % ( - self.sim_dir, - self.input_file, - TrickWorkflow.allowed_phase_range["min"], - TrickWorkflow.allowed_phase_range["max"], - ) - ) + if (not isinstance(phase, int) or phase < TrickWorkflow.allowed_phase_range['min'] or + phase > TrickWorkflow.allowed_phase_range['max']): + msg =("ERROR: set_phase() for %s/%s must be an integer between [%s, %s]" + % (self.sim_dir, self.input_file, TrickWorkflow.allowed_phase_range['min'], + TrickWorkflow.allowed_phase_range['max'])) raise RuntimeError(msg) else: self.phase = phase - def compare(self): + def compare( self): """ Execute all internal comparisons for this run @@ -1312,7 +1081,7 @@ def compare(self): """ return any([c.compare() != Job.Status.SUCCESS for c in self.comparisons]) - def report(self, indent=""): + def report(self, indent=''): """ Report this run's information verbosely, ignoring members that are None @@ -1322,23 +1091,16 @@ def report(self, indent=""): prepend the report with this custom string """ - tprint( - indent - + " %-20s %s" - % ( - self.run_job._translate_status() - if self.run_job - else printer.colorstr("NOT RUN", "DARK_YELLOW"), - self.input_file, - ) - ) + tprint(indent + " %-20s %s" % (self.run_job._translate_status() + if self.run_job else printer.colorstr('NOT RUN', 'DARK_YELLOW'), + self.input_file)) if self.comparisons: - tprint(indent + " Run Comparisons:") - for comparison in self.comparisons: - comparison.report(indent=indent + " ") + tprint(indent + " Run Comparisons:") + for comparison in self.comparisons: + comparison.report(indent=indent+' ') if self.analysis: - tprint(indent + " Run Analysis:") - tprint(indent + " " + self.analysis.report()) + tprint(indent + " Run Analysis:") + tprint(indent + " " + self.analysis.report()) def get_run_job(self): """ @@ -1353,35 +1115,25 @@ def get_run_job(self): SingleRun() Job instance for this run """ if not self.run_job: - name = "Run " + name = 'Run ' cmd = "%s cd %s && " % (self.prerun_cmd, self.sim_dir) sim_name = os.path.basename(os.path.normpath(self.sim_dir)) logfile = os.path.join(self.log_dir, unixify_string(self.sim_dir)) if self.valgrind_flags: - cmd += "valgrind %s --log-file=%s " % ( - self.valgrind_flags, - ( - os.path.join(self.log_dir, sim_name) - + "_valgrind_" - + unixify_string(self.input_file) - + ".valgrind" - ), - ) - logfile += "_valgrind" - name += "Valgrind " - logfile += "_" + unixify_string(self.input_file) + ".txt" - cmd += " ./%s %s" % (self.binary, self.input_file) - name += self.sim_dir + " " + self.input_file - - self.run_job = SingleRun( - name=name, - command=(cmd), - expected_exit_status=self.returns, - log_file=logfile, - ) - return self.run_job + cmd += ( "valgrind %s --log-file=%s " % (self.valgrind_flags, + (os.path.join(self.log_dir, sim_name) +'_valgrind_' + + unixify_string(self.input_file) + '.valgrind') )) + logfile += '_valgrind' + name += 'Valgrind ' + logfile += "_" + unixify_string(self.input_file) + '.txt' + cmd += (" ./%s %s" % (self.binary, self.input_file)) + name += self.sim_dir + ' ' + self.input_file + + self.run_job = SingleRun(name=name, command=(cmd), + expected_exit_status=self.returns, log_file=logfile) + return (self.run_job) def _get_range_list(self, pattern): """ @@ -1407,41 +1159,33 @@ def _get_range_list(self, pattern): RuntimeError If pattern is unrecognized or contains errors """ - must_exist = ["[", "]", "-"] + must_exist = ['[', ']', '-'] if any([char not in pattern for char in must_exist]): - msg = ( - 'ERROR: Pattern %s doesn\'t match expected syntax of "[min-max]"' - % (pattern) - ) - raise RuntimeError(msg) - min, max = pattern.strip("[").strip("]").split("-") + msg = ("ERROR: Pattern %s doesn't match expected syntax of \"[min-max]\"" % (pattern)) + raise RuntimeError(msg) + min, max = pattern.strip('[').strip(']').split('-') if len(min) != len(max): - msg = "ERROR: Pattern %s has inconsistent leading zeros." % (pattern) - raise RuntimeError(msg) + msg = ("ERROR: Pattern %s has inconsistent leading zeros." % (pattern)) + raise RuntimeError(msg) leading_zeros = int(len(min)) try: - min = int(min) + min = int(min) except ValueError as e: - msg = ( - "ERROR: Pattern %s minimum cannot be converted to integer. \n%s" - % (pattern, e) - ) - raise RuntimeError(msg) + msg = ("ERROR: Pattern %s minimum cannot be converted to integer. \n%s" % (pattern, e)) + raise RuntimeError(msg) try: - max = int(max) + max = int(max) except ValueError as e: - msg = ( - "ERROR: Pattern %s maximum cannot be converted to integer. \n%s" - % (pattern, e) - ) - raise RuntimeError(msg) + msg = ("ERROR: Pattern %s maximum cannot be converted to integer. \n%s" % (pattern, e)) + raise RuntimeError(msg) if min >= (max): - msg = "ERROR: Pattern %s minimum must be less than maximum." % pattern - raise RuntimeError(msg) + msg = ("ERROR: Pattern %s minimum must be less than maximum." % pattern) + raise RuntimeError(msg) range_list = [] - for num in range(min, max + 1): - range_list.append(str(num).zfill(leading_zeros)) - return list(range_list) + for num in range(min, max+1): + range_list.append(str(num).zfill(leading_zeros)) + return (list(range_list)) + def multiply(self): """ @@ -1481,19 +1225,19 @@ def multiply(self): """ rs = TrickWorkflow._find_range_string(self.input_file) if rs is None: - return [self] + return [self] else: - range_list = self._get_range_list(rs) - multiplied_runs = [] - for i in range_list: - # replace the range string notation with the single run equivalent - acopy = copy.deepcopy(self) - acopy.input_file = self.input_file.replace(rs, i) - # Add comparisons back as multiplied set - for c in acopy.comparisons: - c.pattern_replace(expecting_pattern=rs, replace_with=i) - multiplied_runs.append(acopy) - return multiplied_runs + range_list = self._get_range_list(rs) + multiplied_runs = [] + for i in range_list: + # replace the range string notation with the single run equivalent + acopy = copy.deepcopy(self) + acopy.input_file = self.input_file.replace(rs, i) + # Add comparisons back as multiplied set + for c in acopy.comparisons: + c.pattern_replace(expecting_pattern=rs, replace_with=i) + multiplied_runs.append(acopy) + return multiplied_runs class Comparison(object): """ @@ -1502,7 +1246,6 @@ class Comparison(object): test_data: path to file that represents test data (data generated by a run) baseline_data: path to file that represents baseline data for a run """ - def __init__(self, test_data, baseline_data): """ Initialize this instance. Environment variables used within the test data and baseline data paths will be expanded. @@ -1517,15 +1260,11 @@ def __init__(self, test_data, baseline_data): baseline_data : str Path to a single baseline logged data file """ - self.test_data = os.path.expandvars( - test_data - ) # Test data file with respect to project top level - self.baseline_data = os.path.expandvars( - baseline_data - ) # Baseline data file with respect to project top level - self.status = Job.Status.NOT_STARTED # Status of comparison - self.error = None # Error details if found - self.missing = [] # List of Strings with details of missing files if any + self.test_data = os.path.expandvars(test_data) # Test data file with respect to project top level + self.baseline_data = os.path.expandvars(baseline_data) # Baseline data file with respect to project top level + self.status = Job.Status.NOT_STARTED # Status of comparison + self.error = None # Error details if found + self.missing = [] # List of Strings with details of missing files if any def compare(self): """ @@ -1541,20 +1280,20 @@ def compare(self): status of the comparison: Job.Status.SUCCESS on success, Job.Status.FAILED """ for hs in [self.test_data, self.baseline_data]: - if not os.path.exists(hs): - self.missing.append(hs) - self.status = Job.Status.FAILED + if not os.path.exists(hs) : + self.missing.append(hs) + self.status = Job.Status.FAILED if self.missing: - return self.status - td = hashlib.new("md5", usedforsecurity=False) - bd = hashlib.new("md5", usedforsecurity=False) - td.update(open(self.test_data, "rb").read()) - bd.update(open(self.baseline_data, "rb").read()) - if td.hexdigest() != bd.hexdigest(): + return self.status + td = hashlib.new('md5', usedforsecurity=False) + bd = hashlib.new('md5', usedforsecurity=False) + td.update(open(self.test_data,'rb').read()) + bd.update(open(self.baseline_data,'rb').read()) + if (td.hexdigest() != bd.hexdigest()): self.status = Job.Status.FAILED else: self.status = Job.Status.SUCCESS - return self.status + return self.status def get_status(self): """ @@ -1577,10 +1316,9 @@ def _translate_status(self): Utility function that takes in a status and colors it, for easier reporting """ text, color = { - Job.Status.NOT_STARTED: ("NOT RUN", "DARK_YELLOW"), - Job.Status.SUCCESS: ("OK", "DARK_GREEN"), - Job.Status.FAILED: ("FAIL", "DARK_RED"), - }[self.get_status()] + Job.Status.NOT_STARTED: ('NOT RUN', 'DARK_YELLOW'), + Job.Status.SUCCESS: ('OK', 'DARK_GREEN'), + Job.Status.FAILED: ('FAIL', 'DARK_RED') }[self.get_status()] return printer.colorstr(text, color) def get_dirnames(self): @@ -1602,12 +1340,12 @@ def get_dirnames(self): test_dirname = os.path.dirname(self.test_data) baseline_dirname = os.path.dirname(self.baseline_data) if not os.path.exists(test_dirname): - test_dirname = None + test_dirname = None if not os.path.exists(baseline_dirname): - baseline_dirname = None + baseline_dirname = None return (test_dirname, baseline_dirname) - def report(self, indent=""): + def report (self, indent=''): """ Report this comparison's information verbosely @@ -1617,12 +1355,12 @@ def report(self, indent=""): prepend the report with this custom string """ - string = indent + "%-22s %s" % (self._translate_status(), self.test_data) + string = indent + "%-22s %s" % (self._translate_status(), self.test_data) if self.test_data in self.missing: - string += printer.colorstr(" (missing)", "DARK_RED") - string += " vs. %s" % (self.baseline_data) + string += printer.colorstr(" (missing)", 'DARK_RED') + string += " vs. %s" % (self.baseline_data) if self.baseline_data in self.missing: - string += printer.colorstr(" (missing)", "DARK_RED") + string += printer.colorstr(" (missing)", 'DARK_RED') tprint(string) def pattern_replace(self, expecting_pattern, replace_with): @@ -1645,21 +1383,17 @@ def pattern_replace(self, expecting_pattern, replace_with): """ rsb = TrickWorkflow._find_range_string(self.baseline_data) rst = TrickWorkflow._find_range_string(self.test_data) - if rsb is None and rst is None: # If no patterns, do nothing - return + if rsb is None and rst is None: # If no patterns, do nothing + return if (rsb and rsb != expecting_pattern) or (rst and rst != expecting_pattern): - msg = ( - "ERROR: [min-max] pattern from run (%s) must match pattern in run's" - " comparisons test (%s) and baseline (%s) sections, if specified." - % (expecting_pattern, rst, rsb) - ) - raise RuntimeError(msg) + msg = ("ERROR: [min-max] pattern from run (%s) must match pattern in run's" + " comparisons test (%s) and baseline (%s) sections, if specified." + % (expecting_pattern, rst, rsb)) + raise RuntimeError(msg) if rsb: - self.baseline_data = self.baseline_data.replace( - expecting_pattern, replace_with - ) + self.baseline_data = self.baseline_data.replace(expecting_pattern, replace_with) if rst: - self.test_data = self.test_data.replace(expecting_pattern, replace_with) + self.test_data = self.test_data.replace(expecting_pattern, replace_with) class SingleRun(Job): @@ -1667,10 +1401,7 @@ class SingleRun(Job): A single trick simulation run Job. SingleRun's can optionally connect to the trick variable server to get progress bar information. """ - - def __init__( - self, name, command, log_file, expected_exit_status=0, use_var_server=True - ): + def __init__(self, name, command, log_file, expected_exit_status=0, use_var_server=True): """ Initialize this instance. @@ -1685,29 +1416,25 @@ def __init__( """ self._use_var_server = use_var_server self._connected = False - super().__init__( - name=name, - command=command, - log_file=log_file, - expected_exit_status=expected_exit_status, - ) + super().__init__(name=name, command=command, log_file=log_file, + expected_exit_status=expected_exit_status) def set_use_var_server(self, value): - if not isinstance(value, bool): - msg = "ERROR: SingleRun.set_use_var_server() Requires a True/False value" + if (not isinstance(value, bool)): + msg =("ERROR: SingleRun.set_use_var_server() Requires a True/False value") raise RuntimeError(msg) else: self._use_var_server = value def get_use_var_server(self): - return self._use_var_server + return (self._use_var_server) def start(self): """ Start this Simulation job. Attempts a connection to the sim variable server in another thread after calling the base class Job() start() method """ - super().start() + super(SingleRun, self).start() self._connected = False # Finding a sim via PID can take several seconds. @@ -1717,7 +1444,6 @@ def start(self): # has finished. def connect(): import psutil - while self.get_status() is self.Status.RUNNING: # The base-class start() call will have already populated self._process.pid, # but that pid may be incompatible with the upcoming find_simulation call @@ -1729,26 +1455,24 @@ def connect(): # self._process.pid if not found sim_pid = self._process.pid try: - children = psutil.Process(self._process.pid).children( - recursive=True - ) + children = psutil.Process(self._process.pid).children(recursive=True) outerbreak = False for child in children: for i in child.cmdline(): - if re.search(".*S_main", i): + if re.search('.*S_main', i): sim_pid = child.pid outerbreak = True break if outerbreak: break - except Exception: + except Exception as e: sim_pid = self._process.pid # Now connect to the sim_pid try: self._variable_server = variable_server.find_simulation( - pid=sim_pid, timeout=5 - ) - self._variable_server.add_variables(*self._create_variables()) + pid=sim_pid, timeout=5) + self._variable_server.add_variables( + *self._create_variables()) self._variable_server.set_period(0.1) self._connected = True return @@ -1757,48 +1481,48 @@ def connect(): # If a SingleRun job terminates before the thread can connect to the # variable server, Trick's variable_server module throws an IOError # with the message: The remote endpoint has closed the connection - except IOError: + except IOError as e: pass if self._use_var_server: - thread = threading.Thread(target=connect, name="Looking for " + self.name) + thread = threading.Thread(target=connect, name='Looking for ' + self.name) thread.daemon = True thread.start() def get_status_string_line_count(self): - return super().get_status_string_line_count() + 1 + return super(SingleRun, self).get_status_string_line_count() + 1 def _not_started_string(self): - return super()._not_started_string() + "\n" + return super(SingleRun, self)._not_started_string() + '\n' def _running_string(self): - elapsed_time = super()._running_string() + elapsed_time = super(SingleRun, self)._running_string() if self._connected: - return ( - elapsed_time + self._connected_string() + "\n" + self._connected_bar() - ) + return (elapsed_time + self._connected_string() + '\n' + + self._connected_bar()) - return elapsed_time + "\n" + create_progress_bar(0, "Connecting") + return (elapsed_time + '\n' + + create_progress_bar(0, 'Connecting')) def _success_string(self): - text = super()._success_string() + text = super(SingleRun, self)._success_string() if self._connected: text += self._connected_string() - return text + "\n" + self._success_progress_bar + return text + '\n' + self._success_progress_bar def _failed_string(self): - text = super()._failed_string() + text = super(SingleRun, self)._failed_string() if self._connected: text += self._connected_string() - return text + "\n" + self._failed_progress_bar + return text + '\n' + self._failed_progress_bar def die(self): try: self._variable_server.close() except: pass - super().die() + super(SingleRun, self).die() def __del__(self): try: @@ -1807,24 +1531,25 @@ def __del__(self): pass def _create_variables(self): - self._tics = variable_server.Variable("trick_sys.sched.time_tics", type_=float) + self._tics = variable_server.Variable( + 'trick_sys.sched.time_tics', type_=float) self._tics_per_sec = variable_server.Variable( - "trick_sys.sched.time_tic_value", type_=float - ) + 'trick_sys.sched.time_tic_value', type_=float) self._terminate_time = variable_server.Variable( - "trick_sys.sched.terminate_time", type_=float - ) + 'trick_sys.sched.terminate_time', type_=float) return self._tics, self._tics_per_sec, self._terminate_time def _connected_string(self): - return " {0} {1}".format(self._sim_time(), self._average_speed()) + return ' {0} {1}'.format( + self._sim_time(), self._average_speed()) def _connected_bar(self): if self._terminate_time.value <= 0.0: - progress = 0.0 + progress = 0.0 else: - progress = self._tics.value / self._terminate_time.value - return create_progress_bar(progress, "{0:.1f}%".format(100 * progress)) + progress = self._tics.value / self._terminate_time.value + return create_progress_bar( + progress, '{0:.1f}%'.format(100 * progress)) def _sim_time(self): """ @@ -1835,9 +1560,8 @@ def _sim_time(self): str A string for displaying sim time. """ - return "Sim Time: {0:7.1f} sec".format( - self._tics.value / self._tics_per_sec.value - ) + return 'Sim Time: {0:7.1f} sec'.format( + self._tics.value / self._tics_per_sec.value) def _average_speed(self): """ @@ -1849,8 +1573,8 @@ def _average_speed(self): A string for displaying the ratio of sim time to real time. """ elapsed_time = ( - self._stop_time if self._stop_time else time.time() - ) - self._start_time - return "Average Speed: {0:4.1f} X".format( - self._tics.value / self._tics_per_sec.value / elapsed_time - ) + (self._stop_time if self._stop_time else time.time()) + - self._start_time) + return 'Average Speed: {0:4.1f} X'.format( + self._tics.value / self._tics_per_sec.value / elapsed_time) + diff --git a/share/trick/trickops/WorkflowCommon.py b/share/trick/trickops/WorkflowCommon.py index 0d4083754..7cd58170e 100644 --- a/share/trick/trickops/WorkflowCommon.py +++ b/share/trick/trickops/WorkflowCommon.py @@ -5,36 +5,21 @@ subprocesses to get progress bars and curses display logic for free! """ -import collections -import curses -import datetime -import logging -import multiprocessing -import os -import platform -import re -import signal -import socket -import subprocess -import sys -import textwrap -import time -from pathlib import Path - +import argparse +import curses, textwrap +import pdb, sys, datetime, time, socket, stat +import subprocess, signal, logging +import os, re, collections +import multiprocessing, platform +from contextlib import contextmanager from ColorStr import ColorStr +from pathlib import Path # Create a global color printer printer = ColorStr() - # UTILITY FUNCTIONS -def run_subprocess( - command, - m_shell=False, - m_cwd=None, - m_stdout=subprocess.PIPE, - m_stderr=subprocess.PIPE, -): +def run_subprocess(command, m_shell=False, m_cwd=None, m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE): """ Utility method for running a subprocess and returning stdout, stderr, and return code @@ -63,18 +48,15 @@ def run_subprocess( Collection of process's final exit code, stdout, and stderr """ result = collections.namedtuple("Process", ["code", "stdout", "stderr"]) - p = subprocess.Popen( - command, shell=m_shell, cwd=m_cwd, stdout=m_stdout, stderr=m_stderr - ) + p = subprocess.Popen(command, shell=m_shell, cwd=m_cwd, stdout=m_stdout, stderr=m_stderr) result.stdout, result.stderr = p.communicate() - if result.stdout != None: - result.stdout = result.stdout.decode(errors="ignore") - if result.stderr != None: - result.stderr = result.stderr.decode(errors="ignore") + if (result.stdout != None): + result.stdout = result.stdout.decode(errors='ignore') + if (result.stderr != None): + result.stderr = result.stderr.decode(errors='ignore') result.code = p.returncode return result - def validate_output_file(filename): """ Utility method for validating a file intended to contain some TBD content @@ -98,10 +80,9 @@ def validate_output_file(filename): filename = os.path.abspath(filename) os.makedirs(os.path.dirname(filename), exist_ok=True) Path(filename).touch(exist_ok=True) - return filename - + return (filename) -def tprint(line, color="ENDC", verbosity="INFO"): +def tprint(line, color='ENDC', verbosity='INFO'): """ Utility method for writing text to both the log and stdout, with optional color for stdout @@ -114,19 +95,18 @@ def tprint(line, color="ENDC", verbosity="INFO"): Color/style of text. Options are listed in Colorstr() class. """ colorLine = printer.colorstr(line, color) - sys.stdout.write(colorLine + "\n") - if verbosity == "INFO": + sys.stdout.write(colorLine+"\n") + if verbosity == 'INFO': logging.info(line) - elif verbosity == "DEBUG": + elif verbosity == 'DEBUG': logging.debug(line) - elif verbosity == "ERROR": + elif verbosity == 'ERROR': logging.error(line) - elif verbosity == "WARNING": + elif verbosity == 'WARNING': logging.warning(line) else: pass - def create_progress_bar(fraction, text): """ Utility method that creates a text-based progress bar @@ -147,13 +127,12 @@ def create_progress_bar(fraction, text): str A text-based progress bar string of length 80 """ - text = " " + text + " " + text = ' ' + text + ' ' length = len(text) - bar = list("[{0:<78}]".format("=" * min(78, int(round(fraction * 78))))) + bar = list('[{0:<78}]'.format('=' * min(78, int(round(fraction * 78))))) index = int((len(bar) - length) / 2) bar[index : index + length] = text - return "".join(bar) - + return ''.join(bar) def sanitize_cpus(num_cpus, num_tasks, fallback_cpus): """ @@ -182,25 +161,24 @@ def sanitize_cpus(num_cpus, num_tasks, fallback_cpus): - a description of the boundary violated (may be None) """ if num_cpus < 1: - return (1, "minimum allowable value") + return (1, 'minimum allowable value') else: try: maximum = multiprocessing.cpu_count() - source = "number of logical CPUs" + source = 'number of logical CPUs' except: maximum = 16 - source = "maximum allowed value" + source = 'maximum allowed value' if num_tasks < maximum: maximum = num_tasks - source = "number of runs" + source = 'number of runs' if num_cpus > maximum: return (maximum, source) return (num_cpus, None) - def unixify_string(string): """ Sanitizes a string making it nice for unix by processing special characters. @@ -218,22 +196,20 @@ def unixify_string(string): str the sanitized string """ - return re.sub("['/]", "_", re.sub("[()!?,]", "", string)).replace(" ", "_") - + return re.sub("['/]", '_', re.sub('[()!?,]', '', string)).replace(' ', '_') # CLASSES -class Job: +class Job(object): """ Manages a given command intended to be run as a subprocess and provides methods for getting status information. More specific types of Jobs should inherit from this base clasee. """ + enums = ['NOT_STARTED', 'RUNNING', 'SUCCESS', 'FAILED', 'TIMEOUT'] + Status = collections.namedtuple('Status', enums)(*(range(len(enums)))) - enums = ["NOT_STARTED", "RUNNING", "SUCCESS", "FAILED", "TIMEOUT"] - Status = collections.namedtuple("Status", enums)(*(range(len(enums)))) - - _success_progress_bar = create_progress_bar(1, "Success") - _failed_progress_bar = create_progress_bar(1, "Failed") + _success_progress_bar = create_progress_bar(1, 'Success') + _failed_progress_bar = create_progress_bar(1, 'Failed') def _translate_status(self): """ @@ -241,20 +217,12 @@ def _translate_status(self): codes if FAIL. """ text, color = { - Job.Status.NOT_STARTED: ("NOT RUN", "DARK_YELLOW"), - Job.Status.SUCCESS: ("OK", "DARK_GREEN"), - Job.Status.FAILED: ( - "FAIL:" - + str(self._expected_exit_status) - + "/" - + str(self._exit_status), - "DARK_RED", - ), - Job.Status.TIMEOUT: ( - "TIMEOUT after " + str(self._timeout) + " seconds", - "YELLOW", - ), - }[self.get_status()] + Job.Status.NOT_STARTED: ('NOT RUN', 'DARK_YELLOW'), + Job.Status.SUCCESS: ('OK', 'DARK_GREEN'), + Job.Status.FAILED: ('FAIL:' + str(self._expected_exit_status) + '/' + + str(self._exit_status), 'DARK_RED'), + Job.Status.TIMEOUT: ('TIMEOUT after ' + str(self._timeout) + ' seconds', 'YELLOW') + }[self.get_status()] return printer.colorstr(text, color) def __init__(self, name, command, log_file, expected_exit_status=0): @@ -347,11 +315,7 @@ def get_status(self): if self._stop_time is None: self._stop_time = time.time() - return ( - self.Status.SUCCESS - if self._exit_status is self._expected_exit_status - else self.Status.FAILED - ) + return self.Status.SUCCESS if self._exit_status is self._expected_exit_status else self.Status.FAILED def get_expected_exit_status(self): return self._expected_exit_status @@ -397,7 +361,7 @@ def _not_started_string(self): str A string to be displayed when in the NOT_STARTED state. """ - return "Not started yet" + return 'Not started yet' def _running_string(self): """ @@ -408,7 +372,8 @@ def _running_string(self): str A string to be displayed when in the RUNNING state. """ - return "Elapsed Time: {0:7.1f} sec".format(time.time() - self._start_time) + return 'Elapsed Time: {0:7.1f} sec'.format( + time.time() - self._start_time) def _success_string(self): """ @@ -453,9 +418,11 @@ def _done_string(self): str A string to be displayed when this Job is done. """ - return "Total Time: {0:7.1f} sec".format(self._stop_time - self._start_time) + return 'Total Time: {0:7.1f} sec'.format( + self._stop_time - self._start_time) + - def report(self, indent="", width=100): + def report( self, indent='', width=100): """ Return a colored report string for this job. @@ -464,27 +431,17 @@ def report(self, indent="", width=100): str A olored report string """ - if self.get_status() == Job.Status.NOT_STARTED: - prepend = printer.colorstr("NOT RUN ", "DARK_YELLOW") - elif self.get_expected_exit_status() == self.get_exit_status(): - prepend = printer.colorstr("OK ", "DARK_GREEN") + if self.get_status() == Job.Status.NOT_STARTED: + prepend = printer.colorstr('NOT RUN ', 'DARK_YELLOW') + elif self.get_expected_exit_status() == self.get_exit_status(): + prepend = printer.colorstr('OK ', 'DARK_GREEN') else: - prepend = printer.colorstr( - "FAIL:%-10s " - % ( - str(self.get_exit_status()) - + "/" - + str(self.get_expected_exit_status()) - ), - "DARK_RED", - ) - reportStr = ( - indent - + prepend - + (" %-s" % (textwrap.shorten(self.name, width=width - 10))) - ) + prepend = printer.colorstr('FAIL:%-10s ' % + (str(self.get_exit_status())+'/'+str(self.get_expected_exit_status())) , 'DARK_RED') + reportStr = indent + prepend + (" %-s" % ( textwrap.shorten(self.name, width=width-10))) return reportStr + def die(self): """ Immediately kill this Job and all processes in its process group @@ -497,12 +454,10 @@ def die(self): pass self._close_files() - class FileSizeJob(Job): """ A build job is a subclass of Job() which estimates its progress via file size. """ - def __init__(self, name, command, log_file, size): """ Initialize this instance. @@ -519,28 +474,27 @@ def __init__(self, name, command, log_file, size): The expected number size in bytes of the log_file when the build is complete. Used to estimate progress. """ - super().__init__(name, command, log_file) + super(FileSizeJob, self).__init__(name, command, log_file) self.size = size def get_status_string_line_count(self): - return super().get_status_string_line_count() + 1 + return super(FileSizeJob, self).get_status_string_line_count() + 1 def _not_started_string(self): - return super()._not_started_string() + "\n" + return super(FileSizeJob, self)._not_started_string() + '\n' def _running_string(self): progress = os.path.getsize(self._log_file.name) / float(self.size) - return ( - super()._running_string() - + "\n" - + create_progress_bar(progress, "{0:.1f}%".format(100 * progress)) - ) + return (super(FileSizeJob, self)._running_string() + '\n' + + create_progress_bar(progress, '{0:.1f}%'.format(100 * progress))) def _success_string(self): - return super()._success_string() + "\n" + self._success_progress_bar + return (super(FileSizeJob, self)._success_string() + '\n' + + self._success_progress_bar) def _failed_string(self): - return super()._failed_string() + "\n" + self._failed_progress_bar + return (super(FileSizeJob, self)._failed_string() + '\n' + + self._failed_progress_bar) class WorkflowCommon: @@ -552,15 +506,7 @@ class WorkflowCommon: * Provide log directory where output for jobs can be written * Get installed packages, host, and other OS information """ - - def __init__( - self, - project_top_level, - log_dir="/tmp/", - log_level=logging.DEBUG, - env="", - quiet=False, - ): + def __init__( self, project_top_level, log_dir='/tmp/', log_level=logging.DEBUG, env='', quiet=False ): """ Initialize this instance. @@ -583,14 +529,8 @@ def __init__( self.project_top_level = project_top_level self.log_dir = log_dir # Where all logged output will go # self.log is where all logging for python script layer goes - self.log = validate_output_file( - os.path.join( - self.log_dir, - "log." - + unixify_string(str(datetime.datetime.now()).replace(":", "-")) - + ".txt", - ) - ) + self.log = validate_output_file( os.path.join(self.log_dir,'log.' + + unixify_string(str(datetime.datetime.now()).replace(':','-')) + '.txt')) self.env = env # Project environment literal string, e.g. "source bashrc" self.creation_time = time.time() # When this instance was created self.host_name = socket.gethostname() @@ -602,7 +542,7 @@ def __init__( else: self.quiet = True logging.basicConfig(filename=self.log, level=log_level) - os.chdir(self.project_top_level) # Automatically chdir to top of project + os.chdir(self.project_top_level) # Automatically chdir to top of project def _cleanup(self): """ @@ -628,28 +568,20 @@ def get_installed_packages(self, verbose=False): A list of installed packages, or None if they cannot be determined """ self.this_os = self.get_operating_system() - if self.this_os == "debian": + if self.this_os == 'debian': list_cmd = "dpkg-query -W -f='${binary:Package}\n'" - elif self.this_os == "redhat": + elif self.this_os == 'redhat': list_cmd = "rpm -qa " else: return None - result = run_subprocess( - command=list_cmd, - m_shell=True, - m_stdout=subprocess.PIPE, - m_stderr=subprocess.PIPE, - ) + result = run_subprocess(command=list_cmd, m_shell=True, + m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE) if result.code != 0: if verbose: - tprint( - "ERROR: Trouble finding platform packages, error:\n" - + result.stderr, - "DARK_RED", - ) + tprint("ERROR: Trouble finding platform packages, error:\n" + result.stderr, 'DARK_RED') return None - return result.stdout.strip().split("\n") + return(result.stdout.strip().split("\n")) def get_operating_system(self): """ @@ -661,34 +593,26 @@ def get_operating_system(self): Returns one of these strings: debian, redhat, windows, darwin, unknown """ # Windows is easy - if platform.system().lower() == "windows": - return "windows" + if platform.system().lower() == 'windows': + return 'windows' # Darwin (Mac OSX) is easy - if platform.system().lower() == "darwin": - return "darwin" + if platform.system().lower() == 'darwin': + return 'darwin' # Linux is hard # NOTE: sys.platform will not distinguish between flavors of linux and platform.linux_distribution() # doesn't exist on all linux distributions! Also we can't rely on 'import distro' because it's # only pip-installable at the moment. -Jordan 2/2021 # Determine platform the old fashioned way, by seeing what package manager exists - rpm_exist = not run_subprocess( - command="which rpm", - m_shell=True, - m_stdout=subprocess.PIPE, - m_stderr=subprocess.PIPE, - ).code - apt_exist = not run_subprocess( - command="which apt", - m_shell=True, - m_stdout=subprocess.PIPE, - m_stderr=subprocess.PIPE, - ).code + rpm_exist = not run_subprocess(command='which rpm', m_shell=True, + m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE).code + apt_exist = not run_subprocess(command='which apt', m_shell=True, + m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE).code if rpm_exist: - return "redhat" + return 'redhat' elif apt_exist: - return "debian" + return 'debian' else: - return "unknown" + return 'unknown' def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None): """ @@ -709,16 +633,14 @@ def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None) True if any job failed or was not run. False if all jobs completed successfully. """ - if not os.environ.get("TERM") and not self.quiet: + if not os.environ.get('TERM') and not self.quiet: tprint( - "The TERM environment variable must be set when\n" - "TrickWorkflow.quiet is False. This is usually set by one\n" - "of the shell's configuration files (.profile, .cshrc, etc).\n" - "However, if this was executed via a non-interactive,\n" - "non-login shell (for instance: ssh ''), it\n" - "may not be automatically set.", - "DARK_RED", - ) + 'The TERM environment variable must be set when\n' + 'TrickWorkflow.quiet is False. This is usually set by one\n' + "of the shell's configuration files (.profile, .cshrc, etc).\n" + 'However, if this was executed via a non-interactive,\n' + "non-login shell (for instance: ssh ''), it\n" + 'may not be automatically set.', 'DARK_RED') return True num_jobs = len(jobs) @@ -726,16 +648,14 @@ def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None) max_concurrent = num_jobs if header: - header += "\n" + header += '\n' else: - header = "" + header = '' header += ( - "Executing {0} total jobs, running up to {1} simultaneously.\n".format( - num_jobs, max_concurrent - ) - + "Press CTRL+C to terminate early.\n" - ) + 'Executing {0} total jobs, running up to {1} simultaneously.\n' + .format(num_jobs, max_concurrent) + + 'Press CTRL+C to terminate early.\n') logging.info(header) @@ -744,6 +664,7 @@ def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None) # status output is enabled. Otherwise, it will be called # directly. See below. def execute(stdscr=None): + # stdscr is passed via curses.wrapper if stdscr: # Turn off the cursor. Not all terminals may support @@ -779,7 +700,7 @@ def execute(stdscr=None): # extra lines, so pick a realy big width that isn't # likely to cause wrapping. We also need a final # additional line for the cursor to end on. - header_pad = curses.newpad(header.count("\n") + 1, 1000) + header_pad = curses.newpad(header.count('\n') + 1, 1000) header_pad.addstr(header) # Create a pad for the status. @@ -789,11 +710,9 @@ def execute(stdscr=None): # + a blank line after each status string # + a final line for the cursor to end on status_pad = curses.newpad( - sum(job.get_status_string_line_count() for job in jobs) - + 2 * len(jobs) - + 1, - 1000, - ) + sum(job.get_status_string_line_count() for job in jobs) + + 2 * len(jobs) + 1, + 1000) # The top visible status pad line. # Used for scrolling. @@ -801,28 +720,24 @@ def execute(stdscr=None): header_height = header_pad.getmaxyx()[0] status_height = status_pad.getmaxyx()[0] - while any( - job.get_status() in [job.Status.NOT_STARTED, job.Status.RUNNING] - for job in jobs - ): + while any(job.get_status() in + [job.Status.NOT_STARTED, job.Status.RUNNING] + for job in jobs): + # Start waiting jobs if cpus are available - waitingJobs = [ - job for job in jobs if job.get_status() is job.Status.NOT_STARTED - ] + waitingJobs = [job for job in jobs + if job.get_status() is job.Status.NOT_STARTED] if waitingJobs: - available_cpus = max_concurrent - sum( - 1 for job in jobs if job.get_status() is job.Status.RUNNING - ) + available_cpus = max_concurrent - sum(1 for job in jobs + if job.get_status() is job.Status.RUNNING) for i in range(min(len(waitingJobs), available_cpus)): waitingJobs[i].start() if job_timeout is not None: # Check if any jobs have timed out - runningJobs = [ - job for job in jobs if job.get_status() is job.Status.RUNNING - ] + runningJobs = [job for job in jobs if job.get_status() is job.Status.RUNNING] for runningJob in runningJobs: if time.time() - runningJob._start_time > job_timeout: runningJob._timeout = job_timeout @@ -834,12 +749,9 @@ def execute(stdscr=None): status_pad.erase() for i, job in enumerate(jobs): # print the name - status_pad.addstr( - "Job {0:{width}d}/{1}: ".format( - i + 1, num_jobs, width=len(str(num_jobs)) - ) - ) - status_pad.addstr(job.name + "\n", curses.A_BOLD) + status_pad.addstr('Job {0:{width}d}/{1}: '.format( + i + 1, num_jobs, width=len(str(num_jobs)))) + status_pad.addstr(job.name + '\n', curses.A_BOLD) # print the status string if use_colors: @@ -851,9 +763,11 @@ def execute(stdscr=None): color = curses.color_pair(2) else: color = curses.color_pair(0) - status_pad.addstr(job.get_status_string() + "\n\n", color) + status_pad.addstr( + job.get_status_string() + '\n\n', color) else: - status_pad.addstr(job.get_status_string() + "\n\n") + status_pad.addstr( + job.get_status_string() + '\n\n') # handle scrolling while True: @@ -869,12 +783,9 @@ def execute(stdscr=None): # prevent scrolling beyond the bounds of status_pad screen_height, screen_width = stdscr.getmaxyx() top_line = max( - 0, - min( - top_line, - status_height - 2 - (screen_height - header_height), - ), - ) + 0, + min(top_line, + status_height - 2 - (screen_height - header_height))) # Resizing the terminal can cause the actual # screen width or height to become smaller than @@ -888,16 +799,10 @@ def execute(stdscr=None): # have and ignore errors. try: header_pad.noutrefresh( - 0, 0, 0, 0, screen_height - 1, screen_width - 1 - ) + 0, 0, 0, 0, screen_height - 1, screen_width - 1) status_pad.noutrefresh( - top_line, - 0, - header_height, - 0, - screen_height - 1, - screen_width - 1, - ) + top_line, 0, header_height, 0, + screen_height - 1, screen_width - 1) except curses.error: pass curses.doupdate() @@ -921,43 +826,36 @@ def execute(stdscr=None): execute() except BaseException as exception: - logging.exception("") + logging.exception('') tprint( - "An exception occurred. See the log for details.\n\n" - " " + repr(exception) + "\n\n" - "Terminating all jobs. Please wait for cleanup to finish. " - "CTRL+C may leave orphaned processes.", - "DARK_RED", - "ERROR", - ) + 'An exception occurred. See the log for details.\n\n' + ' ' + repr(exception) + "\n\n" + 'Terminating all jobs. Please wait for cleanup to finish. ' + 'CTRL+C may leave orphaned processes.', 'DARK_RED', + 'ERROR') # kill all the jobs for job in jobs: job.die() - tprint("All jobs terminated.\n", "DARK_RED") + tprint('All jobs terminated.\n', 'DARK_RED') # print summary - summary = "Job Summary\n" + summary = 'Job Summary\n' for i, job in enumerate(jobs): - summary += "Job {0:{width}d}/{1}: {2}\n{3}\n".format( - i + 1, - num_jobs, - job.name, - job.get_status_string(), - width=len(str(num_jobs)), - ) + summary += 'Job {0:{width}d}/{1}: {2}\n{3}\n'.format( + i + 1, num_jobs, job.name, job.get_status_string(), + width=len(str(num_jobs))) logging.info(summary) for job in jobs: text, color = { - job.Status.NOT_STARTED: ("was not run", "GREY40"), - job.Status.SUCCESS: ("succeeded", "DARK_GREEN"), - job.Status.FAILED: ("failed", "DARK_RED"), - job.Status.TIMEOUT: ("timed out", "YELLOW"), - }[job.get_status()] + job.Status.NOT_STARTED: ('was not run', 'GREY40'), + job.Status.SUCCESS: ('succeeded', 'DARK_GREEN'), + job.Status.FAILED: ('failed', 'DARK_RED'), + job.Status.TIMEOUT: ('timed out', 'YELLOW') }[job.get_status()] - text = job.name + " " + text + text = job.name + ' ' + text # Print the summary status even if self.quiet is True tprint(text, color) diff --git a/share/trick/trickops/send_hs.py b/share/trick/trickops/send_hs.py index eeb20c86b..e885a046f 100644 --- a/share/trick/trickops/send_hs.py +++ b/share/trick/trickops/send_hs.py @@ -1,5 +1,5 @@ -import os -import re +import re, os +import pdb # This global is the result of hours of frustration and debugging. This is only used by doctest # but appears to be the only solution to the problem of __file__ not being an absolute path in @@ -11,17 +11,12 @@ # https://note.nkmk.me/en/python-script-file-path/ # I do not like adding globals to "production code" just to facilitate a testing mechanism, but # I don't know of any cleaner way way to do this. -Jordan 12/2024 -this_trick = os.path.normpath( - os.path.join(os.path.dirname(os.path.realpath(__file__)), "../../..") -) - - -class send_hs: +this_trick = os.path.normpath(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../..')) +class send_hs(object): """ Utility class for parsing simulation diagnostic data at the end of a Trick-generated send_hs output file. """ - def __init__(self, hs_file=None): """ Initialize this instance. @@ -35,8 +30,8 @@ def __init__(self, hs_file=None): """ self.hs_file = hs_file self.num_lines = None - self._diagnostics = {} # Internal dict of diagnostic keys and values - self._missing_diagnostics = [] # List of diagnostics we failed to find + self._diagnostics = {} # Internal dict of diagnostic keys and values + self._missing_diagnostics = [] # List of diagnostics we failed to find for k in self.keys(): self._diagnostics[k] = None @@ -58,7 +53,7 @@ def is_valid(self): True if all expected internal members were parsed. False if any member wasn't found. """ - self._missing_diagnostics = [] # Reset the internal list + self._missing_diagnostics = [] # Reset the internal list for d in self._diagnostics: if self._diagnostics[d] is None: self._missing_diagnostics.append(d) @@ -68,7 +63,7 @@ def is_valid(self): return True def missing_diagnostics(self): - """ + ''' Check for validity and return a list of any missing diagnostics that we were unable to find in the send_hs output @@ -80,12 +75,12 @@ def missing_diagnostics(self): ------- list All diagnotics that were unable to be parsed - """ + ''' self.is_valid() return self._missing_diagnostics def parse(self, hs_file=None): - """ + ''' Parse the content of self.hs_file and assign internal variables for each field found if hs_file is given, overwrite self.hs_file with it's value @@ -107,32 +102,30 @@ def parse(self, hs_file=None): ------ RuntimeError If no send_hs file was provided at construction or when calling this function - """ + ''' if hs_file: self.hs_file = hs_file if not self.hs_file or not os.path.isfile(self.hs_file): - raise RuntimeError( - f"send_hs file: '{self.hs_file}' cannot be read." - " You must provide a valid send_hs output file." - ) + raise RuntimeError(f"send_hs file: '{self.hs_file}' cannot be read." + " You must provide a valid send_hs output file.") self.num_lines = 0 with open(self.hs_file) as fp: - # Efficiency guard - we don't do an re pattern match until we've starting parsing the diagnostic msg - start_reading_diagnostics = False - for line in fp: - self.num_lines += 1 - # NOTE this "attempt match" approach is less efficient but it should be - # robust to a future ordering change of the lines from Trick's output. - if start_reading_diagnostics: - for d in self._diagnostics: - if self._diagnostics[d] is None: - self._diagnostics[d] = self._attempt_hs_match(d, line) - # This text precedes the diagnostics output, use it as the trigger to start parsing - if "REALTIME SHUTDOWN STATS:" in line: - start_reading_diagnostics = True - continue + # Efficiency guard - we don't do an re pattern match until we've starting parsing the diagnostic msg + start_reading_diagnostics = False + for line in fp: + self.num_lines += 1 + # NOTE this "attempt match" approach is less efficient but it should be + # robust to a future ordering change of the lines from Trick's output. + if start_reading_diagnostics: + for d in self._diagnostics: + if self._diagnostics[d] is None: + self._diagnostics[d] = self._attempt_hs_match(d, line) + # This text precedes the diagnostics output, use it as the trigger to start parsing + if 'REALTIME SHUTDOWN STATS:' in line: + start_reading_diagnostics = True + continue # Set validity status immediately after parsing self.is_valid() @@ -152,12 +145,12 @@ def _attempt_hs_match(self, name, text): float or None Value of name if found, else: None """ - name = name.replace("(", r"\(").replace(")", r"\)") - m = re.match(name + r": +([-]?[0-9]*\.?[0-9]+)", text.strip()) + name = name.replace('(', '\(').replace(')', '\)') + m = re.match(name+': +([-]?[0-9]*\.?[0-9]+)', text.strip()) if m: - return float(m.group(1)) + return(float(m.group(1))) else: - return None + return(None) def keys(self) -> list: """ @@ -174,25 +167,24 @@ def keys(self) -> list: list All possible diagnostic names that get() can accept """ - return [ - "ACTUAL INIT TIME", - "ACTUAL ELAPSED TIME", - "SIMULATION START TIME", - "SIMULATION STOP TIME", - "SIMULATION ELAPSED TIME", - "USER CPU TIME USED", - "SYSTEM CPU TIME USED", - "SIMULATION / CPU TIME", - "INITIALIZATION USER CPU TIME", - "INITIALIZATION SYSTEM CPU TIME", - "SIMULATION RAM USAGE", - "VOLUNTARY CONTEXT SWITCHES (INIT)", - "INVOLUNTARY CONTEXT SWITCHES (INIT)", - "VOLUNTARY CONTEXT SWITCHES (RUN)", - "INVOLUNTARY CONTEXT SWITCHES (RUN)", - ] - - def get_diagnostic(self, name: str) -> float: + return (['ACTUAL INIT TIME', + 'ACTUAL ELAPSED TIME', + 'SIMULATION START TIME', + 'SIMULATION STOP TIME', + 'SIMULATION ELAPSED TIME', + 'USER CPU TIME USED', + 'SYSTEM CPU TIME USED', + 'SIMULATION / CPU TIME', + 'INITIALIZATION USER CPU TIME', + 'INITIALIZATION SYSTEM CPU TIME', + 'SIMULATION RAM USAGE', + 'VOLUNTARY CONTEXT SWITCHES (INIT)', + 'INVOLUNTARY CONTEXT SWITCHES (INIT)', + 'VOLUNTARY CONTEXT SWITCHES (RUN)', + 'INVOLUNTARY CONTEXT SWITCHES (RUN)' + ]) + + def get_diagnostic (self, name: str) -> float: """ Get a diagnostic value by it's name or partial name The first name matched in the self._diagnostics dict will be returned @@ -221,10 +213,8 @@ def get_diagnostic(self, name: str) -> float: for d in self._diagnostics: if name in d: - return self._diagnostics[d] - raise LookupError( - f"Unable to get diagnostic '{name}'. Is it spelled correctly?" - ) + return(self._diagnostics[d]) + raise LookupError(f"Unable to get diagnostic '{name}'. Is it spelled correctly?") def get(self, name: str) -> float: """ @@ -245,10 +235,10 @@ def get(self, name: str) -> float: return self.get_diagnostic(name) @property - def diagnostics(self) -> dict: + def diagnostics (self) -> dict: return dict(self._diagnostics) - def get_diagnostics(self) -> dict: + def get_diagnostics (self) -> dict: """ Get all diagnostics stored in internal self._diagnostics dictionary @@ -261,4 +251,4 @@ def get_diagnostics(self) -> dict: dict A copy of the internal self._diagnostics dictionary """ - return self.diagnostics + return (self.diagnostics) diff --git a/trickops.py b/trickops.py index 3795eab5b..a944d7cb8 100644 --- a/trickops.py +++ b/trickops.py @@ -1,49 +1,21 @@ import argparse import os -import signal import sys -from pathlib import Path -from typing import Optional, Union -sys.path.append(str(Path(__file__).resolve().parent / "share" / "trick" / "trickops")) +thisdir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) +sys.path.append(os.path.join(thisdir,"share/trick/trickops")) -from TrickWorkflow import TrickWorkflow +from TrickWorkflow import * from WorkflowCommon import Job -THIS_DIR = Path(__file__).resolve().parent - -# Runs ordered by phase: some test sims depend on artifacts from an earlier -# phase (SIM_stls / SIM_checkpoint_data_recording read checkpoints written by a -# prior run; SIM_test_varserver has runs that cannot be concurrent; -# SIM_mc_generation generates runs and then executes them). -PHASES = (-1, 0, 1, 2, 3) - -# SIM_test_varserv's err1/err2 runs are expected to exit with SIGUSR1. The exit -# code differs Mac vs Linux, so it can't be hardcoded in the yaml; tag them here. -VARSERV_SIGUSR1_RUNS = frozenset({ - "Run test/SIM_test_varserv RUN_test/err1_test.py", - "Run test/SIM_test_varserv RUN_test/err2_test.py", -}) - -# Jobs skipped on macOS (SIM_trickified_shared does not build/run there). -DARWIN_SKIP_BUILDS = frozenset({"Build test/SIM_trickified_shared"}) -DARWIN_SKIP_RUNS = frozenset({"Run test/SIM_trickified_shared RUN_test/unit_test.py"}) - +max_retries = 5 class SimTestWorkflow(TrickWorkflow): - def __init__( - self, - quiet: bool, - trick_top_level: Union[str, Path], - cpus: int, - config_file: str, - trick_dir: Optional[Union[str, Path]] = None, - ) -> None: + def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): self.cpus = cpus - project_root = Path(trick_top_level) - # Create the trick_test directory if it doesn't already exist - (project_root / "trick_test").mkdir(exist_ok=True) + if not os.path.exists(trick_top_level + "/trick_test"): + os.makedirs(trick_top_level + "/trick_test") # trick_dir defaults to trick_top_level (historical behavior: sims and # the installed Trick used by bin/trick-CP live in the same in-source @@ -52,100 +24,90 @@ def __init__( # installed product (bin/trick-CP, libexec/, share/) may be a staged # out-of-source CMake install instead. if trick_dir is None: - trick_dir = project_root + trick_dir = trick_top_level # Base Class initialize, this creates internal management structures - super().__init__( - project_top_level=str(project_root), - log_dir=str(project_root / "trickops_logs"), - trick_dir=str(trick_dir), - config_file=str(project_root / config_file), + TrickWorkflow.__init__( + self, + project_top_level=(trick_top_level), + log_dir=(trick_top_level + "/trickops_logs/"), + trick_dir=trick_dir, + config_file=(trick_top_level + "/" + config_file), cpus=self.cpus, quiet=quiet, ) + def run( self ): + build_jobs = self.get_jobs(kind='build') + + # This is awful but I can't think of another way around it + # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux + # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? + import signal + run_names = ["Run test/SIM_test_varserv RUN_test/err1_test.py", "Run test/SIM_test_varserv RUN_test/err2_test.py"] + for job in [job for job in self.get_jobs(kind='run') if job.name in run_names]: + job._expected_exit_status = signal.SIGUSR1.value + + # Several test sims have runs that require ordering via phases: + # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run + # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs + # - SIM_test_varserver has 3 runs that cannot be concurrent + # - SIM_mc_generation generates runs and then runs them + phases = [-1, 0, 1, 2, 3] + + analysis_jobs = self.get_jobs(kind='analyze') + if platform == "darwin": + for job in build_jobs: + if job.name == "Build test/SIM_trickified_shared" : + print("REMOVING JOB: " + job.name) + build_jobs.remove(job) + builds_status = self.execute_jobs(build_jobs, max_concurrent=self.cpus, header='Executing all sim builds.') + + jobs = build_jobs + + run_status = 0 + for phase in phases: + run_jobs = self.get_jobs(kind='run', phase=phase) + if platform == "darwin": + for job in run_jobs: + if job.name == "Run test/SIM_trickified_shared RUN_test/unit_test.py" : + print("REMOVING JOB: " + job.name) + run_jobs.remove(job) + this_status = self.execute_jobs(run_jobs, max_concurrent=self.cpus, header="Executing phase " + str(phase) + " runs.", job_timeout=1000) + run_status = run_status or this_status + jobs += run_jobs + + comparison_result = self.compare() + analysis_status = self.execute_jobs(analysis_jobs, max_concurrent=self.cpus, header='Executing all analysis.') + + self.report() # Print Verbose report + self.status_summary() # Print a Succinct summary + + # Dump failing logs + for job in jobs: + if job.get_status() == Job.Status.FAILED or job.get_status() == Job.Status.TIMEOUT: + print ("*"*120) + if job.get_status() == Job.Status.FAILED: + header = "Failing job: " + job.name + else: + header = "Timed out job: " + job.name + + numspaces = int((120 - 20 - len(header))/2 -2) + print("*"*10, " "*numspaces, header, " "*numspaces, "*"*10,) + print ("*"*120) + print(open(job.log_file, "r").read()) + print ("*"*120, "\n\n\n") + + return (builds_status or run_status or len(self.config_errors) > 0 or comparison_result or analysis_status) - def _skip_darwin(self, jobs: list, skip_names: frozenset) -> list: - """Return jobs with the macOS-excluded ones filtered out (announcing each).""" - if self.platform != "darwin": - return jobs - kept = [] - for job in jobs: - if job.name in skip_names: - print(f"REMOVING JOB: {job.name}") - else: - kept.append(job) - return kept - - def run(self) -> int: - build_jobs = self._skip_darwin(self.get_jobs(kind="build"), DARWIN_SKIP_BUILDS) - analysis_jobs = self.get_jobs(kind="analyze") - - for job in self.get_jobs(kind="run"): - if job.name in VARSERV_SIGUSR1_RUNS: - job._expected_exit_status = signal.SIGUSR1.value - - builds_status = self.execute_jobs( - build_jobs, max_concurrent=self.cpus, header="Executing all sim builds." - ) - - jobs = list(build_jobs) - run_status = 0 - for phase in PHASES: - run_jobs = self._skip_darwin( - self.get_jobs(kind="run", phase=phase), DARWIN_SKIP_RUNS - ) - this_status = self.execute_jobs( - run_jobs, - max_concurrent=self.cpus, - header=f"Executing phase {phase} runs.", - job_timeout=1000, - ) - run_status = run_status or this_status - jobs += run_jobs - - comparison_result = self.compare() - analysis_status = self.execute_jobs( - analysis_jobs, max_concurrent=self.cpus, header="Executing all analysis." - ) - - self.report() # Print Verbose report - self.status_summary() # Print a Succinct summary - - # Dump failing logs - for job in jobs: - status = job.get_status() - if status in (Job.Status.FAILED, Job.Status.TIMEOUT): - header = ( - f"Failing job: {job.name}" - if status == Job.Status.FAILED - else f"Timed out job: {job.name}" - ) - print("*" * 120) - print(f" {header} ".center(120, "*")) - print("*" * 120) - print(Path(job.log_file).read_text()) - print("*" * 120, "\n\n\n") - - return ( - builds_status - or run_status - or len(self.config_errors) > 0 - or comparison_result - or analysis_status - ) - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Build, run, and compare all test sims for Trick", - formatter_class=argparse.ArgumentDefaultsHelpFormatter, - ) +if __name__ == "__main__": + parser = argparse.ArgumentParser(description='Build, run, and compare all test sims for Trick', + formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument( "--trick_top_level", type=str, help="Path to the project tree containing the sim " "directories referenced by --config_file", - default=str(THIS_DIR), + default=thisdir, ) parser.add_argument( "--trick_dir", @@ -155,38 +117,20 @@ def parse_args() -> argparse.Namespace: "behavior, where sims and the Trick install share one tree).", default=None, ) - parser.add_argument( - "--quiet", - action="store_true", - help="Suppress progress bars (automatically set to True if environment variable CI is present).", - ) - parser.add_argument( - "--cpus", - type=int, - default=os.cpu_count() or 8, - help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " - "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.", - ) - parser.add_argument( - "--config_file", - type=str, - help="Run configuration file to use, relative to trick_top_level", - default="test_sims.yml", + parser.add_argument( "--quiet", action="store_true", help="Suppress progress bars (automatically set to True if environment variable CI is present).") + parser.add_argument( "--cpus", type=int, default=(os.cpu_count() if os.cpu_count() is not None else 8), + help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " + "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.") + parser.add_argument( "--config_file", type=str, help="Run configuration file to use, relative to trick_top_level", default="test_sims.yml") + + myargs = parser.parse_args() + should_be_quiet = myargs.quiet or os.getenv('CI') is not None + sys.exit( + SimTestWorkflow( + quiet=should_be_quiet, + trick_top_level=myargs.trick_top_level, + trick_dir=myargs.trick_dir, + cpus=myargs.cpus, + config_file=myargs.config_file, + ).run() ) - return parser.parse_args() - - -def main() -> int: - args = parse_args() - quiet = args.quiet or os.getenv("CI") is not None - return SimTestWorkflow( - quiet=quiet, - trick_top_level=args.trick_top_level, - trick_dir=args.trick_dir, - cpus=args.cpus, - config_file=args.config_file, - ).run() - - -if __name__ == "__main__": - raise SystemExit(main()) From d8d3d37c493d0db99ef91f12fab21cce113e74d7 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Fri, 17 Jul 2026 17:51:08 -0500 Subject: [PATCH 27/28] ruff: lint --- trickops.py | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/trickops.py b/trickops.py index a944d7cb8..7175513e6 100644 --- a/trickops.py +++ b/trickops.py @@ -1,21 +1,24 @@ import argparse import os import sys +from pathlib import Path -thisdir = os.path.abspath(os.path.dirname(os.path.realpath(__file__))) -sys.path.append(os.path.join(thisdir,"share/trick/trickops")) +sys.path.append(str(Path(__file__).resolve().parent / "share" / "trick" / "trickops")) -from TrickWorkflow import * +from TrickWorkflow import TrickWorkflow from WorkflowCommon import Job +thisdir = Path(__file__).resolve().parent + max_retries = 5 class SimTestWorkflow(TrickWorkflow): def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): self.cpus = cpus + trick_top_level = Path(trick_top_level) + # Create the trick_test directory if it doesn't already exist - if not os.path.exists(trick_top_level + "/trick_test"): - os.makedirs(trick_top_level + "/trick_test") + (trick_top_level / "trick_test").mkdir(exist_ok=True) # trick_dir defaults to trick_top_level (historical behavior: sims and # the installed Trick used by bin/trick-CP live in the same in-source @@ -25,14 +28,16 @@ def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): # out-of-source CMake install instead. if trick_dir is None: trick_dir = trick_top_level + else: + trick_dir = Path(trick_dir) # Base Class initialize, this creates internal management structures TrickWorkflow.__init__( self, - project_top_level=(trick_top_level), - log_dir=(trick_top_level + "/trickops_logs/"), - trick_dir=trick_dir, - config_file=(trick_top_level + "/" + config_file), + project_top_level=str(trick_top_level), + log_dir=str(trick_top_level / "trickops_logs"), + trick_dir=str(trick_dir), + config_file=str(trick_top_level / config_file), cpus=self.cpus, quiet=quiet, ) @@ -55,7 +60,7 @@ def run( self ): phases = [-1, 0, 1, 2, 3] analysis_jobs = self.get_jobs(kind='analyze') - if platform == "darwin": + if self.platform == "darwin": for job in build_jobs: if job.name == "Build test/SIM_trickified_shared" : print("REMOVING JOB: " + job.name) @@ -67,7 +72,7 @@ def run( self ): run_status = 0 for phase in phases: run_jobs = self.get_jobs(kind='run', phase=phase) - if platform == "darwin": + if self.platform == "darwin": for job in run_jobs: if job.name == "Run test/SIM_trickified_shared RUN_test/unit_test.py" : print("REMOVING JOB: " + job.name) @@ -94,7 +99,7 @@ def run( self ): numspaces = int((120 - 20 - len(header))/2 -2) print("*"*10, " "*numspaces, header, " "*numspaces, "*"*10,) print ("*"*120) - print(open(job.log_file, "r").read()) + print(Path(job.log_file).read_text()) print ("*"*120, "\n\n\n") return (builds_status or run_status or len(self.config_errors) > 0 or comparison_result or analysis_status) @@ -107,7 +112,7 @@ def run( self ): type=str, help="Path to the project tree containing the sim " "directories referenced by --config_file", - default=thisdir, + default=str(thisdir), ) parser.add_argument( "--trick_dir", From 86bb385d1c3a093b65de07f937aa7763759d9fe8 Mon Sep 17 00:00:00 2001 From: brendan fattig Date: Fri, 17 Jul 2026 17:58:47 -0500 Subject: [PATCH 28/28] ruff format --- share/trick/trickops/WorkflowCommon.py | 383 ++++++++++++++++--------- trickops.py | 187 +++++++----- 2 files changed, 366 insertions(+), 204 deletions(-) diff --git a/share/trick/trickops/WorkflowCommon.py b/share/trick/trickops/WorkflowCommon.py index 7cd58170e..71e446fea 100644 --- a/share/trick/trickops/WorkflowCommon.py +++ b/share/trick/trickops/WorkflowCommon.py @@ -6,20 +6,39 @@ """ import argparse -import curses, textwrap -import pdb, sys, datetime, time, socket, stat -import subprocess, signal, logging -import os, re, collections -import multiprocessing, platform +import collections +import curses +import datetime +import logging +import multiprocessing +import os +import pdb +import platform +import re +import signal +import socket +import stat +import subprocess +import sys +import textwrap +import time from contextlib import contextmanager -from ColorStr import ColorStr from pathlib import Path +from ColorStr import ColorStr + # Create a global color printer printer = ColorStr() + # UTILITY FUNCTIONS -def run_subprocess(command, m_shell=False, m_cwd=None, m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE): +def run_subprocess( + command, + m_shell=False, + m_cwd=None, + m_stdout=subprocess.PIPE, + m_stderr=subprocess.PIPE, +): """ Utility method for running a subprocess and returning stdout, stderr, and return code @@ -48,15 +67,18 @@ def run_subprocess(command, m_shell=False, m_cwd=None, m_stdout=subprocess.PIPE, Collection of process's final exit code, stdout, and stderr """ result = collections.namedtuple("Process", ["code", "stdout", "stderr"]) - p = subprocess.Popen(command, shell=m_shell, cwd=m_cwd, stdout=m_stdout, stderr=m_stderr) + p = subprocess.Popen( + command, shell=m_shell, cwd=m_cwd, stdout=m_stdout, stderr=m_stderr + ) result.stdout, result.stderr = p.communicate() - if (result.stdout != None): - result.stdout = result.stdout.decode(errors='ignore') - if (result.stderr != None): - result.stderr = result.stderr.decode(errors='ignore') + if result.stdout != None: + result.stdout = result.stdout.decode(errors="ignore") + if result.stderr != None: + result.stderr = result.stderr.decode(errors="ignore") result.code = p.returncode return result + def validate_output_file(filename): """ Utility method for validating a file intended to contain some TBD content @@ -80,9 +102,10 @@ def validate_output_file(filename): filename = os.path.abspath(filename) os.makedirs(os.path.dirname(filename), exist_ok=True) Path(filename).touch(exist_ok=True) - return (filename) + return filename + -def tprint(line, color='ENDC', verbosity='INFO'): +def tprint(line, color="ENDC", verbosity="INFO"): """ Utility method for writing text to both the log and stdout, with optional color for stdout @@ -95,18 +118,19 @@ def tprint(line, color='ENDC', verbosity='INFO'): Color/style of text. Options are listed in Colorstr() class. """ colorLine = printer.colorstr(line, color) - sys.stdout.write(colorLine+"\n") - if verbosity == 'INFO': + sys.stdout.write(colorLine + "\n") + if verbosity == "INFO": logging.info(line) - elif verbosity == 'DEBUG': + elif verbosity == "DEBUG": logging.debug(line) - elif verbosity == 'ERROR': + elif verbosity == "ERROR": logging.error(line) - elif verbosity == 'WARNING': + elif verbosity == "WARNING": logging.warning(line) else: pass + def create_progress_bar(fraction, text): """ Utility method that creates a text-based progress bar @@ -127,12 +151,13 @@ def create_progress_bar(fraction, text): str A text-based progress bar string of length 80 """ - text = ' ' + text + ' ' + text = " " + text + " " length = len(text) - bar = list('[{0:<78}]'.format('=' * min(78, int(round(fraction * 78))))) + bar = list("[{0:<78}]".format("=" * min(78, int(round(fraction * 78))))) index = int((len(bar) - length) / 2) bar[index : index + length] = text - return ''.join(bar) + return "".join(bar) + def sanitize_cpus(num_cpus, num_tasks, fallback_cpus): """ @@ -161,24 +186,25 @@ def sanitize_cpus(num_cpus, num_tasks, fallback_cpus): - a description of the boundary violated (may be None) """ if num_cpus < 1: - return (1, 'minimum allowable value') + return (1, "minimum allowable value") else: try: maximum = multiprocessing.cpu_count() - source = 'number of logical CPUs' + source = "number of logical CPUs" except: maximum = 16 - source = 'maximum allowed value' + source = "maximum allowed value" if num_tasks < maximum: maximum = num_tasks - source = 'number of runs' + source = "number of runs" if num_cpus > maximum: return (maximum, source) return (num_cpus, None) + def unixify_string(string): """ Sanitizes a string making it nice for unix by processing special characters. @@ -196,7 +222,8 @@ def unixify_string(string): str the sanitized string """ - return re.sub("['/]", '_', re.sub('[()!?,]', '', string)).replace(' ', '_') + return re.sub("['/]", "_", re.sub("[()!?,]", "", string)).replace(" ", "_") + # CLASSES class Job(object): @@ -205,11 +232,12 @@ class Job(object): for getting status information. More specific types of Jobs should inherit from this base clasee. """ - enums = ['NOT_STARTED', 'RUNNING', 'SUCCESS', 'FAILED', 'TIMEOUT'] - Status = collections.namedtuple('Status', enums)(*(range(len(enums)))) - _success_progress_bar = create_progress_bar(1, 'Success') - _failed_progress_bar = create_progress_bar(1, 'Failed') + enums = ["NOT_STARTED", "RUNNING", "SUCCESS", "FAILED", "TIMEOUT"] + Status = collections.namedtuple("Status", enums)(*(range(len(enums)))) + + _success_progress_bar = create_progress_bar(1, "Success") + _failed_progress_bar = create_progress_bar(1, "Failed") def _translate_status(self): """ @@ -217,12 +245,20 @@ def _translate_status(self): codes if FAIL. """ text, color = { - Job.Status.NOT_STARTED: ('NOT RUN', 'DARK_YELLOW'), - Job.Status.SUCCESS: ('OK', 'DARK_GREEN'), - Job.Status.FAILED: ('FAIL:' + str(self._expected_exit_status) + '/' + - str(self._exit_status), 'DARK_RED'), - Job.Status.TIMEOUT: ('TIMEOUT after ' + str(self._timeout) + ' seconds', 'YELLOW') - }[self.get_status()] + Job.Status.NOT_STARTED: ("NOT RUN", "DARK_YELLOW"), + Job.Status.SUCCESS: ("OK", "DARK_GREEN"), + Job.Status.FAILED: ( + "FAIL:" + + str(self._expected_exit_status) + + "/" + + str(self._exit_status), + "DARK_RED", + ), + Job.Status.TIMEOUT: ( + "TIMEOUT after " + str(self._timeout) + " seconds", + "YELLOW", + ), + }[self.get_status()] return printer.colorstr(text, color) def __init__(self, name, command, log_file, expected_exit_status=0): @@ -315,7 +351,11 @@ def get_status(self): if self._stop_time is None: self._stop_time = time.time() - return self.Status.SUCCESS if self._exit_status is self._expected_exit_status else self.Status.FAILED + return ( + self.Status.SUCCESS + if self._exit_status is self._expected_exit_status + else self.Status.FAILED + ) def get_expected_exit_status(self): return self._expected_exit_status @@ -361,7 +401,7 @@ def _not_started_string(self): str A string to be displayed when in the NOT_STARTED state. """ - return 'Not started yet' + return "Not started yet" def _running_string(self): """ @@ -372,8 +412,7 @@ def _running_string(self): str A string to be displayed when in the RUNNING state. """ - return 'Elapsed Time: {0:7.1f} sec'.format( - time.time() - self._start_time) + return "Elapsed Time: {0:7.1f} sec".format(time.time() - self._start_time) def _success_string(self): """ @@ -418,11 +457,9 @@ def _done_string(self): str A string to be displayed when this Job is done. """ - return 'Total Time: {0:7.1f} sec'.format( - self._stop_time - self._start_time) - + return "Total Time: {0:7.1f} sec".format(self._stop_time - self._start_time) - def report( self, indent='', width=100): + def report(self, indent="", width=100): """ Return a colored report string for this job. @@ -431,17 +468,27 @@ def report( self, indent='', width=100): str A olored report string """ - if self.get_status() == Job.Status.NOT_STARTED: - prepend = printer.colorstr('NOT RUN ', 'DARK_YELLOW') - elif self.get_expected_exit_status() == self.get_exit_status(): - prepend = printer.colorstr('OK ', 'DARK_GREEN') + if self.get_status() == Job.Status.NOT_STARTED: + prepend = printer.colorstr("NOT RUN ", "DARK_YELLOW") + elif self.get_expected_exit_status() == self.get_exit_status(): + prepend = printer.colorstr("OK ", "DARK_GREEN") else: - prepend = printer.colorstr('FAIL:%-10s ' % - (str(self.get_exit_status())+'/'+str(self.get_expected_exit_status())) , 'DARK_RED') - reportStr = indent + prepend + (" %-s" % ( textwrap.shorten(self.name, width=width-10))) + prepend = printer.colorstr( + "FAIL:%-10s " + % ( + str(self.get_exit_status()) + + "/" + + str(self.get_expected_exit_status()) + ), + "DARK_RED", + ) + reportStr = ( + indent + + prepend + + (" %-s" % (textwrap.shorten(self.name, width=width - 10))) + ) return reportStr - def die(self): """ Immediately kill this Job and all processes in its process group @@ -454,10 +501,12 @@ def die(self): pass self._close_files() + class FileSizeJob(Job): """ A build job is a subclass of Job() which estimates its progress via file size. """ + def __init__(self, name, command, log_file, size): """ Initialize this instance. @@ -481,20 +530,27 @@ def get_status_string_line_count(self): return super(FileSizeJob, self).get_status_string_line_count() + 1 def _not_started_string(self): - return super(FileSizeJob, self)._not_started_string() + '\n' + return super(FileSizeJob, self)._not_started_string() + "\n" def _running_string(self): progress = os.path.getsize(self._log_file.name) / float(self.size) - return (super(FileSizeJob, self)._running_string() + '\n' + - create_progress_bar(progress, '{0:.1f}%'.format(100 * progress))) + return ( + super(FileSizeJob, self)._running_string() + + "\n" + + create_progress_bar(progress, "{0:.1f}%".format(100 * progress)) + ) def _success_string(self): - return (super(FileSizeJob, self)._success_string() + '\n' + - self._success_progress_bar) + return ( + super(FileSizeJob, self)._success_string() + + "\n" + + self._success_progress_bar + ) def _failed_string(self): - return (super(FileSizeJob, self)._failed_string() + '\n' + - self._failed_progress_bar) + return ( + super(FileSizeJob, self)._failed_string() + "\n" + self._failed_progress_bar + ) class WorkflowCommon: @@ -506,7 +562,15 @@ class WorkflowCommon: * Provide log directory where output for jobs can be written * Get installed packages, host, and other OS information """ - def __init__( self, project_top_level, log_dir='/tmp/', log_level=logging.DEBUG, env='', quiet=False ): + + def __init__( + self, + project_top_level, + log_dir="/tmp/", + log_level=logging.DEBUG, + env="", + quiet=False, + ): """ Initialize this instance. @@ -529,8 +593,14 @@ def __init__( self, project_top_level, log_dir='/tmp/', log_level=logging.DEBUG, self.project_top_level = project_top_level self.log_dir = log_dir # Where all logged output will go # self.log is where all logging for python script layer goes - self.log = validate_output_file( os.path.join(self.log_dir,'log.' + - unixify_string(str(datetime.datetime.now()).replace(':','-')) + '.txt')) + self.log = validate_output_file( + os.path.join( + self.log_dir, + "log." + + unixify_string(str(datetime.datetime.now()).replace(":", "-")) + + ".txt", + ) + ) self.env = env # Project environment literal string, e.g. "source bashrc" self.creation_time = time.time() # When this instance was created self.host_name = socket.gethostname() @@ -542,7 +612,7 @@ def __init__( self, project_top_level, log_dir='/tmp/', log_level=logging.DEBUG, else: self.quiet = True logging.basicConfig(filename=self.log, level=log_level) - os.chdir(self.project_top_level) # Automatically chdir to top of project + os.chdir(self.project_top_level) # Automatically chdir to top of project def _cleanup(self): """ @@ -568,20 +638,28 @@ def get_installed_packages(self, verbose=False): A list of installed packages, or None if they cannot be determined """ self.this_os = self.get_operating_system() - if self.this_os == 'debian': + if self.this_os == "debian": list_cmd = "dpkg-query -W -f='${binary:Package}\n'" - elif self.this_os == 'redhat': + elif self.this_os == "redhat": list_cmd = "rpm -qa " else: return None - result = run_subprocess(command=list_cmd, m_shell=True, - m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE) + result = run_subprocess( + command=list_cmd, + m_shell=True, + m_stdout=subprocess.PIPE, + m_stderr=subprocess.PIPE, + ) if result.code != 0: if verbose: - tprint("ERROR: Trouble finding platform packages, error:\n" + result.stderr, 'DARK_RED') + tprint( + "ERROR: Trouble finding platform packages, error:\n" + + result.stderr, + "DARK_RED", + ) return None - return(result.stdout.strip().split("\n")) + return result.stdout.strip().split("\n") def get_operating_system(self): """ @@ -593,26 +671,34 @@ def get_operating_system(self): Returns one of these strings: debian, redhat, windows, darwin, unknown """ # Windows is easy - if platform.system().lower() == 'windows': - return 'windows' + if platform.system().lower() == "windows": + return "windows" # Darwin (Mac OSX) is easy - if platform.system().lower() == 'darwin': - return 'darwin' + if platform.system().lower() == "darwin": + return "darwin" # Linux is hard # NOTE: sys.platform will not distinguish between flavors of linux and platform.linux_distribution() # doesn't exist on all linux distributions! Also we can't rely on 'import distro' because it's # only pip-installable at the moment. -Jordan 2/2021 # Determine platform the old fashioned way, by seeing what package manager exists - rpm_exist = not run_subprocess(command='which rpm', m_shell=True, - m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE).code - apt_exist = not run_subprocess(command='which apt', m_shell=True, - m_stdout=subprocess.PIPE, m_stderr=subprocess.PIPE).code + rpm_exist = not run_subprocess( + command="which rpm", + m_shell=True, + m_stdout=subprocess.PIPE, + m_stderr=subprocess.PIPE, + ).code + apt_exist = not run_subprocess( + command="which apt", + m_shell=True, + m_stdout=subprocess.PIPE, + m_stderr=subprocess.PIPE, + ).code if rpm_exist: - return 'redhat' + return "redhat" elif apt_exist: - return 'debian' + return "debian" else: - return 'unknown' + return "unknown" def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None): """ @@ -633,14 +719,16 @@ def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None) True if any job failed or was not run. False if all jobs completed successfully. """ - if not os.environ.get('TERM') and not self.quiet: + if not os.environ.get("TERM") and not self.quiet: tprint( - 'The TERM environment variable must be set when\n' - 'TrickWorkflow.quiet is False. This is usually set by one\n' - "of the shell's configuration files (.profile, .cshrc, etc).\n" - 'However, if this was executed via a non-interactive,\n' - "non-login shell (for instance: ssh ''), it\n" - 'may not be automatically set.', 'DARK_RED') + "The TERM environment variable must be set when\n" + "TrickWorkflow.quiet is False. This is usually set by one\n" + "of the shell's configuration files (.profile, .cshrc, etc).\n" + "However, if this was executed via a non-interactive,\n" + "non-login shell (for instance: ssh ''), it\n" + "may not be automatically set.", + "DARK_RED", + ) return True num_jobs = len(jobs) @@ -648,14 +736,16 @@ def execute_jobs(self, jobs, max_concurrent=None, header=None, job_timeout=None) max_concurrent = num_jobs if header: - header += '\n' + header += "\n" else: - header = '' + header = "" header += ( - 'Executing {0} total jobs, running up to {1} simultaneously.\n' - .format(num_jobs, max_concurrent) + - 'Press CTRL+C to terminate early.\n') + "Executing {0} total jobs, running up to {1} simultaneously.\n".format( + num_jobs, max_concurrent + ) + + "Press CTRL+C to terminate early.\n" + ) logging.info(header) @@ -700,7 +790,7 @@ def execute(stdscr=None): # extra lines, so pick a realy big width that isn't # likely to cause wrapping. We also need a final # additional line for the cursor to end on. - header_pad = curses.newpad(header.count('\n') + 1, 1000) + header_pad = curses.newpad(header.count("\n") + 1, 1000) header_pad.addstr(header) # Create a pad for the status. @@ -710,9 +800,11 @@ def execute(stdscr=None): # + a blank line after each status string # + a final line for the cursor to end on status_pad = curses.newpad( - sum(job.get_status_string_line_count() for job in jobs) + - 2 * len(jobs) + 1, - 1000) + sum(job.get_status_string_line_count() for job in jobs) + + 2 * len(jobs) + + 1, + 1000, + ) # The top visible status pad line. # Used for scrolling. @@ -720,24 +812,28 @@ def execute(stdscr=None): header_height = header_pad.getmaxyx()[0] status_height = status_pad.getmaxyx()[0] - while any(job.get_status() in - [job.Status.NOT_STARTED, job.Status.RUNNING] - for job in jobs): - + while any( + job.get_status() in [job.Status.NOT_STARTED, job.Status.RUNNING] + for job in jobs + ): # Start waiting jobs if cpus are available - waitingJobs = [job for job in jobs - if job.get_status() is job.Status.NOT_STARTED] + waitingJobs = [ + job for job in jobs if job.get_status() is job.Status.NOT_STARTED + ] if waitingJobs: - available_cpus = max_concurrent - sum(1 for job in jobs - if job.get_status() is job.Status.RUNNING) + available_cpus = max_concurrent - sum( + 1 for job in jobs if job.get_status() is job.Status.RUNNING + ) for i in range(min(len(waitingJobs), available_cpus)): waitingJobs[i].start() if job_timeout is not None: # Check if any jobs have timed out - runningJobs = [job for job in jobs if job.get_status() is job.Status.RUNNING] + runningJobs = [ + job for job in jobs if job.get_status() is job.Status.RUNNING + ] for runningJob in runningJobs: if time.time() - runningJob._start_time > job_timeout: runningJob._timeout = job_timeout @@ -749,9 +845,12 @@ def execute(stdscr=None): status_pad.erase() for i, job in enumerate(jobs): # print the name - status_pad.addstr('Job {0:{width}d}/{1}: '.format( - i + 1, num_jobs, width=len(str(num_jobs)))) - status_pad.addstr(job.name + '\n', curses.A_BOLD) + status_pad.addstr( + "Job {0:{width}d}/{1}: ".format( + i + 1, num_jobs, width=len(str(num_jobs)) + ) + ) + status_pad.addstr(job.name + "\n", curses.A_BOLD) # print the status string if use_colors: @@ -763,11 +862,9 @@ def execute(stdscr=None): color = curses.color_pair(2) else: color = curses.color_pair(0) - status_pad.addstr( - job.get_status_string() + '\n\n', color) + status_pad.addstr(job.get_status_string() + "\n\n", color) else: - status_pad.addstr( - job.get_status_string() + '\n\n') + status_pad.addstr(job.get_status_string() + "\n\n") # handle scrolling while True: @@ -783,9 +880,12 @@ def execute(stdscr=None): # prevent scrolling beyond the bounds of status_pad screen_height, screen_width = stdscr.getmaxyx() top_line = max( - 0, - min(top_line, - status_height - 2 - (screen_height - header_height))) + 0, + min( + top_line, + status_height - 2 - (screen_height - header_height), + ), + ) # Resizing the terminal can cause the actual # screen width or height to become smaller than @@ -799,10 +899,16 @@ def execute(stdscr=None): # have and ignore errors. try: header_pad.noutrefresh( - 0, 0, 0, 0, screen_height - 1, screen_width - 1) + 0, 0, 0, 0, screen_height - 1, screen_width - 1 + ) status_pad.noutrefresh( - top_line, 0, header_height, 0, - screen_height - 1, screen_width - 1) + top_line, + 0, + header_height, + 0, + screen_height - 1, + screen_width - 1, + ) except curses.error: pass curses.doupdate() @@ -826,36 +932,43 @@ def execute(stdscr=None): execute() except BaseException as exception: - logging.exception('') + logging.exception("") tprint( - 'An exception occurred. See the log for details.\n\n' - ' ' + repr(exception) + "\n\n" - 'Terminating all jobs. Please wait for cleanup to finish. ' - 'CTRL+C may leave orphaned processes.', 'DARK_RED', - 'ERROR') + "An exception occurred. See the log for details.\n\n" + " " + repr(exception) + "\n\n" + "Terminating all jobs. Please wait for cleanup to finish. " + "CTRL+C may leave orphaned processes.", + "DARK_RED", + "ERROR", + ) # kill all the jobs for job in jobs: job.die() - tprint('All jobs terminated.\n', 'DARK_RED') + tprint("All jobs terminated.\n", "DARK_RED") # print summary - summary = 'Job Summary\n' + summary = "Job Summary\n" for i, job in enumerate(jobs): - summary += 'Job {0:{width}d}/{1}: {2}\n{3}\n'.format( - i + 1, num_jobs, job.name, job.get_status_string(), - width=len(str(num_jobs))) + summary += "Job {0:{width}d}/{1}: {2}\n{3}\n".format( + i + 1, + num_jobs, + job.name, + job.get_status_string(), + width=len(str(num_jobs)), + ) logging.info(summary) for job in jobs: text, color = { - job.Status.NOT_STARTED: ('was not run', 'GREY40'), - job.Status.SUCCESS: ('succeeded', 'DARK_GREEN'), - job.Status.FAILED: ('failed', 'DARK_RED'), - job.Status.TIMEOUT: ('timed out', 'YELLOW') }[job.get_status()] + job.Status.NOT_STARTED: ("was not run", "GREY40"), + job.Status.SUCCESS: ("succeeded", "DARK_GREEN"), + job.Status.FAILED: ("failed", "DARK_RED"), + job.Status.TIMEOUT: ("timed out", "YELLOW"), + }[job.get_status()] - text = job.name + ' ' + text + text = job.name + " " + text # Print the summary status even if self.quiet is True tprint(text, color) diff --git a/trickops.py b/trickops.py index 7175513e6..77ea582d7 100644 --- a/trickops.py +++ b/trickops.py @@ -12,6 +12,7 @@ max_retries = 5 + class SimTestWorkflow(TrickWorkflow): def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): self.cpus = cpus @@ -41,72 +42,107 @@ def __init__(self, quiet, trick_top_level, cpus, config_file, trick_dir=None): cpus=self.cpus, quiet=quiet, ) - def run( self ): - build_jobs = self.get_jobs(kind='build') - - # This is awful but I can't think of another way around it - # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux - # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? - import signal - run_names = ["Run test/SIM_test_varserv RUN_test/err1_test.py", "Run test/SIM_test_varserv RUN_test/err2_test.py"] - for job in [job for job in self.get_jobs(kind='run') if job.name in run_names]: - job._expected_exit_status = signal.SIGUSR1.value - - # Several test sims have runs that require ordering via phases: - # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run - # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs - # - SIM_test_varserver has 3 runs that cannot be concurrent - # - SIM_mc_generation generates runs and then runs them - phases = [-1, 0, 1, 2, 3] - - analysis_jobs = self.get_jobs(kind='analyze') - if self.platform == "darwin": - for job in build_jobs: - if job.name == "Build test/SIM_trickified_shared" : - print("REMOVING JOB: " + job.name) - build_jobs.remove(job) - builds_status = self.execute_jobs(build_jobs, max_concurrent=self.cpus, header='Executing all sim builds.') - - jobs = build_jobs - - run_status = 0 - for phase in phases: - run_jobs = self.get_jobs(kind='run', phase=phase) + + def run(self): + build_jobs = self.get_jobs(kind="build") + + # This is awful but I can't think of another way around it + # SIM_test_varserver has 2 tests that should return the code for SIGUSR1, the number is different on Mac vs Linux + # so it can't be hardcoded in the input yml file. Maybe this is a case having a label on a run would be cleaner? + import signal + + run_names = [ + "Run test/SIM_test_varserv RUN_test/err1_test.py", + "Run test/SIM_test_varserv RUN_test/err2_test.py", + ] + for job in [job for job in self.get_jobs(kind="run") if job.name in run_names]: + job._expected_exit_status = signal.SIGUSR1.value + + # Several test sims have runs that require ordering via phases: + # - SIM_stls dumps a checkpoint that is then read in and checked by a subsequent run + # - SIM_checkpoint_data_recording dumps checkpoints that are read by subsequent runs + # - SIM_test_varserver has 3 runs that cannot be concurrent + # - SIM_mc_generation generates runs and then runs them + phases = [-1, 0, 1, 2, 3] + + analysis_jobs = self.get_jobs(kind="analyze") if self.platform == "darwin": - for job in run_jobs: - if job.name == "Run test/SIM_trickified_shared RUN_test/unit_test.py" : - print("REMOVING JOB: " + job.name) - run_jobs.remove(job) - this_status = self.execute_jobs(run_jobs, max_concurrent=self.cpus, header="Executing phase " + str(phase) + " runs.", job_timeout=1000) - run_status = run_status or this_status - jobs += run_jobs - - comparison_result = self.compare() - analysis_status = self.execute_jobs(analysis_jobs, max_concurrent=self.cpus, header='Executing all analysis.') - - self.report() # Print Verbose report - self.status_summary() # Print a Succinct summary - - # Dump failing logs - for job in jobs: - if job.get_status() == Job.Status.FAILED or job.get_status() == Job.Status.TIMEOUT: - print ("*"*120) - if job.get_status() == Job.Status.FAILED: - header = "Failing job: " + job.name - else: - header = "Timed out job: " + job.name - - numspaces = int((120 - 20 - len(header))/2 -2) - print("*"*10, " "*numspaces, header, " "*numspaces, "*"*10,) - print ("*"*120) - print(Path(job.log_file).read_text()) - print ("*"*120, "\n\n\n") - - return (builds_status or run_status or len(self.config_errors) > 0 or comparison_result or analysis_status) + for job in build_jobs: + if job.name == "Build test/SIM_trickified_shared": + print("REMOVING JOB: " + job.name) + build_jobs.remove(job) + builds_status = self.execute_jobs( + build_jobs, max_concurrent=self.cpus, header="Executing all sim builds." + ) + + jobs = build_jobs + + run_status = 0 + for phase in phases: + run_jobs = self.get_jobs(kind="run", phase=phase) + if self.platform == "darwin": + for job in run_jobs: + if ( + job.name + == "Run test/SIM_trickified_shared RUN_test/unit_test.py" + ): + print("REMOVING JOB: " + job.name) + run_jobs.remove(job) + this_status = self.execute_jobs( + run_jobs, + max_concurrent=self.cpus, + header="Executing phase " + str(phase) + " runs.", + job_timeout=1000, + ) + run_status = run_status or this_status + jobs += run_jobs + + comparison_result = self.compare() + analysis_status = self.execute_jobs( + analysis_jobs, max_concurrent=self.cpus, header="Executing all analysis." + ) + + self.report() # Print Verbose report + self.status_summary() # Print a Succinct summary + + # Dump failing logs + for job in jobs: + if ( + job.get_status() == Job.Status.FAILED + or job.get_status() == Job.Status.TIMEOUT + ): + print("*" * 120) + if job.get_status() == Job.Status.FAILED: + header = "Failing job: " + job.name + else: + header = "Timed out job: " + job.name + + numspaces = int((120 - 20 - len(header)) / 2 - 2) + print( + "*" * 10, + " " * numspaces, + header, + " " * numspaces, + "*" * 10, + ) + print("*" * 120) + print(Path(job.log_file).read_text()) + print("*" * 120, "\n\n\n") + + return ( + builds_status + or run_status + or len(self.config_errors) > 0 + or comparison_result + or analysis_status + ) + if __name__ == "__main__": - parser = argparse.ArgumentParser(description='Build, run, and compare all test sims for Trick', - formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser = argparse.ArgumentParser( + description="Build, run, and compare all test sims for Trick", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) parser.add_argument( "--trick_top_level", type=str, @@ -122,14 +158,27 @@ def run( self ): "behavior, where sims and the Trick install share one tree).", default=None, ) - parser.add_argument( "--quiet", action="store_true", help="Suppress progress bars (automatically set to True if environment variable CI is present).") - parser.add_argument( "--cpus", type=int, default=(os.cpu_count() if os.cpu_count() is not None else 8), - help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " - "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.") - parser.add_argument( "--config_file", type=str, help="Run configuration file to use, relative to trick_top_level", default="test_sims.yml") + parser.add_argument( + "--quiet", + action="store_true", + help="Suppress progress bars (automatically set to True if environment variable CI is present).", + ) + parser.add_argument( + "--cpus", + type=int, + default=(os.cpu_count() if os.cpu_count() is not None else 8), + help="Number of cpus to use for testing. For builds this number is used for MAKEFLAGS *and* number of " + "concurrent builds (cpus^2). For sim runs this controls the maximum number of simultaneous runs.", + ) + parser.add_argument( + "--config_file", + type=str, + help="Run configuration file to use, relative to trick_top_level", + default="test_sims.yml", + ) myargs = parser.parse_args() - should_be_quiet = myargs.quiet or os.getenv('CI') is not None + should_be_quiet = myargs.quiet or os.getenv("CI") is not None sys.exit( SimTestWorkflow( quiet=should_be_quiet,