(ROOT Flake) Setup of Dependent Modular Flakes for Portable development

Any help would be appreciated. Hopefully this is concise enough.

  1. Goal: Use multiple flakes to provide the necessary framework to build portable C++ applications.
  2. Problem: Applications built without a flake are non-portable. Using a flake allows a portable build. Libraries that are built using a flake are placed into the nix store. CMake cannot find the library “package” path in the store to build my other CMake based C++ application.
  3. System Outputs:
    • CMake based C++ libraries
    • CMake based C++ applications
    • ReactJs/Node application
    • Java/Maven application.
  4. What works:
    • Building the library only using the common flake.nix works and it installs the libraries into the store.
    • Building the libraries and applications using only cmake works but creates non-portable output.
  5. What doesn’t work:
    • Use a flakes to build the CMake C++ application that uses the libraries.

      • Could not find a package configuration file provided by “MercuryCommon”
        with any of the following names:
        MercuryCommonConfig.cmake
        mercurycommon-config.cmake

    • The root flake.nix file to build the components by calling nested flakes.

      • error: flake ‘git+file:///home/user/mercury?shallow=1’ does not provide attribute ‘packages.x86_64-linux.common’, ‘legacyPackages.x86_64-linux.common’ or ‘common’

  6. HELP WITH:
    • Details on how to pass the library nix store path to other CMake builds.
    • Details on how to build a root level flake.nix file.
  7. Components:
  • Root flake.nix:
# Top-level flake.nix
{
    description = "Workspace with build prioritization";
    inputs = {
      nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
      common.url = "path:./processing/common";
      archive.url = "path:./processing/mailarchive";
    };

    outputs = { self, common, archive, nixpkgs }: {
      # Define top-level outputs, e.g., a combined development shell
      devShells.x86_64-linux = nixpkgs.lib.mkMerge [
        common.devShells.x86_64-linux
        archive.devShells.x86_64-linux
      ];

    };
}
  • Common flake.nix
{
  description = "CMake-based project using Catch2";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
    flake-utils.url = "github:numtide/flake-utils";    
    catch2 = {
      url = "github:catchorg/Catch2/v3.4.0";
      flake = false;
    };    
    pdfhummus = {
      url = "github:galkahana/PDF-Writer/v4.6.2";
      flake = false;
    };
    zxing-221 = {
      url = "github:nu-book/zxing-cpp/v2.2.1";
      flake = false;
    };
  };

  outputs = { self, nixpkgs, flake-utils, catch2, pdfhummus, zxing-221 }: 

      let

        pkgs = import nixpkgs { 
          system = "x86_64-linux"; 
          overlays = [
            (final: prev: {
              # Primary zxing-cpp override
              zxing-cpp = prev.zxing-cpp.overrideAttrs (old: {
                version = "2.2.1";
                src = zxing-221;
                
                # Update hash if needed (run build to get the correct hash)
                # hash = "sha256-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=";
                
                # Maintain original build parameters
                nativeBuildInputs = old.nativeBuildInputs or [] ++ [ prev.cmake ];
                buildInputs = old.buildInputs or [] ++ [ prev.stb ];
              });
            })
          ];
        };  

      in {
        packages.x86_64-linux.default = pkgs.stdenv.mkDerivation {
          pname = "common";
          version = "3.2.0";
          src = ./.;

          nativeBuildInputs = [ pkgs.cmake pkgs.pkg-config ];
          buildInputs = [ 
            pkgs.unixODBC 
            pkgs.unixODBCDrivers.psql 
            pkgs.libuuid 
            pkgs.leptonica
            pkgs.tesseract 
            pkgs.pcre 
            pkgs.opencv 
            pkgs.zbar 
            pkgs.zxing-cpp
            pkgs.freeimage
            pkgs.libarchive
            pkgs.openssl
            pkgs.libesmtp
            pkgs.log4cpp
            pkgs.xercesc
            pkgs.xalanc
            pkgs.libtiff
            pkgs.libpng
            pkgs.libjpeg
            pkgs.hunspell
            pkgs.hunspellDicts.en-us
            pkgs.nmap
            pkgs.libxml2
            pkgs.bzip2
            pkgs.gmime3
            (pkgs.omniorb.overrideAttrs (old: rec {
              version = "4.3.2";
              src = pkgs.fetchurl {
                url = "https://sourceforge.net/projects/omniorb/files/omniORB/omniORB-${version}/omniORB-${version}.tar.bz2/download";
                name = "omniORB-${version}.tar.bz2";
                sha256 = "sha256-HHRTMNAZBK/Xoe0KWJa5puU6waS4ZKSFA7k8fuy/H6g="; 
              };
              buildInputs = (old.buildInputs or []) ++ [ pkgs.openssl ];
              configureFlags = (old.configureFlags or []) ++ [ "--with-openssl=${pkgs.openssl.dev}" ];
            }))                
          ];        

          preConfigure = ''
              echo "PRECONFIGURE PHASE>>>>>> ${catch2} & ${pdfhummus}"
              mkdir -p extern/Catch2
              cp -r ${catch2}/* extern/Catch2
              chmod -R ug+w extern/Catch2
              mkdir -p extern/PDFHummus
              cp -r ${pdfhummus}/* extern/PDFHummus
              chmod -R ug+w extern/PDFHummus
          '';

          # Phase 2: Configure
          configurePhase = ''
              runHook preConfigure
              echo "CONFIGURE PHASE>>>>>>"
              cmake -B build -S . \
                  -DCMAKE_INSTALL_PREFIX=$out \
                  -DCMAKE_BUILD_TYPE=Release \
                  -DCMAKE_CATCH2_DIR=$PWD/extern/Catch2 \
                  -DCMAKE_PDFHUMMUS_DIR=$PWD/extern/PDFHummus
          '';
        
          # Phase 3: Build
          buildPhase = ''
            NIX_CFLAGS_COMPILE="$(pkg-config --cflags opencv4) $NIX_CFLAGS_COMPILE"
            NIX_CFLAGS_COMPILE="$(pkg-config --cflags opencv4) $NIX_CFLAGS_COMPILE"
            cmake --build build --parallel $NIX_BUILD_CORES
          '';

          # Phase 4: Unit test
          checkPhase = ''
            ctest --output-on-failure
          '';

          # Phase 5: Install with verification
          installPhase = ''          
            mkdir -p $out/{include,lib}
            # First try CMake's native install
            cmake --install build --prefix $out
            
            if [ -d "build/include" ]; then
              cp -rv build/include/*.h $out/include/ || true
            elif [ -d "build/result/include" ]; then
              cp -rv build/result/include/*.h $out/include/ || true
            elif [ -d "include" ]; then
              cp -rv include/* $out/include/ || true
            fi            

            # Generate MercuryCommon CMake package config
            mkdir -p $out/lib/cmake/MercuryCommon

            cat > $out/lib/cmake/MercuryCommon/MercuryCommonConfig.cmake <<EOF
# Mercury Common Libraries Package
set(MERCCOMM_FOUND TRUE)

# Include directory (shared by all libs)
set(MERCCOMM_INCLUDE_DIR "\''${CMAKE_CURRENT_LIST_DIR}/../../../include")

# Import all libraries
foreach(lib 
  address company image ocr archive text config crypto
  file pdf email logging processing sysutil database test
)
  add_library(MercuryCommon::\''${lib} SHARED IMPORTED)
  set_target_properties(MercuryCommon::\''${lib} PROPERTIES
    IMPORTED_LOCATION "\''${CMAKE_CURRENT_LIST_DIR}/../../../lib/libmerc\''${lib}.so"
    INTERFACE_INCLUDE_DIRECTORIES "\''${MERCCOMM_INCLUDE_DIR}"
  )
endforeach()

# Convenience variables
set(MERCCOMM_LIBRARIES
  MercuryCommon::address
  MercuryCommon::company
  MercuryCommon::image
  MercuryCommon::ocr
  MercuryCommon::archive
  MercuryCommon::text
  MercuryCommon::config
  MercuryCommon::crypto
  MercuryCommon::file
  MercuryCommon::pdf
  MercuryCommon::email
  MercuryCommon::logging
  MercuryCommon::processing
  MercuryCommon::sysutil
  MercuryCommon::database
  MercuryCommon::test
)
EOF

            # Version file
            cat > $out/lib/cmake/MercuryCommon/MercuryCommonConfigVersion.cmake <<EOF
set(PACKAGE_VERSION "3.2.0")
if(PACKAGE_FIND_VERSION VERSION_EQUAL 3.2.0)
  set(PACKAGE_VERSION_COMPATIBLE TRUE)
  set(PACKAGE_VERSION_EXACT TRUE)
endif()
EOF
          '';

          # Debugging hooks
          postInstall = ''
            # Verify installation
            if [ ! -d "$out/lib" ]; then
              echo "ERROR: No library files were installed to $out"
              exit 1
            elif [ ! -d "$out/include" ]; then
              echo "ERROR: No include files were installed to $out"
              exit 1
            fi
          '';

          # Extra protection against empty outputs
          dontInstall = false;
          doCheck = true;
        };

        devShells.x86_64-linux.default = pkgs.mkShell {
          packages = [ pkgs.cmake catch2 pdfhummus ];
          shellHook = ''
            mkdir -p extern/Catch2
            cp -r ${catch2}/* extern/Catch2
            chmod -R ug+w extern/Catch2
            mkdir -p extern/PDFHummus
            cp -r ${pdfhummus}/* extern/PDFHummus
            chmod -R ug+w extern/PDFHummus
          '';
        };
      };
}
  • archive.nix file:
{
  description = "CMake-based project using Catch2";

  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.11";
    flake-utils.url = "github:numtide/flake-utils";    
    catch2 = {
      url = "github:catchorg/Catch2/v3.4.0";
      flake = false;
    };    
    pdfhummus = {
      url = "github:galkahana/PDF-Writer/v4.6.2";
      flake = false;
    };
    common 
  };

  outputs = { self, nixpkgs, flake-utils, catch2, pdfhummus }: 

      let
        pkgs = import nixpkgs { system = "x86_64-linux"; };  
      in {
        packages.x86_64-linux.default = pkgs.stdenv.mkDerivation {
          pname = "archive";
          version = "3.2.0";
          src = ./.;

          nativeBuildInputs = [ pkgs.cmake pkgs.pkg-config ];
          buildInputs = [ 
            pkgs.unixODBC 
            pkgs.unixODBCDrivers.psql 
            pkgs.libuuid 
            pkgs.log4cpp
            pkgs.libarchive
            pkgs.libesmtp
            pkgs.pcre 
            pkgs.openssl
          ];        

          preConfigure = ''
              echo "PRECONFIGURE PHASE>>>>>> ${catch2} & ${pdfhummus}"
              mkdir -p extern/Catch2
              cp -r ${catch2}/* extern/Catch2
              chmod -R ug+w extern/Catch2
              mkdir -p extern/PDFHummus
              cp -r ${pdfhummus}/* extern/PDFHummus
              chmod -R ug+w extern/PDFHummus
          '';

          # Phase 2: Configure
          configurePhase = ''
              runHook preConfigure
              echo "CONFIGURE PHASE>>>>>>"
              cmake -B build -S . \
                  -DCMAKE_INSTALL_PREFIX=$out \
                  -DCMAKE_BUILD_TYPE=Release \
                  -DCMAKE_CATCH2_DIR=extern/Catch2 \
                  -DCMAKE_PDFHUMMUS_DIR=extern/PDFHummus
          '';
        
          # Phase 3: Build
          buildPhase = ''
            NIX_CFLAGS_COMPILE="$(pkg-config --cflags opencv4) $NIX_CFLAGS_COMPILE"
            NIX_CFLAGS_COMPILE="$(pkg-config --cflags opencv4) $NIX_CFLAGS_COMPILE"
            cmake --build build --parallel $NIX_BUILD_CORES
          '';

          # Phase 4: Unit test
          checkPhase = ''
            ctest --output-on-failure
          '';

          # Phase 5: Install with verification
          installPhase = ''
            cmake --install build
            
            # Verify installation
            if [ ! -d "$out" ]; then
              echo "ERROR: No files were installed to $out"
              exit 1
            fi
            echo "=== Installed files ==="
            find $out -type f
          '';

          # Extra protection against empty outputs
          dontInstall = false;
          doCheck = true;
        };

        devShells.x86_64-linux.default = pkgs.mkShell {
          packages = [ pkgs.cmake catch2 pdfhummus ];
          shellHook = ''
            mkdir -p extern/Catch2
            cp -r ${catch2}/* extern/Catch2
            chmod -R ug+w extern/Catch2
            mkdir -p extern/PDFHummus
            cp -r ${pdfhummus}/* extern/PDFHummus
            chmod -R ug+w extern/PDFHummus
          '';
        };
      };
}