aboutsummaryrefslogtreecommitdiff
path: root/bpkg/rep-list.cxx
blob: 73f1bb5884ad4554f505ac0717f7fdc654b6f90a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// file      : bpkg/rep-list.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <bpkg/rep-list.hxx>

#include <set>
#include <iostream>  // cout

#include <bpkg/package.hxx>
#include <bpkg/package-odb.hxx>
#include <bpkg/database.hxx>
#include <bpkg/diagnostics.hxx>

using namespace std;

namespace bpkg
{
  // Print the repository dependencies, recursively.
  //
  // Each line has the following form:
  //
  // [(complement|prerequisite) ]<name> <location>[ (<fragment>)]
  //
  // and is indented with 2 additional spaces for each recursion level.
  //
  // Note that we can end up with a repository dependency cycle via
  // prerequisites. Thus we need to make sure that the repository is not in
  // the dependency chain yet.
  //
  using repositories = set<reference_wrapper<const shared_ptr<repository>>,
                           compare_reference_target>;

  static void
  print_dependencies (const rep_list_options& o,
                      const shared_ptr<repository>& r,
                      string& indent,
                      repositories& chain)
  {
    assert (!r->name.empty ()); // Can't be the root repository.

    if (!chain.insert (r).second) // Is already in the chain.
      return;

    indent += "  ";

    auto print_repo = [&o, &indent, &chain] (
      const shared_ptr<repository>& r,
      const char* role,
      const repository::fragment_type& fr)
    {
      cout << indent << role << ' ' << r->name << ' ' << r->location;

      if (!fr.friendly_name.empty ())
        cout << " ("  << fr.friendly_name << ")";

      cout << endl;

      print_dependencies (o, r, indent, chain);
    };

    for (const repository::fragment_type& rfr: r->fragments)
    {
      shared_ptr<repository_fragment> fr (rfr.fragment.load ());

      if (o.complements ())
      {
        for (const lazy_weak_ptr<repository>& rp: fr->complements)
        {
          // Skip the root complement (see rep_fetch() for details).
          //
          if (rp.object_id () == "")
            continue;

          print_repo (rp.load (), "complement", rfr);
        }
      }

      if (o.prerequisites ())
      {
        for (const lazy_weak_ptr<repository>& rp: fr->prerequisites)
          print_repo (rp.load (), "prerequisite", rfr);
      }
    }

    indent.resize (indent.size () - 2);
    chain.erase (r);
  }

  static inline void
  print_dependencies (const rep_list_options& o,
                      const shared_ptr<repository>& r)
  {
    string indent;
    repositories chain;
    print_dependencies (o, r, indent, chain);
  }

  int
  rep_list (const rep_list_options& o, cli::scanner& args)
  {
    tracer trace ("rep_list");

    dir_path c (o.directory ());
    l4 ([&]{trace << "configuration: " << c;});

    if (args.more ())
      fail << "unexpected argument '" << args.next () << "'" <<
        info << "run 'bpkg help rep-list' for more information";

    database db (open (c, trace));
    transaction t (db);
    session s; // Repository dependencies can have cycles.

    shared_ptr<repository_fragment> root (db.load<repository_fragment> (""));

    for (const lazy_weak_ptr<repository>& rp: root->complements)
    {
      shared_ptr<repository> r (rp.load ());
      cout << r->location.canonical_name () << " " << r->location << endl;

      if (o.complements () || o.prerequisites ())
        print_dependencies (o, r);
    }

    t.commit ();

    return 0;
  }
}