Skip to content

Commit 2e45e5f

Browse files
committed
feat(cli): support vix.app modules integration
1 parent b5d794e commit 2e45e5f

7 files changed

Lines changed: 257 additions & 30 deletions

File tree

include/vix/cli/app/AppManifest.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,24 @@ namespace vix::cli::app
168168
*/
169169
std::vector<std::string> defines;
170170

171+
/**
172+
* @brief Internal application modules generated by `vix modules`.
173+
*
174+
* Each entry maps to a CMake alias target using the current
175+
* application name as project prefix.
176+
*
177+
* Example:
178+
* - "auth" -> <app-name>::auth
179+
* - "orders" -> <app-name>::orders
180+
*
181+
* Modules are loaded from:
182+
*
183+
* cmake/vix_modules.cmake
184+
*
185+
* relative to the original user project directory.
186+
*/
187+
std::vector<std::string> modules;
188+
171189
/**
172190
* @brief CMake targets or libraries to link.
173191
*/

include/vix/cli/commands/modules/ModulesUtils.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
*/
1313

1414
#ifndef VIX_CLI_MODULES_UTILS_HPP
15-
#define VIX_CLI_MODULES_UTISL_HPP
15+
#define VIX_CLI_MODULES_UTILS_HPP
1616

1717
#include <filesystem>
1818
#include <optional>
@@ -56,6 +56,14 @@ namespace vix::commands::modules_cmd::utils
5656
/// Falls back to "myproj" if not found.
5757
std::string detect_project_name_from_cmake(const std::filesystem::path &root);
5858

59+
/// Parses the project name from the `name = ...` field in vix.app.
60+
/// Falls back to "myproj" if not found.
61+
std::string detect_project_name_from_vix_app(const std::filesystem::path &root);
62+
63+
/// Detects the project name from CMakeLists.txt first, then vix.app.
64+
/// Falls back to "myproj" if no project name can be detected.
65+
std::string detect_project_name(const std::filesystem::path &root);
66+
5967
} // namespace vix::commands::modules_cmd::utils
6068

6169
#endif

src/app/AppCMakeGenerator.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,38 @@ namespace vix::cli::app
8383
return value;
8484
}
8585

86+
static std::string normalize_module_id(std::string name)
87+
{
88+
name.erase(
89+
name.begin(),
90+
std::find_if(
91+
name.begin(),
92+
name.end(),
93+
[](unsigned char c)
94+
{
95+
return std::isspace(c) == 0;
96+
}));
97+
98+
name.erase(
99+
std::find_if(
100+
name.rbegin(),
101+
name.rend(),
102+
[](unsigned char c)
103+
{
104+
return std::isspace(c) == 0;
105+
})
106+
.base(),
107+
name.end());
108+
109+
for (char &c : name)
110+
{
111+
if (c == '-')
112+
c = '_';
113+
}
114+
115+
return name;
116+
}
117+
86118
// ---------------------------------------------------------------
87119
// Standard normalization
88120
// ---------------------------------------------------------------
@@ -283,6 +315,19 @@ namespace vix::cli::app
283315
out << "set(CMAKE_CXX_EXTENSIONS OFF)\n\n";
284316
}
285317

318+
static void emit_modules_loader(
319+
std::ostringstream &out,
320+
const fs::path &projectDir)
321+
{
322+
const fs::path loader =
323+
projectDir / "cmake" / "vix_modules.cmake";
324+
325+
out << "# Internal application modules declared by `vix modules`\n";
326+
out << "if(EXISTS " << cmake_quoted_path(loader) << ")\n";
327+
out << " include(" << cmake_quoted_path(loader) << ")\n";
328+
out << "endif()\n\n";
329+
}
330+
286331
static bool is_vix_package(const AppPackage &pkg)
287332
{
288333
const std::string name = lower_copy(pkg.name);
@@ -431,6 +476,39 @@ namespace vix::cli::app
431476
manifest.links);
432477
}
433478

479+
static void emit_modules_links(
480+
std::ostringstream &out,
481+
const AppManifest &manifest,
482+
const std::string &targetName)
483+
{
484+
if (manifest.modules.empty())
485+
return;
486+
487+
out << "# Modules declared in vix.app\n";
488+
489+
for (const std::string &module : manifest.modules)
490+
{
491+
const std::string normalized = normalize_module_id(module);
492+
const std::string alias = manifest.name + "::" + normalized;
493+
494+
out << "if(TARGET " << alias << ")\n";
495+
out << " target_link_libraries("
496+
<< targetName
497+
<< " PRIVATE "
498+
<< alias
499+
<< ")\n";
500+
out << "else()\n";
501+
out << " message(FATAL_ERROR \"vix.app module not found: "
502+
<< normalized
503+
<< " (expected target "
504+
<< alias
505+
<< ")\")\n";
506+
out << "endif()\n";
507+
}
508+
509+
out << "\n";
510+
}
511+
434512
static void emit_output_dir(
435513
std::ostringstream &out,
436514
const AppManifest &manifest,
@@ -646,10 +724,15 @@ namespace vix::cli::app
646724
// targets are available to target_link_libraries.
647725
emit_packages(out, manifest.packages);
648726

727+
// User application modules must be loaded before linking their
728+
// alias targets into the generated application target.
729+
emit_modules_loader(out, projectDir);
730+
649731
emit_target(out, manifest, targetName, projectDir);
650732
emit_includes_and_defines(out, manifest, targetName, projectDir);
651733
emit_options_and_features(out, manifest, targetName);
652734
emit_links(out, manifest, targetName);
735+
emit_modules_links(out, manifest, targetName);
653736
emit_output_dir(out, manifest, targetName);
654737
emit_resources(out, manifest, targetName, projectDir);
655738
emit_tests(out, manifest, targetName, projectDir);

src/app/AppManifest.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,12 @@ namespace vix::cli::app
582582
return true;
583583
}
584584

585+
if (normalizedKey == "modules")
586+
{
587+
manifest.modules = values;
588+
return true;
589+
}
590+
585591
if (normalizedKey == "compile_options" ||
586592
normalizedKey == "compileoptions" ||
587593
normalizedKey == "cxxflags")

src/commands/ModulesCommand.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ namespace vix::commands::ModulesCommand
108108
const auto root = utils::resolve_root(opt.dir);
109109

110110
const std::string project = opt.project.empty()
111-
? utils::detect_project_name_from_cmake(root)
111+
? utils::detect_project_name(root)
112112
: opt.project;
113113

114114
if (opt.subcmd == "init")

src/commands/modules/ModulesCommands.cpp

Lines changed: 76 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
namespace vix::commands::modules_cmd::commands
2323
{
24-
2524
namespace fs = std::filesystem;
2625
namespace cnt = vix::commands::modules_cmd::content;
2726
namespace utils = vix::commands::modules_cmd::utils;
@@ -90,10 +89,31 @@ namespace vix::commands::modules_cmd::commands
9089
return false;
9190
}
9291

93-
if (patchRoot && !cnt::patch_root_cmakelists_include(root))
92+
const bool hasRootCMake =
93+
utils::exists_file(root / "CMakeLists.txt");
94+
95+
const bool hasVixApp =
96+
utils::exists_file(root / "vix.app");
97+
98+
if (patchRoot && hasRootCMake)
9499
{
95-
ui::err_line(std::cout, "Failed to patch root CMakeLists.txt.");
96-
return false;
100+
if (!cnt::patch_root_cmakelists_include(root))
101+
{
102+
ui::err_line(std::cout, "Failed to patch root CMakeLists.txt.");
103+
return false;
104+
}
105+
}
106+
else if (patchRoot && hasVixApp)
107+
{
108+
ui::warn_line(
109+
std::cout,
110+
"vix.app project detected. CMake patch skipped.");
111+
}
112+
else if (patchRoot)
113+
{
114+
ui::warn_line(
115+
std::cout,
116+
"CMakeLists.txt not found. Skipping root patch.");
97117
}
98118

99119
print_modules_banner("modules", "initialized");
@@ -103,8 +123,10 @@ namespace vix::commands::modules_cmd::commands
103123
print_command_step(1, "modules/", "module directory");
104124
print_command_step(2, "cmake/vix_modules.cmake", "module loader");
105125

106-
if (patchRoot)
126+
if (patchRoot && hasRootCMake)
127+
{
107128
print_command_step(3, "CMakeLists.txt", "patched");
129+
}
108130

109131
sep();
110132
section("next");
@@ -187,54 +209,80 @@ namespace vix::commands::modules_cmd::commands
187209
return false;
188210
}
189211

190-
if (patchRootLink && !cnt::patch_root_cmakelists_link_module(root, project, module))
212+
const bool hasRootCMake =
213+
utils::exists_file(root / "CMakeLists.txt");
214+
215+
const bool hasVixApp =
216+
utils::exists_file(root / "vix.app");
217+
218+
if (patchRootLink && hasRootCMake)
191219
{
192-
ui::err_line(std::cout, "Failed to patch root CMakeLists.txt with module link.");
193-
return false;
220+
if (!cnt::patch_root_cmakelists_link_module(root, project, module))
221+
{
222+
ui::err_line(std::cout, "Failed to patch root CMakeLists.txt with module link.");
223+
return false;
224+
}
225+
}
226+
else if (patchRootLink && hasVixApp)
227+
{
228+
ui::warn_line(
229+
std::cout,
230+
"vix.app project detected. Add the module in vix.app.");
231+
}
232+
else if (patchRootLink)
233+
{
234+
ui::warn_line(
235+
std::cout,
236+
"CMakeLists.txt not found. Skipping auto-link.");
194237
}
195238

196-
print_modules_banner(normalized, "module");
239+
print_modules_banner(normalized, "module created");
197240
sep();
198241

199242
section("files");
200243
print_command_step(
201244
1,
202-
"modules/" + normalized + "/include/" + normalized + "/api.hpp",
203-
"public header");
245+
"modules/" + normalized + "/");
204246

205247
print_command_step(
206248
2,
207-
"modules/" + normalized + "/src/" + normalized + ".cpp",
208-
"implementation");
249+
"modules/" + normalized + "/include/" + normalized + "/api.hpp");
209250

210251
print_command_step(
211252
3,
212-
"modules/" + normalized + "/CMakeLists.txt",
213-
"target");
214-
215-
sep();
216-
section("target");
217-
print_command_step(1, cnt::module_alias_name(project, module), "CMake alias");
253+
"modules/" + normalized + "/src/" + normalized + ".cpp");
218254

219255
sep();
220256
section("next");
221-
print_command_step(
222-
1,
223-
"#include <" + normalized + "/api.hpp>",
224-
"include");
225257

226-
if (patchRootLink)
258+
if (hasVixApp)
227259
{
228-
print_command_step(2, "vix build", "compile");
260+
print_command_step(
261+
1,
262+
"add \"" + normalized + "\" to modules in vix.app");
263+
264+
print_command_step(2, "vix build");
265+
}
266+
else if (patchRootLink && hasRootCMake)
267+
{
268+
print_command_step(
269+
1,
270+
"#include <" + normalized + "/api.hpp>");
271+
272+
print_command_step(2, "vix build");
229273
}
230274
else
231275
{
276+
print_command_step(
277+
1,
278+
"#include <" + normalized + "/api.hpp>");
279+
232280
print_command_step(
233281
2,
234-
"target_link_libraries(" + project + " PRIVATE " + cnt::module_alias_name(project, module) + ")",
235-
"link manually");
236-
}
282+
"target_link_libraries(" + project + " PRIVATE " + cnt::module_alias_name(project, module) + ")");
237283

284+
print_command_step(3, "vix build");
285+
}
238286
return true;
239287
}
240288

0 commit comments

Comments
 (0)