Skip to content

Commit 07cb8d7

Browse files
committed
feat(run): introduce ultra-fast direct C++ execution with smart CMake fallback
- add ScriptProbe to detect script complexity and choose execution strategy - implement DirectScriptRunner for zero-CMake fast path - introduce CMakeScriptPlan for clean fallback pipeline - fix missing exePath and configSignature in CMake plan - restore correct lifecycle: materialize -> compute -> configure -> build - reintroduce needConfigure logic for incremental builds - support --local-cache for isolated script builds Result: - simple scripts run instantly (~0.1s) - complex scripts fallback to CMake automatically - stable and deterministic build behavior
1 parent 5c95d2b commit 07cb8d7

8 files changed

Lines changed: 1778 additions & 333 deletions

File tree

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

Lines changed: 287 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,36 @@ namespace vix::commands::RunCommand::detail
5656
Manifest
5757
};
5858

59+
/**
60+
* @brief Execution strategy selected for a single C++ script.
61+
*
62+
* Direct means a fast compile-and-run path without generated CMake.
63+
* CMakeFallback means the legacy generated CMake project pipeline.
64+
*/
65+
enum class ScriptExecutionStrategy
66+
{
67+
None,
68+
Direct,
69+
CMakeFallback
70+
};
71+
72+
/**
73+
* @brief Coarse reason explaining why a script cannot use direct compilation.
74+
*/
75+
enum class ScriptFallbackReason
76+
{
77+
None,
78+
UsesCompiledDeps,
79+
UsesVixRuntime,
80+
UsesOrm,
81+
UsesDatabase,
82+
UsesMySql,
83+
RequiresCMakeTargets,
84+
UnsupportedFlags,
85+
UnsupportedLayout,
86+
Unknown
87+
};
88+
5989
/**
6090
* @brief Parsing state and user-facing options for `vix run`.
6191
*
@@ -180,6 +210,139 @@ namespace vix::commands::RunCommand::detail
180210
std::string runCmd;
181211
};
182212

213+
/**
214+
* @brief Parsed compile-related flags extracted from script mode arguments.
215+
*/
216+
struct ScriptCompileFlags
217+
{
218+
std::vector<std::string> includeDirs;
219+
std::vector<std::string> systemIncludeDirs;
220+
std::vector<std::string> defines;
221+
std::vector<std::string> compileOpts;
222+
};
223+
224+
/**
225+
* @brief Parsed link-related flags extracted from script mode arguments.
226+
*/
227+
struct ScriptLinkFlags
228+
{
229+
std::vector<std::string> libs;
230+
std::vector<std::string> libDirs;
231+
std::vector<std::string> linkOpts;
232+
};
233+
234+
/**
235+
* @brief High-level feature detection result for a C++ script.
236+
*/
237+
struct ScriptFeatures
238+
{
239+
bool usesVix = false;
240+
bool usesOrm = false;
241+
bool usesDb = false;
242+
bool usesMySql = false;
243+
};
244+
245+
/**
246+
* @brief Result of probing a script before choosing the execution engine.
247+
*
248+
* This structure gathers the information needed to decide whether the script
249+
* can be compiled directly or should fall back to the generated CMake path.
250+
*/
251+
struct ScriptProbeResult
252+
{
253+
ScriptExecutionStrategy strategy = ScriptExecutionStrategy::None;
254+
ScriptFallbackReason fallbackReason = ScriptFallbackReason::None;
255+
256+
bool canUseDirectCompile = false;
257+
bool shouldUseCMakeFallback = true;
258+
259+
bool usesVixRuntime = false;
260+
bool usesCompiledDeps = false;
261+
bool requiresCMakeTargets = false;
262+
263+
ScriptFeatures features;
264+
ScriptCompileFlags compileFlags;
265+
ScriptLinkFlags linkFlags;
266+
267+
std::vector<std::string> includeDirs;
268+
std::vector<std::string> systemIncludeDirs;
269+
std::vector<std::string> defines;
270+
std::vector<std::string> compileOpts;
271+
std::vector<std::string> libDirs;
272+
std::vector<std::string> libs;
273+
std::vector<std::string> linkOpts;
274+
275+
std::vector<std::string> orderedDepIds;
276+
std::vector<fs::path> compiledDepPaths;
277+
std::vector<fs::path> headerOnlyDepIncludeDirs;
278+
};
279+
280+
/**
281+
* @brief Cache metadata for a directly compiled script artifact.
282+
*/
283+
struct DirectScriptCacheState
284+
{
285+
fs::path rootDir;
286+
fs::path binaryPath;
287+
fs::path metaFile;
288+
fs::path stdoutLogPath;
289+
fs::path stderrLogPath;
290+
291+
std::string cacheKey;
292+
bool cacheHit = false;
293+
bool needsRebuild = true;
294+
};
295+
296+
/**
297+
* @brief Concrete plan for the fast direct-compile script path.
298+
*/
299+
struct DirectScriptPlan
300+
{
301+
fs::path scriptPath;
302+
fs::path workingDir;
303+
fs::path binaryPath;
304+
fs::path cacheDir;
305+
306+
std::string exeName;
307+
std::string cacheKey;
308+
std::string compileCmd;
309+
std::string runCmd;
310+
311+
bool shouldCompile = true;
312+
bool shouldRun = true;
313+
bool passthroughRuntime = false;
314+
int effectiveTimeoutSec = 0;
315+
316+
ScriptProbeResult probe;
317+
};
318+
319+
/**
320+
* @brief Concrete plan for the generated CMake fallback path.
321+
*/
322+
struct CMakeScriptPlan
323+
{
324+
fs::path scriptPath;
325+
fs::path scriptsRoot;
326+
fs::path projectDir;
327+
fs::path cmakeListsPath;
328+
fs::path buildDir;
329+
fs::path exePath;
330+
fs::path signatureFile;
331+
fs::path configureLogPath;
332+
fs::path buildLogPath;
333+
334+
std::string exeName;
335+
std::string targetName;
336+
std::string configSignature;
337+
338+
bool useVixRuntime = false;
339+
bool shouldConfigure = true;
340+
bool shouldBuild = true;
341+
bool shouldRun = true;
342+
bool passthroughRuntime = false;
343+
int effectiveTimeoutSec = 0;
344+
};
345+
183346
/**
184347
* @brief Result returned by script-mode execution helpers.
185348
*/
@@ -349,21 +512,125 @@ namespace vix::commands::RunCommand::detail
349512
bool alreadyHandled);
350513

351514
// ===========================================================================
352-
// Script mode
515+
// Script probing / planning
353516
// ===========================================================================
354517

355518
/**
356-
* @brief Return the root directory used for generated script projects.
519+
* @brief Detect whether a .cpp script uses the Vix runtime.
357520
*/
358-
fs::path get_scripts_root(bool localCache);
521+
bool script_uses_vix(const fs::path &cppPath);
359522

360523
/**
361-
* @brief Detect whether a .cpp script uses the Vix runtime.
524+
* @brief Detect high-level features used by a C++ script.
362525
*/
363-
bool script_uses_vix(const fs::path &cppPath);
526+
ScriptFeatures detect_script_features(const fs::path &cppPath);
527+
528+
/**
529+
* @brief Parse compile flags from script-mode forwarded arguments.
530+
*/
531+
ScriptCompileFlags parse_compile_flags(const std::vector<std::string> &flags);
532+
533+
/**
534+
* @brief Parse link flags from script-mode forwarded arguments.
535+
*/
536+
ScriptLinkFlags parse_link_flags(const std::vector<std::string> &flags);
537+
538+
/**
539+
* @brief Probe a single C++ script and choose the execution strategy.
540+
*/
541+
ScriptProbeResult probe_single_cpp_script(const Options &opt);
542+
543+
/**
544+
* @brief Return true when the probed script can use direct compilation.
545+
*
546+
* The direct path is intentionally strict. It is reserved for simple,
547+
* single-file, header-only friendly scripts that do not require runtime
548+
* targets, compiled dependencies, or custom link steps.
549+
*/
550+
inline bool script_can_use_direct_compile(const ScriptProbeResult &probe) noexcept
551+
{
552+
if (!probe.canUseDirectCompile)
553+
return false;
554+
555+
if (probe.strategy != ScriptExecutionStrategy::Direct)
556+
return false;
557+
558+
if (probe.usesVixRuntime)
559+
return false;
560+
561+
if (probe.usesCompiledDeps)
562+
return false;
563+
564+
if (probe.requiresCMakeTargets)
565+
return false;
566+
567+
if (!probe.libs.empty())
568+
return false;
569+
570+
if (!probe.libDirs.empty())
571+
return false;
572+
573+
if (!probe.linkOpts.empty())
574+
return false;
575+
576+
if (!probe.compiledDepPaths.empty())
577+
return false;
578+
579+
return true;
580+
}
581+
582+
/**
583+
* @brief Return true when the probed script should use generated CMake fallback.
584+
*/
585+
inline bool script_needs_cmake_fallback(const ScriptProbeResult &probe) noexcept
586+
{
587+
return probe.shouldUseCMakeFallback ||
588+
probe.strategy == ScriptExecutionStrategy::CMakeFallback;
589+
}
364590

365591
/**
366-
* @brief Generate the CMakeLists.txt content used by script mode.
592+
* @brief Build the fast direct-compile plan for a probed script.
593+
*/
594+
DirectScriptPlan make_direct_script_plan(
595+
const Options &opt,
596+
const ScriptProbeResult &probe);
597+
598+
/**
599+
* @brief Build the generated CMake fallback plan for a probed script.
600+
*/
601+
CMakeScriptPlan make_cmake_script_plan(
602+
const Options &opt,
603+
const ScriptProbeResult &probe);
604+
605+
/**
606+
* @brief Compute the global cache root used for directly compiled scripts.
607+
*/
608+
fs::path get_direct_scripts_cache_root();
609+
610+
/**
611+
* @brief Compute the cache key used for a directly compiled script.
612+
*/
613+
std::string make_direct_script_cache_key(
614+
const fs::path &cppPath,
615+
const ScriptProbeResult &probe,
616+
const Options &opt);
617+
618+
/**
619+
* @brief Load the cache state for a directly compiled script plan.
620+
*/
621+
DirectScriptCacheState load_direct_script_cache_state(const DirectScriptPlan &plan);
622+
623+
// ===========================================================================
624+
// Script mode execution
625+
// ===========================================================================
626+
627+
/**
628+
* @brief Return the root directory used for generated script projects.
629+
*/
630+
fs::path get_scripts_root(bool localCache);
631+
632+
/**
633+
* @brief Generate the CMakeLists.txt content used by script fallback mode.
367634
*/
368635
std::string make_script_cmakelists(
369636
const std::string &exeName,
@@ -374,10 +641,23 @@ namespace vix::commands::RunCommand::detail
374641
bool withMySql);
375642

376643
/**
377-
* @brief Execute a single C++ file with the script pipeline.
644+
* @brief Execute a single C++ file using the best available script engine.
645+
*
646+
* This dispatcher probes the script first, then selects either the direct
647+
* compile path or the generated CMake fallback path.
378648
*/
379649
int run_single_cpp(const Options &opt);
380650

651+
/**
652+
* @brief Execute a single C++ file with the fast direct-compile engine.
653+
*/
654+
int run_single_cpp_direct(const Options &opt, const DirectScriptPlan &plan);
655+
656+
/**
657+
* @brief Execute a single C++ file with the generated CMake fallback engine.
658+
*/
659+
int run_single_cpp_cmake(const Options &opt, const CMakeScriptPlan &plan);
660+
381661
/**
382662
* @brief Execute a single C++ file in watch mode.
383663
*/
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/**
2+
*
3+
* @file DirectScriptRunner.hpp
4+
* @author Gaspard Kirira
5+
*
6+
* Copyright 2025, Gaspard Kirira. All rights reserved.
7+
* https://github.com/vixcpp/vix
8+
* Use of this source code is governed by an MIT license
9+
* that can be found in the License file.
10+
*
11+
* Vix.cpp
12+
*
13+
*/
14+
#ifndef VIX_CLI_DIRECT_SCRIPT_RUNNER_HPP
15+
#define VIX_CLI_DIRECT_SCRIPT_RUNNER_HPP
16+
17+
#include <filesystem>
18+
#include <string>
19+
20+
#include <vix/cli/commands/run/RunDetail.hpp>
21+
22+
namespace vix::commands::RunCommand::detail
23+
{
24+
namespace fs = std::filesystem;
25+
26+
/**
27+
* @brief Return the global cache root used for directly compiled scripts.
28+
*/
29+
fs::path get_direct_scripts_cache_root();
30+
31+
/**
32+
* @brief Compute the cache key used for a directly compiled script.
33+
*/
34+
std::string make_direct_script_cache_key(
35+
const fs::path &cppPath,
36+
const ScriptProbeResult &probe,
37+
const Options &opt);
38+
39+
/**
40+
* @brief Load cache metadata for a direct script plan.
41+
*/
42+
DirectScriptCacheState load_direct_script_cache_state(const DirectScriptPlan &plan);
43+
44+
/**
45+
* @brief Build a direct compile plan for a probed script.
46+
*/
47+
DirectScriptPlan make_direct_script_plan(
48+
const Options &opt,
49+
const ScriptProbeResult &probe);
50+
51+
/**
52+
* @brief Execute a single C++ file with the fast direct-compile engine.
53+
*/
54+
int run_single_cpp_direct(const Options &opt, const DirectScriptPlan &plan);
55+
56+
} // namespace vix::commands::RunCommand::detail
57+
58+
#endif

0 commit comments

Comments
 (0)