Skip to content

Commit 9dff41d

Browse files
committed
feat(cli): add single-file binary export to vix build
2 parents 90c7d6a + b7561ef commit 9dff41d

3 files changed

Lines changed: 117 additions & 0 deletions

File tree

include/vix/cli/commands/run/RunDetail.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,13 @@ namespace vix::commands::RunCommand::detail
640640
*/
641641
int run_single_cpp(const Options &opt);
642642

643+
/**
644+
* @brief Build a single C++ script and return the produced executable path.
645+
*
646+
* This uses the same script engine as `vix run`, but stops after build.
647+
*/
648+
int build_script_executable(const Options &opt, fs::path &exePath);
649+
643650
/**
644651
* @brief Execute a single C++ file with the fast direct-compile engine.
645652
*/

include/vix/cli/process/Process.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,9 @@ namespace vix::cli::process
157157

158158
bool exportBin = false;
159159
std::string outPath;
160+
161+
bool singleCpp = false;
162+
fs::path cppFile;
160163
};
161164

162165
/**

src/commands/BuildCommand.cpp

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,18 @@
4040
#include <vix/cli/util/Strings.hpp>
4141
#include <vix/cli/util/Ui.hpp>
4242

43+
#include <vix/cli/commands/run/detail/ScriptProbe.hpp>
44+
#include <vix/cli/commands/run/detail/DirectScriptRunner.hpp>
45+
#include <vix/cli/commands/run/detail/ScriptCMake.hpp>
46+
#include <vix/cli/commands/run/RunDetail.hpp>
47+
4348
namespace fs = std::filesystem;
4449
using namespace vix::cli::style;
4550
namespace process = vix::cli::process;
4651
namespace util = vix::cli::util;
4752
namespace build = vix::cli::build;
4853
namespace artifact_cache = vix::cli::cache;
54+
namespace run_detail = vix::commands::RunCommand::detail;
4955

5056
namespace vix::commands::BuildCommand
5157
{
@@ -329,6 +335,31 @@ namespace vix::commands::BuildCommand
329335
{
330336
o.withMySql = true;
331337
}
338+
else if (!a.empty() && a[0] != '-')
339+
{
340+
if (o.singleCpp)
341+
{
342+
error("Only one single C++ source file can be passed to vix build.");
343+
exitCode = 2;
344+
return o;
345+
}
346+
347+
fs::path candidate = fs::path(a);
348+
if (candidate.extension() == ".cpp" ||
349+
candidate.extension() == ".cc" ||
350+
candidate.extension() == ".cxx")
351+
{
352+
o.singleCpp = true;
353+
o.cppFile = fs::absolute(candidate);
354+
}
355+
else
356+
{
357+
error("Unknown positional argument: " + a);
358+
hint("For single-file mode, pass a .cpp file.");
359+
exitCode = 2;
360+
return o;
361+
}
362+
}
332363
else if (a.rfind("--preset=", 0) == 0)
333364
{
334365
o.preset = a.substr(std::string("--preset=").size());
@@ -1072,6 +1103,9 @@ namespace vix::commands::BuildCommand
10721103
{
10731104
const fs::path cwd = fs::current_path();
10741105

1106+
if (opt_.singleCpp)
1107+
return run_single_cpp_build();
1108+
10751109
const auto planOpt = make_plan(opt_, cwd);
10761110
if (!planOpt)
10771111
{
@@ -1432,6 +1466,9 @@ namespace vix::commands::BuildCommand
14321466
return 0;
14331467
}
14341468

1469+
private:
1470+
int run_single_cpp_build();
1471+
14351472
private:
14361473
process::Options opt_;
14371474
process::Plan plan_{};
@@ -1467,6 +1504,76 @@ namespace vix::commands::BuildCommand
14671504
return cmd.run();
14681505
}
14691506

1507+
int BuildCommand::run_single_cpp_build()
1508+
{
1509+
if (opt_.cppFile.empty())
1510+
{
1511+
error("No C++ source file provided.");
1512+
return 1;
1513+
}
1514+
1515+
if (!fs::exists(opt_.cppFile))
1516+
{
1517+
error("Source file not found: " + opt_.cppFile.string());
1518+
return 1;
1519+
}
1520+
1521+
if (!opt_.exportBin && opt_.outPath.empty())
1522+
{
1523+
error("Single-file build requires --bin or --out <path>.");
1524+
hint("Example: vix build main.cpp --out app.exe");
1525+
return 2;
1526+
}
1527+
1528+
run_detail::Options runOpt{};
1529+
runOpt.singleCpp = true;
1530+
runOpt.cppFile = fs::absolute(opt_.cppFile);
1531+
1532+
runOpt.preset = opt_.preset;
1533+
runOpt.dir = opt_.dir;
1534+
runOpt.jobs = opt_.jobs;
1535+
runOpt.clean = opt_.clean;
1536+
1537+
runOpt.quiet = opt_.quiet;
1538+
runOpt.verbose = opt_.verbose;
1539+
1540+
runOpt.withSqlite = opt_.withSqlite;
1541+
runOpt.withMySql = opt_.withMySql;
1542+
1543+
runOpt.enableSanitizers = false;
1544+
runOpt.enableUbsanOnly = false;
1545+
1546+
runOpt.forceServerLike = false;
1547+
runOpt.forceScriptLike = true;
1548+
1549+
runOpt.watch = false;
1550+
runOpt.timeoutSec = 0;
1551+
runOpt.cwd.clear();
1552+
1553+
runOpt.runArgs.clear();
1554+
runOpt.runEnv.clear();
1555+
runOpt.scriptFlags = opt_.cmakeArgs;
1556+
1557+
fs::path exePath;
1558+
const int code = run_detail::build_script_executable(runOpt, exePath);
1559+
if (code != 0)
1560+
return code;
1561+
1562+
if (exePath.empty() || !fs::exists(exePath))
1563+
{
1564+
error("Built executable was not produced.");
1565+
return 1;
1566+
}
1567+
1568+
fs::path dest;
1569+
if (opt_.exportBin)
1570+
dest = fs::current_path() / exePath.filename();
1571+
else
1572+
dest = fs::absolute(fs::path(opt_.outPath));
1573+
1574+
return export_built_binary(exePath, dest, opt_.quiet) ? 0 : 1;
1575+
}
1576+
14701577
int help()
14711578
{
14721579
std::ostream &out = std::cout;

0 commit comments

Comments
 (0)