aboutsummaryrefslogtreecommitdiff
path: root/bdep/sync.cxx
blob: 3d2ee9cea83474d04b99f96dba63026b061a54f6 (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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// file      : bdep/sync.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <bdep/sync.hxx>

#include <bdep/database.hxx>
#include <bdep/diagnostics.hxx>

#include <bdep/fetch.hxx>

using namespace std;

namespace bdep
{
  void
  cmd_sync (const common_options& co,
            const dir_path& prj,
            const shared_ptr<configuration>& c,
            bool fetch,
            bool yes)
  {
    assert (!c->packages.empty ());

    // Do a separate fetch instead of letting pkg-build do it. This way we get
    // better control of the diagnostics (no "fetching ..." for the project
    // itself). We also make sure that if the user specifies a repository for
    // a dependency to upgrade, then that repository is listed as part of the
    // project prerequisites/complements. Or, in other words, we only want to
    // allow specifying the location as a proxy for specifying version (i.e.,
    // "I want this dependency upgraded to the latest version available from
    // this repository").
    //
    // We also use the repository name rather than then location as a sanity
    // check (the repository must have been added as part of init).
    //
    if (fetch)
      run_bpkg (co,
                "fetch",
                "-d", c->path,
                "--shallow",
                "dir:" + prj.string ());

    // Prepare the pkg-spec.
    //
    string spec;
    for (const package_state& p: c->packages)
    {
      if (!spec.empty ())
        spec += ',';

      spec += p.name;
    }

    spec += '@';
    spec += prj.string ();

    // It feels right to drop unused dependencies without any confirmation.
    //
    // @@ TODO: right now it is silent. Can we print a plan without the
    //    prompts? Probably also a good idea even if from build system
    //    hook... Issue: drop dependents has no "drop " prefix. Also indented
    //    stuff out of nowhere will look odd.
    //
    run_bpkg (co,
              "build",
              "-d", c->path,
              "--no-fetch",
              "--configure-only",
              "--drop-prerequisite",
              "--keep-out",
              (yes ? "--yes" : nullptr),
              spec);
  }

  int
  cmd_sync (const cmd_sync_options& o, cli::scanner&)
  {
    tracer trace ("sync");

    // The --immediate or --recursive option can only be specified with
    // an explicit --upgrade or --patch.
    //
    if (const char* n = (o.immediate () ? "--immediate" :
                         o.recursive () ? "--recursive" : nullptr))
    {
      if (!o.upgrade () && !o.patch ())
        fail << n << " requires explicit --upgrade|-u or --patch|-p";
    }

    // We could be running from a package directory (or the user specified one
    // with -d) that has not been init'ed in this configuration. We want to
    // diagnose that since such a package will not be present in the bpkg
    // configuration. But if we are running from the project, then we don't
    // want to treat all the available packages as specified by the user (thus
    // load_packages=false).
    //
    project_packages pp (
      find_project_packages (o,
                             false /* ignore_packages */,
                             false /* load_packages */));

    const dir_path& prj (pp.project);

    database db (open (prj, trace));

    transaction t (db.begin ());
    configurations cfgs (find_configurations (prj, t, o));
    t.commit ();

    // If specified, verify packages are present in each configuration.
    //
    if (!pp.packages.empty ())
      verify_project_packages (pp, cfgs);

    // Synchronize each configuration skipping empty ones.
    //
    bool first (true);
    for (const shared_ptr<configuration>& c: cfgs)
    {
      if (c->packages.empty ())
      {
        if (verb)
          info << "skipping empty configuration " << *c;

        continue;
      }

      // If we are synchronizing multiple configurations, separate them with a
      // blank line and print the configuration name/directory.
      //
      if (verb && cfgs.size () > 1)
      {
        text << (first ? "" : "\n")
             << "synchronizing with configuration " << *c;

        first = false;
      }

      bool fetch (o.fetch () || o.fetch_full ());

      if (fetch)
        cmd_fetch (o, prj, c, o.fetch_full ());

      // Don't re-fetch if we just fetched.
      //
      cmd_sync (o, prj, c, !fetch);

      //@@ TODO: sync upgrade (see status for structure ideas). Pass o.yes().
    }

    return 0;
  }
}