commit 42048db0265cefd27c04baf64240ef309e6d7f35
Author: david.sorber <david.sorber@gmail.com>
Date:   Sun Nov 2 11:02:15 2025 -0500

    Update PCA CMakeLists.txt. Build libpackjpg as a dependent external
    project.

diff --git a/software/photo_compress_archiver/CMakeLists.txt b/software/photo_compress_archiver/CMakeLists.txt
index 72f6a19..b247509 100644
--- a/software/photo_compress_archiver/CMakeLists.txt
+++ b/software/photo_compress_archiver/CMakeLists.txt
@@ -1,12 +1,149 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 3.16)
 PROJECT("Photo Compress Archiver")
-CMAKE_MINIMUM_REQUIRED(VERSION 3.2)
 
-SET(CMAKE_CXX_STANDARD 11)
+################################################################################
+# Check GCC version
+################################################################################
+IF ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
+   # Require at least gcc 10.3 for C++20 support
+   IF (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 10.3)
+      MESSAGE(FATAL_ERROR "GCC version must be at least 10.3!")
+   ENDIF()
+ENDIF()
+
+# This is a neat trick to prevent in source builds
+IF (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
+   MESSAGE(FATAL_ERROR "Prevented in-tree build. This is bad practice; you "
+           "should be running cmake from inside a build directory.")
+ENDIF()
+
+
+################################################################################
+# Build configuration options
+################################################################################
+
+IF (NOT CMAKE_BUILD_TYPE)
+   SET(CMAKE_BUILD_TYPE ReleaseWithDebug CACHE STRING
+           "Choose the type of build, options are: None Debug ReceiverRelease ReleaseWithDebug."
+           FORCE )
+ENDIF()
+
+MESSAGE(STATUS "Build Type: " ${CMAKE_BUILD_TYPE})
+
+ADD_CUSTOM_TARGET(Debug
+        COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug ${CMAKE_SOURCE_DIR}
+        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
+        COMMENT "Switch CMAKE_BUILD_TYPE to Debug"
+)
+
+ADD_CUSTOM_TARGET(ReceiverRelease
+        COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=PCARelease ${CMAKE_SOURCE_DIR}
+        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
+        COMMENT "Switch CMAKE_BUILD_TYPE to ReceiverRelease"
+)
+
+ADD_CUSTOM_TARGET(ReleaseWithDebug
+        COMMAND ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=ReleaseWithDebug ${CMAKE_SOURCE_DIR}
+        COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target all
+        COMMENT "Switch CMAKE_BUILD_TYPE to ReleaseWithDebug"
+)
+
+
+# Use /etc/os-release to determine Linux flavor and version
+EXECUTE_PROCESS(COMMAND bash -c "grep \"^NAME=\" /etc/os-release | cut -c7- | rev | cut -c2- | rev"
+        OUTPUT_VARIABLE DISTRIB_ID
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+EXECUTE_PROCESS(COMMAND bash -c "grep \"^VERSION_ID=\" /etc/os-release | cut -c13- | rev | cut -c2- | rev"
+        OUTPUT_VARIABLE DISTRIB_RELEASE
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+# Output the parsed ID and release version
+MESSAGE(STATUS "DISTRIB_ID:      " ${DISTRIB_ID})
+MESSAGE(STATUS "DISTRIB_RELEASE: " ${DISTRIB_RELEASE})
+
+# Do not let CMake add the build directory path to RPATH. This means that CL/SL
+# cannot be run from the build directory and must be properly installed.
+SET(CMAKE_SKIP_BUILD_RPATH  TRUE)
+
+# Uncomment this line to enable AddressSanitizer
+# SET(CMAKE_CXX_FLAGS "-fsanitize=address -fno-omit-frame-pointer")
+
+# Detect presence of mold linker and use it if found
+IF (EXISTS "/usr/bin/mold")
+   MESSAGE(STATUS "Using mold linker!")
+   # Note this only works on gcc >= 12.1
+   SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fuse-ld=mold")
+   SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=mold")
+ELSE()
+   MESSAGE(WARNING "Mold linker not detected. Consider installing mold (add BL APT repo: "
+           "https://athena.blacklynx.tech/apt/): sudo apt install mold")
+ENDIF()
+
+#
+# Set the compile flags
+#
+SET(CMAKE_CXX_STANDARD 20)
+SET(CMAKE_CXX_STANDARD_REQUIRED ON)
+SET(CMAKE_CXX_EXTENSIONS OFF)
+# SET(CMAKE_CXX_VISIBILITY_PRESET hidden)
+
+# This length is used along with the __FILE__ macro for logging
+STRING(LENGTH ${CMAKE_CURRENT_SOURCE_DIR} BPLEN)
+ADD_DEFINITIONS("-DBUILD_PATH_LEN=${BPLEN}")
+
+# Ensure symbols from statically linked libraries are hidden
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--exclude-libs=ALL")
+
+# Enable warnings
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter")
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-parameter")
+
+IF (CMAKE_BUILD_TYPE STREQUAL "PCARelease")
 
-INCLUDE_DIRECTORIES(.)
-INCLUDE_DIRECTORIES(/usr/include/)
-INCLUDE_DIRECTORIES(/opt/local/include/)
+   # Tune for Haswell minimum
+   SET(COMPILE_FLAGS "-O3 -flto=auto -ffast-math -ffp-contract=fast -fno-verbose-asm -march=skylake -mfma -mavx2")
 
+   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
+   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
+
+ELSEIF (CMAKE_BUILD_TYPE STREQUAL "ReleaseWithDebug")
+
+   # Tune for Haswell minimum
+   #    SET(COMPILE_FLAGS "-g -O3 -flto=auto -march=haswell -mtune=generic -mavx2")
+   SET(COMPILE_FLAGS "-g -O3 -flto=auto -ffast-math -ffp-contract=fast -fno-verbose-asm -march=skylake -mfma -mavx2")
+
+   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
+   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
+
+ELSEIF (CMAKE_BUILD_TYPE STREQUAL "Debug")
+
+   SET(COMPILE_FLAGS "-g -O0 -march=skylake -mtune=generic")
+
+   SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMPILE_FLAGS}")
+   SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMPILE_FLAGS}")
+
+ENDIF()
+
+# Set the library install path depending on the Linux flavor
+SET(LIBRARY_INSTALL_PATH "")
+IF ((DISTRIB_ID STREQUAL "Ubuntu") OR (DISTRIB_ID STREQUAL "Linux Mint"))
+   SET(LIBRARY_INSTALL_PATH "usr/lib/x86_64-linux-gnu/")
+ELSE()
+   # CentOS/RedHat/Scientific
+   SET(LIBRARY_INSTALL_PATH "usr/lib64/")
+ENDIF()
+
+IF(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+   set(CMAKE_INSTALL_PREFIX "/" CACHE PATH "..." FORCE)
+ENDIF()
+
+
+################################################################################
+# Library Dependencies
+################################################################################
 SET(EXTERNAL_LIBS)
 
 FIND_PACKAGE(Threads)
@@ -17,23 +154,64 @@ IF (Boost_FOUND)
    SET(EXTERNAL_LIBS ${EXTERNAL_LIBS} ${Boost_LIBRARIES})
 ENDIF()
 
-# NOTE: PCA now uses the libpackjpg that I created instead of the executable
-# Not sure why this is required... but doesn't seem to work otherwise
-SET(LIBPACKJPG)
-FIND_LIBRARY(LIBPACKJPG packjpg PATHS /opt/local/lib)
-#~ MESSAGE(STATUS LIBPACKJPG: ${LIBPACKJPG})
-SET(EXTERNAL_LIBS ${EXTERNAL_LIBS} ${LIBPACKJPG})
-#~ MESSAGE(STATUS LIBS: ${EXTERNAL_LIBS})
+## NOTE: PCA now uses the libpackjpg that I created instead of the executable
+## Not sure why this is required... but doesn't seem to work otherwise
+#SET(LIBPACKJPG)
+#FIND_LIBRARY(LIBPACKJPG packjpg PATHS /opt/local/lib)
+##~ MESSAGE(STATUS LIBPACKJPG: ${LIBPACKJPG})
+#SET(EXTERNAL_LIBS ${EXTERNAL_LIBS} ${LIBPACKJPG})
+
+
+################################################################################
+# External projects
+################################################################################
+INCLUDE(ExternalProject)
+
+#
+# libpackjpg
+#
+SET(PACKJPG_PREFIX ${CMAKE_BINARY_DIR}/libpackjpg)
+
+# TODO: only build/install libpackjpg.a
+ExternalProject_Add(libpackjpg_local
+        GIT_REPOSITORY      "https://brutus.gleeze.com/git/main"
+        PREFIX              ${PACKJPG_PREFIX}
+        INSTALL_DIR         ${PACKJPG_PREFIX}/install
+        SOURCE_SUBDIR       software/libpackjpg/lib_src
+        CMAKE_ARGS          -DCMAKE_INSTALL_PREFIX=${PACKJPG_PREFIX}/install -DCMAKE_BUILD_TYPE=Release)
+
+
+INCLUDE_DIRECTORIES(${PACKJPG_PREFIX}/install/usr/include)
+
+SET(libpackjpg_static_lib ${PACKJPG_PREFIX}/install/${LIBRARY_INSTALL_PATH}/libpackjpg.a)
+MESSAGE(STATUS "libpackjpg path: ${libpackjpg_static_lib}")
+
+SET_SOURCE_FILES_PROPERTIES(
+        ${libpackjpg_static_lib} PROPERTIES
+        EXTERNAL_OBJECT TRUE  # Identifies this as an object file (static lib)
+        GENERATED TRUE        # Avoids need for file to exist at configure-time
+)
+
+
+################################################################################
+# Source
+################################################################################
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/src)
+INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/external)
 
-# PCA sources
 SET(pca_sources
-    ../PhotoCompressArchiver.cc
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/PhotoCompressArchiver.cc
 )
 
-# Build the PCA executable
-#~ ADD_DEFINITIONS("-std=c++11 -g -O3 -march=native")
-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -g -O3 -march=native -fuse-ld=gold -flto")
+
+################################################################################
+# Target: pca executable
+################################################################################
+
 ADD_EXECUTABLE(pca
-    ${pca_sources}
-    ../main.cc)
-TARGET_LINK_LIBRARIES(pca ${CMAKE_THREAD_LIBS_INIT} ${EXTERNAL_LIBS} stdc++fs)
+   ${pca_sources}
+   ${CMAKE_CURRENT_SOURCE_DIR}/src/main.cc)
+
+ADD_DEPENDENCIES(pca libpackjpg_local)
+
+TARGET_LINK_LIBRARIES(pca ${EXTERNAL_LIBS} ${libpackjpg_static_lib})
diff --git a/software/photo_compress_archiver/src/main.cc b/software/photo_compress_archiver/src/main.cc
index c277115..3cd3128 100644
--- a/software/photo_compress_archiver/src/main.cc
+++ b/software/photo_compress_archiver/src/main.cc
@@ -13,7 +13,7 @@ namespace bpo = boost::program_options;
 void header()
 {
     std::cout << BOLD << "Photo Compress Archiver -- " << version << "\n" << ENDC;
-    std::cout << "  Copyright 2017-2018 -- David Sorber\n";
+    std::cout << "  Copyright 2017-2025 -- David Sorber\n";
     std::cout << "  >>> Utilizing packJPG by Matthias Stirner\n" << std::endl;    
 }
 
