Directory: | ./ |
---|---|
File: | src/wrapper_generator.cpp |
Date: | 2025-04-25 19:10:50 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 120 | 127 | 94.5% |
Branches: | 181 | 220 | 82.3% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /*************************************** | ||
2 | Auteur : Pierre Aubert | ||
3 | Mail : pierre.aubert@lapp.in2p3.fr | ||
4 | Licence : CeCILL-C | ||
5 | ****************************************/ | ||
6 | |||
7 | #include "cmakelist_generator.h" | ||
8 | #include "wrapper_module_generator.h" | ||
9 | #include "wrapper_test.h" | ||
10 | #include "wrapper_generator.h" | ||
11 | |||
12 | ///Create the config of the wrapper | ||
13 | /** @param projectConfig : main configuration | ||
14 | * @return wrapper configuration | ||
15 | */ | ||
16 | 5 | ProjectConfig project_createWrapperConfig(const ProjectConfig & projectConfig){ | |
17 | 5 | ProjectConfig wrapper(projectConfig); | |
18 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | wrapper.name = "Py"+projectConfig.name; |
19 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | wrapper.url = wrapper.url.replace(projectConfig.name, wrapper.name); |
20 | |||
21 | 5 | return wrapper; | |
22 | } | ||
23 | |||
24 | ///Create the readme of the project directory | ||
25 | /** @param fileName : name of the file to be created | ||
26 | * @param projectConfig : config of the project | ||
27 | * @return true on success, false otherwise | ||
28 | */ | ||
29 | 5 | bool project_wrapper_generator_readme(const PPath & fileName, const ProjectConfig & projectConfig){ | |
30 |
1/1✓ Branch 1 taken 5 times.
|
5 | PString body; |
31 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n\n"; |
32 | |||
33 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "# Remarks\n\n"; |
34 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "This project was generated automatically by [phoenix_filegenerator](https://gitlab.in2p3.fr/CTA-LAPP/PHOENIX_LIBS2/PhoenixFileGenerator)\n"; |
35 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "using [phoenix_generator](https://gitlab.in2p3.fr/CTA-LAPP/PHOENIX_LIBS2/PhoenixGenerator) library\n\n"; |
36 | |||
37 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "# Code\n\n"; |
38 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | body += "Version : " + projectConfig.version + "\n\n"; |
39 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | body += projectConfig.url + "\n\n"; |
40 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "# Description\n\n"; |
41 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "Python wrapper version.\n"; |
42 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | body += projectConfig.description + "\n\n"; |
43 | |||
44 |
1/1✓ Branch 1 taken 5 times.
|
10 | return fileName.saveFileContent(body); |
45 | 5 | } | |
46 | |||
47 | ///Create the readme of the project directory | ||
48 | /** @param fileName : name of the file to be created | ||
49 | * @param projectConfig : config of the project | ||
50 | * @param baseImplInclude : basic include of the module implementation | ||
51 | * @param vecClassConfig : vector of class confoguration | ||
52 | * @param vecInclude : vector of includes | ||
53 | * @return true on success, false otherwise | ||
54 | */ | ||
55 | 5 | bool project_wrapper_generator_setuppy(const PPath & fileName, const ProjectConfig & projectConfig, const PString & baseImplInclude, const std::vector<PClassConfig> & vecClassConfig, const PVecPath & vecInclude){ | |
56 |
4/4✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
|
10 | PString body, moduleName = "py" + projectConfig.name.toLower(); |
57 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
58 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | body += getCMakeListsHeader(); |
59 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "from setuptools import setup\n"; |
60 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "from setuptools import Extension\n"; |
61 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "import sys\n"; |
62 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "import numpy as np\n"; |
63 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "import subprocess\n"; |
64 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
65 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "dataStreamIncludeDir = subprocess.run(['phoenixdatastream-config', '--include'], capture_output = True, text = True).stdout.strip(\"\\n\")\n"; |
66 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "dataStreamLibDir = subprocess.run(['phoenixdatastream-config', '--lib'], capture_output = True, text = True).stdout.strip(\"\\n\")\n"; |
67 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
68 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "ext_modules = [\n"; |
69 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | body += "\tExtension(\""+moduleName+"\", sources=([\n"; |
70 |
5/5✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
✓ Branch 13 taken 5 times.
|
5 | body += "\t\t\""+moduleName+"/"+baseImplInclude+"_wrapper.cpp\",\n"; |
71 |
6/6✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
✓ Branch 13 taken 5 times.
✓ Branch 16 taken 5 times.
|
5 | body += "\t\t\""+moduleName+"/"+projectConfig.name.toLower()+"_module.cpp\"\n"; |
72 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t]),\n"; |
73 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\textra_compile_args = [\"-O3\", \"-Werror\", \"-g\"],\n"; |
74 | |||
75 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | body += "\tinclude_dirs = [np.get_include(), dataStreamIncludeDir, \"./\", \""+moduleName+"/\"],\n"; |
76 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\tlibraries=[\"phoenix_data_stream\"],\n"; |
77 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\tlibrary_dirs=[dataStreamLibDir],\n"; |
78 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t)\n"; |
79 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "]\n\n"; |
80 | |||
81 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "try:\n"; |
82 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\tsetup(\n"; |
83 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | body += "\t\tname=\""+moduleName+"\",\n"; |
84 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | body += "\t\tversion=\""+projectConfig.version+"\",\n"; |
85 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t\text_modules=ext_modules,\n"; |
86 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t)\n"; |
87 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "except Exception as e:\n"; |
88 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\tprint(str(e))\n"; |
89 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\tsys.exit(-1)\n"; |
90 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
91 |
1/1✓ Branch 1 taken 5 times.
|
10 | return fileName.saveFileContent(body); |
92 | 5 | } | |
93 | |||
94 | ///Create the readme of the project directory | ||
95 | /** @param fileName : name of the file to be created | ||
96 | * @param projectConfig : config of the project | ||
97 | * @param baseImplInclude : basic include of the module implementation | ||
98 | * @param vecClassConfig : vector of class confoguration | ||
99 | * @param vecInclude : vector of includes | ||
100 | * @return true on success, false otherwise | ||
101 | */ | ||
102 | 5 | bool project_wrapper_generator_pyprojectToml(const PPath & fileName, const ProjectConfig & projectConfig, const PString & baseImplInclude, const std::vector<PClassConfig> & vecClassConfig, const PVecPath & vecInclude){ | |
103 |
1/1✓ Branch 1 taken 5 times.
|
5 | PString body; |
104 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
105 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
|
5 | body += getCMakeListsHeader(); |
106 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "[project]\n"; |
107 |
4/4✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
|
5 | body += "name = \"py"+projectConfig.name.toLower()+"\"\n"; |
108 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | body += "version = \""+projectConfig.version+"\"\n"; |
109 |
6/6✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
✓ Branch 13 taken 5 times.
✓ Branch 16 taken 5 times.
|
5 | body += "description = \""+projectConfig.description.replace("\n", " ")+"\"\n"; |
110 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "readme = \"README.md\"\n"; |
111 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "license = \"CeCILL-C\"\n"; |
112 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "dependencies = [\n"; |
113 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t\"numpy\"\n"; |
114 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "]\n"; |
115 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
116 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "[build-system]\n"; |
117 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "requires = [\n"; |
118 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t\"numpy\",\n"; |
119 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t\"setuptools >= 74.1\",\n"; |
120 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\t\"cython\"\n"; |
121 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "]\n"; |
122 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "build-backend = \"setuptools.build_meta\"\n"; |
123 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
124 | // body += "[tool.setuptools]\n"; | ||
125 | // body += "ext-modules = [\n"; | ||
126 | // body += "\t{\n"; | ||
127 | // body += "\t\tname = \""+projectConfig.name.toLower()+"\",\n"; | ||
128 | // body += "\t\tsources = [\n"; | ||
129 | // body += "\t\t\t\""+projectConfig.name.toLower()+"/"+baseImplInclude+"_wrapper.cpp\",\n"; | ||
130 | // body += "\t\t\t\""+projectConfig.name.toLower()+"/"+projectConfig.name+"_module.cpp\"\n"; | ||
131 | // body += "\t\t],\n"; | ||
132 | // body += "\t\textra_compile_args = [\"-O3\", \"-Werror\", \"-g\"]\n"; | ||
133 | // body += "\t}\n"; | ||
134 | // body += "\n"; | ||
135 | // body += "]\n"; | ||
136 |
1/1✓ Branch 1 taken 5 times.
|
5 | body += "\n"; |
137 |
1/1✓ Branch 1 taken 5 times.
|
10 | return fileName.saveFileContent(body); |
138 | 5 | } | |
139 | |||
140 | ///Update the vector of class configuration | ||
141 | /** @param[out] vecClassConfig : vector of class configuration to be updated | ||
142 | */ | ||
143 | 5 | void updateVecClassConfig(std::vector<PClassConfig> & vecClassConfig){ | |
144 | 5 | std::map<std::string, std::string> mapEnum; | |
145 |
2/2✓ Branch 4 taken 7 times.
✓ Branch 5 taken 5 times.
|
12 | for(std::vector<PClassConfig>::iterator it(vecClassConfig.begin()); it != vecClassConfig.end(); ++it){ |
146 |
3/3✓ Branch 2 taken 7 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 6 times.
|
7 | if(it->getIsEnum()){ |
147 |
7/7✓ Branch 2 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 10 taken 1 times.
✓ Branch 13 taken 1 times.
✓ Branch 16 taken 1 times.
✓ Branch 19 taken 1 times.
✓ Branch 22 taken 1 times.
|
1 | mapEnum[it->getName() + "::" + it->getName()] = it->getName(); |
148 | 1 | size_t i(0lu); | |
149 |
1/1✓ Branch 2 taken 1 times.
|
1 | std::vector<PClassAttribute> & vecAttr = it->getListAttribute(); |
150 |
2/2✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
|
3 | for(std::vector<PClassAttribute>::iterator itAttr(vecAttr.begin()); itAttr != vecAttr.end(); ++itAttr){ |
151 |
2/3✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
✗ Branch 6 not taken.
|
2 | if(itAttr->getDefaultValue() == ""){ |
152 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 5 taken 2 times.
|
2 | itAttr->setDefaultValue(PString::toString(i)); |
153 | } | ||
154 | 2 | ++i; | |
155 | } | ||
156 | } | ||
157 | } | ||
158 |
2/2✓ Branch 4 taken 7 times.
✓ Branch 5 taken 5 times.
|
12 | for(std::vector<PClassConfig>::iterator itClass(vecClassConfig.begin()); itClass != vecClassConfig.end(); ++itClass){ |
159 |
1/1✓ Branch 2 taken 7 times.
|
7 | std::vector<PClassAttribute> & vecAttr = itClass->getListAttribute(); |
160 |
2/2✓ Branch 4 taken 20 times.
✓ Branch 5 taken 7 times.
|
27 | for(std::vector<PClassAttribute>::iterator itAttr(vecAttr.begin()); itAttr != vecAttr.end(); ++itAttr){ |
161 |
2/2✓ Branch 2 taken 20 times.
✓ Branch 5 taken 20 times.
|
20 | std::map<std::string, std::string>::iterator it(mapEnum.find(itAttr->getType())); |
162 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 19 times.
|
20 | if(it != mapEnum.end()){ |
163 |
2/2✓ Branch 3 taken 1 times.
✓ Branch 6 taken 1 times.
|
1 | itAttr->setType(it->second); |
164 |
1/1✓ Branch 2 taken 1 times.
|
1 | itAttr->setIsEnum(true); |
165 | } | ||
166 |
5/8✓ Branch 2 taken 20 times.
✓ Branch 5 taken 20 times.
✗ Branch 6 not taken.
✓ Branch 9 taken 20 times.
✗ Branch 12 not taken.
✓ Branch 13 taken 20 times.
✗ Branch 14 not taken.
✓ Branch 15 taken 20 times.
|
20 | if(itAttr->getType() == "PPath" || itAttr->getType() == "PString"){ |
167 | ✗ | itAttr->setType("std::string"); | |
168 | } | ||
169 | } | ||
170 | } | ||
171 | 5 | } | |
172 | |||
173 | ///Generate a full python wrapper project with PhoenixDataStream and PhoenixTypeStream | ||
174 | /** @param projectConfig : description of the full project | ||
175 | * @param configFile : configuration file of the | ||
176 | * @return true on success, false otherwise | ||
177 | */ | ||
178 | 5 | bool project_wrapper_generator(const ProjectConfig & projectConfig, const PPath & configFile){ | |
179 |
1/1✓ Branch 1 taken 5 times.
|
5 | ProjectConfig wrapperConfig(project_createWrapperConfig(projectConfig)); |
180 | 5 | PVecPath vecInclude; | |
181 | 5 | std::vector<PClassConfig> vecClassConfig; | |
182 |
2/3✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if(!parserClassConfig(vecClassConfig, vecInclude, configFile)){ |
183 | ✗ | std::cerr << "project_wrapper_generator : can't load file '" << configFile << "'" << std::endl; | |
184 | ✗ | return false; | |
185 | } | ||
186 | //TODO : pass through vecClassConfig and replace PPath and PString by std::string to ensure PhoenixDataStream will work well on them for the wrapper | ||
187 |
1/1✓ Branch 1 taken 5 times.
|
5 | updateVecClassConfig(vecClassConfig); |
188 | |||
189 | //Then, let's create the hierarchy | ||
190 |
1/1✓ Branch 1 taken 5 times.
|
5 | PPath mainProjectDir(wrapperConfig.name); |
191 |
2/3✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if(!mainProjectDir.createDirectory()){ |
192 | ✗ | std::cerr << "project_wrapper_generator : cannot create main directory of project '"<<mainProjectDir<<"'" << std::endl; | |
193 | ✗ | return false; | |
194 | } | ||
195 | //And module dir | ||
196 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
10 | PPath mainProjectModule(mainProjectDir / PPath(wrapperConfig.name.toLower())); |
197 |
2/3✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if(!mainProjectModule.createDirectory()){ |
198 | ✗ | std::cerr << "project_generator : cannot create module directory of project '"<<mainProjectModule<<"'" << std::endl; | |
199 | ✗ | return false; | |
200 | } | ||
201 | //The readme | ||
202 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
10 | PPath readmeFile(mainProjectDir / PPath("README.md")); |
203 |
2/3✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if(!project_wrapper_generator_readme(readmeFile, projectConfig)){return false;} |
204 | |||
205 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
5 | PString baseImplInclude = configFile.getFileName().eraseExtension(); |
206 | //The readme | ||
207 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
10 | PPath setupFile(mainProjectDir / PPath("setup.py")); |
208 |
2/3✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if(!project_wrapper_generator_setuppy(setupFile, projectConfig, baseImplInclude, vecClassConfig, vecInclude)){return false;} |
209 | |||
210 |
3/3✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
|
10 | PPath pyprojectFile(mainProjectDir / PPath("pyproject.toml")); |
211 |
2/3✓ Branch 1 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 5 times.
|
5 | if(!project_wrapper_generator_pyprojectToml(pyprojectFile, projectConfig, baseImplInclude, vecClassConfig, vecInclude)){return false;} |
212 | //The unit tests | ||
213 |
5/6✓ Branch 1 taken 5 times.
✓ Branch 4 taken 5 times.
✓ Branch 7 taken 5 times.
✓ Branch 10 taken 5 times.
✗ Branch 15 not taken.
✓ Branch 16 taken 5 times.
|
5 | if(!project_wrapper_moduleGeneratorTest(mainProjectDir, projectConfig, "py"+projectConfig.name.toLower(), vecClassConfig, vecInclude)){return false;} |
214 | //The full module | ||
215 |
1/1✓ Branch 1 taken 5 times.
|
5 | return project_wrapper_moduleGenerator(mainProjectModule, projectConfig, baseImplInclude, vecClassConfig, vecInclude); |
216 | 5 | } | |
217 | |||
218 | |||
219 | |||
220 |