@@ -474,10 +474,8 @@ namespace vix::commands
474474
475475 dep.dependencies .clear ();
476476
477- if (j. contains ( " dependencies " ) )
477+ auto read_dep_block = [&]( const json &d )
478478 {
479- const auto &d = j[" dependencies" ];
480-
481479 if (d.is_array ())
482480 {
483481 for (const auto &item : d)
@@ -501,7 +499,13 @@ namespace vix::commands
501499 dep.dependencies .push_back (it.key ());
502500 }
503501 }
504- }
502+ };
503+
504+ if (j.contains (" dependencies" ))
505+ read_dep_block (j[" dependencies" ]);
506+
507+ if (dep.dependencies .empty () && j.contains (" deps" ))
508+ read_dep_block (j[" deps" ]);
505509 }
506510
507511 static std::vector<DepResolved> sort_deps_topologically (const std::vector<DepResolved> &deps)
@@ -719,52 +723,192 @@ namespace vix::commands
719723
720724 out << " set(_VIX_DEPS_DIR " << cmake_quote (project_deps_dir ().string ()) << " )\n\n " ;
721725
726+ out << " # ------------------------------------------------------\n " ;
727+ out << " # Internal helpers generated by Vix\n " ;
728+ out << " # ------------------------------------------------------\n\n " ;
729+
730+ out << " function(_vix_disable_dep_extras dep_ns dep_name)\n " ;
731+ out << " string(TOUPPER \" ${dep_ns}\" _VIX_NS_UPPER)\n " ;
732+ out << " string(TOUPPER \" ${dep_name}\" _VIX_NAME_UPPER)\n " ;
733+ out << " \n " ;
734+ out << " # Generic knobs used by many projects\n " ;
735+ out << " set(BUILD_TESTING OFF CACHE BOOL \"\" FORCE)\n " ;
736+ out << " set(BUILD_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
737+ out << " set(ENABLE_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
738+ out << " set(TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
739+ out << " set(UNIT_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
740+ out << " set(BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
741+ out << " set(ENABLE_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
742+ out << " set(EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
743+ out << " set(BUILD_BENCHMARKS OFF CACHE BOOL \"\" FORCE)\n " ;
744+ out << " set(BENCHMARKS OFF CACHE BOOL \"\" FORCE)\n " ;
745+ out << " set(BUILD_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
746+ out << " set(ENABLE_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
747+ out << " set(DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
748+ out << " \n " ;
749+ out << " # Namespace-specific knobs, e.g. CNERIUM_BUILD_TESTS\n " ;
750+ out << " set(${_VIX_NS_UPPER}_BUILD_TESTING OFF CACHE BOOL \"\" FORCE)\n " ;
751+ out << " set(${_VIX_NS_UPPER}_BUILD_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
752+ out << " set(${_VIX_NS_UPPER}_ENABLE_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
753+ out << " set(${_VIX_NS_UPPER}_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
754+ out << " set(${_VIX_NS_UPPER}_UNIT_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
755+ out << " set(${_VIX_NS_UPPER}_BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
756+ out << " set(${_VIX_NS_UPPER}_ENABLE_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
757+ out << " set(${_VIX_NS_UPPER}_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
758+ out << " set(${_VIX_NS_UPPER}_BUILD_BENCHMARKS OFF CACHE BOOL \"\" FORCE)\n " ;
759+ out << " set(${_VIX_NS_UPPER}_BENCHMARKS OFF CACHE BOOL \"\" FORCE)\n " ;
760+ out << " set(${_VIX_NS_UPPER}_BUILD_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
761+ out << " set(${_VIX_NS_UPPER}_ENABLE_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
762+ out << " set(${_VIX_NS_UPPER}_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
763+ out << " \n " ;
764+ out << " # Package-specific knobs, e.g. CNERIUM_HTTP_BUILD_TESTS\n " ;
765+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_BUILD_TESTING OFF CACHE BOOL \"\" FORCE)\n " ;
766+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_BUILD_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
767+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_ENABLE_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
768+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
769+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_UNIT_TESTS OFF CACHE BOOL \"\" FORCE)\n " ;
770+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_BUILD_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
771+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_ENABLE_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
772+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_EXAMPLES OFF CACHE BOOL \"\" FORCE)\n " ;
773+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_BUILD_BENCHMARKS OFF CACHE BOOL \"\" FORCE)\n " ;
774+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_BENCHMARKS OFF CACHE BOOL \"\" FORCE)\n " ;
775+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_BUILD_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
776+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_ENABLE_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
777+ out << " set(${_VIX_NS_UPPER}_${_VIX_NAME_UPPER}_DOCS OFF CACHE BOOL \"\" FORCE)\n " ;
778+ out << " endfunction()\n\n " ;
779+
780+ out << " function(_vix_bridge_alias canonical actual)\n " ;
781+ out << " if(TARGET ${canonical})\n " ;
782+ out << " return()\n " ;
783+ out << " endif()\n " ;
784+ out << " if(NOT TARGET ${actual})\n " ;
785+ out << " return()\n " ;
786+ out << " endif()\n " ;
787+ out << " \n " ;
788+ out << " string(REPLACE \" ::\" \" __\" _VIX_BRIDGE_SAFE ${canonical})\n " ;
789+ out << " set(_VIX_BRIDGE_TARGET \" vix_bridge__${_VIX_BRIDGE_SAFE}\" )\n " ;
790+ out << " \n " ;
791+ out << " if(NOT TARGET ${_VIX_BRIDGE_TARGET})\n " ;
792+ out << " add_library(${_VIX_BRIDGE_TARGET} INTERFACE)\n " ;
793+ out << " target_link_libraries(${_VIX_BRIDGE_TARGET} INTERFACE ${actual})\n " ;
794+ out << " endif()\n " ;
795+ out << " \n " ;
796+ out << " if(NOT TARGET ${canonical})\n " ;
797+ out << " add_library(${canonical} ALIAS ${_VIX_BRIDGE_TARGET})\n " ;
798+ out << " endif()\n " ;
799+ out << " endfunction()\n\n " ;
800+
801+ out << " function(_vix_try_bridge_for_dep dep_ns dep_name)\n " ;
802+ out << " set(_VIX_CANONICAL \" ${dep_ns}::${dep_name}\" )\n " ;
803+ out << " if(TARGET ${_VIX_CANONICAL})\n " ;
804+ out << " return()\n " ;
805+ out << " endif()\n " ;
806+ out << " \n " ;
807+ out << " set(_VIX_CANDIDATES\n " ;
808+ out << " \" ${dep_name}\"\n " ;
809+ out << " \" ${dep_name}::${dep_name}\"\n " ;
810+ out << " \" ${dep_ns}_${dep_name}\"\n " ;
811+ out << " \" ${dep_ns}-${dep_name}\"\n " ;
812+ out << " \" ${dep_ns}.${dep_name}\"\n " ;
813+ out << " )\n " ;
814+ out << " \n " ;
815+ out << " foreach(_VIX_CAND IN LISTS _VIX_CANDIDATES)\n " ;
816+ out << " if(TARGET ${_VIX_CAND})\n " ;
817+ out << " _vix_bridge_alias(${_VIX_CANONICAL} ${_VIX_CAND})\n " ;
818+ out << " return()\n " ;
819+ out << " endif()\n " ;
820+ out << " endforeach()\n " ;
821+ out << " endfunction()\n\n " ;
822+
722823 for (const auto &dep : deps)
723824 {
724825 const std::string safe = cmake_safe_target (dep.id );
725826 const std::string alias = cmake_alias_target (dep.id );
827+ const fs::path depSourceDir = dep.linkDir ;
828+ const fs::path depCMake = depSourceDir / " CMakeLists.txt" ;
829+ const fs::path depIncludeDir = dep.linkDir / dep.include ;
830+ const std::string buildDirName = " _vix_build_" + sanitize_id_dot (dep.id );
831+
832+ const auto slash = dep.id .find (' /' );
833+ const std::string depNs = (slash == std::string::npos) ? dep.id : dep.id .substr (0 , slash);
834+ const std::string depName = (slash == std::string::npos) ? dep.id : dep.id .substr (slash + 1 );
726835
727836 out << " # " << dep.id << " @" << dep.version << " (" << dep.commit << " )\n " ;
728837
729- if (dep.type == " header-only" || dep.type == " header_only" || dep.type == " headers" )
730- {
731- const fs::path inc = dep.linkDir / dep.include ;
732- out << " add_library(" << safe << " INTERFACE)\n " ;
733- out << " add_library(" << alias << " ALIAS " << safe << " )\n " ;
734- out << " target_include_directories(" << safe << " INTERFACE "
735- << cmake_quote (inc.string ()) << " )\n " ;
736- }
737- else if (dep.type == " library" ||
738- dep.type == " header-and-source" ||
739- dep.type == " header_and_source" ||
740- dep.type == " headers-and-sources" )
741- {
742- const fs::path depSourceDir = dep.linkDir ;
743- const std::string buildDirName = " _vix_build_" + sanitize_id_dot (dep.id );
838+ const bool hasCMake = fs::exists (depCMake);
839+ const bool isHeaderOnly =
840+ dep.type == " header-only" ||
841+ dep.type == " header_only" ||
842+ dep.type == " headers" ;
744843
745- out << " if(NOT TARGET " << alias << " )\n " ;
844+ const bool isCompiledLike =
845+ dep.type == " library" ||
846+ dep.type == " header-and-source" ||
847+ dep.type == " header_and_source" ||
848+ dep.type == " headers-and-sources" ;
849+
850+ if (hasCMake)
851+ {
852+ out << " _vix_disable_dep_extras(" << depNs << " " << depName << " )\n " ;
853+ out << " if(EXISTS " << cmake_quote (depCMake.string ()) << " )\n " ;
746854 out << " add_subdirectory("
747855 << cmake_quote (depSourceDir.string ()) << " "
748- << cmake_quote ((project_vix_dir () / buildDirName).string ()) << " )\n " ;
856+ << cmake_quote ((project_vix_dir () / buildDirName).string ())
857+ << " EXCLUDE_FROM_ALL)\n " ;
749858 out << " endif()\n " ;
750-
859+ out << " _vix_try_bridge_for_dep(" << depNs << " " << depName << " )\n " ;
860+ }
861+ else if (isHeaderOnly)
862+ {
751863 out << " if(NOT TARGET " << alias << " )\n " ;
752- out << " message(FATAL_ERROR "
753- << cmake_quote (" Dependency " + dep.id + " did not define expected target " + alias)
754- << " )\n " ;
864+ out << " add_library(" << safe << " INTERFACE)\n " ;
865+ out << " add_library(" << alias << " ALIAS " << safe << " )\n " ;
866+ out << " target_include_directories(" << safe << " INTERFACE "
867+ << cmake_quote (depIncludeDir.string ()) << " )\n " ;
755868 out << " endif()\n " ;
756869 }
757- else
870+ else if (isCompiledLike)
758871 {
759- out << " message(WARNING "
760- << cmake_quote (" Unsupported Vix package type for " + dep.id + " : " + dep.type )
872+ out << " message(FATAL_ERROR "
873+ << cmake_quote (
874+ " Dependency " + dep.id +
875+ " is a compiled package but no CMakeLists.txt was found in " +
876+ depSourceDir.string ())
761877 << " )\n " ;
762878 }
879+ else
880+ {
881+ if (hasCMake)
882+ {
883+ out << " _vix_disable_dep_extras(" << depNs << " " << depName << " )\n " ;
884+ out << " if(EXISTS " << cmake_quote (depCMake.string ()) << " )\n " ;
885+ out << " add_subdirectory("
886+ << cmake_quote (depSourceDir.string ()) << " "
887+ << cmake_quote ((project_vix_dir () / buildDirName).string ())
888+ << " EXCLUDE_FROM_ALL)\n " ;
889+ out << " endif()\n " ;
890+ out << " _vix_try_bridge_for_dep(" << depNs << " " << depName << " )\n " ;
891+ }
892+ else if (fs::exists (depIncludeDir))
893+ {
894+ out << " if(NOT TARGET " << alias << " )\n " ;
895+ out << " add_library(" << safe << " INTERFACE)\n " ;
896+ out << " add_library(" << alias << " ALIAS " << safe << " )\n " ;
897+ out << " target_include_directories(" << safe << " INTERFACE "
898+ << cmake_quote (depIncludeDir.string ()) << " )\n " ;
899+ out << " endif()\n " ;
900+ }
901+ else
902+ {
903+ out << " message(WARNING "
904+ << cmake_quote (" Unsupported Vix package type for " + dep.id + " : " + dep.type )
905+ << " )\n " ;
906+ }
907+ }
763908
764909 out << " \n " ;
765910 }
766911 }
767-
768912 static void print_next_steps (const std::vector<DepResolved> &deps)
769913 {
770914 vix::cli::util::one_line_spacer (std::cout);
@@ -774,6 +918,38 @@ namespace vix::commands
774918 vix::cli::util::info (std::cout, " CMake integration generated" );
775919 std::cout << " \n " ;
776920
921+ std::vector<std::string> aliases;
922+ aliases.reserve (deps.size ());
923+
924+ for (const auto &d : deps)
925+ {
926+ const fs::path depCMake = d.linkDir / " CMakeLists.txt" ;
927+ const fs::path depIncludeDir = d.linkDir / d.include ;
928+
929+ const bool hasCMake = fs::exists (depCMake);
930+ const bool hasIncludeDir = fs::exists (depIncludeDir);
931+
932+ const bool isHeaderOnly =
933+ d.type == " header-only" ||
934+ d.type == " header_only" ||
935+ d.type == " headers" ;
936+
937+ const bool isCompiledLike =
938+ d.type == " library" ||
939+ d.type == " header-and-source" ||
940+ d.type == " header_and_source" ||
941+ d.type == " headers-and-sources" ;
942+
943+ // We only suggest aliases that are expected to exist after include(.vix/vix_deps.cmake):
944+ // - header-only fallback packages created by Vix
945+ // - CMake-based packages that should expose their public alias/target
946+ if ((isHeaderOnly && hasIncludeDir) || hasCMake || isCompiledLike)
947+ aliases.push_back (cmake_alias_target (d.id ));
948+ }
949+
950+ std::sort (aliases.begin (), aliases.end ());
951+ aliases.erase (std::unique (aliases.begin (), aliases.end ()), aliases.end ());
952+
777953 vix::cli::util::warn_line (std::cout, " Next:" );
778954 std::cout << " " << GRAY << " • " << RESET
779955 << " Add this to your "
@@ -782,12 +958,24 @@ namespace vix::commands
782958
783959 std::cout << " include(.vix/vix_deps.cmake)\n " ;
784960 std::cout << " add_executable(app main.cpp)\n " ;
785- std::cout << " target_link_libraries(app PRIVATE" ;
786961
787- for (const auto &d : deps)
788- std::cout << " " << cmake_alias_target (d.id );
962+ if (!aliases.empty ())
963+ {
964+ std::cout << " target_link_libraries(app PRIVATE" ;
965+
966+ for (const auto &alias : aliases)
967+ std::cout << " " << alias;
789968
790- std::cout << " )\n\n " ;
969+ std::cout << " )\n " ;
970+ }
971+
972+ std::cout << " \n " ;
973+
974+ std::cout << " " << GRAY << " • " << RESET
975+ << " Link only the packages your target actually uses.\n " ;
976+ std::cout << " " << GRAY << " • " << RESET
977+ << " Packages with their own CMakeLists.txt are loaded automatically through "
978+ << CYAN << " .vix/vix_deps.cmake" << RESET << " .\n\n " ;
791979 }
792980
793981 static int install_global_package (const std::string &specRaw)
0 commit comments