Skip to content

Commit eade49e

Browse files
committed
feat: build profiles ([profile.<name>] + --profile)
Adds bundled build settings: built-in release (-O2) / dev (-O0 -g) / dist (-O3 -flto -s), overridable/extendable via [profile.<name>] (opt/debug/lto/ strip) in mcpp.toml. --profile selects; flags.cppm applies opt/debug/lto to compile and lto/strip to link. Verified: release/dev/dist emit distinct flags.
1 parent b17adb9 commit eade49e

3 files changed

Lines changed: 67 additions & 6 deletions

File tree

src/build/flags.cppm

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,14 @@ CompileFlags compute_flags(const BuildPlan& plan) {
198198
// AR binary
199199
f.arBinary = mcpp::toolchain::archive_tool(plan.toolchain);
200200

201-
// Opt level (musl ICE workaround)
202-
std::string opt_flag = isMuslTc ? " -Og" : " -O2";
201+
// Opt level + debug come from the resolved build profile
202+
// ([profile.<name>] → buildConfig). musl keeps -Og as an ICE workaround
203+
// unless the profile pins -O0.
204+
auto& prof = plan.manifest.buildConfig;
205+
std::string opt_flag = isMuslTc && prof.optLevel != "0"
206+
? " -Og" : (" -O" + prof.optLevel);
207+
if (prof.debug) opt_flag += " -g";
208+
if (prof.lto) opt_flag += " -flto";
203209

204210
// User link flags
205211
std::string user_ldflags;
@@ -279,13 +285,17 @@ CompileFlags compute_flags(const BuildPlan& plan) {
279285
payload_ld += " -Wl,--dynamic-linker=" + escape_path(loader);
280286
}
281287

288+
std::string link_extra;
289+
if (prof.lto) link_extra += " -flto";
290+
if (prof.strip) link_extra += " -s";
291+
282292
if constexpr (mcpp::platform::is_windows) {
283-
f.ld = user_ldflags;
293+
f.ld = user_ldflags + link_extra;
284294
} else if constexpr (mcpp::platform::needs_explicit_libcxx) {
285-
f.ld = std::format("{}{}{} -lc++{}", full_static, static_stdlib, b_flag, user_ldflags);
295+
f.ld = std::format("{}{}{} -lc++{}{}", full_static, static_stdlib, b_flag, user_ldflags, link_extra);
286296
} else {
287-
f.ld = std::format("{}{}{}{}{}{}{}", full_static, static_stdlib, link_toolchain_flags, b_flag,
288-
runtime_dirs, payload_ld, user_ldflags);
297+
f.ld = std::format("{}{}{}{}{}{}{}{}", full_static, static_stdlib, link_toolchain_flags, b_flag,
298+
runtime_dirs, payload_ld, user_ldflags, link_extra);
289299
}
290300

291301
return f;

src/cli.cppm

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,7 @@ struct BuildOverrides {
11561156
std::string target_triple; // empty = host triple, fall through to [toolchain]
11571157
bool force_static = false; // --static (or implied by musl target)
11581158
std::string package_filter; // -p <name>: only build this workspace member
1159+
std::string profile; // --profile <name> (default "release")
11591160
};
11601161

11611162
// `prepare_build` builds the BuildContext for any verb that compiles.
@@ -1312,6 +1313,21 @@ prepare_build(bool print_fingerprint,
13121313
// 1. project mcpp.toml [toolchain].<platform> or .default
13131314
// 2. global ~/.mcpp/config.toml [toolchain].default
13141315
// 3. hard error (no system fallback)
1316+
// Resolve the build profile: --profile (default "release") → built-in
1317+
// defaults, overlaid by any [profile.<name>] from the manifest → buildConfig.
1318+
{
1319+
std::string pname = overrides.profile.empty() ? "release" : overrides.profile;
1320+
mcpp::manifest::Profile pr;
1321+
if (pname == "dev" || pname == "debug") { pr.optLevel = "0"; pr.debug = true; }
1322+
else if (pname == "dist") { pr.optLevel = "3"; pr.lto = true; pr.strip = true; }
1323+
else { pr.optLevel = "2"; } // release
1324+
if (auto it = m->profiles.find(pname); it != m->profiles.end()) pr = it->second;
1325+
m->buildConfig.optLevel = pr.optLevel;
1326+
m->buildConfig.debug = pr.debug;
1327+
m->buildConfig.lto = pr.lto;
1328+
m->buildConfig.strip = pr.strip;
1329+
}
1330+
13151331
auto tcSpec = m->toolchain.for_platform(kCurrentPlatform);
13161332
if (!tcSpec.has_value()) {
13171333
auto cfg = get_cfg();
@@ -3530,6 +3546,7 @@ int cmd_build(const mcpplibs::cmdline::ParsedArgs& parsed) {
35303546
BuildOverrides ov;
35313547
if (auto t = parsed.value("target")) ov.target_triple = *t;
35323548
if (auto p = parsed.value("package")) ov.package_filter = *p;
3549+
if (auto pr = parsed.value("profile")) ov.profile = *pr;
35333550
ov.force_static = parsed.is_flag_set("static");
35343551

35353552
// P0: try fast-path if inputs haven't changed.
@@ -5576,6 +5593,8 @@ int run(int argc, char** argv) {
55765593
"Force static linking (-static). On Linux, prefer pairing with --target <arch>-linux-musl"))
55775594
.option(cl::Option("package").short_name('p').takes_value().value_name("NAME")
55785595
.help("Build only the named workspace member"))
5596+
.option(cl::Option("profile").takes_value().value_name("NAME")
5597+
.help("Build profile: release (default) | dev | dist | <[profile.*] name>"))
55795598
.action(wrap_rc(cmd_build)))
55805599
.subcommand(cl::App("run")
55815600
.description("Build + run a binary target (after `--`, args are passed to it)")

src/manifest.cppm

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ struct BuildConfig {
103103
std::vector<std::string> cxxflags;
104104
std::vector<std::string> ldflags;
105105
std::string cStandard;
106+
// Resolved build-profile knobs (from [profile.<name>] + built-in defaults).
107+
std::string optLevel = "2"; // -O level
108+
bool debug = false; // -g
109+
bool lto = false; // -flto
110+
bool strip = false; // link -s
106111
};
107112

108113
// `[runtime]` — requirements needed when launching built binaries.
@@ -175,6 +180,14 @@ struct WorkspaceConfig {
175180
bool present = false;
176181
};
177182

183+
// [profile.<name>] — bundled build settings (opt level, debug, lto, strip).
184+
struct Profile {
185+
std::string optLevel = "2";
186+
bool debug = false;
187+
bool lto = false;
188+
bool strip = false;
189+
};
190+
178191
struct Manifest {
179192
std::filesystem::path sourcePath; // mcpp.toml's filesystem path
180193

@@ -191,6 +204,7 @@ struct Manifest {
191204
Toolchain toolchain; // optional; empty == fallback
192205
BuildConfig buildConfig;
193206
RuntimeConfig runtimeConfig;
207+
std::map<std::string, Profile> profiles; // [profile.<name>]
194208

195209
// [target.<triple>] tables — empty if user didn't declare any.
196210
std::map<std::string, TargetEntry> targetOverrides;
@@ -469,6 +483,24 @@ std::expected<Manifest, ManifestError> parse_string(std::string_view content,
469483
}
470484

471485
// [targets.*] — M5.0: now optional. If absent, defer to auto-inference (in load()).
486+
// [profile.<name>] — bundled build settings.
487+
if (auto* profile_table = doc->get_table("profile");
488+
profile_table && !profile_table->empty()) {
489+
for (auto& [pname, pval] : *profile_table) {
490+
if (!pval.is_table()) continue;
491+
auto& tt = pval.as_table();
492+
Profile pr;
493+
if (auto it = tt.find("opt"); it != tt.end()) {
494+
if (it->second.is_string()) pr.optLevel = it->second.as_string();
495+
else if (it->second.is_int()) pr.optLevel = std::to_string(it->second.as_int());
496+
}
497+
if (auto it = tt.find("debug"); it != tt.end() && it->second.is_bool()) pr.debug = it->second.as_bool();
498+
if (auto it = tt.find("lto"); it != tt.end() && it->second.is_bool()) pr.lto = it->second.as_bool();
499+
if (auto it = tt.find("strip"); it != tt.end() && it->second.is_bool()) pr.strip = it->second.as_bool();
500+
m.profiles[pname] = pr;
501+
}
502+
}
503+
472504
auto* targets_table = doc->get_table("targets");
473505
if (targets_table && !targets_table->empty()) {
474506
for (auto& [tname, tval] : *targets_table) {

0 commit comments

Comments
 (0)