@@ -507,6 +507,16 @@ namespace vix::cli::build
507507 return BuildNodeKind::Config;
508508 }
509509
510+ /*
511+ * Utility/phony/generated outputs are intentionally imported as Config.
512+ *
513+ * They are useful as graph dependencies, but they are not safe direct
514+ * Graph Executor targets yet. BuildGraphExecutor will reject them and
515+ * fallback to CMake/Ninja.
516+ */
517+ if (edge.kind == NinjaEdgeKind::Utility)
518+ return BuildNodeKind::Config;
519+
510520 return BuildNodeKind::Unknown;
511521 }
512522
@@ -592,12 +602,23 @@ namespace vix::cli::build
592602 if (!edge.valid ())
593603 return false ;
594604
605+ /*
606+ * Compile commands are imported from compile_commands.json because that
607+ * gives Vix the exact compiler argv, working directory and object output.
608+ */
595609 if (edge.kind == NinjaEdgeKind::Compile)
596610 return false ;
597611
598612 if (edge.kind == NinjaEdgeKind::Unknown)
599613 return false ;
600614
615+ /*
616+ * Import Link/Archive/Copy/Install/Utility edges.
617+ *
618+ * The executor will decide later if a target is safe to execute through
619+ * Graph Executor. Importing the DAG is useful even when execution falls
620+ * back to CMake/Ninja.
621+ */
601622 return true ;
602623 }
603624 } // namespace
@@ -863,6 +884,16 @@ namespace vix::cli::build
863884
864885 std::size_t imported = 0 ;
865886
887+ std::unordered_map<std::string, std::string> outputToTask;
888+
889+ for (const auto &kv : tasks_)
890+ {
891+ const BuildTask &task = kv.second ;
892+
893+ for (const std::string &outputId : task.outputs )
894+ outputToTask[outputId] = task.id ;
895+ }
896+
866897 for (const NinjaEdge &edge : ninjaBuild->edges )
867898 {
868899 if (!should_import_ninja_edge (edge))
@@ -881,11 +912,10 @@ namespace vix::cli::build
881912 task.workingDirectory = ninjaBuild->directory ;
882913
883914 /*
884- * Do not expand Ninja rule commands here .
915+ * We intentionally delegate non-compile Ninja edges back to Ninja .
885916 *
886- * build.ninja is already a complete execution graph. For now we import
887- * the DAG structure only. Execution remains delegated to Ninja/CMake until
888- * Vix has a full Ninja variable expander and target-aware executor.
917+ * Vix imports the DAG and target metadata here, but does not yet expand
918+ * Ninja variables or reimplement every CMake-generated build rule.
889919 */
890920 task.command = {
891921 " ninja" ,
@@ -895,44 +925,39 @@ namespace vix::cli::build
895925
896926 task.commandHash = command_hash_for_argv (task.command );
897927
898- for (const fs::path &input : edge.explicitInputs )
928+ std::vector<std::string> inputNodeIds;
929+
930+ auto import_input = [&](const fs::path &input)
899931 {
900932 const BuildNodeKind inputKind = node_kind_from_ninja_input (input);
901933
902934 BuildNode inputNode = make_file_build_node (inputKind, input);
903935
904936 /*
905937 * Keep Ninja import cheap.
906- * scan_project() and load_dependency_files() already hash real project
907- * inputs where needed. Ninja edges may reference many generated files.
938+ * Real source/header hashes are provided by scan_project() and .d files.
939+ * Ninja can reference many generated/internal files.
908940 */
909941 inputNode.hash .clear ();
910942
911943 add_node (inputNode);
944+
912945 task.add_input (inputNode.id );
913- }
946+ inputNodeIds. push_back (inputNode. id );
914947
915- for (const fs::path &input : edge.implicitInputs )
916- {
917- const BuildNodeKind inputKind = node_kind_from_ninja_input (input);
948+ const auto producerIt = outputToTask.find (inputNode.id );
949+ if (producerIt != outputToTask.end ())
950+ task.add_dependency (producerIt->second );
951+ };
918952
919- BuildNode inputNode = make_file_build_node (inputKind, input);
920- inputNode. hash . clear ( );
953+ for ( const fs::path &input : edge. explicitInputs )
954+ import_input (input );
921955
922- add_node (inputNode);
923- task.add_input (inputNode.id );
924- }
956+ for (const fs::path &input : edge.implicitInputs )
957+ import_input (input);
925958
926959 for (const fs::path &input : edge.orderOnlyInputs )
927- {
928- const BuildNodeKind inputKind = node_kind_from_ninja_input (input);
929-
930- BuildNode inputNode = make_file_build_node (inputKind, input);
931- inputNode.hash .clear ();
932-
933- add_node (inputNode);
934- task.add_input (inputNode.id );
935- }
960+ import_input (input);
936961
937962 for (const fs::path &output : edge.outputs )
938963 {
@@ -945,7 +970,7 @@ namespace vix::cli::build
945970 BuildNode outputNode = make_file_build_node (outputKind, output);
946971 outputNode.hash .clear ();
947972
948- for (const auto &inputId : task. inputs )
973+ for (const auto &inputId : inputNodeIds )
949974 outputNode.add_dependency (inputId);
950975
951976 add_node (outputNode);
@@ -956,6 +981,10 @@ namespace vix::cli::build
956981 continue ;
957982
958983 add_task (task);
984+
985+ for (const std::string &outputId : task.outputs )
986+ outputToTask[outputId] = task.id ;
987+
959988 ++imported;
960989 }
961990
0 commit comments