Index: lib/Comms/CMakeLists.txt =================================================================== diff -u -refc0e8ccb0ee99f25834eb5a19dee1cf2e50c532 -r4dccc470aedf6f68a0ad73c1101f8a96188cbdb1 --- lib/Comms/CMakeLists.txt (.../CMakeLists.txt) (revision efc0e8ccb0ee99f25834eb5a19dee1cf2e50c532) +++ lib/Comms/CMakeLists.txt (.../CMakeLists.txt) (revision 4dccc470aedf6f68a0ad73c1101f8a96188cbdb1) @@ -47,11 +47,10 @@ PUBLIC_HEADER "${INCLUDES}" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib" - ADDITIONAL_CLEAN_FILES " \ - ${CMAKE_CURRENT_SOURCE_DIR}/bin; \ - ${CMAKE_CURRENT_SOURCE_DIR}/lib; \ - ${CMAKE_CURRENT_SOURCE_DIR}/cmake \ - " + ADDITIONAL_CLEAN_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/bin" + "${CMAKE_CURRENT_SOURCE_DIR}/lib" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ) target_link_libraries(${PROJECT_NAME} PUBLIC Index: lib/Comms/Comms.pro =================================================================== diff -u -refc0e8ccb0ee99f25834eb5a19dee1cf2e50c532 -r4dccc470aedf6f68a0ad73c1101f8a96188cbdb1 --- lib/Comms/Comms.pro (.../Comms.pro) (revision efc0e8ccb0ee99f25834eb5a19dee1cf2e50c532) +++ lib/Comms/Comms.pro (.../Comms.pro) (revision 4dccc470aedf6f68a0ad73c1101f8a96188cbdb1) @@ -39,10 +39,13 @@ INCLUDEPATH += $$PWD/include -# MessageSpool uses the SQLite C API directly (see docs/SDD/AgentMigrationPlan.md -# section 5.5). Requires libsqlite3-dev at build time. LIBS += -lsqlite3 +# qmake only removes the shared objects on distclean. +# The following ensures that they are removed on clean, too. +# It also ensures that all versioned symlinks (*.so.1, *.so.1.0, etc) are removed. +QMAKE_CLEAN += $$DESTDIR/libComms.so* + isEmpty(PREFIX): PREFIX = $$PWD/../../install target.path = $$PREFIX/lib Index: lib/MsgUtils/CMakeLists.txt =================================================================== diff -u -ra5781739bcbe58c754aff8861561495624bc5b67 -r4dccc470aedf6f68a0ad73c1101f8a96188cbdb1 --- lib/MsgUtils/CMakeLists.txt (.../CMakeLists.txt) (revision a5781739bcbe58c754aff8861561495624bc5b67) +++ lib/MsgUtils/CMakeLists.txt (.../CMakeLists.txt) (revision 4dccc470aedf6f68a0ad73c1101f8a96188cbdb1) @@ -76,13 +76,12 @@ PUBLIC_HEADER "${INCLUDES}" ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/lib" - ADDITIONAL_CLEAN_FILES " \ - ${CMAKE_CURRENT_SOURCE_DIR}/bin; \ - ${CMAKE_CURRENT_SOURCE_DIR}/lib; \ - ${CMAKE_CURRENT_SOURCE_DIR}/cmake; \ - ${GENERATED_INCLUDES}; \ - ${GENERATED_SRCS} \ - " + ADDITIONAL_CLEAN_FILES + "${CMAKE_CURRENT_SOURCE_DIR}/bin" + "${CMAKE_CURRENT_SOURCE_DIR}/lib" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + ${GENERATED_INCLUDES} + ${GENERATED_SRCS} ) target_link_libraries(${PROJECT_NAME} PUBLIC Index: lib/MsgUtils/MsgUtils.pro =================================================================== diff -u -ra5781739bcbe58c754aff8861561495624bc5b67 -r4dccc470aedf6f68a0ad73c1101f8a96188cbdb1 --- lib/MsgUtils/MsgUtils.pro (.../MsgUtils.pro) (revision a5781739bcbe58c754aff8861561495624bc5b67) +++ lib/MsgUtils/MsgUtils.pro (.../MsgUtils.pro) (revision 4dccc470aedf6f68a0ad73c1101f8a96188cbdb1) @@ -118,20 +118,15 @@ HEADERS += $$GENERATED_INCLUDES SOURCES += $$GENERATED_SRCS -# Create the dependencies between the object files and generated files so generated files -# are made before trying to compile them into objects. -protoutils_obj.target = LeahiMsgProtoUtils.o -protoutils_obj.depends = $$GEN_INCLUDE_DIR/LeahiMsgDefs.pb.h -QMAKE_EXTRA_TARGETS += protoutils_obj +# Ensure the generated headers are dependencies of the object files that need them +# and are created first. +generated_headers_first.target = $(OBJECTS) +generated_headers_first.depends = \ + $$GEN_INCLUDE_DIR/LeahiMsgDefs.h \ + $$GEN_INCLUDE_DIR/LeahiMsgDefs.pb.h \ + $$GEN_INCLUDE_DIR/LeahiMsgProtoUtils.h +QMAKE_EXTRA_TARGETS += generated_headers_first -msgdefs_obj.target = LeahiMsgDefs.o -msgdefs_obj.depends = $$GEN_INCLUDE_DIR/LeahiMsgDefs.h -QMAKE_EXTRA_TARGETS += msgdefs_obj - -pb_obj.target = LeahiMsgDefs.pb.o -pb_obj.depends = $$GEN_INCLUDE_DIR/LeahiMsgDefs.pb.h -QMAKE_EXTRA_TARGETS += pb_obj - INCLUDEPATH += $$PWD/include LIBS += -lprotobuf @@ -158,6 +153,9 @@ export_pri.files = $$PWD/MsgUtils.pri INSTALLS += export_pri +# qmake only removes the shared objects on distclean, so remove them on clean too +QMAKE_CLEAN += $$DESTDIR/libMsgUtils.so* + QMAKE_CLEAN += \ $$GEN_INCLUDE_DIR/LeahiMsgDefs.h \ $$GEN_INCLUDE_DIR/LeahiMsgDefs.pb.h \ Index: lib/MsgUtils/cmake/MsgUtils.cmake =================================================================== diff -u -r64243101dff61b5c1a40b96ef33080236999acf6 -r4dccc470aedf6f68a0ad73c1101f8a96188cbdb1 --- lib/MsgUtils/cmake/MsgUtils.cmake (.../MsgUtils.cmake) (revision 64243101dff61b5c1a40b96ef33080236999acf6) +++ lib/MsgUtils/cmake/MsgUtils.cmake (.../MsgUtils.cmake) (revision 4dccc470aedf6f68a0ad73c1101f8a96188cbdb1) @@ -93,6 +93,14 @@ ) add_custom_target(${_target_name} DEPENDS ${_output_ini}) + + # CMake adds every custom-command OUTPUT to the clean target. Unlike the other + # generated files, this INI is persistent, so a clean must not delete it. + # CMake offers no per-output exemption, so the directory-scoped property is required, + # but a side effect is that the calling directory must not rely on clean removing other + # custom outputs in this directory. It is ok for now since the _output_ini is the only + # file in the directory, but may need to be addressed later if more files are added. + set_directory_properties(PROPERTIES CLEAN_NO_CUSTOM TRUE) endfunction() # \brief This function adds a custom command to generate a Protobuf file from inputted message conf files Index: lib/MsgUtils/codegen.pri =================================================================== diff -u -rb915ccc1a72fcca21908c35140af7ac7df1f1f37 -r4dccc470aedf6f68a0ad73c1101f8a96188cbdb1 --- lib/MsgUtils/codegen.pri (.../codegen.pri) (revision b915ccc1a72fcca21908c35140af7ac7df1f1f37) +++ lib/MsgUtils/codegen.pri (.../codegen.pri) (revision 4dccc470aedf6f68a0ad73c1101f8a96188cbdb1) @@ -2,8 +2,8 @@ # Included by the components that generate code (e.g. MsgUtils and CloudConnect). # # qmake evaluates system() at qmake time, while QMAKE_EXTRA_COMPILERS / -# QMAKE_EXTRA_TARGETS run at make time. The venv is created here (once, shared); -# the generators are wired as make-time rules in the including .pro so they +# QMAKE_EXTRA_TARGETS run at make time. The venv is created here once and shared. +# The generators are wired as make-time rules in the including .pro so they # re-run on input change. PROJECT_ROOT = $$clean_path($$PWD/../..) @@ -18,13 +18,42 @@ PROJECT_PYTHON = $$PROJECT_VENV_DIR/bin/python # Create the venv, then editable-install the msgutils package so the generator -# scripts can `import msgutils`. Runs once per qmake invocation; pip is a skipped -# when already satisfied. +# scripts can `import msgutils`. +# +# A missing python3-venv reports nothing here and later becomes a generator rule +# failing to find an interpreter. This checks and reports here if it is missing. !exists($$PROJECT_PYTHON) { message("Creating project Python venv at $$PROJECT_VENV_DIR") - system(python3 -m venv $$shell_quote($$PROJECT_VENV_DIR)) + + !system(python3 -m venv $$shell_quote($$PROJECT_VENV_DIR)) { + # $${} braces: qmake allows '.' in a variable name, so "$$VAR." reads as a + # variable named "VAR." and expands to nothing. + error("Could not create the Python venv at $${PROJECT_VENV_DIR}. On Debian/Ubuntu this usually means the venv module is missing: sudo apt install python3-venv") + } + + # venv can exit 0 and still leave no interpreter (interrupted, disk full). + !exists($$PROJECT_PYTHON) { + error("The venv at $${PROJECT_VENV_DIR} was created but has no interpreter at $${PROJECT_PYTHON}. Remove the directory and re-run qmake.") + } } -system($$shell_quote($$PROJECT_PYTHON) -m pip install --upgrade pip --quiet) -system($$shell_quote($$PROJECT_PYTHON) -m pip install -e $$shell_quote($$MSGUTILS_SCRIPTS_DIR) --quiet) +# Allow a machine with a working venv and no network can still build. +!system($$shell_quote($$PROJECT_PYTHON) -m pip install --upgrade pip --quiet) { + warning("Could not upgrade pip in $${PROJECT_VENV_DIR}; continuing with the installed version.") +} + +# The generators import msgutils, but a failed reinstall is survivable when the venv +# already has it. +!system($$shell_quote($$PROJECT_PYTHON) -m pip install -e $$shell_quote($$MSGUTILS_SCRIPTS_DIR) --quiet) { + !system($$shell_quote($$PROJECT_PYTHON) -c $$shell_quote(import msgutils)) { + error("Could not install msgutils from $${MSGUTILS_SCRIPTS_DIR} into $${PROJECT_VENV_DIR}, and it is not importable. The code generators cannot run.") + } + warning("Could not reinstall msgutils from $${MSGUTILS_SCRIPTS_DIR}; continuing with the copy already in the venv.") +} + PROTOC = protoc +# Missing protoc reports nothing here and later becomes a generator rule failing with "command not found". +# This checks and reports here if it is missing. +!system($$PROTOC --version > /dev/null 2>&1) { + error("Could not run '$${PROTOC}'. On Debian/Ubuntu install it with: sudo apt install protobuf-compiler") +}