]> git.tdb.fi Git - builder.git/blobdiff - source/lib/target.cpp
Refactor transitive dependencies to work on all targets
[builder.git] / source / lib / target.cpp
index f13afb8d07bb68c8da074a217dbd6c6bbd5ee293..0d823eef6618814c01043f9a71805b562ccad536 100644 (file)
@@ -1,6 +1,7 @@
 #include <msp/core/algorithm.h>
 #include <msp/fs/stat.h>
 #include <msp/fs/utils.h>
+#include <msp/strings/utils.h>
 #include "builder.h"
 #include "filetarget.h"
 #include "sourcepackage.h"
@@ -11,6 +12,8 @@
 using namespace std;
 using namespace Msp;
 
+vector<Target *> Target::prepare_stack;
+
 Target::Target(Builder &b, const string &n):
        builder(b),
        name(n)
@@ -45,6 +48,50 @@ void Target::add_side_effect(Target &se)
        se.signal_bubble_rebuild.connect(sigc::mem_fun(this, &Target::check_rebuild));
 }
 
+bool Target::find_transitive_dependencies()
+{
+       vector<Target *> found_deps;
+       for(Target *d: depends)
+               if(!d->is_buildable())
+                       find_transitive_dependencies(*d, found_deps);
+
+       bool any_added = false;
+       for(Target *d: found_deps)
+               if(!any_equals(depends, d))
+               {
+                       any_added = true;
+                       add_dependency(*d);
+                       if(d->get_real_target()->is_buildable())
+                               d->signal_modified.connect([this]{ rescan_trans_deps = true; });
+               }
+
+       return any_added;
+}
+
+void Target::find_transitive_dependencies(Target &tgt, vector<Target *> &found_deps) const
+{
+       tgt.prepare();
+
+       Target *rtgt = tgt.get_real_target();
+       Dependencies deps_to_add = rtgt->get_transitive_dependencies();
+       if(rtgt!=&tgt)
+       {
+               for(Target *&d: deps_to_add)
+                       d = resolve_transitive_dependency(tgt, *d);
+       }
+
+       for(Target *d: deps_to_add)
+               if(d)
+               {
+                       auto i = lower_bound(found_deps, d);
+                       if(i==found_deps.end() || *i!=d)
+                       {
+                               found_deps.insert(i, d);
+                               find_transitive_dependencies(*d, found_deps);
+                       }
+               }
+}
+
 Target *Target::get_buildable_target()
 {
        if(primary_target)
@@ -67,7 +114,16 @@ Target *Target::get_buildable_target()
        }
 
        if(self_ok)
+       {
+               if(rescan_trans_deps)
+               {
+                       rescan_trans_deps = false;
+                       if(find_transitive_dependencies())
+                               return get_buildable_target();
+               }
+
                return this;
+       }
 
        return 0;
 }
@@ -111,16 +167,33 @@ void Target::mark_rebuild(const string &reason)
 
 void Target::prepare()
 {
+       if(primary_target && primary_target->state<PREPARING)
+               primary_target->prepare();
+
        if(state>PREPARING)
                return;
        if(state==PREPARING)
        {
-               builder.get_logger().log("problems", "Dependency cycle detected at %s", name);
-               problems.push_back("Dependency cycle detected");
+               auto i = find(prepare_stack, this);
+               if(i!=prepare_stack.end())
+               {
+                       string cycle;
+                       for(; i!=prepare_stack.end(); ++i)
+                               append(cycle, " -> ", (*i)->name);
+                       append(cycle, " -> ", name);
+                       builder.get_logger().log("problems", "Dependency cycle detected: %s", cycle);
+                       problems.push_back(format("Dependency cycle detected: %s", cycle));
+               }
+               else
+               {
+                       builder.get_logger().log("problems", "Dependency cycle detected at %s", name);
+                       problems.push_back("Dependency cycle detected");
+               }
                state = BROKEN;
                return;
        }
 
+       PushPrepare _push(this);
        state = PREPARING;
        /* Prepare existing dependencies early, because their information may be
        needed to find other dependencies. */
@@ -130,6 +203,7 @@ void Target::prepare()
                tool->prepare();
 
        find_dependencies();
+       find_transitive_dependencies();
        bool broken = !problems.empty();
 
        if(tool)