From a14df03f1bc6349f5dd7221ec2b85f4be725c8b0 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 15 Sep 2026 15:18:43 -0700 Subject: [PATCH 1/4] Install the binaries to a prefix instead of running from the build tree --- CMakeLists.txt | 2 ++ README.md | 19 ++++++++++++++++--- camerad/CMakeLists.txt | 2 ++ emulator/CMakeLists.txt | 2 ++ utils/CMakeLists.txt | 5 +++++ 5 files changed, 27 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34ce0d6..6ccb33a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,8 @@ set(LIBRARY_OUTPUT_PATH ${PROJECT_BASE_DIR}/lib) find_package(Threads) find_package(nlohmann_json REQUIRED) +include(GNUInstallDirs) + # Declared here so the static libs get PIC, which ELF needs for a shared module option(BUILD_PYTHON_MODULE "Build the camera_interface Python module" OFF) if (BUILD_PYTHON_MODULE) diff --git a/README.md b/README.md index 46fd110..3c29aec 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,20 @@ If you encounter any problems or have questions about this project, please open $ make ``` -5. **Run the Camera Server:** +5. **(Optional) Install:** `make install` places `camerad`, `emulator`, + `socksend`, `listener` and, with `-DENABLE_SHM_OUTPUT=ON`, `shm_reader` + under `${CMAKE_INSTALL_PREFIX}/bin`, which defaults to `/usr/local`: + + ```bash + $ cmake -DCONTROLLER=archon -DCMAKE_INSTALL_PREFIX=$HOME/.local .. + $ make && make install + ``` + + Without this the binaries are only ever run from `bin/` in the source tree, + so a rebuild replaces whatever is deployed and there is no way to keep two + versions or to tell which one is running. + +6. **Run the Camera Server:** The configuration file is passed with `--config` and is required. @@ -102,7 +115,7 @@ If you encounter any problems or have questions about this project, please open Logging always goes to a daily file under `LOGPATH`. Whether it is also written to stderr follows `--foreground`, so an operator watching a console sees it and a daemon does not duplicate its whole log into the stderr redirect. `LOG_STDERR` in the `.cfg` overrides that either way. -6. **(Optional) Run the Archon Emulator:** +7. **(Optional) Run the Archon Emulator:** ```bash $ ../bin/emulator -i @@ -110,7 +123,7 @@ If you encounter any problems or have questions about this project, please open The emulator reads `EMULATOR_PORT` and `EMULATOR_SYSTEM` from the same `.cfg` the server uses, so point `ARCHON_IP`/`ARCHON_PORT` at it to run without hardware. `-i generic` suits the shipped test configs. -7. **(Optional) Run Unit Tests.** The tests are excluded from the default target, so build them first: +8. **(Optional) Run Unit Tests.** The tests are excluded from the default target, so build them first: ```bash $ make run_unit_tests diff --git a/camerad/CMakeLists.txt b/camerad/CMakeLists.txt index 7a3c112..714c2ff 100644 --- a/camerad/CMakeLists.txt +++ b/camerad/CMakeLists.txt @@ -186,6 +186,8 @@ target_link_libraries(camerad ${ZMQ_LIB} ) +install(TARGETS camerad RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + # ---------------------------------------------------------------------------- # optional Python module # ---------------------------------------------------------------------------- diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index 3949f79..736b86c 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -48,6 +48,8 @@ target_link_libraries(emulator ) target_include_directories(emulator PRIVATE ${CFITSIO_INCLUDE_DIRS}) target_link_directories(emulator PRIVATE ${CFITSIO_LIBRARY_DIRS}) + +install(TARGETS emulator RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) elseif( ${INTERFACE_TYPE} STREQUAL "AstroCam" ) message( STATUS "emulator not implemented for AstroCam" ) else() diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 97304e1..87dffba 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -74,3 +74,8 @@ add_executable(listener add_executable(socksend ${PROJECT_UTILS_DIR}/sendcmd.cpp ) + +install(TARGETS listener socksend RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +if (ENABLE_SHM_OUTPUT) + install(TARGETS shm_reader RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +endif() From 2722017753d79e352dae79b375f022111fb29862 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 15 Sep 2026 15:50:12 -0700 Subject: [PATCH 2/4] Prefix installed tool binaries with camerad- and drop listener from install --- .github/workflows/emulator-integration.yml | 24 +++++++++++----------- README.md | 23 ++++++++++++++------- emulator/CMakeLists.txt | 3 +++ utils/CMakeLists.txt | 6 +++++- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/.github/workflows/emulator-integration.yml b/.github/workflows/emulator-integration.yml index 2fe6bdb..db1ce7f 100644 --- a/.github/workflows/emulator-integration.yml +++ b/.github/workflows/emulator-integration.yml @@ -56,12 +56,12 @@ jobs: run: | mkdir -p /tmp/ci_milk_shm /tmp/ci_fits_test - bin/emulator config/frame_outputs_test/frame_outputs_test.cfg -i generic & + bin/camerad-emulator config/frame_outputs_test/frame_outputs_test.cfg -i generic & sleep 2 bin/camerad --foreground --config config/frame_outputs_test/frame_outputs_test.cfg & sleep 3 - send() { bin/socksend -p 3131 -t 60 "$1"; } + send() { bin/camerad-socksend -p 3131 -t 60 "$1"; } send "open" send "load" @@ -74,11 +74,11 @@ jobs: # succeeded -- shm_reader's exit code and the FITS file's existence # are the real pass/fail signal sleep 1 - bin/shm_reader ci_frame_outputs_shm /tmp/ci_milk_shm + bin/camerad-shm-reader ci_frame_outputs_shm /tmp/ci_milk_shm ls /tmp/ci_fits_test/ci_frame_outputs_*.fits pkill -f 'bin/camerad' || true - pkill -f 'bin/emulator' || true + pkill -f 'bin/camerad-emulator' || true python-module-emulator-test: runs-on: ubuntu-latest @@ -132,7 +132,7 @@ jobs: run: | mkdir -p /tmp/ci_milk_shm /tmp/ci_fits_test - bin/emulator config/frame_outputs_test/frame_outputs_test.cfg -i generic & + bin/camerad-emulator config/frame_outputs_test/frame_outputs_test.cfg -i generic & sleep 2 # No camerad process here: the module owns the Archon connection, and @@ -141,7 +141,7 @@ jobs: --config config/frame_outputs_test/frame_outputs_test.cfg \ --fits-dir /tmp/ci_fits_test - pkill -f 'bin/emulator' || true + pkill -f 'bin/camerad-emulator' || true cryoscope-emulator-test: runs-on: ubuntu-latest @@ -179,12 +179,12 @@ jobs: - name: CryoScope synthetic test run: | - bin/emulator config/cryoscope/cryoscope.cfg -i generic & + bin/camerad-emulator config/cryoscope/cryoscope.cfg -i generic & sleep 2 bin/camerad --foreground --config config/cryoscope/cryoscope.cfg & sleep 3 - send() { bin/socksend -p 3031 -t 60 "$1"; } + send() { bin/camerad-socksend -p 3031 -t 60 "$1"; } send "open" send "load" @@ -195,7 +195,7 @@ jobs: echo "$resp" | grep -q "DONE" || exit 1 pkill -f 'bin/camerad' || true - pkill -f 'bin/emulator' || true + pkill -f 'bin/camerad-emulator' || true sleep 1 - name: CryoScope FITS playback test @@ -203,12 +203,12 @@ jobs: cp config/cryoscope/cryoscope.cfg /tmp/cryoscope_fits_test.cfg echo "EMULATOR_DATADIR=config/cryoscope" >> /tmp/cryoscope_fits_test.cfg - bin/emulator /tmp/cryoscope_fits_test.cfg -i generic & + bin/camerad-emulator /tmp/cryoscope_fits_test.cfg -i generic & sleep 2 bin/camerad --foreground --config /tmp/cryoscope_fits_test.cfg & sleep 3 - send() { bin/socksend -p 3031 -t 60 "$1"; } + send() { bin/camerad-socksend -p 3031 -t 60 "$1"; } send "open" send "load" @@ -220,4 +220,4 @@ jobs: echo "$resp" | grep -q "DONE" || exit 1 pkill -f 'bin/camerad' || true - pkill -f 'bin/emulator' || true + pkill -f 'bin/camerad-emulator' || true diff --git a/README.md b/README.md index 3c29aec..bf04aa9 100644 --- a/README.md +++ b/README.md @@ -82,18 +82,27 @@ If you encounter any problems or have questions about this project, please open $ make ``` -5. **(Optional) Install:** `make install` places `camerad`, `emulator`, - `socksend`, `listener` and, with `-DENABLE_SHM_OUTPUT=ON`, `shm_reader` - under `${CMAKE_INSTALL_PREFIX}/bin`, which defaults to `/usr/local`: +5. **(Optional) Install:** `make install` copies the binaries into + `${CMAKE_INSTALL_PREFIX}/bin`, which defaults to `/usr/local`: ```bash $ cmake -DCONTROLLER=archon -DCMAKE_INSTALL_PREFIX=$HOME/.local .. $ make && make install ``` - Without this the binaries are only ever run from `bin/` in the source tree, - so a rebuild replaces whatever is deployed and there is no way to keep two - versions or to tell which one is running. + | Binary | Built when | + |-----------------------|--------------------------| + | `camerad` | always | + | `camerad-socksend` | always | + | `camerad-emulator` | `-DINTERFACE_TYPE=Archon` (the default) | + | `camerad-shm-reader` | `-DENABLE_SHM_OUTPUT=ON` | + + The tools carry a `camerad-` prefix because names like `socksend` are too + generic for a directory shared with every other package. + + Without installing, the binaries are only ever run from `bin/` in the source + tree, so a rebuild replaces whatever is deployed and there is no way to keep + two versions or to tell which one is running. 6. **Run the Camera Server:** @@ -118,7 +127,7 @@ If you encounter any problems or have questions about this project, please open 7. **(Optional) Run the Archon Emulator:** ```bash - $ ../bin/emulator -i + $ ../bin/camerad-emulator -i ``` The emulator reads `EMULATOR_PORT` and `EMULATOR_SYSTEM` from the same `.cfg` the server uses, so point `ARCHON_IP`/`ARCHON_PORT` at it to run without hardware. `-i generic` suits the shipped test configs. diff --git a/emulator/CMakeLists.txt b/emulator/CMakeLists.txt index 736b86c..f447161 100644 --- a/emulator/CMakeLists.txt +++ b/emulator/CMakeLists.txt @@ -49,6 +49,9 @@ target_link_libraries(emulator target_include_directories(emulator PRIVATE ${CFITSIO_INCLUDE_DIRS}) target_link_directories(emulator PRIVATE ${CFITSIO_LIBRARY_DIRS}) +# Prefixed because "emulator" is too generic for a shared bin directory +set_target_properties(emulator PROPERTIES OUTPUT_NAME camerad-emulator) + install(TARGETS emulator RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) elseif( ${INTERFACE_TYPE} STREQUAL "AstroCam" ) message( STATUS "emulator not implemented for AstroCam" ) diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index 87dffba..5723f10 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -75,7 +75,11 @@ add_executable(socksend ${PROJECT_UTILS_DIR}/sendcmd.cpp ) -install(TARGETS listener socksend RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) +# Prefixed because these names are too generic for a shared bin directory +set_target_properties(socksend PROPERTIES OUTPUT_NAME camerad-socksend) + +install(TARGETS socksend RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) if (ENABLE_SHM_OUTPUT) + set_target_properties(shm_reader PROPERTIES OUTPUT_NAME camerad-shm-reader) install(TARGETS shm_reader RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() From ad21362c4c2f49c32e7c013bd4ca9c0c5387d771 Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 15 Sep 2026 16:57:32 -0700 Subject: [PATCH 3/4] Install the camera_interface module to the prefix lib directory --- README.md | 24 ++++++++++++++++-------- python/CMakeLists.txt | 2 ++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index bf04aa9..d10da43 100644 --- a/README.md +++ b/README.md @@ -82,24 +82,32 @@ If you encounter any problems or have questions about this project, please open $ make ``` -5. **(Optional) Install:** `make install` copies the binaries into - `${CMAKE_INSTALL_PREFIX}/bin`, which defaults to `/usr/local`: +5. **(Optional) Install:** `make install` copies what the build produced under + `${CMAKE_INSTALL_PREFIX}`, which defaults to `/usr/local`: ```bash $ cmake -DCONTROLLER=archon -DCMAKE_INSTALL_PREFIX=$HOME/.local .. $ make && make install ``` - | Binary | Built when | - |-----------------------|--------------------------| - | `camerad` | always | - | `camerad-socksend` | always | - | `camerad-emulator` | `-DINTERFACE_TYPE=Archon` (the default) | - | `camerad-shm-reader` | `-DENABLE_SHM_OUTPUT=ON` | + | Artifact | Installed to | Built when | + |----------------------|--------------|--------------------------| + | `camerad` | `bin` | always | + | `camerad-socksend` | `bin` | always | + | `camerad-emulator` | `bin` | `-DINTERFACE_TYPE=Archon` (the default) | + | `camerad-shm-reader` | `bin` | `-DENABLE_SHM_OUTPUT=ON` | + | `camera_interface` | `lib` | `-DBUILD_PYTHON_MODULE=ON` | The tools carry a `camerad-` prefix because names like `socksend` are too generic for a directory shared with every other package. + The Python module installs to `lib`, so importing it means putting that + directory on `PYTHONPATH`: + + ```bash + $ PYTHONPATH=$HOME/.local/lib python3 -c "import camera_interface" + ``` + Without installing, the binaries are only ever run from `bin/` in the source tree, so a rebuild replaces whatever is deployed and there is no way to keep two versions or to tell which one is running. diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 38e757c..07f4abb 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -59,3 +59,5 @@ target_link_libraries(camera_interface PRIVATE ${ZMQPP_LIB} ${ZMQ_LIB} ) + +install(TARGETS camera_interface LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) From f239d4d206b33ac47985d12c3b24836841d4e59d Mon Sep 17 00:00:00 2001 From: Mike Langmayr <1809691+mikelangmayr@users.noreply.github.com> Date: Tue, 15 Sep 2026 17:10:41 -0700 Subject: [PATCH 4/4] Verify make install in CI --- .github/workflows/emulator-integration.yml | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/emulator-integration.yml b/.github/workflows/emulator-integration.yml index db1ce7f..86be029 100644 --- a/.github/workflows/emulator-integration.yml +++ b/.github/workflows/emulator-integration.yml @@ -119,14 +119,14 @@ jobs: sudo make install sudo ldconfig - - name: Build the Python module and emulator + - name: Build everything run: | mkdir -p ${{github.workspace}}/build cd ${{github.workspace}}/build cmake -DCONTROLLER=archon -DINSTRUMENT=hispec_tracking_camera \ -DENABLE_SHM_OUTPUT=ON -DImageStreamIO_DIR=/usr/local/lib/cmake \ -DBUILD_PYTHON_MODULE=ON .. - make camera_interface emulator -j$(nproc) + make -j$(nproc) - name: Python module control test run: | @@ -143,6 +143,24 @@ jobs: pkill -f 'bin/camerad-emulator' || true + - name: Install to a prefix and verify + run: | + cmake --install build --prefix /tmp/camerad-prefix + + for tool in camerad camerad-socksend camerad-emulator camerad-shm-reader; do + test -x "/tmp/camerad-prefix/bin/$tool" || { echo "not installed: $tool"; exit 1; } + done + + # Run from /tmp so the build tree cannot satisfy the import and mask a + # missing install rule + cd /tmp + PYTHONPATH=/tmp/camerad-prefix/lib python3 - <<'PY' + import camera_interface as ci + assert ci.__file__.startswith("/tmp/camerad-prefix/lib/"), ci.__file__ + assert ci.instrument_name() == "hispec_tracking_camera", ci.instrument_name() + print("imported", ci.__file__) + PY + cryoscope-emulator-test: runs-on: ubuntu-latest timeout-minutes: 10