Skip to content

Commit d0b0dff

Browse files
committed
fix(run): resolve executable targets from build dirs (bin/, scan) instead of assuming project name
1 parent 631c5f7 commit d0b0dff

1 file changed

Lines changed: 145 additions & 9 deletions

File tree

src/commands/RunCommand.cpp

Lines changed: 145 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,122 @@ namespace
137137
return out;
138138
}
139139

140+
static std::optional<fs::path> resolve_runnable_executable(
141+
const fs::path &buildDir,
142+
const std::string &projectName)
143+
{
144+
auto executable_name = [](const std::string &name) -> std::string
145+
{
146+
#ifdef _WIN32
147+
return name + ".exe";
148+
#else
149+
return name;
150+
#endif
151+
};
152+
153+
auto is_executable_candidate = [](const fs::path &p) -> bool
154+
{
155+
std::error_code ec{};
156+
157+
if (!fs::is_regular_file(p, ec) || ec)
158+
return false;
159+
160+
#ifdef _WIN32
161+
return p.extension() == ".exe";
162+
#else
163+
const auto perms = fs::status(p, ec).permissions();
164+
if (ec)
165+
return false;
166+
167+
using pr = fs::perms;
168+
return (perms & pr::owner_exec) != pr::none ||
169+
(perms & pr::group_exec) != pr::none ||
170+
(perms & pr::others_exec) != pr::none;
171+
#endif
172+
};
173+
174+
auto looks_like_test_binary = [](const fs::path &p) -> bool
175+
{
176+
const std::string n = p.filename().string();
177+
return n.find("_test") != std::string::npos ||
178+
n.find("_tests") != std::string::npos ||
179+
n.rfind("test_", 0) == 0;
180+
};
181+
182+
const std::string exeFileName = executable_name(projectName);
183+
184+
// 1) Exact common locations
185+
const std::vector<fs::path> preferred = {
186+
buildDir / exeFileName,
187+
buildDir / "bin" / exeFileName,
188+
buildDir / "src" / exeFileName};
189+
190+
for (const auto &p : preferred)
191+
{
192+
if (is_executable_candidate(p))
193+
return p;
194+
}
195+
196+
// 2) Recursive scan
197+
std::vector<fs::path> exactNameCandidates;
198+
std::vector<fs::path> otherCandidates;
199+
200+
std::error_code ec{};
201+
for (auto it = fs::recursive_directory_iterator(
202+
buildDir,
203+
fs::directory_options::skip_permission_denied,
204+
ec);
205+
!ec && it != fs::recursive_directory_iterator();
206+
++it)
207+
{
208+
const fs::path p = it->path();
209+
210+
if (p.string().find("CMakeFiles") != std::string::npos)
211+
continue;
212+
213+
if (!is_executable_candidate(p))
214+
continue;
215+
216+
if (looks_like_test_binary(p))
217+
continue;
218+
219+
#ifdef _WIN32
220+
const std::string baseName = p.stem().string();
221+
#else
222+
const std::string baseName = p.filename().string();
223+
#endif
224+
225+
if (baseName == projectName)
226+
exactNameCandidates.push_back(p);
227+
else
228+
otherCandidates.push_back(p);
229+
}
230+
231+
auto prefer_bin_path = [](const fs::path &a, const fs::path &b) -> bool
232+
{
233+
const bool aBin = a.string().find("/bin/") != std::string::npos ||
234+
a.string().find("\\bin\\") != std::string::npos;
235+
const bool bBin = b.string().find("/bin/") != std::string::npos ||
236+
b.string().find("\\bin\\") != std::string::npos;
237+
238+
if (aBin != bBin)
239+
return aBin;
240+
241+
return a.string().size() < b.string().size();
242+
};
243+
244+
if (!exactNameCandidates.empty())
245+
{
246+
std::sort(exactNameCandidates.begin(), exactNameCandidates.end(), prefer_bin_path);
247+
return exactNameCandidates.front();
248+
}
249+
250+
if (otherCandidates.size() == 1)
251+
return otherCandidates.front();
252+
253+
return std::nullopt;
254+
}
255+
140256
void apply_manifest_auto_deps_includes(Options &opt, const fs::path &manifestFile)
141257
{
142258
const fs::path manifestDir = manifestFile.parent_path();
@@ -797,10 +913,18 @@ namespace
797913
progress.phase_start("Run application");
798914

799915
const std::string exeName = projectDir.filename().string();
800-
fs::path exePath = buildDir / exeName;
801-
#ifdef _WIN32
802-
exePath += ".exe";
803-
#endif
916+
auto exePathOpt = resolve_runnable_executable(buildDir, exeName);
917+
918+
if (!exePathOpt)
919+
{
920+
error("Built executable not found for project: " + exeName);
921+
hint("Resolved build directory: " + buildDir.string());
922+
hint("No runnable application target could be resolved automatically.");
923+
hint("If your executable uses a custom output path or custom target name, add a manifest field to specify it.");
924+
return 1;
925+
}
926+
927+
fs::path exePath = *exePathOpt;
804928

805929
if (!fs::exists(exePath))
806930
{
@@ -960,10 +1084,18 @@ namespace
9601084
std::cout << "\n";
9611085

9621086
const std::string exeName = projectDir.filename().string();
963-
fs::path exePath = buildDir / exeName;
964-
#ifdef _WIN32
965-
exePath += ".exe";
966-
#endif
1087+
auto exePathOpt = resolve_runnable_executable(buildDir, exeName);
1088+
1089+
if (!exePathOpt)
1090+
{
1091+
error("Built executable not found for project: " + exeName);
1092+
hint("Resolved build directory: " + buildDir.string());
1093+
hint("No runnable application target could be resolved automatically.");
1094+
hint("If your executable uses a custom output path or custom target name, add a manifest field to specify it.");
1095+
return 1;
1096+
}
1097+
1098+
fs::path exePath = *exePathOpt;
9671099

9681100
if (exeName == "vix")
9691101
{
@@ -1161,7 +1293,9 @@ namespace vix::commands::RunCommand
11611293
out << " --auto-deps=local Same as --auto-deps\n";
11621294
out << " --auto-deps=up Also search deps in parent folders (future/optional)\n";
11631295
out << " --san Enable ASan and UBSan\n";
1164-
out << " --ubsan Enable UBSan only\n\n";
1296+
out << " --ubsan Enable UBSan only\n";
1297+
out << " --with-sqlite Enable SQLite support for script mode\n";
1298+
out << " --with-mysql Enable MySQL support for script mode\n\n";
11651299

11661300
out << "Documentation:\n";
11671301
out << " --docs Enable auto docs (sets VIX_DOCS=1)\n";
@@ -1200,6 +1334,8 @@ namespace vix::commands::RunCommand
12001334
out << " # Script mode (.cpp)\n";
12011335
out << " vix run main.cpp --cwd ./data --args --config --args config.json\n";
12021336
out << " vix run main.cpp --run hello 123 test\n";
1337+
out << " vix run main.cpp --with-sqlite\n";
1338+
out << " vix run main.cpp --with-mysql\n";
12031339
out << " vix run main.cpp -- -O2 -DNDEBUG --run hello 123\n";
12041340
out << " vix run main.cpp -- -lssl -lcrypto\n\n";
12051341

0 commit comments

Comments
 (0)