-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathcmd_self.cppm
More file actions
150 lines (132 loc) · 5.59 KB
/
Copy pathcmd_self.cppm
File metadata and controls
150 lines (132 loc) · 5.59 KB
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
// mcpp.cli.cmd_self — CLI parsing + routing for the `mcpp self` family,
// doctor, why, env and explain. Implementations live in mcpp.doctor.
module;
#include <cstdio>
#include <cstdlib>
export module mcpp.cli.cmd_self;
import std;
import mcpplibs.cmdline;
import mcpp.doctor;
import mcpp.toolchain.fingerprint; // MCPP_VERSION
import mcpp.home;
import mcpp.platform;
import mcpp.toolchain.triple; // pins::host_default_toolchain (WS8)
import mcpp.toolchain.msvc; // msvc_available_here
import mcpp.wire;
import mcpp.libs.json;
namespace mcpp::cli {
// `self env --format json`.
//
// Deliberately NOT `env_report()` plus a serializer. That path calls
// `config::load_or_init()`, which on a machine that has never run mcpp
// creates $MCPP_HOME -- measured: `config.toml`, `registry/`, `cache/`,
// `bin/`, `build-cache/`, `log/`, six entries, where `mcpp --version`,
// `xpkg parse` and `cache list` create none. Reporting where things ARE
// should not be what puts them there.
//
// So the machine path computes paths and reads what already exists. When
// nothing does, it says so (`initialized: false`) and reports the paths mcpp
// WOULD use, rather than creating them to be able to answer. A client asking
// "where is your home" on a fresh machine gets an answer and an untouched
// disk.
//
// The human path is unchanged: someone typing `mcpp self env` at a prompt
// expects mcpp to set itself up, and always has.
namespace {
nlohmann::json env_data_readonly() {
namespace fs = std::filesystem;
const auto home = mcpp::home::root();
std::error_code ec;
const auto registry = home / "registry";
const auto config = home / "config.toml";
const bool initialized = fs::exists(config, ec);
auto s = [](const fs::path& p) { return p.string(); };
return nlohmann::json{
{"initialized", initialized},
{"mcppHome", s(home)},
{"registry", s(registry)},
{"xlingsHome", s(registry)}, // registryDir unless overridden
{"xlingsBinary", s(registry / "bin" /
("xlings" + std::string(mcpp::platform::exe_suffix)))},
{"config", s(config)},
{"buildCache", s(mcpp::home::cache_root())},
{"mcppVersion", std::string(mcpp::toolchain::MCPP_VERSION)},
// What a build with nothing configured resolves on this host (WS8):
// the one function the first run uses. Probing for a usable MSVC is
// read-only, as the rest of this path is.
{"defaultToolchain", std::string(mcpp::toolchain::triple::pins::host_default_toolchain(
mcpp::toolchain::msvc::msvc_available_here(registry / "data" / "xpkgs")))},
};
}
} // namespace
export int cmd_env(const mcpplibs::cmdline::ParsedArgs& parsed) {
if (auto f = parsed.value("format")) {
auto fmt = mcpp::wire::parse_format(*f);
if (!fmt) {
std::println(stderr, "error: {}",
mcpp::wire::unsupported_format(*f));
return 2;
}
mcpp::wire::emit({
.kind = "mcpp.env",
.effects = {}, // this path creates nothing; see above
.data = env_data_readonly(),
});
return 0;
}
return mcpp::doctor::env_report();
}
export int cmd_doctor(const mcpplibs::cmdline::ParsedArgs& /*parsed*/) {
return mcpp::doctor::doctor_report();
}
export int cmd_why(const mcpplibs::cmdline::ParsedArgs& parsed) {
// `--format` IS ONLY DEFINED FOR THE `toolchain` TOPIC, and saying so
// beats emitting an envelope whose `data` silently omits what was asked
// for. `runtime` and `deps` have their own shapes and no consumer yet;
// inventing one here would publish a contract nobody has read.
if (auto f = parsed.value("format")) {
auto fmt = mcpp::wire::parse_format(*f);
if (!fmt) {
std::println(stderr, "error: {}",
mcpp::wire::unsupported_format(*f));
return 2;
}
const std::string topic = parsed.positional(0);
if (!topic.empty() && topic != "toolchain") {
std::println(stderr,
"error: --format json is defined for `mcpp why toolchain`; "
"'{}' has no machine-readable shape yet", topic);
return 2;
}
return mcpp::doctor::why_toolchain_json(
parsed.option_or_empty("target").value(),
parsed.option_or_empty("toolchain").value());
}
return mcpp::doctor::why_report(parsed.positional(0),
parsed.option_or_empty("features").value());
}
// Also called directly by the dispatcher for the legacy `--explain CODE` form.
export int cmd_explain(std::string_view code) {
return mcpp::doctor::explain_code(code);
}
export int cmd_self_version(const mcpplibs::cmdline::ParsedArgs& /*parsed*/) {
std::println("mcpp {}", mcpp::toolchain::MCPP_VERSION);
return 0;
}
export int cmd_self_init(const mcpplibs::cmdline::ParsedArgs& parsed) {
return mcpp::doctor::self_init(parsed.is_flag_set("force"));
}
export int cmd_self_config(const mcpplibs::cmdline::ParsedArgs& parsed) {
return mcpp::doctor::self_config(parsed.option_or_empty("mirror").value());
}
// Used both by `mcpp explain <CODE>` (top-level) and `mcpp self explain
// <CODE>` (legacy alias).
export int cmd_explain_action(const mcpplibs::cmdline::ParsedArgs& parsed) {
std::string code = parsed.positional(0);
if (code.empty()) {
std::println(stderr, "error: explain requires an error code (e.g. E0001)");
return 2;
}
return cmd_explain(code);
}
} // namespace mcpp::cli