Skip to content

Commit e265ad7

Browse files
committed
fix(errors): detect missing CMake link targets
1 parent 809a610 commit e265ad7

2 files changed

Lines changed: 129 additions & 25 deletions

File tree

src/cmake/CMakeBuild.cpp

Lines changed: 56 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -426,6 +426,58 @@ namespace vix::cli::build
426426
currentProgressLine.clear();
427427
};
428428

429+
auto is_failed_line = [](const std::string &line) -> bool
430+
{
431+
return line.rfind("FAILED:", 0) == 0;
432+
};
433+
434+
auto looks_like_live_error_line = [&](const std::string &line) -> bool
435+
{
436+
if (line.empty())
437+
return false;
438+
439+
if (is_failed_line(line))
440+
return true;
441+
442+
if (line.rfind("ninja:", 0) == 0)
443+
return true;
444+
445+
if (line.find(": error:") != std::string::npos)
446+
return true;
447+
448+
if (line.find(" error: ") != std::string::npos)
449+
return true;
450+
451+
if (line.find("fatal error:") != std::string::npos)
452+
return true;
453+
454+
if (line.find("undefined reference to") != std::string::npos)
455+
return true;
456+
457+
if (line.find("collect2: error:") != std::string::npos)
458+
return true;
459+
460+
if (line.find("ld: error:") != std::string::npos)
461+
return true;
462+
463+
if (line.find("CMake Error") != std::string::npos)
464+
return true;
465+
466+
if (line.rfind("-->", 0) == 0)
467+
return true;
468+
469+
if (line == "code:")
470+
return true;
471+
472+
if (line.rfind("hint:", 0) == 0)
473+
return true;
474+
475+
if (line.rfind("at:", 0) == 0)
476+
return true;
477+
478+
return false;
479+
};
480+
429481
auto should_echo_line = [&](const std::string &line) -> bool
430482
{
431483
if (quiet)
@@ -437,6 +489,9 @@ namespace vix::cli::build
437489
if (!progressOnly)
438490
return true;
439491

492+
if (line == "ninja: build stopped: subcommand failed.")
493+
return false;
494+
440495
if (line == "ninja: no work to do.")
441496
return false;
442497

@@ -446,13 +501,7 @@ namespace vix::cli::build
446501
if (line.find("Copy compile_commands.json to project root") != std::string::npos)
447502
return false;
448503

449-
if (line.rfind("FAILED:", 0) == 0)
450-
return true;
451-
452-
if (line.rfind("ninja:", 0) == 0)
453-
return true;
454-
455-
return false;
504+
return looks_like_live_error_line(line);
456505
};
457506

458507
std::string buf(16 * 1024, '\0');
Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/**
22
*
3-
* @file CMakeBuildErrors.hpp
3+
* @file CMakeBuildErrors.cpp
44
* @author Gaspard Kirira
55
*
66
* Copyright 2025, Gaspard Kirira. All rights reserved.
@@ -12,34 +12,89 @@
1212
*
1313
*/
1414
#include <vix/cli/errors/build/CMakeBuildErrors.hpp>
15-
1615
#include <iostream>
16+
#include <regex>
1717
#include <string>
18-
18+
#include <string_view>
19+
#include <iostream>
1920
#include <vix/cli/Style.hpp>
2021

2122
using namespace vix::cli::style;
2223

2324
namespace vix::cli::errors::build
2425
{
25-
bool handleCMakeBuildError(std::string_view log)
26+
namespace
2627
{
27-
const bool cacheDirMismatch =
28-
log.find("The current CMakeCache.txt directory") != std::string_view::npos;
28+
bool handle_cache_mismatch(std::string_view log)
29+
{
30+
const bool cacheDirMismatch =
31+
log.find("The current CMakeCache.txt directory") != std::string_view::npos;
32+
const bool sourceMismatch =
33+
log.find("does not match the source") != std::string_view::npos &&
34+
log.find("used to generate cache") != std::string_view::npos;
35+
36+
if (!cacheDirMismatch && !sourceMismatch)
37+
return false;
38+
39+
error("CMake configure failed: stale build cache detected.");
40+
hint("Your build directory was generated from another project location.");
41+
hint("Remove the old build directory and reconfigure.");
42+
hint("Recommended: vix build --clean");
43+
hint("Manual fix: rm -rf build-ninja build-dev build-release");
44+
return true;
45+
}
46+
47+
bool handle_missing_link_target(std::string_view log)
48+
{
49+
const bool hasMissingTarget =
50+
log.find("but the target was not found") != std::string_view::npos &&
51+
log.find("target_link_libraries") != std::string_view::npos;
52+
53+
if (!hasMissingTarget)
54+
return false;
55+
56+
std::string targetName;
57+
std::string missingLink;
58+
59+
{
60+
const std::regex reTarget(R"re(Target\s+"([^"]+)")re");
61+
std::match_results<std::string_view::const_iterator> m;
2962

30-
const bool sourceMismatch =
31-
log.find("does not match the source") != std::string_view::npos &&
32-
log.find("used to generate cache") != std::string_view::npos;
63+
if (std::regex_search(log.begin(), log.end(), m, reTarget) && m.size() >= 2)
64+
targetName = m[1].str();
65+
}
3366

34-
if (!cacheDirMismatch && !sourceMismatch)
35-
return false;
67+
{
68+
const std::regex reMissing(R"re(links\s+to:\s*\n\s*\n\s*([^\s][^\n]*))re");
69+
std::match_results<std::string_view::const_iterator> m;
3670

37-
error("CMake configure failed: stale build cache detected.");
38-
hint("Your build directory was generated from another project location.");
39-
hint("Remove the old build directory and reconfigure.");
40-
hint("Recommended: vix build --clean");
41-
hint("Manual fix: rm -rf build-ninja build-dev build-release");
71+
if (std::regex_search(log.begin(), log.end(), m, reMissing) && m.size() >= 2)
72+
missingLink = m[1].str();
73+
}
4274

43-
return true;
75+
error("Build failed: unresolved CMake target.");
76+
77+
if (!targetName.empty())
78+
std::cerr << " target: " << targetName << "\n";
79+
80+
if (!missingLink.empty())
81+
std::cerr << " missing: " << RED << missingLink << RESET << "\n";
82+
83+
std::cerr << "\n";
84+
hint("Fix the target name or make sure it is defined before linking.");
85+
86+
return true;
87+
}
88+
89+
} // namespace
90+
91+
bool handleCMakeBuildError(std::string_view log)
92+
{
93+
if (handle_cache_mismatch(log))
94+
return true;
95+
if (handle_missing_link_target(log))
96+
return true;
97+
return false;
4498
}
45-
}
99+
100+
} // namespace vix::cli::errors::build

0 commit comments

Comments
 (0)