File indexing completed on 2024-05-19 05:41:57
0001 // ct_lvtcgn_generatecode.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtcgn_generatecode.h> 0021 0022 #include <filesystem> 0023 #include <iostream> 0024 #include <pybind11/embed.h> 0025 #include <pybind11/stl.h> 0026 0027 namespace py = pybind11; 0028 0029 namespace Codethink::lvtcgn::mdl { 0030 0031 IPhysicalEntityInfo::~IPhysicalEntityInfo() = default; 0032 ICodeGenerationDataProvider::~ICodeGenerationDataProvider() = default; 0033 0034 // TODO [#437]: Check if we can't use enums from lvtshr directly after architecture review. 0035 enum class pyDiagramType { UnknownEntityType = 0, PackageGroupType = 1, PackageType = 10, ComponentType = 100 }; 0036 0037 class pyPhysicalEntityInfoWrapper { 0038 public: 0039 pyPhysicalEntityInfoWrapper(std::reference_wrapper<IPhysicalEntityInfo> impl): impl(impl) 0040 { 0041 } 0042 0043 [[nodiscard]] std::string name() const 0044 { 0045 return impl.get().name(); 0046 } 0047 0048 [[nodiscard]] pyDiagramType type() const 0049 { 0050 auto type = impl.get().type(); 0051 if (type == "Component") { 0052 return pyDiagramType::ComponentType; 0053 } 0054 if (type == "Package") { 0055 return pyDiagramType::PackageType; 0056 } 0057 if (type == "PackageGroup") { 0058 return pyDiagramType::PackageGroupType; 0059 } 0060 return pyDiagramType::UnknownEntityType; 0061 } 0062 0063 [[nodiscard]] std::shared_ptr<pyPhysicalEntityInfoWrapper> parent() 0064 { 0065 auto parent = impl.get().parent(); 0066 if (!parent) { 0067 return nullptr; 0068 } 0069 return std::make_shared<pyPhysicalEntityInfoWrapper>(*parent); 0070 } 0071 0072 [[nodiscard]] std::vector<std::shared_ptr<pyPhysicalEntityInfoWrapper>> forwardDependencies() const 0073 { 0074 auto deps = impl.get().fwdDependencies(); 0075 auto pyFwdDeps = std::vector<std::shared_ptr<pyPhysicalEntityInfoWrapper>>(); 0076 for (auto dep : deps) { 0077 pyFwdDeps.push_back(std::make_shared<pyPhysicalEntityInfoWrapper>(dep)); 0078 } 0079 return pyFwdDeps; 0080 } 0081 0082 [[nodiscard]] std::vector<std::shared_ptr<pyPhysicalEntityInfoWrapper>> children() const 0083 { 0084 auto children = impl.get().children(); 0085 auto pyChildren = std::vector<std::shared_ptr<pyPhysicalEntityInfoWrapper>>(); 0086 for (auto c : children) { 0087 pyChildren.push_back(std::make_shared<pyPhysicalEntityInfoWrapper>(c)); 0088 } 0089 return pyChildren; 0090 } 0091 0092 private: 0093 std::reference_wrapper<IPhysicalEntityInfo> impl; 0094 }; 0095 0096 // NOLINTBEGIN 0097 PYBIND11_EMBEDDED_MODULE(pycgn, m) 0098 { 0099 m.doc() = "Module containing entities exposed to python for code generation scripts"; 0100 0101 py::class_<pyPhysicalEntityInfoWrapper, std::shared_ptr<pyPhysicalEntityInfoWrapper>>(m, "PhysicalEntity") 0102 .def("name", &pyPhysicalEntityInfoWrapper::name, "Physical entity name without namespaces.") 0103 .def("type", 0104 &pyPhysicalEntityInfoWrapper::type, 0105 "Physical entity type (Component, Package, etc.) See DiagramType.") 0106 .def("parent", 0107 &pyPhysicalEntityInfoWrapper::parent, 0108 "Returns the Physical entity in which this entity is contained. If there's no parent, returns None.") 0109 .def("forwardDependencies", 0110 &pyPhysicalEntityInfoWrapper::forwardDependencies, 0111 "Returns a list of entities that this Physical entity depends on.") 0112 .def("children", 0113 &pyPhysicalEntityInfoWrapper::children, 0114 "Returns a list of entities that are children of this entity."); 0115 0116 py::enum_<pyDiagramType>(m, "DiagramType") 0117 .value("Component", pyDiagramType::ComponentType) 0118 .value("Package", pyDiagramType::PackageType) 0119 .value("PackageGroup", pyDiagramType::PackageGroupType) 0120 .value("UnknownEntity", pyDiagramType::UnknownEntityType) 0121 .export_values(); 0122 } 0123 // NOLINTEND 0124 0125 cpp::result<void, CodeGenerationError> 0126 CodeGeneration::generateCodeFromScript(const std::string& scriptPath, 0127 const std::string& outputDir, 0128 ICodeGenerationDataProvider& dataProvider, 0129 std::optional<std::function<void(CodeGenerationStep const&)>> callback) 0130 { 0131 std::filesystem::path path(scriptPath); 0132 auto modulePath = path.parent_path().string(); 0133 auto moduleName = path.stem().string(); 0134 0135 py::gil_scoped_acquire _; 0136 0137 auto pySys = py::module_::import("sys"); 0138 pySys.attr("path").attr("append")(modulePath); 0139 0140 auto pyCgn = py::module_::import("pycgn"); 0141 auto pyUserModuleResult = [&moduleName]() -> cpp::result<py::module_, CodeGenerationError> { 0142 try { 0143 return py::module_::import(moduleName.c_str()); 0144 } catch (py::error_already_set const& e) { 0145 return cpp::fail(CodeGenerationError{CodeGenerationError::Kind::PythonError, e.what()}); 0146 } 0147 }(); 0148 if (pyUserModuleResult.has_error()) { 0149 return cpp::fail(pyUserModuleResult.error()); 0150 } 0151 auto pyUserModule = pyUserModuleResult.value(); 0152 if (!py::hasattr(pyUserModule, "buildPhysicalEntity")) { 0153 return cpp::fail(CodeGenerationError{CodeGenerationError::Kind::ScriptDefinitionError, 0154 "Expected function named buildPhysicalEntity"}); 0155 } 0156 0157 try { 0158 using InfoVec = std::vector<std::reference_wrapper<IPhysicalEntityInfo>>; 0159 0160 auto user_ctx = py::dict(); 0161 0162 if (py::hasattr(pyUserModule, "beforeProcessEntities")) { 0163 if (callback) { 0164 (*callback)(BeforeProcessEntitiesStep{}); 0165 } 0166 auto const& beforeProcessEntities = pyUserModule.attr("beforeProcessEntities"); 0167 beforeProcessEntities(outputDir, user_ctx); 0168 } 0169 0170 auto const& buildPhysicalEntity = pyUserModule.attr("buildPhysicalEntity"); 0171 std::function<void(InfoVec const&)> recursiveBuild = [&](InfoVec const& entities) -> void { 0172 for (auto refWrapEntity : entities) { 0173 auto& entity = refWrapEntity.get(); 0174 if (!entity.selectedForCodeGeneration()) { 0175 continue; 0176 } 0177 if (callback) { 0178 (*callback)(ProcessEntityStep{entity.name()}); 0179 } 0180 0181 buildPhysicalEntity(pyCgn, pyPhysicalEntityInfoWrapper{entity}, outputDir, user_ctx); 0182 auto children = entity.children(); 0183 recursiveBuild(children); 0184 } 0185 }; 0186 recursiveBuild(dataProvider.topLevelEntities()); 0187 0188 if (py::hasattr(pyUserModule, "afterProcessEntities")) { 0189 if (callback) { 0190 (*callback)(AfterProcessEntitiesStep{}); 0191 } 0192 auto const& afterProcessEntities = pyUserModule.attr("afterProcessEntities"); 0193 afterProcessEntities(outputDir, user_ctx); 0194 } 0195 } catch (std::runtime_error const& e) { 0196 return cpp::fail(CodeGenerationError{CodeGenerationError::Kind::PythonError, e.what()}); 0197 } 0198 0199 return {}; 0200 } 0201 0202 } // namespace Codethink::lvtcgn::mdl