GCC Code Coverage Report


Directory: ./
File: src/BackEnd/nanobind_generator.cpp
Date: 2026-10-16 17:52:29
Exec Total Coverage
Lines: 62 70 88.6%
Functions: 3 3 100.0%
Branches: 129 164 78.7%

Line Branch Exec Source
1 /***************************************
2 Auteur : Thibaut Oprinsen, Pierre Aubert
3 Mail : thibaut.oprinsen@lapp.in2p3.fr, pierre.aubert@lapp.in2p3.fr
4 Licence : LGPL-3.0-only
5 ****************************************/
6
7 #include <set>
8 #include "header_generator.h"
9 #include "nanobind_generator.h"
10 #include "phoenix_color.h"
11 #include "openFileStream.h"
12
13 ///Generate nanobind wrapper class
14 /** @param manager : PTraitBackendManager which handles all trait backend
15 * @param baseFileName : base file name to be used (without path and extension)
16 * @param sourceFile : source file to be saved
17 * @param headerFile : header to be used with this source file
18 * @param extraInclude : extra include to be added in the source file
19 * @param vecClassConfig : vector of configuration of the classes to be used
20 * @param mode : mode of the generator
21 * @return true on success, false otherwise
22 */
23 2 bool generator_nanobind_class(const PNanobindTraitBackendManager &manager, PPath& baseFileName, const PPath &sourceFile, const PPath &headerFile, const PVecPath &extraInclude, const PVecClassConfig &vecClassConfig, const GeneratorMode &mode){
24
2/3
✓ Branch 0 (2→3) taken 2 times.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→13) taken 2 times.
2 if(baseFileName == ""){
25 ✗ std::cerr << termRed() << "generator_class_cpp : baseFileName is empty" << termDefault() << std::endl;
26 ✗ return false;
27 }
28
1/1
✓ Branch 0 (13→14) taken 2 times.
2 std::ofstream fs;
29
2/3
✓ Branch 0 (14→15) taken 2 times.
✗ Branch 2 (15→16) not taken.
✓ Branch 3 (15→17) taken 2 times.
2 if(!openFileStream(fs, sourceFile)){return false;}
30
1/1
✓ Branch 0 (17→18) taken 2 times.
2 licenceSave(fs);
31 //nanobind.h includes Python.h, which must come before any system header (it defines _POSIX_C_SOURCE/_XOPEN_SOURCE)
32
3/3
✓ Branch 0 (18→19) taken 2 times.
✓ Branch 2 (19→20) taken 2 times.
✓ Branch 4 (20→21) taken 2 times.
2 fs << std::endl << "#include <nanobind/nanobind.h>" << std::endl;
33
5/5
✓ Branch 0 (21→22) taken 2 times.
✓ Branch 2 (22→23) taken 2 times.
✓ Branch 4 (23→24) taken 2 times.
✓ Branch 6 (24→25) taken 2 times.
✓ Branch 8 (25→26) taken 2 times.
2 fs << "#include \"" << headerFile.getFileName() << "\"" << std::endl;
34 2 std::set<std::string> extraIncludes;
35
2/2
✓ Branch 0 (42→29) taken 4 times.
✓ Branch 1 (42→43) taken 2 times.
12 for (std::vector<PClassConfig>::const_iterator it(vecClassConfig.begin()); it != vecClassConfig.end(); ++it){
36
1/1
✓ Branch 0 (31→32) taken 4 times.
8 manager.headerExtraInclude(extraIncludes, *it, mode);
37 }
38
2/2
✓ Branch 0 (50→45) taken 3 times.
✓ Branch 1 (50→51) taken 2 times.
5 for (const std::string & include : extraIncludes){
39
2/2
✓ Branch 0 (46→47) taken 3 times.
✓ Branch 2 (47→48) taken 3 times.
3 fs << include << std::endl;
40 }
41
3/3
✓ Branch 0 (51→52) taken 2 times.
✓ Branch 2 (52→53) taken 2 times.
✓ Branch 4 (53→54) taken 2 times.
2 fs << "\nnamespace nb = nanobind;" << std::endl << std::endl;
42
1/1
✓ Branch 0 (54→55) taken 2 times.
2 PString fileModuleName = baseFileName.toLower();
43
4/4
✓ Branch 0 (55→56) taken 2 times.
✓ Branch 2 (56→57) taken 2 times.
✓ Branch 4 (57→58) taken 2 times.
✓ Branch 6 (58→59) taken 2 times.
2 fs << "NB_MODULE(" << fileModuleName << ", m) {" << std::endl;
44
4/4
✓ Branch 0 (59→60) taken 2 times.
✓ Branch 2 (60→61) taken 2 times.
✓ Branch 4 (61→62) taken 2 times.
✓ Branch 6 (62→63) taken 2 times.
2 fs << "\tm.doc() = \"Nanobind bindings for " << baseFileName << " classes\";\n" << std::endl;
45
46
2/2
✓ Branch 0 (138→64) taken 4 times.
✓ Branch 1 (138→139) taken 2 times.
12 for(std::vector<PClassConfig>::const_iterator it(vecClassConfig.begin()); it != vecClassConfig.end(); ++it){
47
3/3
✓ Branch 0 (66→67) taken 4 times.
✓ Branch 2 (67→68) taken 1 times.
✓ Branch 3 (67→99) taken 3 times.
4 if(it->getIsEnum()){
48
11/11
✓ Branch 0 (68→69) taken 1 times.
✓ Branch 2 (71→72) taken 1 times.
✓ Branch 4 (72→73) taken 1 times.
✓ Branch 6 (73→74) taken 1 times.
✓ Branch 8 (76→77) taken 1 times.
✓ Branch 10 (77→78) taken 1 times.
✓ Branch 12 (78→79) taken 1 times.
✓ Branch 14 (81→82) taken 1 times.
✓ Branch 16 (82→83) taken 1 times.
✓ Branch 18 (83→84) taken 1 times.
✓ Branch 20 (84→85) taken 1 times.
4 fs << "\tnb::enum_<" << it->getName() << "::" << it->getName() << ">(m, \"" << it->getName() << "\")" << std::endl;
49
2/2
✓ Branch 0 (85→86) taken 1 times.
✓ Branch 2 (86→87) taken 1 times.
1 fs << "\t\t//Values" << std::endl;
50
1/1
✓ Branch 0 (89→90) taken 1 times.
2 manager.registerProperty(fs, *it, mode);
51
1/1
✓ Branch 0 (92→93) taken 1 times.
2 manager.registerStaticMethod(fs, *it, mode);
52
1/1
✓ Branch 0 (95→96) taken 1 times.
2 manager.registerMethod(fs, *it, mode);
53
3/3
✓ Branch 0 (96→97) taken 1 times.
✓ Branch 2 (97→98) taken 1 times.
✓ Branch 4 (98→128) taken 1 times.
1 fs << ";" << std::endl << std::endl;
54 }else{
55
8/8
✓ Branch 0 (99→100) taken 3 times.
✓ Branch 2 (102→103) taken 3 times.
✓ Branch 4 (103→104) taken 3 times.
✓ Branch 6 (104→105) taken 3 times.
✓ Branch 8 (107→108) taken 3 times.
✓ Branch 10 (108→109) taken 3 times.
✓ Branch 12 (109→110) taken 3 times.
✓ Branch 14 (110→111) taken 3 times.
9 fs << "\tnb::class_<" << it->getName() << ">(m, \"" << it->getName() << "\")" << std::endl;
56
2/2
✓ Branch 0 (111→112) taken 3 times.
✓ Branch 2 (112→113) taken 3 times.
3 fs << "\t\t//Attributes" << std::endl;
57
1/1
✓ Branch 0 (115→116) taken 3 times.
6 manager.registerProperty(fs, *it, mode);
58
1/1
✓ Branch 0 (118→119) taken 3 times.
6 manager.registerConstructor(fs, *it, mode);
59
1/1
✓ Branch 0 (121→122) taken 3 times.
6 manager.registerStaticMethod(fs, *it, mode);
60
61
1/1
✓ Branch 0 (124→125) taken 3 times.
6 manager.registerMethod(fs, *it, mode);
62
3/3
✓ Branch 0 (125→126) taken 3 times.
✓ Branch 2 (126→127) taken 3 times.
✓ Branch 4 (127→128) taken 3 times.
3 fs << ";" << std::endl << std::endl;
63 }
64 }
65
2/2
✓ Branch 0 (139→140) taken 2 times.
✓ Branch 2 (140→141) taken 2 times.
2 fs << "}" << std::endl; // Close NB_MODULE
66
1/1
✓ Branch 0 (141→142) taken 2 times.
2 fs.close();
67 2 return true;
68 2 }
69
70 ///Generate test with nanobind
71 /** @param manager : PTraitBackendManager which handles all trait backend
72 * @param outputTestDir : output test directory
73 * @param vecClassConfig : configuration to be generated
74 * @param projectName : name of the project
75 * @param baseFileName : base file name to be used (without path and extension)
76 * @param mode : mode of the generator
77 * @return true on success, false otherwise
78 */
79 2 bool generator_nanobind_test(const PNanobindTraitBackendManager & manager, const PPath & outputTestDir, const std::vector<PClassConfig> & vecClassConfig, const PString & projectName, const PPath & baseFileName, const GeneratorMode & mode){
80
2/2
✓ Branch 0 (73→3) taken 4 times.
✓ Branch 1 (73→74) taken 2 times.
12 for(std::vector<PClassConfig>::const_iterator it(vecClassConfig.begin()); it != vecClassConfig.end(); ++it){
81
2/2
✓ Branch 0 (5→6) taken 4 times.
✓ Branch 2 (6→7) taken 4 times.
8 PString name = it->getName();
82
7/7
✓ Branch 0 (7→8) taken 4 times.
✓ Branch 2 (8→9) taken 4 times.
✓ Branch 4 (9→10) taken 4 times.
✓ Branch 6 (10→11) taken 4 times.
✓ Branch 8 (11→12) taken 4 times.
✓ Branch 10 (12→13) taken 4 times.
✓ Branch 12 (13→14) taken 4 times.
4 PPath outputCurrentTestDir(outputTestDir + PString("/TEST_") + name.toUpper() + "_BINDINGS");
83
2/3
✓ Branch 0 (20→21) taken 4 times.
✗ Branch 2 (21→22) not taken.
✓ Branch 3 (21→27) taken 4 times.
4 if(!outputCurrentTestDir.createDirectory()){
84 ✗ std::cerr << "generator_class_test : cannot create TEST directory '"<<outputCurrentTestDir<<"'" << std::endl;
85 ✗ return false;
86 }
87
1/1
✓ Branch 0 (27→28) taken 4 times.
4 std::ofstream fs;
88
8/9
✓ Branch 0 (28→29) taken 4 times.
✓ Branch 2 (29→30) taken 4 times.
✓ Branch 4 (30→31) taken 4 times.
✓ Branch 6 (31→32) taken 4 times.
✓ Branch 8 (32→33) taken 4 times.
✓ Branch 10 (33→34) taken 4 times.
✓ Branch 12 (34→35) taken 4 times.
✗ Branch 14 (41→42) not taken.
✓ Branch 15 (41→43) taken 4 times.
4 if(!openFileStream(fs, PPath(outputCurrentTestDir + PString("/test_") + name + "_binding.py"))){return false;}
89
1/1
✓ Branch 0 (43→44) taken 4 times.
4 manager.headerTestInclude(fs, mode, baseFileName);
90
1/1
✓ Branch 0 (46→47) taken 4 times.
8 manager.testFunction(fs, *it, mode, baseFileName);
91
1/1
✓ Branch 0 (47→48) taken 4 times.
4 fs.close();
92
3/6
✓ Branch 0 (50→51) taken 4 times.
✗ Branch 1 (50→52) not taken.
✓ Branch 2 (55→56) taken 4 times.
✗ Branch 3 (55→57) not taken.
✓ Branch 4 (60→61) taken 4 times.
✗ Branch 5 (60→65) not taken.
12 }
93 2 return true;
94 }
95
96 ///Generate the full sources and related unit tests from configuration
97 /** @param manager : PTraitBackendManager which handles all trait backend
98 * @param projectParam : description of classes and how and where to generate sources and test
99 * @return true on success, false otherwise
100 */
101 1 bool generator_nanobind_full(const PNanobindTraitBackendManager & manager, const ProjectParam & projectParam)
102 {
103 1 bool b(true);
104 1 b &= projectParam.outputSourceDir.createDirectory();
105 1 b &= projectParam.outputTestDir.createDirectory();
106 1 const PVecDataConfig & vecDataConfig = projectParam.vecDataConfig;
107
108
2/2
✓ Branch 0 (74→6) taken 2 times.
✓ Branch 1 (74→75) taken 1 times.
4 for(const PDataConfig & config : vecDataConfig){
109
1/1
✓ Branch 0 (8→9) taken 2 times.
2 const PVecPath & extraInclude = config.getVecInclude();
110
4/4
✓ Branch 0 (9→10) taken 2 times.
✓ Branch 2 (10→11) taken 2 times.
✓ Branch 4 (11→12) taken 2 times.
✓ Branch 6 (12→13) taken 2 times.
2 PPath baseFileName = config.getFileName().getFileName().eraseExtension();
111
4/4
✓ Branch 0 (14→15) taken 2 times.
✓ Branch 2 (15→16) taken 2 times.
✓ Branch 4 (16→17) taken 2 times.
✓ Branch 6 (17→18) taken 2 times.
2 PPath headerFile = projectParam.outputSourceDir / PPath(baseFileName + ".h");
112
4/4
✓ Branch 0 (21→22) taken 2 times.
✓ Branch 2 (22→23) taken 2 times.
✓ Branch 4 (23→24) taken 2 times.
✓ Branch 6 (24→25) taken 2 times.
2 PPath sourceFile = projectParam.outputSourceDir / PPath(baseFileName + "_binding.cpp");
113
3/4
✓ Branch 0 (28→29) taken 2 times.
✓ Branch 2 (29→30) taken 2 times.
✗ Branch 4 (30→31) not taken.
✓ Branch 5 (30→42) taken 2 times.
2 if(!generator_nanobind_class(manager, baseFileName, sourceFile, headerFile, extraInclude, config.getVecClassConfig(), projectParam.mode)){
114 ✗ std::cerr << termRed() << "generator_nanobind_full : cannot generate nanobind sources at '"<<projectParam.outputSourceDir<<"'" << termDefault() << std::endl;
115 ✗ b = false; //best effort strategy
116 }
117
3/3
✓ Branch 0 (42→43) taken 2 times.
✓ Branch 2 (43→44) taken 2 times.
✓ Branch 4 (44→45) taken 2 times.
2 PPath outputCurrentTestDir = projectParam.outputTestDir / PPath("TEST_PYTHON_BINDINGS");
118
3/4
✓ Branch 0 (47→48) taken 2 times.
✓ Branch 2 (48→49) taken 2 times.
✗ Branch 4 (49→50) not taken.
✓ Branch 5 (49→61) taken 2 times.
2 if(!generator_nanobind_test(manager, outputCurrentTestDir, config.getVecClassConfig(), projectParam.name, baseFileName, projectParam.mode)){
119 ✗ std::cerr << termRed() << "generator_nanobind_full : cannot generate nanobind tests at '"<<projectParam.outputTestDir<<"'" << termDefault() << std::endl;
120 ✗ b = false; //best effort strategy
121 }
122 2 }
123 1 return b;
124 }
125