aboutsummaryrefslogtreecommitdiff
path: root/build/cxx/module.cxx
blob: 495819d9a382d7848a6c58578b129de8a47a7b5a (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
// file      : build/cxx/module.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <build/cxx/module>

#include <butl/process>
#include <butl/fdstream>

#include <build/scope>
#include <build/diagnostics>

#include <build/bin/module>

#include <build/config/utility>

using namespace std;
using namespace butl;

namespace build
{
  namespace cxx
  {
    void
    init (scope& root, scope& base, const location& l)
    {
      //@@ TODO: avoid multiple inits (generally, for modules).
      //

      tracer trace ("cxx::init");

      //@@ Should it be this way?
      //
      if (&root != &base)
        fail (l) << "cxx module must be initialized in project root scope";

      // Initialize the bin module.
      //
      bin::init (root, base, l);

      //@@ TODO: need to register target types, rules here instead of main().

      const dir_path& out_root (root.path ());
      level4 ([&]{trace << "for " << out_root;});

      // Configure.
      //

      // config.cxx
      //
      {
        auto r (config::required (root, "config.cxx", "g++"));

        // If we actually set a new value, test it by trying to execute.
        //
        if (r.second)
        {
          const string& cxx (r.first);
          const char* args[] = {cxx.c_str (), "-dumpversion", nullptr};

          if (verb)
            print_process (args);
          else
            text << "test " << cxx;

          string ver;
          try
          {
            process pr (args, false, false, true);
            ifdstream is (pr.in_ofd);

            bool r (getline (is, ver));

            if (!pr.wait ())
              throw failed ();

            if (!r)
              fail << "unexpected output from " << cxx;
          }
          catch (const process_error& e)
          {
            error << "unable to execute " << cxx << ": " << e.what ();

            if (e.child ())
              exit (1);

            throw failed ();
          }

          if (verb)
            text << cxx << " " << ver;
        }
      }

      // config.cxx.{p,c,l}options
      // config.cxx.libs
      //
      // These are optional. We also merge them into the corresponding
      // cxx.* variables.
      //
      if (auto* v = config::optional<list_value> (root, "config.cxx.poptions"))
        root.append ("cxx.poptions") += *v;

      if (auto* v = config::optional<list_value> (root, "config.cxx.coptions"))
        root.append ("cxx.coptions") += *v;

      if (auto* v = config::optional<list_value> (root, "config.cxx.loptions"))
        root.append ("cxx.loptions") += *v;

      if (auto* v = config::optional<list_value> (root, "config.cxx.libs"))
        root.append ("cxx.libs") += *v;
    }
  }
}