pyx-core 1.19.2__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (553) hide show
  1. PyXMake/Build/Make.py +4518 -0
  2. PyXMake/Build/__init__.py +34 -0
  3. PyXMake/Build/__install__.py +176 -0
  4. PyXMake/Build/cmd/windows/iniCompiler.bat +35 -0
  5. PyXMake/Build/config/.f2py_f2cmap +6 -0
  6. PyXMake/Build/config/stm_choco.ps1 +61 -0
  7. PyXMake/Build/config/stm_color.py +57 -0
  8. PyXMake/Build/config/stm_conf.py +243 -0
  9. PyXMake/Build/config/stm_doc_config +2492 -0
  10. PyXMake/Build/config/stm_doc_footer.html +0 -0
  11. PyXMake/Build/config/stm_docker.ps1 +98 -0
  12. PyXMake/Build/config/stm_docker_ports.ps1 +88 -0
  13. PyXMake/Build/config/stm_docu.yml +29 -0
  14. PyXMake/Build/config/stm_layout.html +4 -0
  15. PyXMake/Build/config/stm_logo.ico +0 -0
  16. PyXMake/Build/config/stm_logo.png +0 -0
  17. PyXMake/Build/config/stm_makefile +123 -0
  18. PyXMake/Build/config/stm_pyfilter.py +61 -0
  19. PyXMake/Build/config/stm_sphinx.sty +1858 -0
  20. PyXMake/Build/config/stm_style.css +9 -0
  21. PyXMake/Tools/ErrorHandling.py +112 -0
  22. PyXMake/Tools/Utility.py +2065 -0
  23. PyXMake/Tools/__init__.py +33 -0
  24. PyXMake/VTL/__init__.py +796 -0
  25. PyXMake/VTL/__install__.py +109 -0
  26. PyXMake/VTL/abaqus.py +154 -0
  27. PyXMake/VTL/api.py +86 -0
  28. PyXMake/VTL/app.py +87 -0
  29. PyXMake/VTL/archive.py +90 -0
  30. PyXMake/VTL/bundle.py +76 -0
  31. PyXMake/VTL/chocolatey.py +126 -0
  32. PyXMake/VTL/cmake.py +78 -0
  33. PyXMake/VTL/cmd/linux/stm_cara_software.sh +533 -0
  34. PyXMake/VTL/cmd/linux/stm_conda_env.sh +113 -0
  35. PyXMake/VTL/cmd/linux/stm_jenkins_job.sh +50 -0
  36. PyXMake/VTL/cmd/linux/stm_lab_image.sh +66 -0
  37. PyXMake/VTL/cmd/linux/stm_lab_pages.sh +28 -0
  38. PyXMake/VTL/cmd/linux/stm_routines_docs.sh +37 -0
  39. PyXMake/VTL/cmd/windows/stm_conda.bat +18 -0
  40. PyXMake/VTL/cmd/windows/stm_lab.bat +117 -0
  41. PyXMake/VTL/cmd/windows/stm_latex.bat +32 -0
  42. PyXMake/VTL/cmd/windows/stm_make.bat +315 -0
  43. PyXMake/VTL/cmd/windows/stm_post.bat +63 -0
  44. PyXMake/VTL/cmd/windows/stm_server.bat +86 -0
  45. PyXMake/VTL/coverage.py +72 -0
  46. PyXMake/VTL/cxx.py +100 -0
  47. PyXMake/VTL/doc/documentation.rst +35 -0
  48. PyXMake/VTL/doc/pyx_core/core.rst +42 -0
  49. PyXMake/VTL/doc/pyx_core/html/____init_____8py_source.html +90 -0
  50. PyXMake/VTL/doc/pyx_core/html/____install_____8py_source.html +91 -0
  51. PyXMake/VTL/doc/pyx_core/html/_build_2____init_____8py_source.html +90 -0
  52. PyXMake/VTL/doc/pyx_core/html/_error_handling_8py_source.html +101 -0
  53. PyXMake/VTL/doc/pyx_core/html/_make_8py_source.html +235 -0
  54. PyXMake/VTL/doc/pyx_core/html/_tools_2____init_____8py_source.html +90 -0
  55. PyXMake/VTL/doc/pyx_core/html/_utility_8py_source.html +129 -0
  56. PyXMake/VTL/doc/pyx_core/html/_v_t_l_2____init_____8py_source.html +95 -0
  57. PyXMake/VTL/doc/pyx_core/html/abaqus_8py_source.html +93 -0
  58. PyXMake/VTL/doc/pyx_core/html/annotated.html +149 -0
  59. PyXMake/VTL/doc/pyx_core/html/annotated_dup.js +4 -0
  60. PyXMake/VTL/doc/pyx_core/html/api_8py_source.html +95 -0
  61. PyXMake/VTL/doc/pyx_core/html/app_8py_source.html +92 -0
  62. PyXMake/VTL/doc/pyx_core/html/bc_s.png +0 -0
  63. PyXMake/VTL/doc/pyx_core/html/bdwn.png +0 -0
  64. PyXMake/VTL/doc/pyx_core/html/bundle_8py_source.html +92 -0
  65. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx-members.html +138 -0
  66. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx.html +625 -0
  67. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx.js +20 -0
  68. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx.png +0 -0
  69. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom-members.html +132 -0
  70. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom.html +428 -0
  71. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom.js +13 -0
  72. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom.png +0 -0
  73. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen-members.html +133 -0
  74. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen.html +490 -0
  75. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen.js +15 -0
  76. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen.png +0 -0
  77. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran-members.html +143 -0
  78. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran.html +702 -0
  79. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran.js +28 -0
  80. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran.png +0 -0
  81. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make-members.html +128 -0
  82. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make.html +951 -0
  83. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make.js +38 -0
  84. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make.png +0 -0
  85. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_n_s_i_s-members.html +135 -0
  86. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_n_s_i_s.html +452 -0
  87. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_n_s_i_s.js +14 -0
  88. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_n_s_i_s.png +0 -0
  89. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x-members.html +135 -0
  90. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x.html +442 -0
  91. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x.js +14 -0
  92. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x.png +0 -0
  93. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer-members.html +136 -0
  94. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer.html +478 -0
  95. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer.js +18 -0
  96. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer.png +0 -0
  97. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h-members.html +150 -0
  98. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h.html +906 -0
  99. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h.js +33 -0
  100. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h.png +0 -0
  101. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx-members.html +142 -0
  102. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx.html +441 -0
  103. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx.js +19 -0
  104. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx.png +0 -0
  105. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error-members.html +95 -0
  106. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error.html +175 -0
  107. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error.js +5 -0
  108. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error.png +0 -0
  109. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error-members.html +97 -0
  110. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error.html +187 -0
  111. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error.js +5 -0
  112. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error.png +0 -0
  113. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error-members.html +99 -0
  114. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error.html +242 -0
  115. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error.js +7 -0
  116. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error.png +0 -0
  117. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory-members.html +98 -0
  118. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory.html +138 -0
  119. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory.js +8 -0
  120. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory.png +0 -0
  121. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_get_data_from_pickle-members.html +96 -0
  122. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_get_data_from_pickle.html +221 -0
  123. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_get_data_from_pickle.js +5 -0
  124. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_get_data_from_pickle.png +0 -0
  125. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_update_z_i_p-members.html +103 -0
  126. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_update_z_i_p.html +153 -0
  127. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_update_z_i_p.js +13 -0
  128. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_update_z_i_p.png +0 -0
  129. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_command_runner-members.html +94 -0
  130. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_command_runner.html +124 -0
  131. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_command_runner.js +4 -0
  132. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_command_runner.png +0 -0
  133. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_test-members.html +102 -0
  134. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_test.html +176 -0
  135. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_test.js +9 -0
  136. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1___base_test.png +0 -0
  137. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean-members.html +94 -0
  138. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean.html +115 -0
  139. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean.js +4 -0
  140. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean.png +0 -0
  141. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_in_development_test-members.html +102 -0
  142. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_in_development_test.html +150 -0
  143. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_in_development_test.js +4 -0
  144. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_in_development_test.png +0 -0
  145. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_long_test-members.html +102 -0
  146. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_long_test.html +150 -0
  147. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_long_test.js +4 -0
  148. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_long_test.png +0 -0
  149. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test-members.html +102 -0
  150. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test.html +150 -0
  151. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test.js +4 -0
  152. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test.png +0 -0
  153. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac-members.html +109 -0
  154. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac.html +175 -0
  155. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac.js +7 -0
  156. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac.png +0 -0
  157. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac-members.html +105 -0
  158. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac.html +160 -0
  159. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac.js +5 -0
  160. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac.png +0 -0
  161. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac-members.html +103 -0
  162. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac.html +151 -0
  163. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac.js +4 -0
  164. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac.png +0 -0
  165. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam-members.html +105 -0
  166. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam.html +154 -0
  167. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam.js +5 -0
  168. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam.png +0 -0
  169. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore-members.html +105 -0
  170. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore.html +154 -0
  171. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore.js +5 -0
  172. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore.png +0 -0
  173. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper-members.html +105 -0
  174. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper.html +154 -0
  175. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper.js +5 -0
  176. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper.png +0 -0
  177. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac-members.html +105 -0
  178. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac.html +154 -0
  179. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac.js +5 -0
  180. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac.png +0 -0
  181. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck-members.html +105 -0
  182. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck.html +154 -0
  183. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck.js +5 -0
  184. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck.png +0 -0
  185. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake-members.html +105 -0
  186. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake.html +154 -0
  187. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake.js +5 -0
  188. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake.png +0 -0
  189. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos-members.html +108 -0
  190. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos.html +166 -0
  191. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos.js +4 -0
  192. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos.png +0 -0
  193. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__boxbeam-members.html +108 -0
  194. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__boxbeam.html +159 -0
  195. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__boxbeam.png +0 -0
  196. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac-members.html +108 -0
  197. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac.html +172 -0
  198. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac.js +6 -0
  199. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac.png +0 -0
  200. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam-members.html +108 -0
  201. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam.html +172 -0
  202. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam.js +6 -0
  203. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam.png +0 -0
  204. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac-members.html +108 -0
  205. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac.html +172 -0
  206. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac.js +6 -0
  207. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac.png +0 -0
  208. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint-members.html +102 -0
  209. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint.html +173 -0
  210. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint.js +9 -0
  211. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint.png +0 -0
  212. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app-members.html +105 -0
  213. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app.html +224 -0
  214. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app.js +12 -0
  215. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app.png +0 -0
  216. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle-members.html +103 -0
  217. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle.html +218 -0
  218. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle.js +10 -0
  219. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle.png +0 -0
  220. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom-members.html +108 -0
  221. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom.html +233 -0
  222. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom.js +15 -0
  223. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom.png +0 -0
  224. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen-members.html +103 -0
  225. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen.html +223 -0
  226. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen.js +10 -0
  227. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen.png +0 -0
  228. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py-members.html +108 -0
  229. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py.html +235 -0
  230. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py.js +15 -0
  231. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py.png +0 -0
  232. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran-members.html +108 -0
  233. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran.html +236 -0
  234. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran.js +15 -0
  235. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran.png +0 -0
  236. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx-members.html +104 -0
  237. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx.html +221 -0
  238. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx.js +11 -0
  239. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx.png +0 -0
  240. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab-members.html +104 -0
  241. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab.html +157 -0
  242. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab.js +5 -0
  243. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab.png +0 -0
  244. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam-members.html +108 -0
  245. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam.html +175 -0
  246. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam.js +7 -0
  247. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam.png +0 -0
  248. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac-members.html +108 -0
  249. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac.html +175 -0
  250. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac.js +7 -0
  251. PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac.png +0 -0
  252. PyXMake/VTL/doc/pyx_core/html/classes.html +131 -0
  253. PyXMake/VTL/doc/pyx_core/html/closed.png +0 -0
  254. PyXMake/VTL/doc/pyx_core/html/cxx_8py_source.html +93 -0
  255. PyXMake/VTL/doc/pyx_core/html/dir_4335ce4a64d0d4f9f4c8f0a782dfdfeb.html +90 -0
  256. PyXMake/VTL/doc/pyx_core/html/dir_4335ce4a64d0d4f9f4c8f0a782dfdfeb.js +6 -0
  257. PyXMake/VTL/doc/pyx_core/html/dir_7e461070e7b716e896e0d97cd6a82321.html +90 -0
  258. PyXMake/VTL/doc/pyx_core/html/dir_7e461070e7b716e896e0d97cd6a82321.js +6 -0
  259. PyXMake/VTL/doc/pyx_core/html/doc.png +0 -0
  260. PyXMake/VTL/doc/pyx_core/html/doxygen.css +1596 -0
  261. PyXMake/VTL/doc/pyx_core/html/doxygen.png +0 -0
  262. PyXMake/VTL/doc/pyx_core/html/doxygen_8py_source.html +93 -0
  263. PyXMake/VTL/doc/pyx_core/html/dynsections.js +104 -0
  264. PyXMake/VTL/doc/pyx_core/html/files.html +118 -0
  265. PyXMake/VTL/doc/pyx_core/html/files.js +21 -0
  266. PyXMake/VTL/doc/pyx_core/html/folderclosed.png +0 -0
  267. PyXMake/VTL/doc/pyx_core/html/folderopen.png +0 -0
  268. PyXMake/VTL/doc/pyx_core/html/functions.html +463 -0
  269. PyXMake/VTL/doc/pyx_core/html/functions_func.html +242 -0
  270. PyXMake/VTL/doc/pyx_core/html/functions_vars.html +345 -0
  271. PyXMake/VTL/doc/pyx_core/html/hierarchy.html +145 -0
  272. PyXMake/VTL/doc/pyx_core/html/hierarchy.js +69 -0
  273. PyXMake/VTL/doc/pyx_core/html/ifort_8py_source.html +93 -0
  274. PyXMake/VTL/doc/pyx_core/html/index.html +90 -0
  275. PyXMake/VTL/doc/pyx_core/html/java_8py_source.html +93 -0
  276. PyXMake/VTL/doc/pyx_core/html/jquery.js +87 -0
  277. PyXMake/VTL/doc/pyx_core/html/menu.js +26 -0
  278. PyXMake/VTL/doc/pyx_core/html/menudata.js +90 -0
  279. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make.html +164 -0
  280. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make.js +6 -0
  281. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build.html +128 -0
  282. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build.js +4 -0
  283. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build_1_1____install____.html +131 -0
  284. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build_1_1_make.html +201 -0
  285. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build_1_1_make.js +13 -0
  286. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools.html +128 -0
  287. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools.js +5 -0
  288. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_error_handling.html +131 -0
  289. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_error_handling.js +6 -0
  290. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_utility.html +907 -0
  291. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_utility.js +6 -0
  292. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l.html +350 -0
  293. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l.js +4 -0
  294. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1abaqus.html +222 -0
  295. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1api.html +268 -0
  296. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1app.html +251 -0
  297. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1bundle.html +199 -0
  298. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1cxx.html +219 -0
  299. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1doxygen.html +230 -0
  300. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ifort.html +414 -0
  301. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1java.html +242 -0
  302. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1py2x.html +264 -0
  303. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1sphinx.html +307 -0
  304. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ssh__f2py.html +296 -0
  305. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ssh__ifort.html +489 -0
  306. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ssh__make.html +275 -0
  307. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1stm__make.html +185 -0
  308. PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1stm__make.js +34 -0
  309. PyXMake/VTL/doc/pyx_core/html/namespacemembers.html +252 -0
  310. PyXMake/VTL/doc/pyx_core/html/namespacemembers_func.html +245 -0
  311. PyXMake/VTL/doc/pyx_core/html/namespacemembers_vars.html +95 -0
  312. PyXMake/VTL/doc/pyx_core/html/namespaces.html +116 -0
  313. PyXMake/VTL/doc/pyx_core/html/namespaces.js +4 -0
  314. PyXMake/VTL/doc/pyx_core/html/nav_f.png +0 -0
  315. PyXMake/VTL/doc/pyx_core/html/nav_g.png +0 -0
  316. PyXMake/VTL/doc/pyx_core/html/nav_h.png +0 -0
  317. PyXMake/VTL/doc/pyx_core/html/navtree.css +146 -0
  318. PyXMake/VTL/doc/pyx_core/html/navtree.js +517 -0
  319. PyXMake/VTL/doc/pyx_core/html/navtreedata.js +35 -0
  320. PyXMake/VTL/doc/pyx_core/html/navtreeindex0.js +253 -0
  321. PyXMake/VTL/doc/pyx_core/html/navtreeindex1.js +197 -0
  322. PyXMake/VTL/doc/pyx_core/html/open.png +0 -0
  323. PyXMake/VTL/doc/pyx_core/html/py2x_8py_source.html +93 -0
  324. PyXMake/VTL/doc/pyx_core/html/resize.js +114 -0
  325. PyXMake/VTL/doc/pyx_core/html/search/all_0.html +26 -0
  326. PyXMake/VTL/doc/pyx_core/html/search/all_0.js +8 -0
  327. PyXMake/VTL/doc/pyx_core/html/search/all_1.html +26 -0
  328. PyXMake/VTL/doc/pyx_core/html/search/all_1.js +14 -0
  329. PyXMake/VTL/doc/pyx_core/html/search/all_10.html +26 -0
  330. PyXMake/VTL/doc/pyx_core/html/search/all_10.js +15 -0
  331. PyXMake/VTL/doc/pyx_core/html/search/all_11.html +26 -0
  332. PyXMake/VTL/doc/pyx_core/html/search/all_11.js +7 -0
  333. PyXMake/VTL/doc/pyx_core/html/search/all_12.html +26 -0
  334. PyXMake/VTL/doc/pyx_core/html/search/all_12.js +5 -0
  335. PyXMake/VTL/doc/pyx_core/html/search/all_13.html +26 -0
  336. PyXMake/VTL/doc/pyx_core/html/search/all_13.js +4 -0
  337. PyXMake/VTL/doc/pyx_core/html/search/all_14.html +26 -0
  338. PyXMake/VTL/doc/pyx_core/html/search/all_14.js +7 -0
  339. PyXMake/VTL/doc/pyx_core/html/search/all_2.html +26 -0
  340. PyXMake/VTL/doc/pyx_core/html/search/all_2.js +8 -0
  341. PyXMake/VTL/doc/pyx_core/html/search/all_3.html +26 -0
  342. PyXMake/VTL/doc/pyx_core/html/search/all_3.js +12 -0
  343. PyXMake/VTL/doc/pyx_core/html/search/all_4.html +26 -0
  344. PyXMake/VTL/doc/pyx_core/html/search/all_4.js +12 -0
  345. PyXMake/VTL/doc/pyx_core/html/search/all_5.html +26 -0
  346. PyXMake/VTL/doc/pyx_core/html/search/all_5.js +9 -0
  347. PyXMake/VTL/doc/pyx_core/html/search/all_6.html +26 -0
  348. PyXMake/VTL/doc/pyx_core/html/search/all_6.js +13 -0
  349. PyXMake/VTL/doc/pyx_core/html/search/all_7.html +26 -0
  350. PyXMake/VTL/doc/pyx_core/html/search/all_7.js +15 -0
  351. PyXMake/VTL/doc/pyx_core/html/search/all_8.html +26 -0
  352. PyXMake/VTL/doc/pyx_core/html/search/all_8.js +13 -0
  353. PyXMake/VTL/doc/pyx_core/html/search/all_9.html +26 -0
  354. PyXMake/VTL/doc/pyx_core/html/search/all_9.js +5 -0
  355. PyXMake/VTL/doc/pyx_core/html/search/all_a.html +26 -0
  356. PyXMake/VTL/doc/pyx_core/html/search/all_a.js +9 -0
  357. PyXMake/VTL/doc/pyx_core/html/search/all_b.html +26 -0
  358. PyXMake/VTL/doc/pyx_core/html/search/all_b.js +10 -0
  359. PyXMake/VTL/doc/pyx_core/html/search/all_c.html +26 -0
  360. PyXMake/VTL/doc/pyx_core/html/search/all_c.js +6 -0
  361. PyXMake/VTL/doc/pyx_core/html/search/all_d.html +26 -0
  362. PyXMake/VTL/doc/pyx_core/html/search/all_d.js +8 -0
  363. PyXMake/VTL/doc/pyx_core/html/search/all_e.html +26 -0
  364. PyXMake/VTL/doc/pyx_core/html/search/all_e.js +46 -0
  365. PyXMake/VTL/doc/pyx_core/html/search/all_f.html +26 -0
  366. PyXMake/VTL/doc/pyx_core/html/search/all_f.js +5 -0
  367. PyXMake/VTL/doc/pyx_core/html/search/classes_0.html +26 -0
  368. PyXMake/VTL/doc/pyx_core/html/search/classes_0.js +5 -0
  369. PyXMake/VTL/doc/pyx_core/html/search/classes_1.html +26 -0
  370. PyXMake/VTL/doc/pyx_core/html/search/classes_1.js +5 -0
  371. PyXMake/VTL/doc/pyx_core/html/search/classes_10.html +26 -0
  372. PyXMake/VTL/doc/pyx_core/html/search/classes_10.js +4 -0
  373. PyXMake/VTL/doc/pyx_core/html/search/classes_11.html +26 -0
  374. PyXMake/VTL/doc/pyx_core/html/search/classes_11.js +5 -0
  375. PyXMake/VTL/doc/pyx_core/html/search/classes_2.html +26 -0
  376. PyXMake/VTL/doc/pyx_core/html/search/classes_2.js +4 -0
  377. PyXMake/VTL/doc/pyx_core/html/search/classes_3.html +26 -0
  378. PyXMake/VTL/doc/pyx_core/html/search/classes_3.js +7 -0
  379. PyXMake/VTL/doc/pyx_core/html/search/classes_4.html +26 -0
  380. PyXMake/VTL/doc/pyx_core/html/search/classes_4.js +10 -0
  381. PyXMake/VTL/doc/pyx_core/html/search/classes_5.html +26 -0
  382. PyXMake/VTL/doc/pyx_core/html/search/classes_5.js +4 -0
  383. PyXMake/VTL/doc/pyx_core/html/search/classes_6.html +26 -0
  384. PyXMake/VTL/doc/pyx_core/html/search/classes_6.js +7 -0
  385. PyXMake/VTL/doc/pyx_core/html/search/classes_7.html +26 -0
  386. PyXMake/VTL/doc/pyx_core/html/search/classes_7.js +4 -0
  387. PyXMake/VTL/doc/pyx_core/html/search/classes_8.html +26 -0
  388. PyXMake/VTL/doc/pyx_core/html/search/classes_8.js +5 -0
  389. PyXMake/VTL/doc/pyx_core/html/search/classes_9.html +26 -0
  390. PyXMake/VTL/doc/pyx_core/html/search/classes_9.js +5 -0
  391. PyXMake/VTL/doc/pyx_core/html/search/classes_a.html +26 -0
  392. PyXMake/VTL/doc/pyx_core/html/search/classes_a.js +4 -0
  393. PyXMake/VTL/doc/pyx_core/html/search/classes_b.html +26 -0
  394. PyXMake/VTL/doc/pyx_core/html/search/classes_b.js +4 -0
  395. PyXMake/VTL/doc/pyx_core/html/search/classes_c.html +26 -0
  396. PyXMake/VTL/doc/pyx_core/html/search/classes_c.js +4 -0
  397. PyXMake/VTL/doc/pyx_core/html/search/classes_d.html +26 -0
  398. PyXMake/VTL/doc/pyx_core/html/search/classes_d.js +13 -0
  399. PyXMake/VTL/doc/pyx_core/html/search/classes_e.html +26 -0
  400. PyXMake/VTL/doc/pyx_core/html/search/classes_e.js +6 -0
  401. PyXMake/VTL/doc/pyx_core/html/search/classes_f.html +26 -0
  402. PyXMake/VTL/doc/pyx_core/html/search/classes_f.js +5 -0
  403. PyXMake/VTL/doc/pyx_core/html/search/close.png +0 -0
  404. PyXMake/VTL/doc/pyx_core/html/search/functions_0.html +26 -0
  405. PyXMake/VTL/doc/pyx_core/html/search/functions_0.js +6 -0
  406. PyXMake/VTL/doc/pyx_core/html/search/functions_1.html +26 -0
  407. PyXMake/VTL/doc/pyx_core/html/search/functions_1.js +11 -0
  408. PyXMake/VTL/doc/pyx_core/html/search/functions_10.html +26 -0
  409. PyXMake/VTL/doc/pyx_core/html/search/functions_10.js +4 -0
  410. PyXMake/VTL/doc/pyx_core/html/search/functions_2.html +26 -0
  411. PyXMake/VTL/doc/pyx_core/html/search/functions_2.js +4 -0
  412. PyXMake/VTL/doc/pyx_core/html/search/functions_3.html +26 -0
  413. PyXMake/VTL/doc/pyx_core/html/search/functions_3.js +6 -0
  414. PyXMake/VTL/doc/pyx_core/html/search/functions_4.html +26 -0
  415. PyXMake/VTL/doc/pyx_core/html/search/functions_4.js +4 -0
  416. PyXMake/VTL/doc/pyx_core/html/search/functions_5.html +26 -0
  417. PyXMake/VTL/doc/pyx_core/html/search/functions_5.js +5 -0
  418. PyXMake/VTL/doc/pyx_core/html/search/functions_6.html +26 -0
  419. PyXMake/VTL/doc/pyx_core/html/search/functions_6.js +7 -0
  420. PyXMake/VTL/doc/pyx_core/html/search/functions_7.html +26 -0
  421. PyXMake/VTL/doc/pyx_core/html/search/functions_7.js +14 -0
  422. PyXMake/VTL/doc/pyx_core/html/search/functions_8.html +26 -0
  423. PyXMake/VTL/doc/pyx_core/html/search/functions_8.js +5 -0
  424. PyXMake/VTL/doc/pyx_core/html/search/functions_9.html +26 -0
  425. PyXMake/VTL/doc/pyx_core/html/search/functions_9.js +5 -0
  426. PyXMake/VTL/doc/pyx_core/html/search/functions_a.html +26 -0
  427. PyXMake/VTL/doc/pyx_core/html/search/functions_a.js +5 -0
  428. PyXMake/VTL/doc/pyx_core/html/search/functions_b.html +26 -0
  429. PyXMake/VTL/doc/pyx_core/html/search/functions_b.js +9 -0
  430. PyXMake/VTL/doc/pyx_core/html/search/functions_c.html +26 -0
  431. PyXMake/VTL/doc/pyx_core/html/search/functions_c.js +5 -0
  432. PyXMake/VTL/doc/pyx_core/html/search/functions_d.html +26 -0
  433. PyXMake/VTL/doc/pyx_core/html/search/functions_d.js +6 -0
  434. PyXMake/VTL/doc/pyx_core/html/search/functions_e.html +26 -0
  435. PyXMake/VTL/doc/pyx_core/html/search/functions_e.js +4 -0
  436. PyXMake/VTL/doc/pyx_core/html/search/functions_f.html +26 -0
  437. PyXMake/VTL/doc/pyx_core/html/search/functions_f.js +4 -0
  438. PyXMake/VTL/doc/pyx_core/html/search/mag_sel.png +0 -0
  439. PyXMake/VTL/doc/pyx_core/html/search/namespaces_0.html +26 -0
  440. PyXMake/VTL/doc/pyx_core/html/search/namespaces_0.js +25 -0
  441. PyXMake/VTL/doc/pyx_core/html/search/nomatches.html +12 -0
  442. PyXMake/VTL/doc/pyx_core/html/search/search.css +271 -0
  443. PyXMake/VTL/doc/pyx_core/html/search/search.js +791 -0
  444. PyXMake/VTL/doc/pyx_core/html/search/search_l.png +0 -0
  445. PyXMake/VTL/doc/pyx_core/html/search/search_m.png +0 -0
  446. PyXMake/VTL/doc/pyx_core/html/search/search_r.png +0 -0
  447. PyXMake/VTL/doc/pyx_core/html/search/searchdata.js +27 -0
  448. PyXMake/VTL/doc/pyx_core/html/search/variables_0.html +26 -0
  449. PyXMake/VTL/doc/pyx_core/html/search/variables_0.js +4 -0
  450. PyXMake/VTL/doc/pyx_core/html/search/variables_1.html +26 -0
  451. PyXMake/VTL/doc/pyx_core/html/search/variables_1.js +6 -0
  452. PyXMake/VTL/doc/pyx_core/html/search/variables_2.html +26 -0
  453. PyXMake/VTL/doc/pyx_core/html/search/variables_2.js +5 -0
  454. PyXMake/VTL/doc/pyx_core/html/search/variables_3.html +26 -0
  455. PyXMake/VTL/doc/pyx_core/html/search/variables_3.js +4 -0
  456. PyXMake/VTL/doc/pyx_core/html/search/variables_4.html +26 -0
  457. PyXMake/VTL/doc/pyx_core/html/search/variables_4.js +7 -0
  458. PyXMake/VTL/doc/pyx_core/html/search/variables_5.html +26 -0
  459. PyXMake/VTL/doc/pyx_core/html/search/variables_5.js +5 -0
  460. PyXMake/VTL/doc/pyx_core/html/search/variables_6.html +26 -0
  461. PyXMake/VTL/doc/pyx_core/html/search/variables_6.js +9 -0
  462. PyXMake/VTL/doc/pyx_core/html/search/variables_7.html +26 -0
  463. PyXMake/VTL/doc/pyx_core/html/search/variables_7.js +8 -0
  464. PyXMake/VTL/doc/pyx_core/html/search/variables_8.html +26 -0
  465. PyXMake/VTL/doc/pyx_core/html/search/variables_8.js +7 -0
  466. PyXMake/VTL/doc/pyx_core/html/search/variables_9.html +26 -0
  467. PyXMake/VTL/doc/pyx_core/html/search/variables_9.js +5 -0
  468. PyXMake/VTL/doc/pyx_core/html/search/variables_a.html +26 -0
  469. PyXMake/VTL/doc/pyx_core/html/search/variables_a.js +6 -0
  470. PyXMake/VTL/doc/pyx_core/html/search/variables_b.html +26 -0
  471. PyXMake/VTL/doc/pyx_core/html/search/variables_b.js +9 -0
  472. PyXMake/VTL/doc/pyx_core/html/search/variables_c.html +26 -0
  473. PyXMake/VTL/doc/pyx_core/html/search/variables_c.js +9 -0
  474. PyXMake/VTL/doc/pyx_core/html/search/variables_d.html +26 -0
  475. PyXMake/VTL/doc/pyx_core/html/search/variables_d.js +4 -0
  476. PyXMake/VTL/doc/pyx_core/html/search/variables_e.html +26 -0
  477. PyXMake/VTL/doc/pyx_core/html/search/variables_e.js +4 -0
  478. PyXMake/VTL/doc/pyx_core/html/search/variables_f.html +26 -0
  479. PyXMake/VTL/doc/pyx_core/html/search/variables_f.js +4 -0
  480. PyXMake/VTL/doc/pyx_core/html/sphinx_8py_source.html +93 -0
  481. PyXMake/VTL/doc/pyx_core/html/splitbar.png +0 -0
  482. PyXMake/VTL/doc/pyx_core/html/ssh__f2py_8py_source.html +93 -0
  483. PyXMake/VTL/doc/pyx_core/html/ssh__ifort_8py_source.html +93 -0
  484. PyXMake/VTL/doc/pyx_core/html/ssh__make_8py_source.html +93 -0
  485. PyXMake/VTL/doc/pyx_core/html/stm__make_8py_source.html +241 -0
  486. PyXMake/VTL/doc/pyx_core/html/stm_logo.png +0 -0
  487. PyXMake/VTL/doc/pyx_core/html/sync_off.png +0 -0
  488. PyXMake/VTL/doc/pyx_core/html/sync_on.png +0 -0
  489. PyXMake/VTL/doc/pyx_core/html/tab_a.png +0 -0
  490. PyXMake/VTL/doc/pyx_core/html/tab_b.png +0 -0
  491. PyXMake/VTL/doc/pyx_core/html/tab_h.png +0 -0
  492. PyXMake/VTL/doc/pyx_core/html/tab_s.png +0 -0
  493. PyXMake/VTL/doc/pyx_core/html/tabs.css +1 -0
  494. PyXMake/VTL/doc/pyx_qmh/DLR_QMH_Guidelines_PyXMake.docx +0 -0
  495. PyXMake/VTL/doc/pyx_qmh/DLR_QMH_Guidelines_PyXMake.pdf +0 -0
  496. PyXMake/VTL/docker.py +86 -0
  497. PyXMake/VTL/doxygen.py +87 -0
  498. PyXMake/VTL/examples/pyx_abaqus.py +154 -0
  499. PyXMake/VTL/examples/pyx_api.py +86 -0
  500. PyXMake/VTL/examples/pyx_app.py +87 -0
  501. PyXMake/VTL/examples/pyx_archive.py +90 -0
  502. PyXMake/VTL/examples/pyx_bundle.py +76 -0
  503. PyXMake/VTL/examples/pyx_chocolatey.py +126 -0
  504. PyXMake/VTL/examples/pyx_cmake.py +78 -0
  505. PyXMake/VTL/examples/pyx_coverage.py +72 -0
  506. PyXMake/VTL/examples/pyx_cxx.py +100 -0
  507. PyXMake/VTL/examples/pyx_docker.py +86 -0
  508. PyXMake/VTL/examples/pyx_doxygen.py +87 -0
  509. PyXMake/VTL/examples/pyx_gfortran.py +207 -0
  510. PyXMake/VTL/examples/pyx_gitlab.py +177 -0
  511. PyXMake/VTL/examples/pyx_ifort.py +204 -0
  512. PyXMake/VTL/examples/pyx_java.py +117 -0
  513. PyXMake/VTL/examples/pyx_latex.py +64 -0
  514. PyXMake/VTL/examples/pyx_openapi.py +168 -0
  515. PyXMake/VTL/examples/pyx_portainer.py +58 -0
  516. PyXMake/VTL/examples/pyx_py2x.py +103 -0
  517. PyXMake/VTL/examples/pyx_pyreq.py +80 -0
  518. PyXMake/VTL/examples/pyx_sphinx.py +82 -0
  519. PyXMake/VTL/examples/pyx_ssh_f2py.py +161 -0
  520. PyXMake/VTL/examples/pyx_ssh_ifort.py +187 -0
  521. PyXMake/VTL/examples/pyx_ssh_make.py +227 -0
  522. PyXMake/VTL/examples/pyx_stm_cara.py +125 -0
  523. PyXMake/VTL/examples/pyx_stm_svn2git.py +123 -0
  524. PyXMake/VTL/gfortran.py +207 -0
  525. PyXMake/VTL/gitlab.py +177 -0
  526. PyXMake/VTL/hooks/windows/stm_pre_commit.bat +35 -0
  527. PyXMake/VTL/ifort.py +204 -0
  528. PyXMake/VTL/java.py +117 -0
  529. PyXMake/VTL/latex.py +64 -0
  530. PyXMake/VTL/make/pyx_module.f90 +28 -0
  531. PyXMake/VTL/make/pyx_muesli.mk +217 -0
  532. PyXMake/VTL/make/pyx_muesli_def.h +265 -0
  533. PyXMake/VTL/make/pyx_muesli_undef.h +27 -0
  534. PyXMake/VTL/openapi.py +168 -0
  535. PyXMake/VTL/portainer.py +58 -0
  536. PyXMake/VTL/py2x.py +103 -0
  537. PyXMake/VTL/pyreq.py +80 -0
  538. PyXMake/VTL/sphinx.py +82 -0
  539. PyXMake/VTL/ssh_f2py.py +161 -0
  540. PyXMake/VTL/ssh_ifort.py +187 -0
  541. PyXMake/VTL/ssh_make.py +227 -0
  542. PyXMake/VTL/stm_cara.py +125 -0
  543. PyXMake/VTL/stm_make.py +1154 -0
  544. PyXMake/VTL/stm_post.py +60 -0
  545. PyXMake/VTL/stm_svn2git.py +123 -0
  546. PyXMake/__init__.py +130 -0
  547. PyXMake/__install__.py +109 -0
  548. PyXMake/__setenv__.py +67 -0
  549. pyx_core-1.19.2.dist-info/LICENSE +11 -0
  550. pyx_core-1.19.2.dist-info/METADATA +155 -0
  551. pyx_core-1.19.2.dist-info/RECORD +553 -0
  552. pyx_core-1.19.2.dist-info/WHEEL +4 -0
  553. pyx_core-1.19.2.dist-info/entry_points.txt +4 -0
PyXMake/Build/Make.py ADDED
@@ -0,0 +1,4518 @@
1
+ # -*- coding: utf-8 -*-
2
+ # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3
+ # % Make Module - Classes and Functions %
4
+ # %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
5
+ """
6
+ Create a make object to define the building environment and to execute the
7
+ build commands. The make event is subdivided in a pre-, main- and a post-build
8
+ event.
9
+
10
+ @note: PyXMake module
11
+ Created on 20.03.2018
12
+
13
+ @version: 1.0
14
+ ----------------------------------------------------------------------------------------------
15
+ @requires:
16
+ -
17
+
18
+ @change:
19
+ -
20
+
21
+ @author: garb_ma [DLR-FA,STM Braunschweig]
22
+ ----------------------------------------------------------------------------------------------
23
+ """
24
+
25
+ ## @package PyXMake.Build.Make
26
+ # Create a make object to define the building environment.
27
+ ## @author
28
+ # Marc Garbade
29
+ ## @date
30
+ # 20.03.2018
31
+ ## @par Notes/Changes
32
+ # - Added documentation // mg 29.03.2018
33
+ try:
34
+ from builtins import object
35
+ except ImportError:
36
+ pass
37
+
38
+ try:
39
+ FileNotFoundError
40
+ except NameError:
41
+ FileNotFoundError = IOError # @ReservedAssignment
42
+
43
+ import sys, os, platform
44
+ import argparse
45
+ import paramiko
46
+ import abc, six
47
+ import shutil
48
+ import inspect
49
+ import stat
50
+ import json
51
+ import copy
52
+ import shlex
53
+ import ntpath
54
+ import posixpath
55
+ import random, string
56
+ import subprocess
57
+ import multiprocessing
58
+ import tempfile
59
+ import colorsys
60
+ import psutil
61
+ import re
62
+ import io
63
+ import uuid
64
+ import socket
65
+ import getpass
66
+ import base64
67
+ import logging
68
+ import warnings
69
+ import importlib
70
+
71
+ import numpy as np
72
+
73
+ from packaging import version
74
+ from shutil import copyfile
75
+ from types import MethodType
76
+ from collections import OrderedDict #@UnresolvedImport
77
+ from PIL import ImageColor
78
+
79
+ try: # pragma: no cover
80
+ ## Add additional path to environment variable
81
+ if os.path.exists(os.path.join(sys.prefix,"conda-meta")) and not os.path.join(sys.prefix,"conda-meta") in os.getenv("PATH",""):
82
+ os.environ["PATH"] = os.pathsep.join([os.path.join(sys.prefix,"Library","bin"),os.getenv("PATH","")])
83
+ # Now the requests module can be load w/o errors.
84
+ import requests
85
+ # Fail gracefully
86
+ except: pass
87
+
88
+ from ..Tools import Utility
89
+
90
+ ## Absolute system path to PyXMake.
91
+ PyXMakePath = Utility.GetPyXMakePath()
92
+ ## Absolute system path to configuration files.
93
+ Path2Config = os.path.join(PyXMakePath,"Build","config")
94
+ # This is the default build option for all classes/methods inherited from this module going forward. Can be overwritten.
95
+ AllowDefaultMakeOption = bool(int("-1" if __debug__ or not getattr(sys,"frozen",False) else int(os.getenv("pyx_default_make_opt","0"))) == -1)
96
+
97
+ ## Create an alias using default logger for all print statements
98
+ logger = logging.getLogger(__name__)
99
+ # setattr(sys.modules[__name__],"print", logger.info)
100
+
101
+ ## @class PyXMake.Build.Make.OS
102
+ # Abstract base class for all system subclasses. Inherited from built-in ABCMeta & object.
103
+ # Compatible with both Python 2.x and 3.x.
104
+ class OS(Utility.AbstractBase):
105
+ """
106
+ Base class of all supported subsystems.
107
+ """
108
+ def __init__(self, *args, **kwargs):
109
+ ## String identifier of current instance.
110
+ self.SystemObjectKind = "Base"
111
+ pass
112
+
113
+ ## @class PyXMake.Build.Make.NT
114
+ # Abstract base class for all NT subclasses. Inherited from built-in ABCMeta & object.
115
+ # Compatible with both Python 2.x and 3.x.
116
+ @six.add_metaclass(abc.ABCMeta)
117
+ class NT(OS):
118
+ """
119
+ Inherited class to NT projects without any presets.
120
+ """
121
+ @abc.abstractmethod
122
+ def __init__(self, *args, **kwargs):
123
+ """
124
+ Initialization of NT class object.
125
+ """
126
+ super(NT, self).__init__(*args, **kwargs)
127
+ ## String identifier of current instance.
128
+ self.SystemObjectKind = "NT"
129
+
130
+ ## @class PyXMake.Build.Make.POSIX
131
+ # Abstract base class for all POSIX subclasses. Inherited from built-in ABCMeta & object.
132
+ # Compatible with both Python 2.x and 3.x.
133
+ @six.add_metaclass(abc.ABCMeta)
134
+ class POSIX(OS):
135
+ """
136
+ Inherited class to POSIX projects without any presets.
137
+ """
138
+ @abc.abstractmethod
139
+ def __init__(self, *args, **kwargs):
140
+ """
141
+ Initialization of POSIX class object.
142
+ """
143
+ super(POSIX, self).__init__(*args, **kwargs)
144
+ ## String identifier of current instance.
145
+ self.SystemObjectKind = "POSIX"
146
+ ## Overwrite create method in all subclasses to use a predefined MakeFile for all builds. This implements
147
+ # the use of gcc, g++ and gfortran as well as Mingw64 and Linux shell support. Convoluted, but working.
148
+ setattr(self, "create", self.__create__)
149
+ # Copy Makefile to current scratch directory.
150
+ copyfile(os.path.join(Path2Config,"stm_makefile"), os.path.join(self.scrtdir,"Makefile"))
151
+ # Add temporary Makefile to tuple scheduled for removal
152
+ self.temps = self.temps + ("Makefile",)
153
+
154
+ def __create__(self, **kwargs): # pragma: no cover
155
+ """
156
+ Unified create function replacing all create commands of ALL classes when used with Mingx64 or on Linux. All builds are solely
157
+ defined by one unified Makefile. in these cases.
158
+ """
159
+ # Access some sub-packages
160
+ from PyXMake.Build import __install__ #@UnresolvedImport
161
+ from PyXMake.VTL import GetPreprocessingCommand
162
+ from numpy.distutils.fcompiler import gnu
163
+ from numpy.distutils import ccompiler
164
+ from numpy.f2py import crackfortran, auxfuncs
165
+ from numpy.distutils import misc_util
166
+ from packaging.version import parse
167
+
168
+ # Space delimiter
169
+ delimn = " ";
170
+
171
+ # Create a local copy of the current environment
172
+ self.environ = copy.deepcopy(getattr(os.environ,"_data",{}))
173
+
174
+ # Validate third party dependencies
175
+ if Utility.GetPlatform() in ["windows"] and not Utility.GetExecutable("choco"): # pragma: no cover
176
+ os.environ["pyx_user_install"] = "chocolatey"
177
+ os.system(" ".join([sys.executable,os.path.abspath(__install__.__file__)]))
178
+
179
+ ## Add local binary directory to the global path
180
+ os.environ["PATH"] = os.pathsep.join([os.getenv("PATH"),os.path.join(os.path.dirname(os.path.abspath(__install__.__file__)),"bin","chocolatey")])
181
+
182
+ try:
183
+ # Get base path, get compiler path defaulting to the latest version and get base initialization shell script
184
+ msys2_base_path, msys2_compiler_path, msys2_shell_initialization = self.setup(mingw = Utility.GetExecutable("choco"))
185
+ # Update PreProcessing command if default is not available
186
+ if self.hasFoss and ("fpp" in getattr(self,"precmd","") and not Utility.GetExecutable("fpp")): # pragma: no cover
187
+ self.precmd = GetPreprocessingCommand(1).split() + self.precmd.split()[4:]; self.precmd.insert(-1,"-o")
188
+ self.precmd = delimn.join(self.precmd)
189
+ # On NT systems using ming, the quotes have to be changed. Otherwise, a syntax error occurs.
190
+ if Utility.GetPlatform() in ["windows"]: self.precmd = self.precmd.replace('"',"'")
191
+ # Assemble the final command
192
+ self.precmd = delimn.join([msys2_shell_initialization,"-c",'"%s"' % self.precmd])
193
+ except:
194
+ # Only meaningful on windows systems
195
+ if Utility.GetPlatform() in ["windows"]: raise ImportError
196
+ # Set a dummy value
197
+ msys2_base_path = ""
198
+ msys2_compiler_path = ""
199
+ msys2_shell_initialization = ""
200
+
201
+ try:
202
+ # Resolving compatibility issue > Python 3.6
203
+ from re.RegexFlag import MULTILINE
204
+ except ImportError:
205
+ # Python 3.5 & lower
206
+ from re import MULTILINE
207
+
208
+ # Go into scratch directory (if defined). This directory has to include a Makefile.
209
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
210
+
211
+ # Verify that module files are in the same path as any output from f2py
212
+ if self.MakeObjectKind in ['Py2X',"f2py"]:
213
+ self.incdirs.append(os.path.join(os.getcwd(),"tmp","Release"))
214
+
215
+ # Rewrite all path to be Linux/Mingw64 compliant
216
+ os.environ["pyx_compiler"] = os.getenv("pyx_compiler").replace(ntpath.sep,posixpath.sep)
217
+
218
+ if Utility.GetPlatform() in ["windows"]:
219
+ # Only add dependencies for MSYS on Windows.
220
+ os.environ["pyx_ldflags"] = delimn.join(["-L"+os.path.join(msys2_base_path,"mingw64","lib"),"-L"+msys2_compiler_path]) + delimn
221
+ if parse(np.__version__) >= parse("1.22.0"): os.environ["pyx_ldflags"] = delimn.join([os.getenv("pyx_ldflags",""), "-D__STDC_NO_THREADS__"]) + delimn
222
+
223
+ # Add all include paths to the command string
224
+ for x in ['-I"'+x+'" ' for x in self.incdirs]: os.environ["pyx_ldflags"] = os.getenv("pyx_ldflags","") + x
225
+
226
+ # Add all dependency paths to the command string
227
+ for x in ['-L"'+x+'" ' for x in self.libdirs]: os.environ["pyx_ldflags"] = os.getenv("pyx_ldflags","") + x
228
+
229
+ # Add all required libraries to the command string
230
+ for x in ['-l'+x+' ' for x in self.libs]: os.environ["pyx_ldflags"] = os.getenv("pyx_ldflags","") + x
231
+
232
+ # Publish additional user commands as an environment variable
233
+ os.environ["pyx_ldflags"] = os.getenv("pyx_ldflags","").replace("\\","/")
234
+
235
+ # Pre-build event (if required)
236
+ try:
237
+ if self.precmd != '' and self.precmd.strip() != self.iniCompiler:
238
+ command = self.precmd.split(delimn); command.insert(-2,"-D__GFORTRAN__");
239
+ # Assemble command
240
+ self.precmd = delimn.join(command)
241
+ # Execute the command
242
+ Utility.Popen(self.precmd, self.verbose)
243
+ except: pass
244
+
245
+ # Loop over all source files and apply cross-compilation pre-processing to all of them.
246
+ try:
247
+ for source in os.getenv("pyx_source").split(delimn):
248
+ Make.F2CPreprocessing(source)
249
+ except FileNotFoundError:
250
+ if os.path.isfile(self.intermediate_wrapper):
251
+ Utility.ReplaceTextinFile(self.intermediate_wrapper, self.wrapper_module, {'%pyx_source%':'"'+self.buildname+'"'}, source=self.scrtdir)
252
+ Make.F2CPreprocessing(self.wrapper_module)
253
+ os.environ["pyx_source"] = self.wrapper_module
254
+ except UnicodeError: pass
255
+
256
+ # Strip decorator commands from the original make command. These are relevant for the Makefile as well.
257
+ os.environ["pyx_cflags"] = delimn.join([os.getenv("pyx_cflags",""),delimn.join([x for x in self.makecmd.split(delimn) if x.startswith("-D")])])
258
+
259
+ ## Base command for all make commands
260
+ # Windows with MSYS2
261
+ command = msys2_shell_initialization
262
+ # Linux
263
+ if Utility.GetPlatform() != "windows": command = delimn.join([';export PYX_BUILDID="'+os.environ["pyx_buildid"]+'"',';export PYX_SOURCE="'+os.environ["pyx_source"]+'"',";bash"])
264
+
265
+ # If used with Python and numpy, check if numpy version is sufficient and patch-able.
266
+ if self.MakeObjectKind in ['Py2X',"f2py"]: # pragma: no cover
267
+ # Modify numpy on the fly
268
+ for x in [ccompiler, crackfortran, gnu, misc_util]:
269
+ # Exception handling when executing in a Docker container instance.
270
+ if Utility.IsDockerContainer() and Utility.GetPlatform() in ["linux"] and not os.access(x.__file__, os.W_OK):
271
+ Permission = str(oct(stat.S_IMODE(os.lstat(x.__file__).st_mode)));
272
+ subprocess.check_call(["sudo","chmod","777",x.__file__])
273
+
274
+ # Cross compilation flags (GNU style format)
275
+ os.environ["pyx_cflags"] += delimn + "-ffree-line-length-0"
276
+ if self.makecmd.find("-fixed") != -1:
277
+ os.environ["pyx_cflags"] += delimn + "-f%s-form" % "fixed"
278
+ else:
279
+ os.environ["pyx_cflags"] += delimn + "-f%s-form" % "free"
280
+
281
+ # Output detailed information from f2py when increasing the verbose level
282
+ if self.verbose >= 2: os.environ["pyx_ldflags"] += delimn + "--verbose"
283
+ else: os.environ["pyx_ldflags"] += delimn + "--quiet"
284
+
285
+ # Add additional command line options to the final command
286
+ if Utility.GetPlatform() != "windows":
287
+ # Fetch compiler request on Linux. Remove file extension and path from f2py call.
288
+ command = command.split(delimn); compiler = Utility.PathLeaf(os.getenv("pyx_compiler"))
289
+ command.insert(0,'export PYX_COMPILER="'+compiler.replace(os.path.splitext(compiler)[1],"")+'"');
290
+ command.insert(0,'export PYX_CFLAGS="'+os.getenv("pyx_cflags")+'"')
291
+ command.insert(0,'export PYX_LDFLAGS="'+os.getenv("pyx_ldflags")+'"')
292
+ command.insert(0,'export LD_RUN_PATH="'+os.pathsep.join(self.libdirs)+'"')
293
+ command.insert(0,'export NPY_DISTUTILS_APPEND_FLAGS=1')
294
+ command = delimn.join(command)
295
+
296
+ ## This beauty ensures backwards compatibility with older f2py versions. Upon 1.19, there persists a bug in the original implementation,
297
+ # preventing the correct version to be identified. This bug has since been resolved. If the version is sufficient, simply run everything on a dummy file.
298
+ with tempfile.NamedTemporaryFile(mode='w+') as __:
299
+ # Modify f2py's routines to support UTF-8 in any case
300
+ with open(gnu.__file__,"r") as f: gnustream = f.read()
301
+ with open(misc_util.__file__,"r") as f: utilstream = f.read();
302
+ with open(auxfuncs.__file__,"r") as f: auxstream = f.read();
303
+ with open(crackfortran.__file__,"r") as f: crackstream = f.read();
304
+ with open(ccompiler.__file__,"r") as f: ccompstream = f.read()
305
+ # Replace substring if version is not sufficient.
306
+ with open(gnu.__file__,"w") as f:
307
+ # Replace the following with numpy's source code. This was fixed in version 1.19.1
308
+ target = "v >= '4.'"; replace = "int(v.split('.')[0]) >= int('4')";
309
+ pattern = re.compile(target,MULTILINE); _ = gnustream;
310
+ # Replace substring if version is not sufficient.
311
+ if Utility.GetPlatform() in ["windows"]: _ = gnustream.replace('["<F90>", "-Wall", "-g"],','["<F90>", "-Wall", "-g","-static","-static-libgcc","-static-libgfortran"],')
312
+ if re.search(pattern,_) and not parse(np.__version__) >= parse("1.19.0"):
313
+ if self.verbose >= 2:
314
+ print("==================================")
315
+ print("On-the-fly patching of numpy version %s" % np.__version__)
316
+ print("==================================")
317
+ _ = re.sub(pattern,replace,_)
318
+ f.write(_)
319
+ # Replace all problematic statements
320
+ with open(misc_util.__file__,"w") as f:
321
+ # Modify statements in-place
322
+ if utilstream.find(str("import textwrap")) != -1:
323
+ _ = utilstream.replace(str("import textwrap"),str("import textwrap, io"))
324
+ else: _ = utilstream.replace(str("import os"),str("import os, io"))
325
+ # Enforce correct encoding.
326
+ _ = _.replace(str("open(source, 'r')"),str('io.open(source, "r", encoding="utf-8")')); f.write(_)
327
+ with open(crackfortran.__file__,"w") as f:
328
+ # Modify statements in-place
329
+ _ = crackstream.replace(str("import fileinput"),str("import fileinput, io"))
330
+ _ = _.replace(str("fileinput.FileInput(ffile)"),str("fileinput.FileInput(ffile,openhook=fileinput.hook_encoded('utf-8'))"))
331
+ # Check if version is below 3.
332
+ if sys.version_info <= (3,0): _ = _.replace(str(r'\xa0'),str(r'\t'))
333
+ # Verify that fixed form format is correctly identified under all circumstances
334
+ if self.makecmd.find("-fixed") != -1 and parse(np.__version__) >= parse("1.26.0"):
335
+ # This has two hits. But one is in the __main__ section of the code and thus irrelevant
336
+ _ = _.replace(str("sourcecodeform = 'free'"),str("sourcecodeform = 'fix'"));
337
+ # Modify source code
338
+ _ = _.replace(str("open(file, 'r')"),str('io.open(file, "r", encoding="utf-8")')); f.write(_)
339
+ # Modify auxiliary code when using old version. Established backwards compatibility.
340
+ if sys.version_info <= (3,0):
341
+ with open(auxfuncs.__file__,"w") as f:
342
+ # Ensure that all rules can be found. No type mixing allowed.
343
+ _ = auxstream.replace(str("if isinstance(rules[k], str)"),str("if isinstance(rules[k], basestring)"))
344
+ _ = _.replace("ret[k] = replace(rules[k], d)","ret[k] = str(replace(str(rules[k]), d))")
345
+ f.write(_)
346
+ ## Always compile all sources with permissive. For now.
347
+ # Use this hack to sneak the compile option to f2py
348
+ if parse(np.__version__) < parse("2.0.0"):
349
+ with open(ccompiler.__file__,"w") as f:
350
+ # Ensure that source code is compiled with relaxed rules
351
+ _ = ccompstream.replace("ccomp = self.compiler_so","self.compiler_so += ['-fpermissive','-fno-strict-aliasing']; ccomp = self.compiler_so")
352
+ f.write(_)
353
+ ## HOTFIX for older interpreters. Do not mess with the default encoding.
354
+ # Modify auxiliary code when using old version. Established backwards compatibility.
355
+ if kwargs.get("use_default_encoding",sys.version_info <= (3,0)):
356
+ # Modify f2py to support UTF-8 in any case
357
+ with open(misc_util.__file__,"w") as f: f.write(utilstream)
358
+ with open(crackfortran.__file__,"w") as f: f.write(crackstream)
359
+ with open(auxfuncs.__file__,"w") as f: f.write(auxstream)
360
+ try:
361
+ # Execute build command
362
+ params = {} if not Utility.IsDockerContainer() else {"shell":True,"collect":True}
363
+ # Modify global encoding of the old interpreter
364
+ environment = os.environ.copy();
365
+ # Update environment variable
366
+ if sys.version_info <= (3, 0): params.update({"env":environment})
367
+ # Do not remove newline from output strings
368
+ if Utility.GetPlatform() in ["windows"] and not Utility.IsDockerContainer():
369
+ params.update({"replace":""})
370
+ command = delimn.join([command,"-c",'"make f2py"'])
371
+ # Execute the command
372
+ Utility.Popen(command, verbosity=self.verbose, **params)
373
+ except:
374
+ pass
375
+ finally:
376
+ # Modify f2py to support UTF-8 in any case
377
+ with open(gnu.__file__,"w") as f: f.write(gnustream)
378
+ with open(misc_util.__file__,"w") as f: f.write(utilstream)
379
+ with open(crackfortran.__file__,"w") as f: f.write(crackstream)
380
+ with open(ccompiler.__file__,"w") as f: f.write(ccompstream)
381
+ with open(auxfuncs.__file__,"w") as f: f.write(auxstream)
382
+ # Print information for the user.
383
+ if self.verbose >= 2:
384
+ print("==================================")
385
+ print("Restoring numpy version %s" % np.__version__)
386
+ print("==================================")
387
+ # Delete temporary folders
388
+ if os.path.exists("tmp") or os.path.exists(os.getenv("pyx_buildid")):
389
+ try:
390
+ shutil.rmtree("tmp"); shutil.rmtree(os.getenv("pyx_buildid"))
391
+ except (FileNotFoundError, OSError) as _: pass
392
+
393
+ # Restore default access rights.
394
+ if Utility.IsDockerContainer() and Utility.GetPlatform in ["linux"]: subprocess.check_call(["sudo","chmod",Permission,gnu.__file__])
395
+
396
+ elif self.MakeObjectKind in ['Fortran',"CCxx"]:
397
+ # Cross compilation flags (GNU style format)
398
+ if self.makecmd.find("-fixed") != -1:
399
+ os.environ["pyx_cflags"] += delimn + "-ffixed-line-length-132 -f%s-form" % "fixed"
400
+ elif self.MakeObjectKind in ['Fortran']:
401
+ os.environ["pyx_cflags"] += delimn + "-ffree-line-length-0 -f%s-form" % "free"
402
+
403
+ # Add additional command line options to the final command
404
+ if Utility.GetPlatform() != "windows":
405
+ command = command.split(delimn);
406
+ command.insert(0,'export PYX_CFLAGS="'+os.getenv("pyx_cflags")+'"')
407
+ command.insert(0,'export PYX_LDFLAGS="'+os.getenv("pyx_ldflags")+'"')
408
+ command = delimn.join(command)
409
+
410
+ # Execute build command
411
+ command = delimn.join([command,"-c",'"make"'])
412
+ Utility.Popen(command, verbosity=self.verbose, replace="")
413
+
414
+ # Add temporary files to tuple scheduled for removal
415
+ self.temps = self.temps + (".o",)
416
+
417
+ # Remove module files. We have to process them later.
418
+ _ = list(self.temps);
419
+ if ".mod" in _: _.remove(".mod");
420
+ self.temps = tuple(_)
421
+
422
+ # Finish and delete redundant files and folders
423
+ Utility.DeleteFilesbyEnding(self.temps)
424
+
425
+ # Copy all unprocessed files to the output folder
426
+ for x in os.listdir(os.getcwd()):
427
+ try:
428
+ ## Accept both OutLibs and OutDir variable. Checks for existence of OutLibs first.
429
+ if os.path.isfile(x) and not x.startswith("."): shutil.move(x,os.path.join(getattr(self,"outlibs",self.outdir),x))
430
+ except: pass
431
+
432
+ # Copy module and header files to output module folder (if required)
433
+ if hasattr(self, "outlibs") and hasattr(self, "outmodule"):
434
+ if self.outlibs != self.outmodule:
435
+ with Utility.ChangedWorkingDirectory(self.outlibs):
436
+ # Only create folders if any module or header files actually exists
437
+ if any([x.endswith((".mod",".h")) for x in os.listdir(os.getcwd())]): os.makedirs(self.outmodule, exist_ok=True)
438
+ for x in os.listdir(os.getcwd()):
439
+ if x.endswith((".mod",".h")): shutil.move(x,os.path.join(self.outmodule,x))
440
+
441
+ # Remove environment variable in case of multiple builds
442
+ os.environ.pop("pyx_cflags","")
443
+ os.environ.pop("pyx_ldflags","")
444
+
445
+ # Recreate environment upon job finished
446
+ for k, v in getattr(self,"environ",{}).items():
447
+ # Prevent problems when keys or values became encoded
448
+ if hasattr(k,"decode"): k = k.decode()
449
+ if hasattr(v,"decode"): v = v.decode()
450
+ os.environ.update({k:v})
451
+ pass
452
+
453
+ ## @class PyXMake.Build.Make.Make
454
+ # Abstract base class for all make objects. Inherited from built-in ABCMeta & object.
455
+ # Compatible with both Python 2.x and 3.x.
456
+ class Make(Utility.AbstractBase):
457
+ """
458
+ Parent class for all make objects.
459
+ """
460
+ @abc.abstractmethod
461
+ def __init__(self, BuildID, Srcs, scratch=os.getcwd(), verbose=0, *args, **kwargs):
462
+ """
463
+ Low-level initialization of parent class.
464
+ """
465
+ ## Base string of build object.
466
+ # Defines the base string of the current build object. The final build name
467
+ # used in the instanced objects is assembled using this immutable base id.
468
+ self.buildid = BuildID
469
+ ## Source file or folders
470
+ self.srcs = []
471
+ self.srcs.append(Srcs)
472
+ self.srcs = list(Utility.ArbitraryFlattening(self.srcs))
473
+ # The given class is run bare. Child classes can be programmed to react properly to this situation
474
+ self.bare = not BuildID and not self.srcs
475
+ ## Source file type
476
+ self.stype = kwargs.get("stype",'Fortran')
477
+ ## Level of verbosity of the current build object.
478
+ # Define the verbosity level of the current build object. Defaults to 0 and suppresses
479
+ # all outputs to the command line. A higher value increases the level of verbosity up to
480
+ # a maximum level of 2.
481
+ self.verbose = verbose
482
+ ## Toggle between free open source software and commercial 3rd party libraries. On POSIX systems,
483
+ # only free open source software is supported. On Windows, the Intel Compiler Library as well as the package manager
484
+ # MINGW64 are natively supported. All other variants have no supported presets.
485
+ self.hasFoss = kwargs.get("foss", Utility.GetExecutable("choco") or Utility.GetPlatform() in ["linux"] or kwargs.get("bash",False))
486
+
487
+ # Set scratch directory & default input/ output locations
488
+ with Utility.ChangedWorkingDirectory(scratch):
489
+ ## Current scratch directory
490
+ self.scrtdir = os.getcwd()
491
+ ## Default search directory for source files.
492
+ self.srcdir = os.getcwd()
493
+ ## Default search directory for output.
494
+ self.outdir = os.getcwd()
495
+
496
+ # Read Intel path from Paths.log. Only if present
497
+ if os.path.exists(os.path.join(PyXMakePath,'Paths.log')):
498
+ with open(os.path.join(PyXMakePath,'Paths.log')) as f:
499
+ content = f.readlines()
500
+ content = [x.strip() for x in content][20]
501
+ # Do not use paths provided explicitly
502
+ else: content = ""
503
+ ## Path to Intel Fortran Compiler (read from Paths.log or empty).
504
+ self.intelpath = content
505
+
506
+ # Initialization of tuple containing temporary files
507
+ ## Tuple of data to be removed after job completion.
508
+ self.temps = ()
509
+
510
+ # Initialization of lists containing additional sources, modules or libraries
511
+ ## List of include directories.
512
+ self.incdirs = []
513
+ ## List of library directories.
514
+ self.libdirs = []
515
+ ## List of actual libraries (by name) used during linking.
516
+ self.libs = []
517
+
518
+ # Initialization of list containing files to be copied to the output directory.
519
+ ## List of files to be copied to the output directory after finish.
520
+ self.copyfiles = []
521
+
522
+ ## Default initialization of compiler script.
523
+ # Only set on NT systems. There is no compiler initialization script for Linux
524
+ self.iniCompiler = ""
525
+
526
+ ## Define the architecture for the build directly by using the keyword argument "arch".
527
+ # Defaults to None, in which case the architecture is determined by using the python executable.
528
+ self.setarch = True if kwargs.get('arch', None) in ['x86', 'x64'] else False
529
+
530
+ ## Default version of Microsoft visual studio used by the Intel Fortran Compiler. Defaults to 'vs2015'.
531
+ self.msvsc = kwargs.get("msvsc",'vs2015')
532
+ os.environ['pyx_msvsc'] = self.msvsc
533
+
534
+ # Set variables in dependence of identified system
535
+ if 'win' in sys.platform:
536
+
537
+ if (('32bit' in platform.architecture() and not self.setarch) or (self.setarch and kwargs.get('arch', None) == "x86")):
538
+ # Set variables for x86
539
+ os.environ['pyx_proc'] = 'x86'
540
+ os.environ['pyx_intel'] = 'ia32'
541
+ ## Processor architecture
542
+ self.architecture = 'x86'
543
+
544
+ elif (('64bit' in platform.architecture() and not self.setarch) or (self.setarch and kwargs.get('arch', None) == "x64")):
545
+ # Set variables for x64
546
+ os.environ['pyx_proc'] = 'amd64'
547
+ os.environ['pyx_intel'] = 'intel64'
548
+ self.architecture = 'x64'
549
+
550
+ ## Executable batch script (including absolute system path) to set up the Intel Fortran Compiler.
551
+ if kwargs.get("initialize",True):
552
+ # Set Intel Compiler path for backwards compatibility
553
+ if not self.intelpath: _, self.intelpath, self.iniCompiler = self.setup(**kwargs)
554
+ # Modern convention
555
+ else: self.iniCompiler = self.setup(**kwargs)[-1]
556
+
557
+ elif Utility.IsDockerContainer() and Utility.GetPlatform() in ["linux"]:
558
+ # Set default environment variables
559
+ os.environ["pyx_intel"] = "intel64_lin"
560
+ # Distributed Intel runtime in Docker.
561
+ self.intelpath = os.path.join(Utility.AsDrive("opt"),"intel","psxe_runtime","linux")
562
+ # There is no difference on linux. This will default to x64 in all cases.
563
+ self.architecture = Utility.GetArchitecture()
564
+
565
+ elif 'linux' in sys.platform:
566
+ # Set default environment variables
567
+ os.environ["pyx_intel"] = ""
568
+ # There is no difference on linux. This will default to x64 in all cases.
569
+ self.architecture = Utility.GetArchitecture()
570
+
571
+ ## Post build command. Defaults to an empty string.
572
+ self.postcmd = ""
573
+
574
+ # Always add MKL library
575
+ ## Uses absolute system path of MKL library.
576
+ if os.path.exists(os.path.join(self.intelpath,"mkl","include")):
577
+ # Additional include source files. They are not part of the redistribution package
578
+ mkl_include_path = os.path.join(self.intelpath,"mkl","include")
579
+ self.incdirs.append(mkl_include_path)
580
+
581
+ if os.path.exists(os.path.join(self.intelpath,"mkl")):
582
+ # Path to MKL and compiler library. These are part of any redistribution package.
583
+ mkl_lib_path = os.path.join(self.intelpath,"mkl","lib",os.environ['pyx_intel'])
584
+ mkl_compiler_path = os.path.join(self.intelpath,"compiler","lib",os.environ['pyx_intel'])
585
+ self.libdirs.extend([mkl_lib_path, mkl_compiler_path])
586
+
587
+ # Update environment build path
588
+ if os.path.exists(os.path.join(self.intelpath,"bin",os.environ['pyx_intel'])):
589
+ os.environ["PATH"] = ";".join([os.getenv("PATH",""),os.path.join(self.intelpath,"bin",os.environ['pyx_intel'])])
590
+
591
+ # Collect all MKL include files (if given!)
592
+ self._mkl_includes = []; mkl_paths = [x for x in self.incdirs if "mkl" in x]
593
+ for path in mkl_paths: self._mkl_includes.extend([x for x in os.listdir(path) if (os.path.isfile(os.path.join(path,x)) and x.endswith((".f90",".F90",".for",".FOR",".f",".F",".f77",".F77")))])
594
+
595
+ # Dynamic second inheritance in dependence of supplied keyword argument POSIX.
596
+ self.__posix__(**kwargs)
597
+
598
+ # Add all source files to list of files to remove from scratch folder. No exceptions.
599
+ self.temps = self.temps + tuple((os.path.join(self.scrtdir,x) for x in self.srcs))
600
+ pass
601
+
602
+ def __posix__(self,**kwargs):
603
+ """
604
+ Request compatibility with Mingw64 and Linux.
605
+ """
606
+ # Dynamic second inheritance in dependence of supplied keyword argument POSIX.
607
+ if kwargs.get("bash", self.hasFoss):
608
+ try:
609
+ # Attempt to initialize POSIX support
610
+ POSIX.__init__(self)
611
+ except:
612
+ pass
613
+ else:
614
+ try:
615
+ ## Delete prototype of create function.
616
+ # Its content was renamed to "create" in POSIX.__init__()
617
+ delattr(self, "__create__")
618
+ except:
619
+ pass
620
+
621
+ @staticmethod
622
+ def __parser__(): # pragma: no cover
623
+ """
624
+ Default parser object for command line interface
625
+
626
+ @author: Marc Garbade
627
+ """
628
+ # Process all known arguments
629
+ parser = argparse.ArgumentParser(add_help=False)
630
+ # Default parser object with settings shared by most commands.
631
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
632
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
633
+ parser.add_argument('-f', '--files', nargs='+', default=[], help="Source file or list of all source files in the order of compilation")
634
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
635
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional directories and files required for the build.")
636
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for the build. Defaults to current workspace.")
637
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
638
+ # Return default parser object
639
+ return parser
640
+
641
+ @staticmethod
642
+ def Detach(): # pragma: no cover
643
+ """
644
+ Detach current console window from parent window.
645
+
646
+ @author: Marc Garbade
647
+ """
648
+ kwargs = {}
649
+ # Set system/version dependent "start_new_session" analogs
650
+ if os.name == 'nt': # Windows
651
+ DETACHED_PROCESS = 0x00000008 # 0x8 | 0x200 == 0x208
652
+ CREATE_NEW_PROCESS_GROUP = subprocess.CREATE_NEW_PROCESS_GROUP
653
+ kwargs.update(creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP)
654
+ elif sys.version_info < (3, 2): # Posix
655
+ kwargs.update(preexec_fn=os.setsid) # @UndefinedVariable
656
+ else: # Python 3.2+ and Unix
657
+ kwargs.update(start_new_session=True)
658
+
659
+ return kwargs
660
+
661
+ @staticmethod
662
+ def F2CPreprocessing(PreprocessingFile):
663
+ """
664
+ Replace incompatible Fortran PreProcessing directives with its C++ counterparts.
665
+
666
+ @author: schu_a1
667
+ """
668
+ try:
669
+ # Resolving compatibility issue > Python 3.6
670
+ from re.RegexFlag import MULTILINE
671
+ except ImportError:
672
+ # Python 3.5 & lower
673
+ from re import MULTILINE
674
+
675
+ def NegationDirective(matchobj):
676
+ """
677
+ Replace .not. with ! (e.g. .not. defined will be !defined)
678
+ """
679
+ newString = ""
680
+ if matchobj.group(1) == ".NOT. ":
681
+ newString += "!"
682
+
683
+ newString += "defined(%s)" % matchobj.group(2)
684
+ return newString
685
+
686
+ def LowerDirective(matchobj):
687
+ """
688
+ All initial directives are given in lower cases.
689
+ """
690
+ return "#" + matchobj.group()[1:].lower()
691
+
692
+ # Definition of pattern to be substituted.
693
+ decorator = re.compile("^!DEC\$ (?=IF|ELSE|ENDIF|END IF)",MULTILINE)
694
+ addition = re.compile("^#(.*?)\.AND\.(.*?)", MULTILINE)
695
+ either = re.compile("^#(.*?)\.OR\.(.*?)", MULTILINE)
696
+ negation = re.compile("(\.NOT\. )?DEFINED\(([A-Za-z0-9_]+)\)", MULTILINE)
697
+ conditionals = re.compile("^#(IF|ENDIF|END IF|ELSE)",MULTILINE)
698
+ space = re.compile("^#(end if)",MULTILINE)
699
+
700
+ ## Load everything into memory
701
+ # Do not specify encoding here.
702
+ try:
703
+ # Default's to system specifics
704
+ with io.open(PreprocessingFile,"r") as f: stream = f.read()
705
+ except:
706
+ # Happens in some windows systems.
707
+ if Utility.GetPlatform() in ["windows"]:
708
+ try:
709
+ ## Try windows specific file format first.
710
+ with io.open(PreprocessingFile,"r", encoding='cp1252') as f: stream = f.read()
711
+ except:
712
+ # If not successful, try to use UTF-8 directly
713
+ with io.open(PreprocessingFile,"r",encoding="utf-8") as f: stream = f.read()
714
+ else:
715
+ # Define encoding explicitly
716
+ with io.open(PreprocessingFile,"r",encoding="utf-8") as f: stream = f.read()
717
+
718
+ # Replace all incompatible patters
719
+ stream = decorator.sub("#",stream)
720
+ stream = addition.sub("#\g<1>&&\g<2>", stream)
721
+ stream = either.sub("#\g<1>||\g<2>", stream)
722
+ stream = negation.sub(NegationDirective, stream)
723
+ stream = conditionals.sub(LowerDirective, stream)
724
+ stream = space.sub("#endif",stream)
725
+
726
+ # Replace the input file with the updated version. Specify the encoding.
727
+ try:
728
+ # Modern convention
729
+ with io.open(PreprocessingFile,"w",encoding="utf-8") as f: f.write(stream)
730
+ except:
731
+ # Legacy version
732
+ with open(PreprocessingFile,"w") as f: f.write(stream)
733
+
734
+ def AddIncludePath(self, includes):
735
+ """
736
+ Define additional include directories containing modules or source files as comma separated list.
737
+ """
738
+ self.incdirs.append(includes)
739
+ self.incdirs = list(Utility.ArbitraryFlattening(self.incdirs))
740
+ pass
741
+
742
+ def AddDependencyPath(self, dependencies):
743
+ """
744
+ Define additional directories containing 3rd party libraries as comma separated list.
745
+ """
746
+ self.libdirs.append(dependencies)
747
+ self.libdirs = list(Utility.ArbitraryFlattening(self.libdirs))
748
+ pass
749
+
750
+ def UseLibraries(self, libs):
751
+ """
752
+ Define which non-default libraries should be used during linking.
753
+ """
754
+ self.libs.append(libs)
755
+ self.libs = list(Utility.ArbitraryFlattening(self.libs))
756
+ pass
757
+
758
+ def SourcePath(self, path):
759
+ """
760
+ Define a new source directory. Input is read from workspace by default.
761
+ """
762
+ self.srcdir = path
763
+ ## Source directory can be parsed as relative or
764
+ # absolute path w.r.t. to is initial calling script.
765
+ with Utility.ChangedWorkingDirectory(self.srcdir):
766
+ self.srcdir = os.path.abspath(os.getcwd())
767
+ pass
768
+
769
+ def OutputPath(self, path, files=""):
770
+ """
771
+ Define a new output directory. Output is written to the workspace by default.
772
+ """
773
+ self.outdir = path
774
+ ## Output directory can be parsed as relative or
775
+ # absolute path w.r.t. to is initial calling script.
776
+ with Utility.ChangedWorkingDirectory(self.outdir):
777
+ self.outdir = os.path.abspath(os.getcwd())
778
+ ## List of files copied to the output directory.
779
+ self.copyfiles.append(files)
780
+ self.copyfiles = list(Utility.ArbitraryFlattening(self.copyfiles))
781
+ pass
782
+
783
+ def Environment(self, path, script="ifortvars.bat"): # pragma: no cover
784
+ """
785
+ Load an additional environment file prior to execution of all commands.
786
+ """
787
+ ## Execute an additional bash script prior to all build commands.
788
+ os.environ['pyx_environment'] = os.path.join(path,script)
789
+ if path.rsplit("\\",1)[1] == "bin":
790
+ self.intelpath = path[::-1].replace("bin"[::-1],"",1)[::-1]
791
+ else:
792
+ self.intelpath = path
793
+
794
+ # Update static MKL library include.
795
+ for inc in self.incdirs:
796
+ if all(x in inc for x in ["mkl","include"]):
797
+ self.incdirs.remove(inc)
798
+
799
+ # Update static MKL library.
800
+ for lib in self.libdirs:
801
+ if all(x in lib for x in ["mkl", "lib"]) or all(x in lib for x in ["compiler","lib"]):
802
+ self.libdirs.remove(lib)
803
+
804
+ # Redefine MKL paths
805
+ mkl_include_path = os.path.join(self.intelpath,"mkl","include")
806
+ mkl_lib_path = os.path.join(self.intelpath,"mkl","lib",os.environ['pyx_intel'])
807
+ mkl_compiler_path = os.path.join(self.intelpath,"compiler","lib",os.environ['pyx_intel'])
808
+ # Add newly created path to library path.
809
+ self.incdirs.append(mkl_include_path)
810
+ self.libdirs.extend([mkl_lib_path, mkl_compiler_path])
811
+ pass
812
+
813
+ def Preprocessing(self, cmdstring='', inend='', outend='', copyfiles=[],
814
+ replace = {'!DEC$ IF':'#IF','!DEC$ ELSE':'#ELSE','!DEC$ ENDIF':'#ENDIF'}):
815
+ """
816
+ Assemble command string for the pre-build event.
817
+ """
818
+ # Space delimiter
819
+ delimn = " "
820
+
821
+ # Add file extension to output file name (if not already been done)
822
+ if not Utility.IsNotEmpty(os.path.splitext(self.buildname)[1]):
823
+ self.buildname = self.buildname+outend
824
+
825
+ # Add source directory to list of include dirs in case of any preprocessing event.
826
+ if self.srcdir not in self.incdirs: self.incdirs += [self.srcdir]
827
+
828
+ # Go into scratch directory (if defined)
829
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
830
+ # Create two temporary file names.
831
+ collsrcfile = Utility.GetTemporaryFileName(filename="coll" + "_", extension=inend)
832
+ presrcfile = Utility.GetTemporaryFileName(filename="pre",extension=outend)
833
+
834
+ # Create a list of all directories to be searched
835
+ search_dir = [self.srcdir]
836
+ try:
837
+ search_dir.extend(self.incdirs)
838
+ except:
839
+ pass
840
+
841
+ # Copy all relevant files from the source folder into the current scratch folder.
842
+ if copyfiles != []:
843
+ for subdir in search_dir:
844
+ # Ignore non-existing folders
845
+ if not os.path.exists(subdir): continue
846
+ src_files = os.listdir(subdir)
847
+ file_names = [x for x in src_files if x in copyfiles]
848
+ for inputfile in file_names:
849
+ full_file_name = os.path.join(subdir, inputfile)
850
+ if (os.path.isfile(full_file_name)):
851
+ shutil.copy(full_file_name, os.getcwd())
852
+ self.temps = self.temps + (inputfile, )
853
+ # Rename source code file
854
+ else:
855
+ Utility.ConcatenateFiles(self.buildname,self.srcs,self.srcdir, inend)
856
+
857
+ # Do not apply any pre-processing (required for e.g. Beos)
858
+ if cmdstring != '':
859
+ # Concatenate all source files into one temporary file
860
+ Utility.ConcatenateFiles(collsrcfile, self.srcs, self.srcdir, ending=inend)
861
+
862
+ # Replace pre-processing commands
863
+ Utility.ReplaceTextinFile(collsrcfile, presrcfile, replace, source=self.scrtdir)
864
+
865
+ # Always align commands with C++.
866
+ Make.F2CPreprocessing(presrcfile)
867
+
868
+ # Add all include directories to the call
869
+ if any(x in cmdstring for x in ["cpp", "fpp"]):
870
+ cmdstring += delimn + delimn.join(['-I"'+x+'" ' for x in self.incdirs])
871
+
872
+ # Assemble command string
873
+ cmdstring += ' %s %s' % (presrcfile, self.buildname)
874
+
875
+ # Store command string in object
876
+ ## Command executed during pre-build event.
877
+ self.precmd = self.iniCompiler+" "+cmdstring
878
+
879
+ # Add temporary files to tuple scheduled for removal
880
+ self.temps = self.temps + (collsrcfile, presrcfile, self.buildname, )
881
+ pass
882
+
883
+ def Build(self, cmdstring, **kwargs): # pragma: no cover
884
+ """
885
+ Assemble command string for the main build event.
886
+ """
887
+ # Initialize command string
888
+ cmd = "";
889
+
890
+ # Only apply linking and include directories for supported make operations
891
+ if not self.MakeObjectKind.lower() in ["doxygen"]:
892
+
893
+ # Add all include paths to the command string
894
+ includes = ['-I"'+x+'" ' for x in self.incdirs]
895
+ for x in includes:
896
+ cmd += x
897
+
898
+ # Add all dependency paths to the command string
899
+ dependencies = ['-L"'+x+'" ' for x in self.libdirs]
900
+ for x in dependencies:
901
+ cmd += x
902
+
903
+ # Add all required libraries to the command string
904
+ libs = ['-l'+x+' ' for x in self.libs]
905
+ for x in libs:
906
+ cmd += x
907
+
908
+ ## Command line arguments passed in by the user.
909
+ self.compargs = cmdstring
910
+ ## Command executed during build event.
911
+ self.makecmd = self.iniCompiler+" "+os.path.join(self.path2exe,self.exe)+" "+ cmd + cmdstring
912
+ pass
913
+
914
+ def Postprocessing(self, cmdstring=''): # pragma: no cover
915
+ """
916
+ Assemble command string for the post-build event.
917
+ """
918
+ ## Command executed during post-build event.
919
+ self.postcmd = self.iniCompiler+" "+cmdstring
920
+ pass
921
+
922
+ @staticmethod
923
+ def sanitize(string, **kwargs): # pragma: no cover
924
+ """
925
+ Provide a dictionary with substrings to replace in the given input.
926
+
927
+ @note: Defaults to replace architecture and platform identifiers
928
+ """
929
+ result = copy.deepcopy(string)
930
+ replacements = kwargs.get("replace",{'{arch}': Utility.GetArchitecture(), "{platform}":Utility.GetPlatform()})
931
+ for key, value in replacements.items():
932
+ result = result.replace(key, value)
933
+ result = Utility.GetSanitizedDataFromCommand([result], is_path=False)[-1]
934
+ return result
935
+
936
+ @staticmethod
937
+ def setup(*args, **kwargs): # pragma: no cover
938
+ """
939
+ Initialize a predefined compiler tool chain of all requirements are met.
940
+
941
+ @note: Only meaningful on NT systems.
942
+ """
943
+ # Future proof by only importing
944
+ try: from packaging.version import Version as StrictVersion
945
+ except ImportError: from distutils.version import StrictVersion
946
+
947
+ delimn = " "
948
+
949
+ # The function is only meaningful on NT systems
950
+ if Utility.GetPlatform() not in ["windows"]: return
951
+
952
+ # Initialize and create tool chain for MSYS2 on Windows
953
+ if kwargs.get("mingw",False):
954
+ # Fetch path to choco directory and determine installation directory of msys64
955
+ _, choco_base_path = Utility.GetExecutable("choco", get_path=True)
956
+ # There are two executables of chocolatey present within one installation folder. Because reasons.
957
+ if "bin" not in os.listdir(os.path.dirname(choco_base_path)): choco_base_path = os.path.join(choco_base_path,'..', '..')
958
+ else: choco_base_path = os.path.join(choco_base_path,'..')
959
+ choco_base_path = os.path.normpath(choco_base_path)
960
+ content = open(os.path.join(choco_base_path,"logs","choco.summary.log"),"r").readlines()
961
+ # Determine msys64 installation directory
962
+ try: _, msys2_base_path = next(iter([x for x in content if all([y in x.lower() for y in ["msys64","software installed to"]])])).split("Software installed to")
963
+ # Well, they changed the logs...
964
+ except StopIteration: _, msys2_base_path = next(iter([x for x in content if all([y in x.lower() for y in ["msys64","installing to"]])])).split("Installing to:")
965
+ # Get base path
966
+ try: msys2_base_path = Utility.GetSanitizedDataFromCommand("r"+msys2_base_path.strip(), allowed_exceptions=(ValueError,))[0]
967
+ except SyntaxError: msys2_base_path = msys2_base_path.strip()
968
+ msys2_base_path = os.path.normpath(msys2_base_path)
969
+ # Get compiler path. Default to the latest version
970
+ msys2_compiler_version = sorted(os.listdir(os.path.join(msys2_base_path,"mingw64","lib","gcc","x86_64-w64-mingw32")),key=StrictVersion)[-1]
971
+ msys2_compiler_path = os.path.join(msys2_base_path,"mingw64","lib","gcc","x86_64-w64-mingw32",msys2_compiler_version)
972
+ # Get base initialization shell script
973
+ msys2_shell_initialization = delimn.join([os.path.join(msys2_base_path,"msys2_shell.cmd"),"-defterm","-mingw64","-no-start","-full-path","-here"])
974
+ # Return all paths
975
+ return (msys2_base_path, msys2_compiler_path, msys2_shell_initialization)
976
+ # Initialize and create tool chain for Visual Studio on Windows
977
+ elif kwargs.get("msvsc",'vs2015') in ["vs2015","vs2017","vs2019","vs2022"]: # pragma: no cover
978
+ # Set Intel Fortran Compler path to None as default value
979
+ intel_compiler_path = ""
980
+ # Get base initialization batch script
981
+ msvsc_shell_initialization = r'"'+os.path.join(PyXMakePath,"Build","cmd","windows","iniCompiler.bat")+'"'
982
+ # Remove support for Paths.log
983
+ if not any([Utility.GetExecutable(x) for x in ["ifx","ifort"]]) and not os.path.exists(os.path.join(Utility.GetPyXMakePath(),"Paths.log")):
984
+ # Get directory of the windows start menu
985
+ allprograms = os.path.join(os.environ['ALLUSERSPROFILE'],"Microsoft","Windows","Start Menu", "Programs")
986
+ # Search for Intel
987
+ for root, _, files in Utility.PathWalk(allprograms):
988
+ if "intel" not in root.lower(): continue
989
+ if not any(x.lower().endswith("lnk") for x in files) or not any("compiler" in x.lower() for x in files): continue
990
+ intellink = os.path.abspath(os.path.join(root,sorted(files)[-1]))
991
+ # Read command from binary link
992
+ try: command = Utility.GetLink(intellink)
993
+ # We found nothing associated with Intel. Skip the rest an issue an error later, if required.
994
+ except UnboundLocalError: return (None, intel_compiler_path, msvsc_shell_initialization )
995
+ intelpath = next(iter(x for x in command.split('"') if "compiler" in x.lower()))
996
+ # Set environment variables
997
+ if not os.getenv("pyx_intel","") and not os.getenv("pyx_msvsc",""):
998
+ os.environ["pyx_intel"], os.environ["pyx_msvsc"] = [x.replace('"',"") for x in command.split(delimn)[-2:]]
999
+ # Set environment explicitly
1000
+ os.environ["pyx_environment"] = intelpath
1001
+ # Set default Intel Fortran Compiler path
1002
+ intel_compiler_path = os.path.abspath(os.path.join(intelpath, os.path.pardir, os.path.pardir))
1003
+ # Attempt to initialize Intel Fortran compiler and read environment data from the call
1004
+ data = Utility.GetEnvironmentFromCommand(os.path.join(Utility.GetPyXMakePath(),"Build","cmd","windows","iniCompiler.bat"))
1005
+ # Update the current environment
1006
+ os.environ.update(data)
1007
+ ## Compiler executables are already in the path variable of the current process.
1008
+ # Do not attempt to set the environment here
1009
+ if any([Utility.GetExecutable(x) for x in ["ifx","ifort"]]): os.environ["pyx_environment"] = "unset"
1010
+ # Return all paths
1011
+ return (None, intel_compiler_path, msvsc_shell_initialization )
1012
+ pass
1013
+
1014
+ @classmethod
1015
+ def run(cls, **kwargs):
1016
+ """
1017
+ Assemble command string for the post-build event.
1018
+ """
1019
+ ## Run a given class with its default CLI settings when supported.
1020
+ if hasattr(cls, "parse"): cls.parse(command=sys.argv, **kwargs)
1021
+ else: RuntimeWarning("Class %s has no associated default parsing feature. Skipping." % str(cls.__name__)) # pragma: no cover
1022
+ pass
1023
+
1024
+ def create(self, **kwargs):
1025
+ """
1026
+ Execute make command
1027
+ """
1028
+ # Dictionary holding all local output settings
1029
+ settings = {"verbosity": self.verbose, "collect": not "cmake" in self.exe or Utility.GetPlatform() in ["linux"]}
1030
+
1031
+ # Execute again to account for input added after compiling command
1032
+ try: self.Build(self.compargs, **kwargs)
1033
+ except: pass
1034
+
1035
+ # Go into scratch directory (if defined)
1036
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
1037
+ ## All files are automatically added to the tuple of temporary files. Thus, these files is this list
1038
+ # were already present when the process has started. Accordingly, do not delete these files since
1039
+ # one cannot be sure the scratch directory was set to a workspace with import files
1040
+ vault = [x for x in os.listdir(os.getcwd()) if x not in self.temps]
1041
+
1042
+ for key in os.environ.keys():
1043
+ # Issue a warning if one of the environment variables pass their maximum limit on windows. Subsequent errors may occur.
1044
+ if Utility.GetPlatform() == "windows" and self.verbose >= 3 and len(os.environ[key]) >= 2048: # pragma: no cover
1045
+ warnings.warn("Environment variable %s" % key + " is exceeding the character limit") #@UndefinedVariable
1046
+
1047
+ # Pre-build event (if required)
1048
+ try:
1049
+ if self.precmd != '':
1050
+ Utility.Popen(self.precmd, **settings)
1051
+ except:
1052
+ pass
1053
+
1054
+ # Build event (if required)
1055
+ try: # pragma: no cover
1056
+ if getattr(self,"mkl_dependency",""):
1057
+ # Add additional MKL dependencies
1058
+ for x in getattr(self,"_mkl_includes",[]):
1059
+ if x not in np.atleast_1d(self.mkl_dependency): continue
1060
+ with open("pyx_bridge_"+str(x), "a+") as mkl_include: mkl_include.write(" include '%s' \n" % str(x))
1061
+ command = self.iniCompiler + " "+'ifort -c "%s"' % str("pyx_bridge_"+str(x))
1062
+ Utility.Popen(command, **settings)
1063
+
1064
+ if self.makecmd != '':
1065
+ # Modify build runtime environment
1066
+ env = os.environ.copy();
1067
+ # PYTHONPATH may not exist
1068
+ if not "python" in self.makecmd: env.pop("PYTHONPATH","") #@UndefinedVariable
1069
+ Utility.Popen(self.makecmd, env=env, **settings)
1070
+ except: pass
1071
+
1072
+ # Post-build event (if required)
1073
+ try:
1074
+ if self.postcmd != '':
1075
+ Utility.Popen(self.postcmd, **settings)
1076
+ except:
1077
+ pass
1078
+
1079
+ # Copy files to predefined output location. Add the original to list of redundant files.
1080
+ # Copy only those files which are not temporary and whose name includes the BuildID.
1081
+ # If a list of files if given, use only those files presented in the list. Add to other files to list
1082
+ # of redundant files.
1083
+ try: # pragma: no cover
1084
+ if self.outdir != os.getcwd():
1085
+ for f in os.listdir(os.getcwd()):
1086
+ # Do not process any file in vault
1087
+ if f in vault: continue
1088
+ # All files from here are created during the process
1089
+ elif self.architecture in f and f != self.buildname and f not in self.temps:
1090
+ if self.copyfiles[0] == "":
1091
+ copyfile(os.path.join(os.getcwd(),f), os.path.join(self.outdir,f))
1092
+ self.temps = self.temps + (f, )
1093
+ if f in self.copyfiles:
1094
+ copyfile(os.path.join(os.getcwd(),f), os.path.join(self.outdir,f))
1095
+ self.temps = self.temps + (f, )
1096
+ elif f not in self.copyfiles and self.copyfiles[0] != "" and not os.path.isdir(f):
1097
+ self.temps = self.temps + (f, )
1098
+ except: pass
1099
+ # Finish and delete redundant files
1100
+ Utility.DeleteFilesbyEnding(self.temps)
1101
+ pass
1102
+
1103
+ ## @class PyXMake.Build.Make.Custom
1104
+ # Base class for all custom build events inherited from Make.
1105
+ class Custom(Make):
1106
+ """
1107
+ Inherited class to build projects without any presets.
1108
+ """
1109
+ def __init__(self, *args, **kwargs):
1110
+ """
1111
+ Initialization of Custom class object.
1112
+ """
1113
+ super(Custom, self).__init__(*args, **kwargs)
1114
+ ## String identifier of current instance.
1115
+ self.MakeObjectKind = "Custom"
1116
+
1117
+ ## The executable command used in all build events.
1118
+ self.exe = "cmd.exe /c &&"
1119
+
1120
+ ## Change default executable settings when source is a CMAKE file
1121
+ if self.srcs[-1] in ["CMakeLists.txt"]:
1122
+ settings = { "search_paths": os.pathsep.join([os.getenv("PATH",os.getcwd()),os.path.join(sys.prefix,"scripts")]) }
1123
+ # Ensure that the latest GNU compiler is fetched by CMAKE.
1124
+ self.exe = ["cmake" if not Utility.GetExecutable("cmake", **settings) else
1125
+ Utility.InQuotes(Utility.GetExecutable("cmake",get_path=True, **settings)[-1]) ][0]
1126
+ # Convert an absolute CMAKE path to POSIX style format to properly work with MingW64
1127
+ if Utility.GetPlatform() in ["windows"] and Utility.GetExecutable("choco") and self.hasFoss: # pragma: no cover
1128
+ self.exe = Utility.GetPathConversion(self.exe, "linux")
1129
+
1130
+ ## Immutable settings for Custom object.
1131
+ # Temporary build name, assembled using BuildID.
1132
+ self.buildname = self.buildid + self.architecture
1133
+
1134
+ # Set environment variables for ABAQUS builds (Defaults to latest version).
1135
+ os.environ["ABQ_ENV_FILE"] = "abaqus_v6.env"
1136
+ os.environ["pyx_abaqus"] = os.getenv("pyx_abaqus","abaqus")
1137
+
1138
+ # Add additional MKL dependencies. Only when MKL support is enabled.
1139
+ self.mkl_dependency = ["mkl_vsl.f90"]
1140
+
1141
+ ## Command line arguments passed in by the user.
1142
+ self.compargs = ""
1143
+
1144
+ # Add temporary files to tuple scheduled for removal.
1145
+ self.temps = self.temps + (os.getenv("ABQ_ENV_FILE"), )
1146
+ pass
1147
+
1148
+ def Build(self, cmdstring, **kwargs):
1149
+ """
1150
+ Assemble command string for the main build event.
1151
+ """
1152
+ delimn = " "
1153
+
1154
+ # Do this part only once!
1155
+ if self.compargs == cmdstring:
1156
+ # Add all include paths to the include environment variable
1157
+ os.environ["INCLUDE"] = os.pathsep.join(list(self.incdirs)) + os.pathsep + os.pathsep.join((os.environ.get("MSMPI_INC",""), os.environ.get("INCLUDE","")))
1158
+
1159
+ # Add all additional library paths to the lib environment variable
1160
+ os.environ["LIB"] = os.pathsep.join(self.libdirs) + os.pathsep + os.pathsep.join((os.environ.get("MSMPI_LIB64",""), os.environ.get("LIB","")))
1161
+
1162
+ # Add scratch and sources to path environment variable
1163
+ os.environ["PATH"] = os.pathsep.join([self.srcdir,self.scrtdir]) + os.pathsep + os.getenv("PATH","")
1164
+
1165
+ ## Execute a CMake build script
1166
+ if "cmake" in self.exe:
1167
+
1168
+ # Create build command
1169
+ command = "%s" % self.exe
1170
+ # Source path quotation is dependent of the underlying system build framework
1171
+ if not self.hasFoss and Utility.GetPlatform() in ["windows"]: command += ' -B build -S '"%s"'' % os.path.abspath(self.srcdir)
1172
+ else: command += " -B build -S '%s'" % os.path.abspath(self.srcdir)
1173
+ # Remove duplicate cmake call
1174
+ command += cmdstring.replace("cmake"," ")
1175
+
1176
+ ## Attribute only contains string when a non-default output directory has been defined.
1177
+ # Update the installation prefix in that case
1178
+ if any(isinstance(x,six.string_types) for x in getattr(self,"copyfiles",[])): # pragma: no cover
1179
+ command += " -DCMAKE_INSTALL_PREFIX='%s'" % os.path.abspath(self.outdir)
1180
+
1181
+ # Collect half the number of available core to speed up the build process
1182
+ cpus = str(multiprocessing.cpu_count()//2) #@UndefinedVariable
1183
+
1184
+ # Deactivate MKL dependency
1185
+ self.mkl_dependency = []
1186
+
1187
+ # Explicitly set all compilers
1188
+ if self.hasFoss: command +=" -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_Fortran_COMPILER=gfortran"
1189
+
1190
+ # Add verbose output
1191
+ if self.verbose >= 2: command +=" -DCMAKE_VERBOSE_MAKEFILE=On"
1192
+
1193
+ # Add all additional supplied user options w/o further verification
1194
+ if kwargs.get("append",[]): command += delimn + delimn.join(kwargs.get("append"))
1195
+
1196
+ # Run CMake on windows with cross compilation
1197
+ if not self.hasFoss and Utility.GetPlatform() in ["windows"]: # pragma: no cover
1198
+ command +=' -DCMAKE_BUILD_TYPE=Release'
1199
+ # Custom header files are required on windows.
1200
+ command +=' -DCMAKE_INCLUDE_PATH="%s"' % os.path.join(PyXMakePath,"VTL","make")
1201
+ # Add local script directory to local search path
1202
+ settings = {"search_paths":os.pathsep.join([os.path.join(sys.prefix,"Scripts"),os.getenv("PATH","")])}
1203
+ # Help CMAKE to find a compatible SED program
1204
+ if Utility.GetExecutable("pythonsed",**settings):
1205
+ command +=' -DSED="%s"' % Utility.GetExecutable("pythonsed", get_path=True, **settings)[-1]
1206
+ elif Utility.GetExecutable("choco"):
1207
+ try:
1208
+ settings = {"search_paths":os.pathsep.join([os.path.join(Make.setup(mingw = Utility.GetExecutable("choco"))[0],"usr","bin"),os.getenv("PATH","")])}
1209
+ command +=' -DSED="%s"' % Utility.GetExecutable("sed", get_path=True, **settings)[-1]
1210
+ except: pass
1211
+ # If SED is found directly. Do nothing
1212
+ elif Utility.GetExecutable("sed"): pass
1213
+ # Support pythonSED executable
1214
+ # Should never happen
1215
+ else: pass
1216
+ # Suppress warnings for developers and set up NMAKE
1217
+ command +=' -Wno-dev -G "NMake Makefiles"'
1218
+ # When using Intel oneAPI environment, use classic compiler for the time being
1219
+ if Utility.GetExecutable("oneapi-cli") and Utility.GetExecutable("ifort"): command += " -DCMAKE_Fortran_COMPILER=ifort"
1220
+ # Get batch script initialization
1221
+ batch = delimn.join([self.iniCompiler,"&&"])
1222
+ # Define build commands
1223
+ self.precmd = delimn.join([batch,'%s' % command])
1224
+ self.makecmd = delimn.join([batch,'%s' % "%s --build build" % self.exe])
1225
+ self.postcmd = delimn.join([batch,'%s' % "%s --install build" % self.exe])
1226
+ # Run using MINGW on NT systems
1227
+ elif Utility.GetPlatform() in ["windows"] and Utility.GetExecutable("choco"): # pragma: no cover
1228
+ command += " -G 'MinGW Makefiles'"
1229
+ # Get MSYS2 initialization
1230
+ msys2_shell = delimn.join([self.setup(mingw=True)[-1],"-c"])
1231
+ # Define build commands
1232
+ self.precmd = delimn.join([msys2_shell,'"%s"' % command])
1233
+ self.makecmd = delimn.join([msys2_shell,'"%s"' % "%s --build build -j %s" % (self.exe, cpus)])
1234
+ self.postcmd = delimn.join([msys2_shell,'"%s"' % "%s --install build" % self.exe])
1235
+ # Run CMake on POSIX systems
1236
+ else:
1237
+ # Define build commands
1238
+ self.precmd = shlex.split(command, posix=not os.name.lower() in ["nt"])
1239
+ self.makecmd = shlex.split("%s --build build -j %s" % (self.exe, cpus), posix=not os.name.lower() in ["nt"])
1240
+ self.postcmd = shlex.split("%s --install build" % self.exe, posix=not os.name.lower() in ["nt"])
1241
+
1242
+ # Add pre-processed source file to environment variable
1243
+ os.environ["pyx_file"] = self.buildname
1244
+
1245
+ # Add exception to pyx_libs when ABAQUS build command is used with vs2015 and higher.
1246
+ if self.msvsc.lower() in ["vs2015", "vs2017","vs2019","vs2022"]:
1247
+ self.libs.extend(["msvcrt", "vcruntime", "ucrt", "legacy_stdio_definitions"])
1248
+ # Remove explicit reference to Microsoft Runtime Library when using lastest Intel OneAPI environment
1249
+ if Utility.GetExecutable("oneapi-cli"): self.libs.remove("msvcrt")
1250
+
1251
+ # Add all libraries to a environment variable
1252
+ pyx_libs = ["'"+x+".lib'" for x in sorted(set(self.libs), key=self.libs.index)]
1253
+
1254
+ # Set environment variable
1255
+ os.environ["pyx_libs"] = ",".join(pyx_libs)
1256
+
1257
+ ## Command line arguments passed in by the user.
1258
+ self.compargs = cmdstring
1259
+ ## Command executed during build event.
1260
+ if not getattr(self, "makecmd",""): self.makecmd = self.iniCompiler+" "+self.exe+" "+cmdstring
1261
+
1262
+ # Add temporary files to tuple scheduled for removal
1263
+ self.temps = self.temps + (self.buildname, )
1264
+ pass
1265
+
1266
+ @classmethod
1267
+ def parse(cls, **kwargs): # pragma: no cover
1268
+ """
1269
+ Execute the current class as a CLI command.
1270
+ """
1271
+ # Import its main from VTL
1272
+ from PyXMake.VTL import cmake
1273
+ # Evaluate current command line
1274
+ command = kwargs.get("command",sys.argv)
1275
+ # Process all known arguments
1276
+ parser = argparse.ArgumentParser(description='CLI wrapper options for CMAKE with more sensible default settings.')
1277
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
1278
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
1279
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for CMake. Defaults to current workspace.")
1280
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
1281
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
1282
+ parser.add_argument("--foss", type=Utility.GetBoolean, const=True, default=True, nargs='?',
1283
+ help="Toggle to enforce free-open source builds. Defaults to True.")
1284
+ # Check all options or run unit tests in default mode
1285
+ try:
1286
+ # Check CLI options
1287
+ _ = command[1]
1288
+ args, unknown = parser.parse_known_args(command[1:])
1289
+ # Project name is mandatory
1290
+ project = args.name[0];
1291
+ # Specification of source directory is mandatory
1292
+ source = args.source[0] ;
1293
+ # Optional non-default output directory
1294
+ try: output = args.output[0]
1295
+ except: output = None
1296
+ # Optional non-default scratch directory
1297
+ try: scratch = args.scratch[0]
1298
+ except: scratch = os.path.abspath(os.getcwd())
1299
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
1300
+ try: verbosity = int(args.verbosity[0])
1301
+ except: verbosity = 0
1302
+ # Optional non-default package check
1303
+ try: foss = args.foss[0]
1304
+ except: foss = Utility.GetPlatform() in ["linux"]
1305
+ # Create a dictionary combining all settings
1306
+ settings = {"source":source, "output":output, "scratch":scratch,
1307
+ "verbosity":verbosity, "foss": foss}
1308
+ # Parse all unknown command line parameter directly to cmake
1309
+ append = [x for x in unknown if not any(y in x for y in vars(args).keys())]
1310
+ if append: settings.update({"append":append})
1311
+ # Use an exception to allow help message to be printed.
1312
+ except Exception as _:
1313
+ # Local imports. These are only meaningful while executing an unit test
1314
+ from PyCODAC.Tools.Utility import GetPyCODACPath
1315
+ # Build all supported features
1316
+ if AllowDefaultMakeOption:
1317
+ # Run compilation of MCODAC using CMake
1318
+ BuildID = "mcd_core";
1319
+ # Compile everything using CMake.
1320
+ cmake(BuildID, source=os.path.join(GetPyCODACPath(),"Core","config"), foss=kwargs.pop("foss",True))
1321
+ else:
1322
+ # Execute CLI command
1323
+ cmake(project, **settings)
1324
+ pass
1325
+
1326
+ ## @class PyXMake.Build.Make.CCxx
1327
+ # Base class for all C/C++ build events inherited from Make.
1328
+ class CCxx(Make,NT,POSIX):
1329
+ """
1330
+ Inherited class to build projects using Intel C/C++.
1331
+ """
1332
+ def __init__(self, *args, **kwargs):
1333
+ """
1334
+ Initialization of C/C++ class object.
1335
+ """
1336
+ super(CCxx, self).__init__(*args, **kwargs)
1337
+ ## String identifier of current instance.
1338
+ self.MakeObjectKind = 'CCxx'
1339
+
1340
+ ## The executable command used in the main build event.
1341
+ self.exe = 'cl.exe'; os.environ["pyx_compiler"] = "gcc"
1342
+
1343
+ ## Static or dynamic link library flag.
1344
+ self.isStatic = True if kwargs.get('lib', 'static') not in ['shared', 'SHARED', 'Shared'] else False
1345
+
1346
+ ## Define if the input should be compiled exactly as provided.
1347
+ # Defaults to False, meaning that merging & pre-processing utilities will be carried out.
1348
+ self.incremental = kwargs.get('incremental', False)
1349
+
1350
+ # Immutable settings for C/Cpp object
1351
+ if self.incremental:
1352
+ self.exe += ' -c %s' % (' '.join(self.srcs))
1353
+ else: # pragma: no cover
1354
+ self.exe += ' -c %s' % (self.buildname)
1355
+
1356
+ ## Name of library, assembled using BuildID.
1357
+ self.libname = self.buildid + self.architecture
1358
+ ## Temporary build name.
1359
+ self.buildname = self.libname
1360
+
1361
+ # Initialization of lists containing additional sources, modules or libraries
1362
+ ## List of libraries which should be statically linked in.
1363
+ self.linkedIn = []
1364
+
1365
+ # Initialization of tuple containing temporary files
1366
+ ## Blank version of tuple to store temporary file names scheduled for removal.
1367
+ self.temps = ()
1368
+
1369
+ # Remove MKL from default command line
1370
+ ## Blank version of list containing library directories without initially specifying MKL.
1371
+ self.libdirs = []
1372
+
1373
+ # Always add conversion headers to the default make directory
1374
+ self.incdirs.append(os.path.join(PyXMakePath,"VTL","make"))
1375
+
1376
+ # Identify source code and BuildID and set the corresponding environment variables for Mingw64 and Linux.
1377
+ if kwargs.get("bash", self.hasFoss):
1378
+ os.environ["pyx_buildid"], os.environ["pyx_source"] = (self.libname, ' '.join([x for x in self.srcs if os.path.splitext(x)[1].lower() in (".c", ".cpp", ".h", ".hpp", "Makefile")]))
1379
+ pass
1380
+
1381
+ def OutputPath(self, libpath=os.getcwd()):
1382
+ """
1383
+ Define output directories for modules and libraries.
1384
+ """
1385
+ ## Output path for library files.
1386
+ self.outlibs = libpath
1387
+ pass
1388
+
1389
+ def Build(self, cmdstring, **kwargs):
1390
+ """
1391
+ Assemble command strings for the main build event.
1392
+ """
1393
+ # Initialize command string
1394
+ cmd = ""
1395
+
1396
+ # Add all include paths to the command string
1397
+ includes = [' -I"'+x+'" ' for x in self.incdirs]
1398
+ for x in includes:
1399
+ cmd += x
1400
+
1401
+ # Choose the librarian and the file extension of the library.
1402
+ if not self.isStatic: # pragma: no cover
1403
+ librarian = 'link -dll -fixed:no -defaultlib:libcmt.lib -nodefaultlib:msvcrt.lib '
1404
+ ext = '.dll'
1405
+ else:
1406
+ librarian = 'lib '
1407
+ ext = '.lib'
1408
+
1409
+ # Build commands using Intel Fortran (immutable)
1410
+ ## Used defined command line options.
1411
+ self.compargs = cmdstring
1412
+ ## Intel Compiler command.
1413
+ self.makecmd = self.iniCompiler+" "
1414
+ self.makecmd += self.exe + cmd + cmdstring + ' && '
1415
+ ## Intel Linker command.
1416
+ self.linkcmd = librarian +'*.obj -nologo -machine:'+self.architecture+' -out:'+os.path.join(self.outlibs,self.libname+ext)
1417
+
1418
+ # Add temporary files to tuple scheduled for removal
1419
+ self.temps = self.temps + (self.libname+'.obj', ".obj",)
1420
+ pass
1421
+
1422
+ @classmethod
1423
+ def parse(cls, **kwargs): # pragma: no cover
1424
+ """
1425
+ Execute the current class as a CLI command.
1426
+ """
1427
+ # Import its main from VTL
1428
+ from PyXMake.VTL import cxx
1429
+ # Evaluate current command line
1430
+ command = kwargs.pop("command",sys.argv)
1431
+ # Process all known arguments
1432
+ parser = argparse.ArgumentParser(description="Build a sCxx project from console.")
1433
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
1434
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
1435
+ parser.add_argument('-f', '--files', nargs='+', default=[], help="Source file or list of all source files in the order of compilation")
1436
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
1437
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional directories and files required for the build.")
1438
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for the build. Defaults to current workspace.")
1439
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
1440
+ parser.add_argument("--incremental", type=Utility.GetBoolean, const=True, default=False, nargs='?',
1441
+ help="Toggle between incremental and non-incremental build. Defaults to False.")
1442
+
1443
+ try:
1444
+ # Check CLI options
1445
+ _ = command[1]
1446
+ args, _ = parser.parse_known_args(command[1:])
1447
+ # Project name is mandatory
1448
+ project = args.name[0]
1449
+ # Specification of source directory is mandatory
1450
+ source = args.source[0] ;
1451
+ # Optional non-default output directory
1452
+ try: files = args.files
1453
+ except: files = []
1454
+ # Optional non-default output directory
1455
+ try: output = args.output[0]
1456
+ except: output = os.path.abspath(os.getcwd())
1457
+ # Optional non-default definition of additional tests cases
1458
+ try:
1459
+ _ = args.include[0]
1460
+ # Collect all given paths. Get system independent format
1461
+ include = Utility.GetSanitizedDataFromCommand(args.include)
1462
+ # No extra test cases have been given
1463
+ except: include = []
1464
+ # Optional incremental build option. Defaults to False.
1465
+ try: incremental = args.incremental[0]
1466
+ except: incremental = False
1467
+ # Optional non-default scratch directory
1468
+ try: scratch = args.scratch[0]
1469
+ except: scratch = os.path.abspath(os.getcwd())
1470
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
1471
+ try: verbosity = int(args.verbosity[0])
1472
+ except: verbosity = 0
1473
+ # Create a dictionary combining all settings
1474
+ settings = {"files":files, "incremental": incremental, "include": include,
1475
+ "source":source, "output":output, "scratch":scratch,
1476
+ "verbosity":verbosity}
1477
+ # Use an exception to allow help message to be printed.
1478
+ except Exception as _:
1479
+ # Build all supported features for current Python version (default options)
1480
+ if AllowDefaultMakeOption:
1481
+ ## This part serves as a unit test. It it not executed by default when installed from PyPi
1482
+ try:
1483
+ # Build Muesli with default settings.
1484
+ BuildID = "muesli"; cxx(BuildID, foss=False, **kwargs)
1485
+ except: pass
1486
+ try:
1487
+ # Build DispLam
1488
+ from PyCODAC.Plugin.DispLam import __path__ as DispLamPath
1489
+ disp_src = os.path.join(DispLamPath[0],"src","displam"); disp_bin=os.path.join(DispLamPath[0],"bin",Utility.GetPlatform())
1490
+ cxx("displam", os.listdir(disp_src), source=disp_src, output=disp_bin, verbosity=0, **kwargs)
1491
+ except: pass
1492
+ # Execute valid CLI command
1493
+ else: cxx(project, **settings)
1494
+ pass
1495
+
1496
+ def create(self): # pragma: no cover
1497
+ """
1498
+ Execute make command
1499
+ """
1500
+ cmd = ''
1501
+
1502
+ # Go into scratch directory (if defined)
1503
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
1504
+
1505
+ # Pre-build event (if required)
1506
+ try:
1507
+ if self.precmd != '':
1508
+ Utility.Popen(self.precmd, self.verbose)
1509
+ except:
1510
+ pass
1511
+
1512
+ # Add all dependency paths to the command string
1513
+ dependencies = [' -LIBPATH:"'+x+'" ' for x in self.libdirs]
1514
+ for y in dependencies:
1515
+ cmd += y
1516
+
1517
+ # Add libraries for linking to the command string
1518
+ linklist = [' '+x+'.lib ' for x in self.libs]
1519
+ for x in linklist:
1520
+ cmd += x
1521
+
1522
+ # Build event (if required)
1523
+ try:
1524
+ if self.makecmd != '':
1525
+ # Execute again to account for input added after compiling command
1526
+ self.Build(self.compargs)
1527
+ Utility.Popen(self.makecmd+self.linkcmd+cmd, self.verbose)
1528
+ except Exception as _: pass
1529
+
1530
+ # Post-build event (if required)
1531
+ try:
1532
+ if self.postcmd != '':
1533
+ Utility.Popen(self.postcmd, self.verbose)
1534
+ except:
1535
+ pass
1536
+
1537
+ # Finish and delete redundant files
1538
+ if not self.isStatic:
1539
+ os.remove(os.path.join(self.outlibs,self.libname+'.exp'))
1540
+ os.remove(os.path.join(self.outlibs,self.libname+'.lib'))
1541
+ Utility.DeleteFilesbyEnding(self.temps)
1542
+ pass
1543
+
1544
+ ## @class PyXMake.Build.Make.Fortran
1545
+ # Base class for all Fortran build events.
1546
+ # Inherited from Make and flavors in dependence of the underlying or requested operating system (optionally).
1547
+ class Fortran(Make,NT,POSIX):
1548
+ """
1549
+ Inherited class to build projects using Intel Fortran.
1550
+ """
1551
+ def __init__(self, *args, **kwargs):
1552
+ """
1553
+ Initialization of Fortran class object.
1554
+ """
1555
+ super(Fortran, self).__init__(*args, **kwargs)
1556
+ ## String identifier of current instance.
1557
+ self.MakeObjectKind = 'Fortran'; os.environ["pyx_compiler"] = "gfortran"
1558
+
1559
+ ## Static or dynamic link library flag.
1560
+ self.isStatic = True if kwargs.get('lib', 'static') not in ['shared', 'SHARED', 'Shared'] else False
1561
+
1562
+ # Add static MKL libraries when creating a shared resource library (for Java applications).
1563
+ if not self.isStatic: # pragma: no cover
1564
+ if self.architecture == "x86":
1565
+ mkl_interface_lib = "mkl_intel_c"
1566
+ else:
1567
+ mkl_interface_lib = "mkl_intel_lp64"
1568
+
1569
+ # Always link statically against MKL library
1570
+ self.libs.append([mkl_interface_lib, "mkl_intel_thread", "mkl_core","libiomp5md"])
1571
+ self.libs = list(Utility.ArbitraryFlattening(self.libs))
1572
+ pass
1573
+
1574
+ # Immutable settings for Fortran object
1575
+ ## Name of library, assembled using BuildID.
1576
+ self.libname = ("lib" if Utility.GetPlatform() in ["linux"] else "") + self.buildid + self.architecture
1577
+ ## Temporary build name.
1578
+ self.buildname = self.libname
1579
+
1580
+ # Activate / deactivate incremental linking
1581
+ self.incremental = kwargs.get("incremental",False)
1582
+
1583
+ # Defined here to be checked later.
1584
+ ## Wrapper interface file for 3rd party FORTRAN code. Automatically creates a module of the underlying source material.
1585
+ self.intermediate_wrapper = ""
1586
+ self.wrapper_source = ""
1587
+ self.wrapper_module = "pyx_module.f90"
1588
+
1589
+ # Initialization of lists containing additional sources, modules or libraries
1590
+ ## List of libraries which should be statically linked in.
1591
+ self.linkedIn = []
1592
+
1593
+ # Remove MKL from default command line
1594
+ ## Blank version of list containing library directories without initially specifying MKL.
1595
+ self.libdirs = []
1596
+
1597
+ # Identify source code and BuildID and set the corresponding environment variables for Mingw64 and Linux.
1598
+ if kwargs.get("bash", self.hasFoss):
1599
+ os.environ["pyx_buildid"], os.environ["pyx_source"] = (self.libname, ' '.join([x for x in self.srcs if os.path.splitext(x)[1].lower() in (".for", ".f95", ".f", ".f90")]))
1600
+ pass
1601
+
1602
+ def OutputPath(self, modulepath=None, libpath=os.getcwd()):
1603
+ """
1604
+ Define output directories for modules and libraries.
1605
+ """
1606
+ # Output module files to scratch directory by default.
1607
+ if modulepath is None:
1608
+ modulepath = self.scrtdir
1609
+ ## Output path for module or header files.
1610
+ self.outmodule = modulepath
1611
+ ## Output path for library files.
1612
+ self.outlibs = libpath
1613
+ pass
1614
+
1615
+ def Preprocessing(self, inend='', outend='', copyfiles=[],
1616
+ replace = {'!DEC$ IF':'#IF','!DEC$ ELSE':'#ELSE','!DEC$ ENDIF':'#ENDIF'},
1617
+ decorator="!DEC$ ATTRIBUTES DLLEXPORT::"):
1618
+ """
1619
+ Assemble command string for the pre-build event.
1620
+ """
1621
+ # Avoid false positives when created a shared resource library.
1622
+ delimn = " "; validater ="bind"
1623
+
1624
+ # Save command if already defined
1625
+ _preprocessing = copy.deepcopy(getattr(self,"precmd",None))
1626
+
1627
+ # Execute base class method
1628
+ Make.Preprocessing(self,"custom", inend, outend, copyfiles, replace)
1629
+
1630
+ # Go into scratch directory (if defined)
1631
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
1632
+
1633
+ # Never merge files unless explicitly allowed
1634
+ if copyfiles:
1635
+ # File might not exists - use exception to catch that
1636
+ try: os.remove(os.path.join(os.getcwd(),self.buildname))
1637
+ except FileNotFoundError: pass
1638
+ # Mimic the previous work flow
1639
+ else:
1640
+ # Get temporary file name
1641
+ presrcfile = self.precmd.split(delimn)[-2]
1642
+
1643
+ # Check if decorators have to be added to the source file.
1644
+ Inputfile = os.path.join(os.getcwd(),presrcfile)
1645
+ Outputfile = os.path.join(os.getcwd(),self.buildname)
1646
+ with open(Inputfile) as infile, open(Outputfile, 'w') as outfile: # pragma: no cover
1647
+ for line in infile:
1648
+ outfile.write(line)
1649
+ if not self.isStatic:
1650
+ xname, xline = line.partition('="')[0], line.partition('="')[2]
1651
+ if xline != '' and validater in xname.lower():
1652
+ outfile.write(decorator+xline.partition('"')[0]+"\n")
1653
+
1654
+ # Recover original command
1655
+ self.precmd = _preprocessing
1656
+ pass
1657
+
1658
+ def Wrapper(self, module_name, source_name=None): # pragma: no cover
1659
+ """
1660
+ Assemble command string for the pre-build event.
1661
+ """
1662
+ # Add module wrapper to the default make directory
1663
+ makedir = os.path.join(PyXMakePath,"VTL","make")
1664
+ TmpFile = Utility.GetTemporaryFileName(extension=str(os.path.splitext(self.wrapper_module)[1]))
1665
+ copyfile(os.path.join(makedir,self.wrapper_module), os.path.join(self.scrtdir,TmpFile))
1666
+
1667
+ if source_name:
1668
+ self.wrapper_source = source_name
1669
+
1670
+ self.intermediate_wrapper = Utility.GetTemporaryFileName(extension=str(os.path.splitext(TmpFile)[1]))
1671
+
1672
+ # Go into scratch directory (if defined)
1673
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
1674
+ # Prepare wrapper module for later use
1675
+ Utility.ReplaceTextinFile(TmpFile, self.intermediate_wrapper, {'%pyx_module%':module_name}, source=self.scrtdir)
1676
+
1677
+ # Add temporary files to tuple scheduled for removal
1678
+ self.temps = self.temps + (TmpFile, self.intermediate_wrapper, self.wrapper_module, )
1679
+ pass
1680
+
1681
+ def Build(self, cmdstring, **kwargs): # pragma: no cover
1682
+ """
1683
+ Assemble command strings for the main build event.
1684
+ """
1685
+ sep = " "; multi_objects = []
1686
+
1687
+ # Initialize command string
1688
+ if not self.incremental:
1689
+ cmd = ' -object:'+self.libname+' -module:'+self.outmodule+' -I:"'+self.outmodule+'"'
1690
+ else:
1691
+ # Add an trailing separator to indicate a folder
1692
+ cmd = ' -object:'+self.scrtdir+os.path.sep+' -module:'+self.outmodule+' -I:"'+self.outmodule+'"'
1693
+
1694
+ # Add all include paths to the command string
1695
+ includes = [' -I"'+x+'" ' for x in self.incdirs]
1696
+ for x in includes:
1697
+ cmd += x
1698
+
1699
+ # Choose the librarian and the file extension of the library.
1700
+ if not self.isStatic:
1701
+ librarian = 'link -dll -fixed:no -defaultlib:libcmt.lib -nodefaultlib:msvcrt.lib '
1702
+ ext = '.dll'
1703
+ else:
1704
+ librarian = 'lib '
1705
+ ext = '.lib'
1706
+
1707
+ # Build commands using Intel Fortran (immutable)
1708
+ ## Used defined command line options.
1709
+ self.compargs = cmdstring
1710
+ ## Intel Compiler command.
1711
+ self.makecmd = self.iniCompiler+" "
1712
+
1713
+ # Check whether an interface module wrapper was added to the current folder
1714
+ if os.path.isfile(self.intermediate_wrapper):
1715
+ makefile = "-fpp "+ self.wrapper_module
1716
+ if (Utility.IsNotEmpty(self.wrapper_source)):
1717
+ self.buildname = self.wrapper_source
1718
+ elif self.incremental:
1719
+ makefile = sep.join([x for x in self.srcs if os.path.splitext(x)[1].lower() in (".for", ".f95", ".f", ".f90")])
1720
+ multi_objects = [os.path.splitext(x)[0]+".obj" for x in self.srcs if os.path.splitext(x)[1].lower() in (".for", ".f95", ".f", ".f90")]
1721
+ else:
1722
+ makefile = self.buildname
1723
+
1724
+ self.makecmd += 'ifort -c '+ makefile + cmd + cmdstring + ' && '
1725
+ ## Intel Linker command.
1726
+ if not multi_objects:
1727
+ self.linkcmd = librarian +self.libname+'.obj -nologo -machine:'+self.architecture+' -out:'+os.path.join(self.outlibs,self.libname+ext)
1728
+ else:
1729
+ self.temps = self.temps + (".obj",)
1730
+ self.linkcmd = librarian +sep.join(multi_objects) +' -nologo -machine:'+self.architecture+' -out:'+os.path.join(self.outlibs,self.libname+ext)
1731
+
1732
+ # Add temporary files to tuple scheduled for removal
1733
+ self.temps = self.temps + (self.libname+'.obj', '.mod')
1734
+ pass
1735
+
1736
+ def create(self, **kwargs): # pragma: no cover
1737
+ """
1738
+ Execute make command
1739
+ """
1740
+ cmd = ''
1741
+
1742
+ # Go into scratch directory (if defined)
1743
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
1744
+
1745
+ # Pre-build event (if required)
1746
+ try:
1747
+ if self.precmd != '':
1748
+ Utility.Popen(self.precmd, self.verbose)
1749
+ except:
1750
+ pass
1751
+
1752
+ # Add all dependency paths to the command string
1753
+ dependencies = [' -LIBPATH:"'+x+'" ' for x in self.libdirs]
1754
+ for y in dependencies:
1755
+ cmd += y
1756
+
1757
+ # Add libraries for linking to the command string
1758
+ linklist = [' '+x+'.lib ' for x in self.libs]
1759
+ for x in linklist:
1760
+ cmd += x
1761
+
1762
+ # Delete old module files
1763
+ if os.path.exists(self.outmodule):
1764
+ # Only meaningful when output directory for a module already exists
1765
+ for f in os.listdir(self.outmodule):
1766
+ if f.endswith('.mod') and not kwargs.get("combine",False):
1767
+ os.remove(os.path.join(self.outmodule,f))
1768
+ else: os.makedirs(self.outmodule)
1769
+
1770
+ # Build event (if required)
1771
+ try:
1772
+ if self.makecmd != '':
1773
+ # Execute again to account for input added after compiling command
1774
+ self.Build(self.compargs)
1775
+ if os.path.isfile(self.intermediate_wrapper):
1776
+ Utility.ReplaceTextinFile(self.intermediate_wrapper, self.wrapper_module, {'%pyx_source%':'"'+self.buildname+'"'}, source=self.scrtdir)
1777
+ Utility.Popen(self.makecmd+self.linkcmd+cmd, self.verbose)
1778
+ except:
1779
+ pass
1780
+
1781
+ # Post-build event (if required)
1782
+ try:
1783
+ if self.postcmd != '':
1784
+ Utility.Popen(self.postcmd, self.verbose)
1785
+ except:
1786
+ pass
1787
+
1788
+ # Combine event (needed for TOMS). Combine multiple libraries into ONE.
1789
+ if self.isStatic and kwargs.get("combine", False):
1790
+ sep = ' '; librarian = 'lib '; ext = '.lib'
1791
+ mergedid = os.path.basename(self.outmodule)
1792
+ multi_libs = [os.path.join(self.outlibs,x) for x in [list(Utility.ArbitraryFlattening(x[2])) for x in Utility.PathWalk(self.outlibs)][0] if x.startswith(mergedid)]
1793
+
1794
+ try:
1795
+ # Remove old combined library from the list.
1796
+ multi_libs.remove(os.path.join(self.outlibs,mergedid+self.architecture+ext))
1797
+ except:
1798
+ pass
1799
+
1800
+ self.postcmd = self.iniCompiler + sep + librarian +sep.join(multi_libs) +' -nologo -machine:'+self.architecture+' -out:'+os.path.join(self.outlibs,mergedid+self.architecture+ext)
1801
+ Utility.Popen(self.postcmd, self.verbose)
1802
+ for lib in multi_libs:
1803
+ os.remove(os.path.join(self.outlibs,lib))
1804
+
1805
+ # Finish and delete redundant files
1806
+ if not self.isStatic:
1807
+ os.remove(os.path.join(self.outlibs,self.libname+'.exp'))
1808
+ os.remove(os.path.join(self.outlibs,self.libname+'.lib'))
1809
+ Utility.DeleteFilesbyEnding(self.temps)
1810
+ pass
1811
+
1812
+ ## @class PyXMake.Build.Make.PyReq
1813
+ # Base class for all PyReq build events. Inherited from Custom.
1814
+ class PyReq(Custom):
1815
+ """
1816
+ Inherited class to build projects using PyReq.
1817
+ """
1818
+ def __init__(self, *args, **kwargs):
1819
+ """
1820
+ Initialization of PyReq class object.
1821
+
1822
+ @note Creates a list of all 3rd party dependencies of a package using PyReq.
1823
+ """
1824
+ super(PyReq, self).__init__(*args, **kwargs)
1825
+ ## String identifier of current instance.
1826
+ self.MakeObjectKind = "PyReq"
1827
+
1828
+ # Remove all default libraries, paths and includes from Make class.
1829
+ self.libs = []
1830
+ self.libdirs = []
1831
+ self.incdirs = []
1832
+
1833
+ # The default is no pre-processing.
1834
+ self.precmd = ""
1835
+ # Do not add version by default.
1836
+ self.compargs = "--no-pin"
1837
+ pass
1838
+
1839
+ def Preprocessing(self, cmdstring=''):
1840
+ """
1841
+ Assemble command string for the pre-build event.
1842
+ """
1843
+ delimn = " "
1844
+ # Store command string in object
1845
+ ## Command executed during pre-build event.
1846
+ self.precmd = delimn.join([self.iniCompiler,cmdstring])
1847
+ pass
1848
+
1849
+ @classmethod
1850
+ def parse(cls, **kwargs): # pragma: no cover
1851
+ """
1852
+ Execute the current class as a CLI command.
1853
+ """
1854
+ # Import its main from VTL
1855
+ from PyXMake.VTL import pyreq
1856
+ # Evaluate current command line
1857
+ command = kwargs.get("command",sys.argv)
1858
+ # Process all known arguments
1859
+ parser = argparse.ArgumentParser(description='CLI wrapper options for dependency detection tool.')
1860
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
1861
+ parser.add_argument('--source', type=str, nargs=1, help="Absolute path to project folder.")
1862
+ parser.add_argument("--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
1863
+ parser.add_argument("--file", type=str, nargs=1, help="Output file name. Defaults to requirements.txt")
1864
+ parser.add_argument("--check", type=Utility.GetBoolean, const=True, default=True, nargs='?',
1865
+ help="Check public PyPi repository to verify the results. Defaults to True.")
1866
+ parser.add_argument('--command', nargs=argparse.REMAINDER, help="Additional command line parameters.")
1867
+ # Command line separator
1868
+ delimn = " "
1869
+ # Check all options or run unit tests in default mode
1870
+ try:
1871
+ # Check CLI options
1872
+ _ = command[1]
1873
+ args, _ = parser.parse_known_args(command[1:])
1874
+ # Project name is mandatory
1875
+ project = args.name[0]
1876
+ # Check optional command line arguments
1877
+ try: source = args.source[0] ;
1878
+ except:
1879
+ # Get path from the project name.
1880
+ handle = importlib.import_module(project)
1881
+ source = os.path.abspath(handle.__path__[0])
1882
+ # Optional non-default output directory
1883
+ try: output = args.output[0]
1884
+ except: output = os.path.abspath(os.getcwd())
1885
+ # Optional non-default package check
1886
+ try: check = args.check[0]
1887
+ except: check = True
1888
+ # Optional non-default output filename
1889
+ try: filename = args.file[0]
1890
+ except: filename = 'requirements.txt'
1891
+ # Optional non-default additional command line options for pipreqs.
1892
+ try: command = args.command[0]
1893
+ except: command = delimn.join(["--ignore","cmd,bin,Core,Plugin","--no-pin"])
1894
+ # Use an exception to allow help message to be printed.
1895
+ except Exception as _:
1896
+ # Build all supported features
1897
+ if AllowDefaultMakeOption:
1898
+ # Command line separator
1899
+ delimn = " "
1900
+ # Get absolute path to gather information about package requirements.
1901
+ from PyXMake import PyXMakePath; from PyCODAC import PyCODACPath #@UnresolvedImport
1902
+ # Gather latest dependencies from PyXMake's code base.
1903
+ pyreq("PyXMake", source=PyXMakePath,
1904
+ compargs=delimn.join(["--ignore","cmd,bin","--no-pin"]), output=os.getcwd(), check=True)
1905
+ # Gather latest dependencies from PyCODAC's code base.
1906
+ pyreq("PyCODAC", source=PyCODACPath,
1907
+ compargs=delimn.join(["--ignore","cmd,bin,Core,Plugin","--no-pin"]), output=os.getcwd(), check=True)
1908
+ try:
1909
+ # Gather latest dependencies from DELiS's code base.
1910
+ pyreq("DELiS", source=os.path.join(PyCODACPath,"Plugin","DELiS","src","delis"),
1911
+ compargs=delimn.join(["--no-pin"]), output=os.getcwd())
1912
+ except: pass
1913
+ try:
1914
+ # Gather latest dependencies from Smetana's code base.
1915
+ pyreq("Smetana", source=os.path.join(PyCODACPath,"Plugin","Smetana","src","smetana"),
1916
+ compargs=delimn.join(["--ignore","gopak,curvefit,test,static","--no-pin"]), output=os.getcwd())
1917
+ except: pass
1918
+ # Execute valid CLI command
1919
+ else: pyreq(project, source, output, check=check, filename=filename, compargs=command)
1920
+ pass
1921
+
1922
+ def create(self,**kwargs):
1923
+ """
1924
+ Execute make command
1925
+ """
1926
+ # Increase recursion limit (required for OCC)
1927
+ sys.setrecursionlimit(kwargs.get("recursion_limit",5000))
1928
+ delimn = " "; linebreak = "\n"
1929
+
1930
+ # Go into scratch folder
1931
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
1932
+
1933
+ # Pre-build event (if required)
1934
+ try: # pragma: no cover
1935
+ if self.precmd.split() > 1 and self.precmd != "":
1936
+ Utility.Popen(self.precmd, self.verbose)
1937
+ except:
1938
+ pass
1939
+
1940
+ # Run build command
1941
+ data = Utility.GetRequirements(self.srcdir, getattr(self,"compargs","--no-pin").split(delimn), **kwargs)
1942
+
1943
+ # Create a summary as a markdown file
1944
+ try:
1945
+ with open(os.path.join(self.outdir,kwargs.get("filename",self.buildid+".md")), "w+") as output:
1946
+ output.write(linebreak.join(str(item) for item in data))
1947
+ except:
1948
+ pass
1949
+ return
1950
+
1951
+ ## @class PyXMake.Build.Make.Py2X
1952
+ # Base class for all Py2X (for now only f2py) build events.
1953
+ # Inherited from Make and flavors in dependence of the underlying or requested operating system (optionally).
1954
+ class Py2X(Make,NT,POSIX):
1955
+ """
1956
+ Inherited class to build projects using Py2X.
1957
+ """
1958
+ def __init__(self, *args, **kwargs): # pragma: no cover
1959
+ """
1960
+ Initialization of Py2X class object.
1961
+
1962
+ @note Currently uses f2py - but should be build with Py2X (DLR) in the future
1963
+ """
1964
+ super(Py2X, self).__init__(*args, **kwargs)
1965
+
1966
+ ## String identifier of current instance.
1967
+ self.MakeObjectKind = 'Py2X'
1968
+
1969
+ ## Define whether Intel's MKL should be statically or dynamically linked.
1970
+ # Defaults to True, meaning that Intel's MKL has to be provided by the operating system.
1971
+ self.no_static_mkl = kwargs.get('no_static_mkl', True)
1972
+
1973
+ ## Define whether Intel's MKL should be discarded
1974
+ # Defaults to False on NT systems. Defaults to True on Linux systems in a Docker instance. Overwrites previous setting.
1975
+ self.no_mkl = kwargs.get("no_mkl", self.hasFoss and (Utility.GetExecutable("choco") or Utility.GetPlatform() in ["linux","cygwin","msys","darwin"]))
1976
+
1977
+ ## Define whether the architecture shall be appended to the build name.
1978
+ # Defaults to False, meaning that the architecture is appended.
1979
+ self.no_append_arch = kwargs.get('no_append_arch', False)
1980
+
1981
+ ## Define if the input should be compiled exactly as provided.
1982
+ # Defaults to False, meaning that merging & pre-processing utilities will be carried out.
1983
+ self.incremental = kwargs.get('incremental', False)
1984
+
1985
+ # Immutable settings for Py2X object
1986
+ ## Absolute system path to Python executable.
1987
+ self.path2exe = sys.executable.replace("\python.exe","")
1988
+ ## The executable command used in the main build event.
1989
+ self.exe = 'python.exe'
1990
+
1991
+ # Interoperability change. Always put executables in quotes (who would have guessed...)
1992
+ self.exe = '"{}" '.format(os.path.join(self.path2exe,self.exe))
1993
+
1994
+ # Get valid f2py call.
1995
+ old_numpy_call = os.path.join(self.path2exe, "Scripts","f2py.py")
1996
+ new_numpy_call = os.path.join(self.path2exe, "Scripts","f2py.exe")
1997
+
1998
+ if os.path.exists(old_numpy_call):
1999
+ self.exe += '"{}"'.format(old_numpy_call)
2000
+ os.environ["pyx_compiler"] = " ".join([sys.executable,old_numpy_call])
2001
+ elif os.path.exists(old_numpy_call):
2002
+ self.exe = '"{}"'.format(new_numpy_call)
2003
+ os.environ["pyx_compiler"] = new_numpy_call
2004
+ else:
2005
+ ## Verify that a f2py version is accessible
2006
+ from numpy import f2py
2007
+ ## We are either on Linux or within a virtual environment (Poetry). Attempt to find f2py through the
2008
+ # $PATH variable. Raise an error to indicate that the operation will not succeed.
2009
+ os.environ.update({"PATH":os.pathsep.join([os.getenv("PATH")]+
2010
+ [os.path.join(f2py.__path__[0].split(os.path.sep+"lib")[0],"bin")]+
2011
+ [os.path.join(os.path.dirname(x),"Scripts") if Utility.GetPlatform() in ["windows"] else
2012
+ os.path.join(os.path.dirname(x),"bin") for x in sys.path if x.endswith("lib")])})
2013
+ # Find f2py in global PATH variable
2014
+ found, self.exe = Utility.GetExecutable("f2py", get_path=True)
2015
+ # Raise a runtime error if unsuccessful
2016
+ if not found: raise RuntimeError("f2py could not be found on the system.")
2017
+ # Otherwise, use the absolute path
2018
+ os.environ["pyx_compiler"] = self.exe
2019
+ self.exe = '"{}"'.format(self.exe)
2020
+ self.path2exe = ""
2021
+
2022
+ ## Temporary build name of current job.
2023
+ self.buildname = self.buildid+"_pyd"+self.architecture+".f90"
2024
+
2025
+ if self.architecture == "x86":
2026
+ mkl_interface_lib = "mkl_intel_c"
2027
+ else:
2028
+ mkl_interface_lib = "mkl_intel_lp64"
2029
+
2030
+ if self.no_mkl:
2031
+ # Remove include files from MKL explicitly if requested
2032
+ if version.parse(np.__version__) >= version.parse("1.26.0"):
2033
+ for x in copy.deepcopy(self.incdirs):
2034
+ if "mkl" in x: self.incdirs.remove(x)
2035
+ # Do nothing
2036
+ else: pass
2037
+ elif not self.no_static_mkl:
2038
+ # Link statically against MKL library. Deactivate this option by default.
2039
+ self.libs.append([mkl_interface_lib, "mkl_intel_thread", "mkl_core", "libiomp5md"])
2040
+ self.libs = list(Utility.ArbitraryFlattening(self.libs))
2041
+ else:
2042
+ # Provide additional default dependencies (required for MCODAC).
2043
+ if Utility.IsDockerContainer() and Utility.GetPlatform() not in ["windows", "msys"]: self.libs.append(["gomp","ifcore"])
2044
+ # Link dynamically against MKL library.
2045
+ self.libs.append("mkl_rt")
2046
+ self.libs = list(Utility.ArbitraryFlattening(self.libs))
2047
+
2048
+ # MKL is requested. Update library path to include local pip dependencies.
2049
+ if not self.no_mkl or not self.no_static_mkl or any(["mkl" in x for x in self.libs]):
2050
+ # Always add local path to library search path.
2051
+ prefix = "Library" if Utility.GetPlatform() in ["windows"] else ""
2052
+ # Catch exception while running in a local virtual environment
2053
+ base = os.path.dirname(os.path.dirname(os.getenv("pyx_compiler", sys.prefix)))
2054
+ # Update library search paths
2055
+ self.libdirs.extend([os.path.join(x,prefix,"lib") for x in (sys.prefix, base)])
2056
+
2057
+ if self.no_append_arch:
2058
+ self.architecture = ''
2059
+
2060
+ # Immutable settings for Py2X object
2061
+ if self.incremental:
2062
+ c_files = [x for x in self.srcs if os.path.splitext(x)[1].lower() in (".for", ".f95", ".f", ".f90")]
2063
+ self.exe += ' -c -m %s %s' % (self.buildid+self.architecture, ' '.join(c_files))
2064
+ else:
2065
+ self.exe += ' -c -m %s %s' % (self.buildid+self.architecture, self.buildname)
2066
+
2067
+ # Copy default mapping file to scratch directory (eq. workspace if not specified)
2068
+ if not self.bare: copyfile(os.path.join(Path2Config,".f2py_f2cmap"), os.path.join(self.scrtdir,".f2py_f2cmap"))
2069
+
2070
+ # Strip path from executable if already present.
2071
+ if self.exe.startswith(self.path2exe,1):
2072
+ self.path2exe = ""
2073
+
2074
+ ## Tuple of temporary files deleted after job completion. Has already stored custom variable declaration
2075
+ # mapping file used by f2py.
2076
+ self.temps = self.temps + (".f2py_f2cmap", self.buildname)
2077
+
2078
+ ## Iterate through all active processes matching the current BuildID
2079
+ # and kill them. Do not attempt on public machines (kills everything silently).
2080
+ if Utility.IsDockerContainer() and not self.bare:
2081
+ for proc in psutil.process_iter():
2082
+ if os.getpid() == proc.pid:
2083
+ continue # Skip self.
2084
+ try:
2085
+ for key, value in proc.as_dict().items():
2086
+ if (str(self.buildid) in str(key) or str(self.buildid) in str(value)) and Utility.IsNotEmpty(self.buildid):
2087
+ # Get process name & PID from process object. Kill the process and write a message.
2088
+ proc_delimn = " "; processName = proc.name(); processID = proc.pid; #proc.kill()
2089
+ print("==================================")
2090
+ print("Found existing child process @ %s" % proc_delimn.join([str(processName),':::',str(processID)]))
2091
+ print("The process is aborted for compilation")
2092
+ print("==================================")
2093
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
2094
+ pass
2095
+
2096
+ # Skip evaluation after one successful run.
2097
+ if not Utility.IsNotEmpty(self.buildid): break
2098
+
2099
+ # Identify source code and BuildID and set the corresponding environment variables for Mingw64 and Linux.
2100
+ if kwargs.get("bash", self.hasFoss):
2101
+ try:
2102
+ os.environ["pyx_buildid"], os.environ["pyx_source"] = (self.buildid+self.architecture, ' '.join(c_files))
2103
+ except:
2104
+ os.environ["pyx_buildid"], os.environ["pyx_source"] = (self.buildid+self.architecture, self.buildname)
2105
+ pass
2106
+
2107
+ @staticmethod
2108
+ def inspect(package,**kwargs): # pragma: no cover
2109
+ """
2110
+ Inspect the content of a given f2py package. Returns all qualified modules with their respective functions
2111
+ """
2112
+ # Create a modifiable condition
2113
+ def condition(x): return kwargs.get("custom_condition",not x.startswith("_") and not x.endswith("__"))
2114
+ # Collect all modules
2115
+ all_modules = [x for x in dir(package) if condition(x)]
2116
+ # Return all functions associated with a given module.
2117
+ return [".".join([package.__name__,x,y]) for x in all_modules for y in dir(getattr(package, x)) if condition(y)]
2118
+
2119
+ @staticmethod
2120
+ def show(package, feature, **kwargs): # pragma: no cover
2121
+ """
2122
+ Inspect the documentation content of a given f2py package feature by default.
2123
+ Optional, define callback to return any other existing attribute.
2124
+ """
2125
+ import functools
2126
+ delimn = "."; anchor = feature.split(".");
2127
+ # Helper functions
2128
+ def rgetattr(obj, attr, *args):
2129
+ """
2130
+ Get an attribute from a nested object.
2131
+
2132
+ @note: https://stackoverflow.com/questions/31174295/getattr-and-setattr-on-nested-subobjects-chained-properties
2133
+ """
2134
+ def _getattr(obj, attr):
2135
+ """
2136
+ Wrapper of existing getattr function
2137
+ """
2138
+ try: result = getattr(obj, attr, *args)
2139
+ except:
2140
+ relative = "."*(anchor.index(str(attr))-1)+str(attr)
2141
+ result = importlib.import_module(relative, package=".".join(anchor[0:relative.count(".")+1]))
2142
+ return result
2143
+ return functools.reduce(_getattr, [obj] + attr.split('.'))
2144
+ # Import the given module
2145
+ module = importlib.import_module(package.__name__)
2146
+ # Collect the requested attribute. Remove the package name itself
2147
+ attributes = delimn.join([x for x in feature.split(delimn) if x not in [package.__name__]])
2148
+ # Return the attributes docstring
2149
+ return getattr(rgetattr(module, attributes),kwargs.get("callback","__doc__")) if (attributes and attributes not in [delimn]) else getattr(module,kwargs.get("callback","__doc__"))
2150
+
2151
+ @staticmethod
2152
+ def callback(*args): # pragma: no cover
2153
+ """
2154
+ Get callback of any Python object.
2155
+ """
2156
+ # Access callback of any Python object directly.
2157
+ return Py2X.show(*args,callback="__call__")
2158
+
2159
+ @classmethod
2160
+ def parse(cls, *args, **kwargs): # pragma: no cover
2161
+ """
2162
+ Execute the current class as a CLI command.
2163
+ """
2164
+ # Import its main from VTL
2165
+ from PyXMake.VTL import py2x
2166
+ # Evaluate current command line
2167
+ command = kwargs.get("command",sys.argv)
2168
+ # Process all known arguments
2169
+ parser = argparse.ArgumentParser(description="Build a shared Fortran library for current Python executable")
2170
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
2171
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
2172
+ parser.add_argument('-f', '--files', nargs='+', default=[], help="Source file or list of all source files in the order of compilation")
2173
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
2174
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional directories and files required for the build.")
2175
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for the build. Defaults to current workspace.")
2176
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
2177
+ parser.add_argument("--format", type=str, nargs=1, help="Toggle between fixed and free format code style. Defaults to Fixed.")
2178
+ parser.add_argument("--incremental", type=Utility.GetBoolean, const=True, default=False, nargs='?',
2179
+ help="Toggle between incremental and non-incremental build. Defaults to False.")
2180
+ parser.add_argument("--foss", type=Utility.GetBoolean, const=True, default=True, nargs='?',
2181
+ help="Toggle to enforce free-open source builds. Defaults to True.")
2182
+ # Check all options or run unit tests in default mode
2183
+ try:
2184
+ # Check CLI options
2185
+ _ = command[1]
2186
+ args, _ = parser.parse_known_args(command[1:])
2187
+ # Project name is mandatory
2188
+ project = args.name[0]
2189
+ # Specification of source directory is mandatory
2190
+ source = args.source[0] ;
2191
+ # Optional non-default output directory
2192
+ try: files = args.files
2193
+ except: files = []
2194
+ # Optional non-default output directory
2195
+ try: output = args.output[0]
2196
+ except: output = os.path.abspath(os.getcwd())
2197
+ # Optional non-default scratch directory
2198
+ try: scratch = args.scratch[0]
2199
+ except: scratch = os.path.abspath(os.getcwd())
2200
+ # Optional non-default definition of additional include directories
2201
+ try:
2202
+ _ = args.include[0]
2203
+ # Collect all given paths. Get system independent format
2204
+ include = Utility.GetSanitizedDataFromCommand(args.include)
2205
+ # No extra test cases have been given
2206
+ except: include = []
2207
+ # Optional code format style definition. Defaults to Fixed.
2208
+ try: fformat = args.format[0]
2209
+ except: fformat = "fixed"
2210
+ # Optional incremental build option. Defaults to False.
2211
+ try: incremental = args.incremental[0]
2212
+ except: incremental = False
2213
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
2214
+ try: verbosity = int(args.verbosity[0])
2215
+ except: verbosity = 0
2216
+ # Optional non-default package check
2217
+ try: foss = args.check[0]
2218
+ except: foss = True
2219
+ # Create a dictionary combining all settings
2220
+ settings = {"files":files, "incremental": incremental, "include": include,
2221
+ "source":source, "output":output, "scratch":scratch,
2222
+ "format":fformat, "foss":foss,
2223
+ "verbosity":verbosity}
2224
+ # Use an exception to allow help message to be printed.
2225
+ except Exception as _:
2226
+ # Local imports. These are only meaningful while executing an unit test
2227
+ from PyXMake import VTL
2228
+ # Build all supported features for current version (default options)
2229
+ if AllowDefaultMakeOption:
2230
+ # Predefined script local variables
2231
+ __arch = Utility.GetArchitecture()
2232
+ __foss = Utility.IsDockerContainer() or Utility.GetPlatform() in ["linux"]
2233
+ try:
2234
+ # Import PyCODAC to build library locally during setup.
2235
+ from PyCODAC.Tools.Utility import GetPyCODACPath
2236
+ # Import and set local path to PyCODAC
2237
+ __mcd_core_path = os.path.join(GetPyCODACPath(),"Core")
2238
+ except ImportError:
2239
+ # This script is not executed as plug-in
2240
+ __mcd_core_path = ""
2241
+ except:
2242
+ # Something else went wrong.
2243
+ from PyXMake.Tools import ErrorHandling
2244
+ ErrorHandling.InputError(20)
2245
+ # Build BoxBeam
2246
+ BuildID = 'bbeam'
2247
+ py2x(BuildID,
2248
+ files=VTL.GetSourceCode(1),
2249
+ source=os.path.join(__mcd_core_path,"external","boxbeam"),
2250
+ libs=[],include=[],dependency=[],verbosity=2,
2251
+ # BoxBeam binary is referenced in PyOCDAC. Updated is performed there
2252
+ output=os.path.join(os.path.join(__mcd_core_path,"bin",Utility.GetPlatform(),__arch)), foss=__foss)
2253
+ # Build Beos
2254
+ BuildID = 'beos'
2255
+ py2x(BuildID,
2256
+ files=VTL.GetSourceCode(2),
2257
+ source=os.path.join(__mcd_core_path,"external","beos"),
2258
+ libs=[],include=[],dependency=[],
2259
+ # Beos binary is referenced in PyOCDAC. Updated is performed there
2260
+ output=os.path.join(os.path.join(__mcd_core_path,"bin",Utility.GetPlatform(),__arch)), foss=__foss)
2261
+ # Build MCODAC (default settings)
2262
+ BuildID = "mcd_core"; py2x(BuildID, foss=__foss)
2263
+ else:
2264
+ fformat = settings.pop("format")
2265
+ # Execute CLI command
2266
+ py2x(project, command=VTL.GetBuildCommand(0, _format=fformat), **settings)
2267
+
2268
+ ## @class PyXMake.Build.Make.PyInstaller
2269
+ # Base class for all PyInstaller build events. Inherited from Make.
2270
+ class PyInstaller(Make):
2271
+ """
2272
+ Inherited class to build projects using PyInstaller.
2273
+ """
2274
+ def __init__(self, *args, **kwargs):
2275
+ """
2276
+ Initialization of PyInstaller class object.
2277
+
2278
+ @note Creates stand-alone application of Python scripts using PyInstaller.
2279
+ """
2280
+ super(PyInstaller, self).__init__(*args, **kwargs)
2281
+ ## String identifier of current instance.
2282
+ self.MakeObjectKind = "PyInstaller"
2283
+
2284
+ # Set build mode
2285
+ self.buildtype = kwargs.get("type","onefile")
2286
+
2287
+ # Remove all default libraries, paths and includes from Make class.
2288
+ self.libs = []
2289
+ self.libdirs = []
2290
+ self.incdirs = []
2291
+
2292
+ # Add specification file to temporaries
2293
+ self.temps += (self.buildid+".spec",)
2294
+ pass
2295
+
2296
+ def Encryption(self, encrypt, **kwargs):
2297
+ """
2298
+ Encrypt byte-code by using user-supplied or randomly generated key.
2299
+
2300
+ @author: garb_ma
2301
+ @param encrypt: Boolean
2302
+ """
2303
+ if encrypt:
2304
+ self.key_string = kwargs.get("key_string",''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase + string.digits) for _ in range(16)))
2305
+ return
2306
+
2307
+ def Preprocessing(self, cmdstring=''):
2308
+ """
2309
+ Assemble command string for the pre-build event.
2310
+ """
2311
+ delimn = " "
2312
+ # Store command string in object
2313
+ ## Command executed during pre-build event.
2314
+ self.precmd = delimn.join([self.iniCompiler,cmdstring])
2315
+ pass
2316
+
2317
+ def Build(self, mode="onefile", **kwargs):
2318
+ """
2319
+ Switch build mode. Defaults to one single file.
2320
+ """
2321
+ import py_compile
2322
+ # Get shorthands for compatibility modes
2323
+ try:
2324
+ from PyInstaller.compat import is_py27 #@UnresolvedImport
2325
+ except:
2326
+ is_py27 = sys.version_info[0] == 2
2327
+ finally:
2328
+ from PyInstaller.compat import is_py35, is_py36, is_py37 #@UnresolvedImport
2329
+
2330
+ # Parse relevant versions from packages
2331
+ from setuptools import __version__ as set_version
2332
+ from PyInstaller import __version__ as pyi_version
2333
+
2334
+ # Set build mode
2335
+ self.buildtype = mode
2336
+
2337
+ # Reset command
2338
+ self.makecmd = ""
2339
+ # Create base command and add additional options based on identified inputs.
2340
+ self.makecmd = ['--name=%s' % self.buildid,'--%s' % self.buildtype, '--clean', "--noconfirm"]#, "--windowed"]
2341
+
2342
+ # Customize the application icon
2343
+ self.makecmd.append("--icon=%s" % kwargs.get("icon",os.path.join(Path2Config,"stm_logo.ico")))
2344
+
2345
+ # Correction of required missing import for new releases of setuptools. Deprecated since major release 49
2346
+ if version.parse("45.0.0") < version.parse(set_version) < version.parse("49.0.0"): # pragma: no cover
2347
+ self.makecmd.insert(1,'--hidden-import=%s' % "pkg_resources.py2_warn")
2348
+ # Adding request library explicitly.
2349
+ if version.parse(set_version) >= version.parse("64.0.0"): self.makecmd.insert(1,'--hidden-import=%s' % "requests")
2350
+
2351
+ # Use UPX compression to create a smaller application folder/file
2352
+ if Utility.GetPlatform() in ["windows"]: # pragma: no cover
2353
+ # Attempt to find UPX from global installation path
2354
+ if Utility.GetExecutable("upx"): self.makecmd.append("--upx-dir=%s" % Utility.GetExecutable("upx", get_path=True)[-1])
2355
+ # Fetch supplied executable from path (deprecated, path will not exists in the future)
2356
+ elif os.path.exists(os.path.join(PyXMakePath,"Build","bin","upx")): self.makecmd.append("--upx-dir=%s" % os.path.join(PyXMakePath,"Build","bin","upx"))
2357
+
2358
+ # Some binaries are unusable after the obfuscation process. We skip those here.
2359
+ if is_py27:
2360
+ excludes = ["python2.dll","python27.dll", "qwindows.dll"]
2361
+ elif is_py35:
2362
+ excludes = [x for x in os.listdir(os.path.dirname(sys.executable)) if x.endswith((".dll"))]
2363
+ elif is_py36:
2364
+ excludes = [x for x in os.listdir(os.path.dirname(sys.executable)) if x.endswith((".dll"))]
2365
+ elif is_py37:
2366
+ excludes = ["python3.dll","python37.dll","ucrtbase.dll", "qwindows.dll"]
2367
+
2368
+ ## Remove the complete scipy package from UPX shrinking
2369
+ # 28.06.2021 // garb_ma
2370
+ try:
2371
+ import scipy
2372
+ # Add scipy exceptions
2373
+ if version.parse(scipy.__version__) <= version.parse("1.6.3"): excludes.extend(Utility.ArbitraryFlattening([x[-1] for x in os.walk(scipy.__path__[0])]))
2374
+ except: pass
2375
+
2376
+ upx_exclude = ["--upx-exclude=%s" % x for x in excludes]
2377
+ self.makecmd.extend(upx_exclude)
2378
+ # Do not execute image size reduction using UPX
2379
+ else: self.makecmd.extend(["--noupx"])
2380
+
2381
+ # Mark additional Python files to be (re-compiled) before copying directly into the executable.
2382
+ add_compile = list(Utility.ArbitraryFlattening(kwargs.get("add_compile",[])))
2383
+
2384
+ # Add additional include directories
2385
+ if hasattr(self, "incdirs"): # pragma: no cover
2386
+ for data in self.incdirs:
2387
+ if os.path.exists(data.split(os.path.pathsep)[0]):
2388
+ new_path = data.split(os.path.pathsep)[0]
2389
+ # Compile source code files to byte-code
2390
+ if Utility.PathLeaf(new_path).endswith(".py") and Utility.PathLeaf(new_path) in add_compile:
2391
+ py_compile.compile(new_path,os.path.join(self.scrtdir,Utility.PathLeaf(new_path)+"c"))
2392
+ self.temps = self.temps + (Utility.PathLeaf(new_path)+"c",)
2393
+ new_path = os.path.join(self.scrtdir,Utility.PathLeaf(new_path)+"c")
2394
+ data = os.path.pathsep.join([new_path,data.split(os.path.pathsep)[1]])
2395
+ self.makecmd.insert(1,'--add-data=%s' % data)
2396
+ else:
2397
+ for path in sys.path:
2398
+ new_path = os.path.join(path,data.split(os.path.pathsep)[0])
2399
+ if os.path.exists(new_path) and os.path.isdir(new_path):
2400
+ # Check if content of folders should be presented in plain text.
2401
+ if not kwargs.get("add_plain",False):
2402
+ for root, _, files in Utility.PathWalk(new_path):
2403
+ # Ignore repository folders (no exceptions!)
2404
+ if any(x in root for x in [".git", ".svn"]):
2405
+ continue
2406
+ for file in files:
2407
+ # Ignore files matching specified patterns
2408
+ if not file.endswith((".pdf")) and not any(x in file for x in [".cpython"]):
2409
+ base_path = data.split(os.path.pathsep)[-1]; sub_path = root.split(base_path)[-1]
2410
+ sub_struct =os.path.join(base_path,sub_path.lstrip(os.path.sep),".")
2411
+
2412
+ if len(data.split(os.path.pathsep)) != 1:
2413
+ save_path = os.path.join(data.split(os.path.pathsep)[-1],".")
2414
+ else:
2415
+ save_path = sub_struct
2416
+ # Skip python files if compiled scripts are available
2417
+ if file.endswith(".py") and os.path.exists(os.path.join(new_path,file+"c")):
2418
+ continue
2419
+ elif file.endswith(".py") and file in add_compile:
2420
+ # Compile all blank source files to byte-code to obfuscate the code.
2421
+ py_compile.compile(os.path.join(new_path.split(data.split(os.path.pathsep)[0])[0],sub_struct.rstrip("."),file),
2422
+ os.path.join(new_path.split(data.split(os.path.pathsep)[0])[0],sub_struct.rstrip("."),file+"c"))
2423
+ file += "c"
2424
+ # Loop over all files within each directory and store them appropriately
2425
+ self.makecmd.insert(1,'--add-data=%s' % os.path.pathsep.join([os.path.join(new_path.split(
2426
+ data.split(os.path.pathsep)[0])[0],sub_struct.rstrip("."),file),save_path]))
2427
+ else:
2428
+ # Add folder content as-is to the application
2429
+ self.makecmd.insert(1,'--add-data=%s' % os.path.pathsep.join([new_path,data.split(os.path.pathsep)[-1]]))
2430
+ break
2431
+ # Check if new directory is a file. Preserve original path, otherwise do nothing.
2432
+ elif os.path.exists(new_path) and os.path.isfile(new_path):
2433
+ if Utility.PathLeaf(new_path).endswith(".py") and Utility.PathLeaf(new_path) in add_compile:
2434
+ py_compile.compile(new_path,os.path.join(self.scrtdir,Utility.PathLeaf(new_path)+"c"))
2435
+ self.temps = self.temps + (Utility.PathLeaf(new_path)+"c",)
2436
+ new_path = os.path.join(self.scrtdir,Utility.PathLeaf(new_path)+"c")
2437
+ self.makecmd.insert(1,'--add-data=%s' % os.path.pathsep.join([new_path,os.path.join(os.path.dirname(data.split(os.path.pathsep)[-1]),".")]))
2438
+ break
2439
+ else:
2440
+ continue
2441
+
2442
+ # Set working (scratch) directory if being defined
2443
+ if hasattr(self, "scrtdir"):
2444
+ self.makecmd.insert(1,'--specpath=%s' % os.path.join(self.scrtdir))
2445
+ self.makecmd.insert(1,'--workpath=%s' % os.path.join(self.scrtdir,"build"))
2446
+
2447
+ # Set output path if being defined. Defaults to current working directory
2448
+ if hasattr(self, "outdir"):
2449
+ self.makecmd.insert(1,'--distpath=%s' % os.path.join(self.outdir))
2450
+
2451
+ # Add additional search paths
2452
+ if hasattr(self, "libdirs"):
2453
+ for path in self.libdirs:
2454
+ self.makecmd.insert(1,'--paths=%s' % path)
2455
+
2456
+ # Add encryption method. Deprecated in
2457
+ if hasattr(self, "key_string") and version.parse(pyi_version) <= version.parse("6.0.0"):
2458
+ self.makecmd.insert(1,'--key=%s' % self.key_string)
2459
+
2460
+ # Add source path (if given!)
2461
+ if hasattr(self, "srcdir") and self.srcdir:
2462
+ self.makecmd.append(os.path.join(self.srcdir,self.srcs[0]))
2463
+ else: # pragma: no cover
2464
+ self.makecmd.append(self.srcs[0])
2465
+
2466
+ # Set verbosity level
2467
+ if hasattr(self, "verbose"): # pragma: no cover
2468
+ if self.verbose <= 0:
2469
+ level = "ERROR"
2470
+ elif self.verbose == 1:
2471
+ level = "WARN"
2472
+ elif self.verbose >= 2:
2473
+ level = "INFO"
2474
+ self.makecmd.insert(1,'--log-level=%s' % level)
2475
+
2476
+ @classmethod
2477
+ def parse(cls, **kwargs): # pragma: no cover
2478
+ """
2479
+ Execute the current class as a CLI command.
2480
+ """
2481
+ # Import its main from VTL
2482
+ from PyXMake.VTL import app
2483
+ # Evaluate current command line
2484
+ command = kwargs.get("command",sys.argv)
2485
+ parser = argparse.ArgumentParser(description="Build a stand-alone python application.")
2486
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
2487
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
2488
+ parser.add_argument('-f', '--file', nargs='+', default=[], help="Main entry point file.")
2489
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional files and folders required by the final executable.")
2490
+ parser.add_argument('-d', '--dependency', nargs='+', default=[], help="Additional search paths to resolve dependencies.")
2491
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
2492
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for the build. Defaults to current workspace.")
2493
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
2494
+ parser.add_argument("-m","--mode", type=str, nargs=1, default="onefile", help="Build type. Can be toggled between 'onefile' and 'onedir'.")
2495
+ parser.add_argument('--icon', type=str, nargs=1, help="Absolute path to a custom icon for the executable. Format: *.ico")
2496
+ parser.add_argument("--encryption", type=Utility.GetBoolean, const=True, default=False, nargs='?',
2497
+ help="Toggle between encryption and non-encryption build. Defaults to False.")
2498
+
2499
+ try:
2500
+ # Check CLI options
2501
+ _ = command[1]
2502
+ args, _ = parser.parse_known_args(command[1:])
2503
+ # Project name is mandatory
2504
+ project = args.name[0]
2505
+ # Specification of source directory is mandatory
2506
+ source = args.source[0] ;
2507
+ # Optional non-default output directory
2508
+ try: file = args.file
2509
+ except: file = []
2510
+ # Optional non-default output directory
2511
+ try: output = args.output[0]
2512
+ except: output = os.path.abspath(os.getcwd())
2513
+ # Optional incremental build option. Defaults to False.
2514
+ try: encryption = args.encryption[0]
2515
+ except: encryption = False
2516
+ # Optional non-default scratch directory
2517
+ try: scratch = args.scratch[0]
2518
+ except: scratch = os.path.abspath(os.getcwd())
2519
+ # Optional non-default additional include files and directories
2520
+ try: include = args.include[0]
2521
+ except: include = []
2522
+ # Optional non-default dependencies
2523
+ try: dependency= args.dependency[0]
2524
+ except: dependency = []
2525
+ # Optional non default build mode. Defaults to bundled one file.
2526
+ try: mode = args.mode[0]
2527
+ except: mode = "onefile"
2528
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
2529
+ try: verbosity = int(args.verbosity[0])
2530
+ except: verbosity = 0
2531
+ # Optional user-defined icon. If left blank, a default icon is used.
2532
+ try: icon = str(args.icon[0])
2533
+ except: icon = None
2534
+ # Create a dictionary combining all settings
2535
+ settings = {"script":file,
2536
+ "source":source, "output":output, "scratch":scratch,
2537
+ "mode":mode, "encryption": encryption,
2538
+ "verbosity":verbosity, "icon":icon}
2539
+ # Only parse these settings when explicitly given
2540
+ if include: settings.update({"include":include})
2541
+ if dependency: settings.update({"dependency":dependency})
2542
+ # Use an exception to allow help message to be printed.
2543
+ except Exception as _:
2544
+ BuildID = "stmlab"
2545
+ # Build all supported features for current Python version (default options)
2546
+ if AllowDefaultMakeOption: app(str(BuildID.lower()), mode="onedir")
2547
+ # Execute valid CLI command
2548
+ else: app(project, **settings)
2549
+ pass
2550
+
2551
+ def create(self,**kwargs):
2552
+ """
2553
+ Execute make command
2554
+ """
2555
+ # Import required package (only when function is actually executed)
2556
+ import PyInstaller.__main__ as pyi
2557
+
2558
+ # Increase recursion limit (required for OCC)
2559
+ sys.setrecursionlimit(kwargs.get("recursion_limit",int(os.getenv("pyx_recursion_limit",5000))))
2560
+
2561
+ # Go into scratch folder
2562
+ with Utility.ChangedWorkingDirectory(self.scrtdir): # pragma: no cover
2563
+
2564
+ # Pre-build event (if required)
2565
+ try:
2566
+ if self.precmd != '':
2567
+ Utility.Popen(self.precmd, self.verbose, collect=False)
2568
+ except:
2569
+ pass
2570
+
2571
+ # Create make command
2572
+ self.Build(mode=self.buildtype, **kwargs)
2573
+
2574
+ # Run build command
2575
+ pyi.run(self.makecmd)
2576
+
2577
+ # Finish and delete redundant files
2578
+ Utility.DeleteFilesbyEnding(self.temps)
2579
+ shutil.rmtree('build', ignore_errors=True)
2580
+
2581
+ # End of class method
2582
+ return # pragma: no cover
2583
+
2584
+ ## @class PyXMake.Build.Make.NSIS
2585
+ # Base class for all NSIS build events. Inherited from Make.
2586
+ class NSIS(Make):
2587
+ """
2588
+ Inherited class to build projects using NSIS.
2589
+ """
2590
+ def __init__(self, *args, **kwargs):
2591
+ """
2592
+ Initialization of PyInstaller class object.
2593
+
2594
+ @note Creates a portable installer of a source folder using NSIS.
2595
+ """
2596
+ super(NSIS, self).__init__(*args, **kwargs)
2597
+ ## String identifier of current instance.
2598
+ self.MakeObjectKind = "NSIS"
2599
+
2600
+ # Remove all default libraries, paths and includes from Make class.
2601
+ self.libs = []
2602
+ self.libdirs = []
2603
+ self.incdirs = []
2604
+
2605
+ # Immutable settings for NSIS object
2606
+ ## Path to NSIS executable.
2607
+ self.path2exe = os.path.join(PyXMakePath,"Build","bin","nsis","App","NSIS")
2608
+ ## Executable of NSIS.
2609
+ self.exe = 'makensis.exe'
2610
+
2611
+ # Initialize build command
2612
+ if Utility.GetExecutable("makensis"): # pragma: no cover
2613
+ self.makecmd = Utility.GetExecutable("makensis", get_path=True)[-1]
2614
+ else: self.makecmd = os.path.join(self.path2exe,self.exe)
2615
+
2616
+ # Add specification file to temporaries
2617
+ self.temps += (self.buildid+".nsh",)
2618
+ pass
2619
+
2620
+ def FTP(self, user, key, upload_file, host='ftp.dlr.de', path="/public/download/nightly"): # pragma: no cover
2621
+ """
2622
+ Define settings to establish a FTP connection.
2623
+ """
2624
+ import ftplib
2625
+ # Establish FTP connection to file sharing server
2626
+ ## Remote workspace. This is the upload directory for the given file
2627
+ self.ftp_client = ftplib.FTP(host, user, key)
2628
+ self.ftp_client.cwd(path)
2629
+ with open(upload_file, 'rb') as file:
2630
+ self.ftp_client.storbinary("STOR %s" % Utility.PathLeaf(upload_file), file)
2631
+ pass
2632
+
2633
+ @classmethod
2634
+ def parse(cls, **kwargs): # pragma: no cover
2635
+ """
2636
+ Execute the current class as a CLI command.
2637
+ """
2638
+ # Import its main from VTL
2639
+ from PyXMake.VTL import bundle
2640
+ # Evaluate current command line
2641
+ command = kwargs.get("command",sys.argv)
2642
+ # Process all known arguments
2643
+ parser = argparse.ArgumentParser(description="Create a portable distribution using a compressed archive or NSIS")
2644
+ parser.add_argument('name', type=str, nargs=1, help="Name of the resulting archive(s).")
2645
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to distribution folder.")
2646
+ parser.add_argument('-f', '--files', nargs='+', default=[], help="Source file or list of all source files used in the creation of the documentation.")
2647
+ parser.add_argument("-o", "--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current working directory.")
2648
+ parser.add_argument("-e","--exclude", nargs='+', default=[], help="File extensions to be ignored during the process")
2649
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for the build. Defaults to current workspace.")
2650
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
2651
+ parser.add_argument("--use_nsis", type=Utility.GetBoolean, const=True, default=False, nargs='?', help="Create an archive using NSIS. Defaults to False.")
2652
+ parser.add_argument('--extensions', nargs=argparse.REMAINDER,
2653
+ help="""
2654
+ All archive extensions. Defaults to tar.gz. List given as string separated by commas.
2655
+ Possible values are <zip>, <tar>, <tar.gz>, <bz2>.
2656
+ """)
2657
+
2658
+ # Command line separator
2659
+ delimn = ","
2660
+
2661
+ try:
2662
+ # Check CLI options
2663
+ _ = command[1]
2664
+ args, _ = parser.parse_known_args(command[1:])
2665
+ # Project name is mandatory
2666
+ project = args.name[0]
2667
+ # Check optional command line arguments
2668
+ source = args.source[0] ;
2669
+ # Optional non-default output directory
2670
+ try: files = args.files
2671
+ except: files = "."
2672
+ # Optional non-default output directory
2673
+ try: output = args.output[0]
2674
+ except: output = os.path.abspath(os.getcwd())
2675
+ # Optional non-default NSIS check. Defaults to False.
2676
+ try: check = args.use_nsis[0]
2677
+ except: check = False
2678
+ # Optional non-default scratch directory
2679
+ try: scratch = args.scratch[0]
2680
+ except: scratch = os.path.abspath(os.getcwd())
2681
+ # Optional non-default exclude pattern
2682
+ try: excludes = args.exclude[0]
2683
+ except: excludes = [".git", ".svn", "__pycache__"]
2684
+ # Optional non-default additional extensions
2685
+ try: extensions= args.extensions[0]
2686
+ except: extensions = delimn.join(["tar","tar.gz","tar.bz2","zip"])
2687
+ finally: extensions = extensions.split(delimn)
2688
+ # Create a dictionary combining all settings
2689
+ settings = {"source":source, "output":output, "extensions":extensions, "files":files,
2690
+ "scratch": scratch, "excludes":excludes, "use_nsis": check}
2691
+ # Use an exception to allow help message to be printed.
2692
+ except Exception as _:
2693
+ # Build all supported features
2694
+ if AllowDefaultMakeOption:
2695
+ # Create PyCODAC installer
2696
+ BuildID = "PyCODAC"; bundle(str(BuildID.lower()), use_nsis=Utility.GetExecutable("makensis"))
2697
+ # Execute valid CLI command
2698
+ else: bundle(project, **settings)
2699
+ pass
2700
+
2701
+ def create(self, **kwargs): # pragma: no cover
2702
+ """
2703
+ Execute make command
2704
+ """
2705
+ space = " "; point = "."; newline = '\n'
2706
+
2707
+ # Create bundle command script
2708
+ script = ["Unicode true"]
2709
+ script.append("!define MULTIUSER_EXECUTIONLEVEL user")
2710
+ script.append("!define ZIP2EXE_NAME `%s`" % self.buildid)
2711
+ script.append("!define ZIP2EXE_OUTFILE `%s`" % os.path.join(self.outdir,point.join([self.buildid,"exe"])))
2712
+ script.append("!define ZIP2EXE_COMPRESSOR_LZMA")
2713
+ script.append("!define ZIP2EXE_INSTALLDIR `$EXEDIR`")
2714
+ script.append("!include `${NSISDIR}\Contrib\zip2exe\Base.nsh`")
2715
+ script.append("!include `${NSISDIR}\Contrib\zip2exe\Modern.nsh`")
2716
+ if kwargs.get("install_path",""):
2717
+ script.append("InstallDir '%s'" % kwargs.get("install_path","$Desktop"))
2718
+ script.append("!insertmacro SECTION_BEGIN")
2719
+ script.append("SetOutPath $INSTDIR")
2720
+ # Add all source files to the bundle
2721
+ for src in self.srcs:
2722
+ script.append("File /r /x *.nsh `%s`" % os.path.join(kwargs.get("assembly_path", self.srcdir),src))
2723
+ script.append("!insertmacro SECTION_END")
2724
+ script.append('Icon "%s"' % kwargs.get("icon",os.path.join(Path2Config,"stm_logo.ico")))
2725
+ script.append("RequestExecutionLevel user")
2726
+
2727
+ # Go into scratch folder
2728
+ with Utility.ChangedWorkingDirectory(self.scrtdir if kwargs.get("use_nsis",True) else tempfile.gettempdir()):
2729
+ # Run build command
2730
+ MakeFile = Utility.GetTemporaryFileName(extension=".nsh")
2731
+
2732
+ # Populate script file with customizable features
2733
+ with open(MakeFile,"w") as f:
2734
+ f.write(newline.join(script))
2735
+
2736
+ # Execute command
2737
+ self.makecmd = space.join([self.makecmd, "/V3", os.path.join(self.scrtdir,MakeFile)])
2738
+
2739
+ ## Adding option to deactivate NSIS and instead use a compressed archive directly.
2740
+ # Useful on POSIX system w/o NSIS support
2741
+ if kwargs.get("use_nsis",True): Utility.Popen(self.makecmd, self.verbose)
2742
+ else:
2743
+ for ext in kwargs.get("extensions",["tar.gz"]): Utility.CreateArchive(os.path.join(self.outdir,".".join([self.buildid,ext])),self.srcdir, **kwargs)
2744
+
2745
+ # Add temporary file to tuple scheduled for removal
2746
+ self.temps += (MakeFile,)
2747
+
2748
+ # Finish and delete redundant files
2749
+ Utility.DeleteFilesbyEnding(self.temps)
2750
+
2751
+ # Upload results to a file sharing server
2752
+ if kwargs.get("upload",False) and not hasattr(self, "ftp_client"):
2753
+ user = kwargs.get("user",os.getenv("username","")); key = kwargs.get("key","")
2754
+ self.FTP(user, key, point.join([os.path.join(self.outdir,self.buildid),"exe"]))
2755
+
2756
+ # Success message
2757
+ print("==================================")
2758
+ print("Uploaded result to given FTP server")
2759
+ print("==================================")
2760
+
2761
+ # End of class method
2762
+ pass
2763
+
2764
+ ## @class PyXMake.Build.Make.Doxygen
2765
+ # Base class for all Doxygen build events. Inherited from Make.
2766
+ class Doxygen(Make):
2767
+ """
2768
+ Inherited class to automatically build a documentation using Doxygen.
2769
+ """
2770
+ def __init__(self, *args,**kwargs):
2771
+ """
2772
+ Initialization of doxygen class object.
2773
+ """
2774
+ super(Doxygen, self).__init__(*args,**kwargs)
2775
+ ## String identifier of current instance.
2776
+ self.MakeObjectKind = 'Doxygen'
2777
+
2778
+ os.environ['dox_pyjava'] = 'NO'
2779
+ os.environ['dox_fortran'] = 'NO'
2780
+ os.environ['dox_ccpp'] = 'NO'
2781
+ os.environ['dox_pdflatex'] = 'NO'
2782
+
2783
+ # Immutable settings for Doxygen object
2784
+ ## Path to Doxygen executable.
2785
+ self.path2exe = os.path.join(PyXMakePath,"Build","bin","doxygen")
2786
+ ## Executable of Doxygen.
2787
+ self.exe = 'doxygen.exe'
2788
+
2789
+ ## Redefine default delete command in dependence of platform
2790
+ remove = "del"
2791
+ # Use rm on Linux platforms.
2792
+ if Utility.GetPlatform() == "linux": remove = "rm -rf"
2793
+
2794
+ # Added Doxygen support for all systems.
2795
+ if Utility.GetExecutable("doxygen"):
2796
+ _, doxy_path = Utility.GetExecutable("doxygen",get_path=True)
2797
+ self.path2exe = os.path.dirname(doxy_path)
2798
+ self.exe = Utility.PathLeaf(doxy_path)
2799
+
2800
+ ## Update configuration file to the latest Doxygen syntax by default. Do not issue a warning (for now!)
2801
+ Utility.Popen(" ".join([os.path.join(self.path2exe,self.exe),"-u",os.path.join(Path2Config,"stm_doc_config")]),verbosity=0)
2802
+
2803
+ ## Type of source file. Can be one of Fortran, CCpp or Other.
2804
+ # Defaults to Fortran if not specified. Starts documentation procedure
2805
+ # for Java/Python if type is neither Fortran nor CCpp.
2806
+ if self.stype == 'Fortran':
2807
+ os.environ['dox_fortran'] = 'YES'
2808
+ ## Temporary build name of current job.
2809
+ self.buildname = self.buildid+"_doc.f90"
2810
+ ## Tuple of temporary files scheduled for removal.
2811
+ self.temps = self.temps + (self.buildname, )
2812
+ elif self.stype == 'CCpp': # pragma: no cover
2813
+ os.environ['dox_ccpp'] = 'YES'
2814
+ self.buildname = self.buildid+"_doc.cpp"
2815
+ self.temps = self.temps + (self.buildname, )
2816
+ else:
2817
+ temp = []; delimn = " "
2818
+ self.buildname = ''
2819
+ temp.append(self.srcs)
2820
+ os.environ['dox_pyjava'] = 'YES'
2821
+ paths = list(Utility.ArbitraryFlattening(temp))
2822
+
2823
+ # Remove empty and/or unnecessary paths from joined input string
2824
+ for y in paths:
2825
+ for x in os.listdir(y):
2826
+ if x.endswith((".java", ".py")):
2827
+ if not any([z == "__init__.py" for z in os.listdir(y)]) and x.endswith((".py")): # pragma: no cover
2828
+ # Automatic documentation of files requires regular packages, not pure folders. Temporarily add an
2829
+ # __init__.py to every folder containing Python scripts. Removed after completion.
2830
+ open(os.path.join(y,"__init__.py"), 'a+').close()
2831
+ # Add absolute path to temporary files scheduled for removal.
2832
+ self.temps = self.temps + (os.path.join(y,"__init__.py"),)
2833
+ continue
2834
+ break
2835
+ else:
2836
+ paths.remove(y)
2837
+ if y in [os.path.dirname(k) for k in list(filter(os.path.isfile, self.temps))]: # pragma: no cover
2838
+ paths.append(y)
2839
+
2840
+ self.buildname = delimn.join(paths)
2841
+
2842
+ for x in list(filter(os.path.isfile, self.temps)):
2843
+ while True: # pragma: no cover
2844
+ try:
2845
+ path = os.path.dirname(x)
2846
+ if os.path.exists(os.path.join(path,"__init__.py")):
2847
+ x = path
2848
+ else:
2849
+ break
2850
+ except:
2851
+ break
2852
+
2853
+ list(filter(os.path.isfile, self.temps))
2854
+
2855
+ # Remove all directories from temporary array. We only seek files.
2856
+ self.temps = tuple(filter(os.path.isfile, self.temps))
2857
+
2858
+ # Check if non-directories exists
2859
+ if list(self.temps): # pragma: no cover
2860
+ # Schedule them for removal
2861
+ rev_command = [remove] + list(self.temps)
2862
+ self.postcmd = delimn.join(rev_command)
2863
+
2864
+ # Immutable environment variables
2865
+ os.environ['dox_input'] = self.buildname
2866
+ os.environ['dox_images'] = Path2Config
2867
+ os.environ['dox_footer'] = os.path.join(Path2Config,"stm_doc_footer.html")
2868
+ os.environ['dox_logo'] = os.path.join(Path2Config,"stm_logo.png")
2869
+ os.environ['dox_pyfilter'] = (sys.executable+" "+os.path.join(Path2Config,"stm_pyfilter.py")) if Utility.GetPlatform() in ["windows","nt"] else (os.path.join(Path2Config,"stm_pyfilter.py"))
2870
+ # Proper handling of Debian / macOS with Doxygen (latest version)
2871
+ if not sys.executable in os.getenv("PATH", "") and not Utility.GetPlatform() in ["windows","nt"]:
2872
+ os.environ['PATH'] = os.pathsep.join([sys.executable,os.getenv("PATH","")]);
2873
+ os.chmod(os.path.join(Path2Config,"stm_pyfilter.py"), stat.S_IWOTH); os.chmod(os.path.join(Path2Config,"stm_pyfilter.py"), stat.S_IXOTH)
2874
+
2875
+ # Set default color scheme.
2876
+ if not all([os.getenv(x) for x in ("dox_hue","dox_sat","dox_gamma")]):
2877
+ from PyXMake.Build.config.stm_color import DLRBlue as dox_color #@UnresolvedImport
2878
+ os.environ['dox_hue'], os.environ['dox_sat'], os.environ['dox_gamma'] = [str(int(round(x))) for x in np.multiply([360.,100.,100],
2879
+ np.array(colorsys.rgb_to_hsv(*(value/255 for value in
2880
+ ImageColor.getcolor(dox_color,"RGB")))))]
2881
+
2882
+ # Remove MKL from default command line
2883
+ ## Blank version of list containing library directories without initially specifying MKL.
2884
+ self.incdirs = []
2885
+ self.libdirs = []
2886
+ pass
2887
+
2888
+ def Settings(self, brief, header, outdir='', **kwargs):
2889
+ """
2890
+ Define environment variables for the default configuration file.
2891
+ """
2892
+ # Overwrite output directory
2893
+ if outdir != '':
2894
+ ## Output directory of current job. Defaults to current workspace if not given.
2895
+ self.outdir = outdir # pragma: no cover
2896
+
2897
+ # Set environment variables for configuration script
2898
+ os.environ['dox_brief'] = brief
2899
+ os.environ['dox_header'] = header
2900
+ os.environ['dox_outdir'] = self.outdir
2901
+
2902
+ # Set color scheme in HUE (HSV)
2903
+ if kwargs.get("color",""): # pragma: no cover
2904
+ # Only modify color scheme if a hex color was explicitly given
2905
+ os.environ['dox_hue'], os.environ['dox_sat'], os.environ['dox_gamma'] = [str(x) for x in np.multiply([360.,100.,100],
2906
+ np.array(colorsys.rgb_to_hsv(*(value/255 for value in
2907
+ ImageColor.getcolor(kwargs.get("color"),"RGB")))))]
2908
+ pass
2909
+
2910
+ @classmethod
2911
+ def parse(cls, **kwargs): # pragma: no cover
2912
+ """
2913
+ Execute the current class as a CLI command.
2914
+ """
2915
+ # Import its main from VTL
2916
+ from PyXMake.VTL import doxygen
2917
+ # Evaluate current command line
2918
+ command = kwargs.get("command",sys.argv)
2919
+ # Process all known arguments
2920
+ parser = argparse.ArgumentParser(description='CLI wrapper options for Doxygen with more sensible default settings.')
2921
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
2922
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
2923
+ parser.add_argument('-t', '--title', nargs='+', default=[], help="Header used in the documentation. If not given, defaults are created from the project name.")
2924
+ parser.add_argument('-f', '--files', nargs='+', default=[], help="Source file or list of all source files used in the creation of the documentation.")
2925
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for Doxygen. Defaults to current workspace.")
2926
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
2927
+ parser.add_argument("-c","--config", type=str, nargs=1, help="Absolute path to user-supplied Doxygen configuration file. Can be left blank.")
2928
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
2929
+ parser.add_argument("-fo","--format", type=str, nargs=1, help="Toggle between Java, Fortran and Python code base. Defaults to Fortran.")
2930
+ parser.add_argument("-fi","--filter", type=str, nargs=1, help="JSON configuration string for this operation")
2931
+ # Check all options or run unit tests in default mode
2932
+ try:
2933
+ # Check CLI options
2934
+ _ = command[1]
2935
+ args, _ = parser.parse_known_args(command[1:])
2936
+ # Project name is mandatory
2937
+ project = args.name[0];
2938
+ # Specification of source directory is mandatory
2939
+ source = args.source[0] ;
2940
+ # Optional non-default project title
2941
+ try:
2942
+ _ = args.title[0]
2943
+ header = [Utility.ArbitraryEval(x) for x in args.title]
2944
+ except: header = [project.title(),"%s Developer Guide" % project.title()]
2945
+ # Optional non-default output directory
2946
+ try: output = args.output[0]
2947
+ except: output = os.path.abspath(os.getcwd())
2948
+ # Optional non-default scratch directory
2949
+ try: scratch = args.scratch[0]
2950
+ except: scratch = os.path.abspath(os.getcwd())
2951
+ # Optional code format style definition. Defaults to Python.
2952
+ try: fformat = args.format[0]
2953
+ except: fformat = "Fortran"
2954
+ # Optional source code filter option
2955
+ scfilter = {"exclude": True, "startswith":(".","__")}
2956
+ # Sanitize user input
2957
+ try: scfilter.update(json.loads(Utility.ArbitraryEval(args.filter[0])))
2958
+ except: pass
2959
+ # Optional non-default source files.
2960
+ try:
2961
+ _ = args.files[0]
2962
+ files = [Utility.ArbitraryEval(x) for x in args.files]
2963
+ except:
2964
+ # Parse files directly if format equals Fortran source. Use folder structure in all other cases.
2965
+ index = 0 if fformat != "Fortran" else -1
2966
+ files = [x[index] for x in Utility.PathWalk(os.path.abspath(source), **scfilter)]
2967
+ # Non default user-supplied configuration file. Defaults to internal configuration file
2968
+ try: config = args.config[0];
2969
+ except: config = os.path.join(Path2Config,"stm_doc_config")
2970
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
2971
+ try: verbosity = int(args.verbosity[0])
2972
+ except: verbosity = 0
2973
+ # Create a dictionary combining all settings
2974
+ settings = {"title":header,
2975
+ "files":files,
2976
+ "ftype":fformat, "config":config,
2977
+ "source":source, "output":output, "scratch":scratch,
2978
+ "verbosity":verbosity}
2979
+ # Use an exception to allow help message to be printed.
2980
+ except Exception as _:
2981
+ # Local imports. These are only meaningful while executing an unit test
2982
+ from PyXMake import VTL
2983
+ from PyCODAC.Tools.Utility import GetPyCODACPath
2984
+ # Build all supported features
2985
+ if AllowDefaultMakeOption:
2986
+ # Build documentation of PyXMake
2987
+ BuildID = "pyx_core"
2988
+ doxygen(BuildID, ["PyXMake", "PyXMake Developer Guide"],
2989
+ [x[0] for x in Utility.PathWalk(PyXMakePath, startswith=(".","__"), contains=("doc","bin","config"), endswith=("make","scratch","examples"))], "Python",
2990
+ output=os.path.join(PyXMakePath,"VTL","doc","pyx_core"))
2991
+ # Build documentation of PyCODAC
2992
+ BuildID = "pyc_core"
2993
+ doxygen(BuildID, ["PyCODAC", "PyCODAC Developer Guide"],
2994
+ [x[0] for x in Utility.PathWalk(GetPyCODACPath(), startswith=(".","__"),
2995
+ contains=("DELiS","Smetana","PyXMake","external","doc","cmd","bin","include","lib","config","fetch"),
2996
+ endswith=("VTL","make","scratch","examples","src","config","solver"))], "Python",
2997
+ output=os.path.join(GetPyCODACPath(),"VTL","doc","pyc_core"))
2998
+ # Build documentation of SubBuckling
2999
+ BuildID = "mcd_subbuck"
3000
+ doxygen(BuildID, ["SubBuck", "SubLaminate Buckling Developer Guide"],
3001
+ [x[0] for x in Utility.PathWalk(os.path.join(Utility.AsDrive("D"),"03_Workspaces","01_Eclipse","mcd_subbuckling"))], "Java",
3002
+ output=os.path.join(GetPyCODACPath(),"VTL","doc","mcd_subbuckling"))
3003
+ # Build documentation of Mapper
3004
+ BuildID = "mcd_mapper"
3005
+ doxygen(BuildID, ["Mapper", "Damage Mapping Developer Guide"],
3006
+ [x[0] for x in Utility.PathWalk(os.path.join(Utility.AsDrive("D"),"03_Workspaces","01_Eclipse","mcd_mapper"))], "Java",
3007
+ output=os.path.join(GetPyCODACPath(),"VTL","doc","mcd_mapper"))
3008
+ # Build documentation of BoxBeam
3009
+ BuildID = "box_core"
3010
+ doxygen(BuildID, ["BoxBeam", "BoxBeam Developer Guide"], VTL.GetSourceCode(1),
3011
+ source=os.path.join(os.path.join(GetPyCODACPath(),"Core"),"external","boxbeam"),
3012
+ output=os.path.join(GetPyCODACPath(),"Plugin","BoxBeam","VTL","doc","box_core"))
3013
+ # Build documentation of MCODAC (Default settings
3014
+ BuildID = 'mcd_core'
3015
+ doxygen(BuildID)
3016
+ else:
3017
+ # Execute CLI command
3018
+ doxygen(project, **settings)
3019
+ pass
3020
+
3021
+ ## @class PyXMake.Build.Make.Latex
3022
+ # Base class for all Latex build events. Inherited from Make.
3023
+ class Latex(Make):
3024
+ """
3025
+ Inherited class to automatically build a documentation using Latex
3026
+ """
3027
+ ## Non-default cross-version class property definition.
3028
+ # Protects a class property from unrestricted access
3029
+ class __property(object):
3030
+ def __init__(self, getter): self.getter= getter
3031
+ def __get__(self, instance, owner): return self.getter(owner)
3032
+
3033
+ # Definition of class attribute
3034
+ base_url = os.getenv("pyx_overleaf_url","https://overleaf.fa-services.intra.dlr.de")
3035
+
3036
+ # Adopt secret name in dependence of being distributed in a Docker container or not.
3037
+ secret = "pyc_overleaf_secret" if os.getenv("pyc_overleaf_secret","") else "pyx_overleaf_secret"
3038
+
3039
+ # Fail gracefully
3040
+ try: secret = open(os.getenv(secret,os.path.join(os.path.expanduser("~"),"Keys","Keepass","fa_overleaf_access")),"r").read()
3041
+ except: auth = {}
3042
+
3043
+ def __init__(self, *args, **kwargs):
3044
+ """
3045
+ Initialization of Latex class object.
3046
+ """
3047
+ # Allow for initialization w/o any source data
3048
+ if len(args) < 2: args += ("",)
3049
+ # Create super class from base class
3050
+ super(Latex, self).__init__(*args,**kwargs)
3051
+ ## String identifier of current instance.
3052
+ self.MakeObjectKind = 'Latex'
3053
+
3054
+ # Validate third party dependencies
3055
+ from six import exec_
3056
+ from PyXMake.Build import __install__ #@UnresolvedImport
3057
+ from sphinx import package_dir
3058
+
3059
+ # Check settings here for older python version compatibility
3060
+ silent_install = kwargs.get("silent_install",False)
3061
+
3062
+ ## Silently install all dependencies on-the-fly. Defaults to False, meaning Latex must be present in Path
3063
+ # or an external API service (like Overleaf) must be used.
3064
+ if silent_install: exec_(open(__install__.__file__).read(),{"__name__": "__main__", "__file__":__install__.__file__})
3065
+
3066
+ # Build script for Latex documentation
3067
+ os.environ["PATH"] += os.pathsep + os.pathsep.join([os.path.join(os.path.dirname(sys.executable),"Scripts")])
3068
+ # Add additional search paths to Latex default search paths
3069
+ os.environ["TEXINPUTS"] = os.getenv("TEXINPUTS", "")
3070
+ # Add custom SPHINX styles to Latex search path
3071
+ os.environ["TEXINPUTS"] += os.pathsep + os.pathsep.join([os.path.join(package_dir,"texinputs"),os.path.join(PyXMakePath,"Build","config")])
3072
+
3073
+ # Immutable settings for Latex object
3074
+ ## Path to Latex executable. Set using environment variable
3075
+ self.path2exe = ""
3076
+ ## Executable of Latex.
3077
+ self.exe = 'texify.exe'
3078
+
3079
+ ## Set default source and output directories for Latex build objects.
3080
+ # By default, both are set to the same folder containing the main source file.
3081
+ if os.path.exists(self.srcs[0]):
3082
+ self.srcdir = os.path.dirname(os.path.abspath(self.srcs[0]))
3083
+ self.outdir = os.path.dirname(os.path.abspath(self.srcs[0]))
3084
+
3085
+ ## Set user-defined secret value, if given
3086
+ if kwargs.get("secret", None): Latex.secret = kwargs.get("secret")
3087
+
3088
+ # Remove MKL from default command line
3089
+ ## Blank version of list containing library directories without initially specifying MKL.
3090
+ self.incdirs = []
3091
+ self.libdirs = []
3092
+
3093
+ @__property
3094
+ def auth(self):
3095
+ """
3096
+ API access token defined as a protected class property for backwards compatibility.
3097
+ Works for both Python 2 and 3. Only meaningful when a remote Latex instance shall be called.
3098
+ """
3099
+ if not getattr(self, "_auth",{}):
3100
+ try:
3101
+ self._auth = {} ;
3102
+ result = self.session(*base64.b64decode(self.secret).decode('utf-8').split(":",1), base_url=self.base_url, use_cache=False) ;
3103
+ self._auth = result[-1] ;
3104
+ except: pass
3105
+ return self._auth
3106
+
3107
+ @classmethod
3108
+ def Settings(cls, **kwargs): # pragma: no cover
3109
+ """
3110
+ Define environment variables for the default configuration file.
3111
+ """
3112
+ # Set environment variables for configuration script
3113
+ os.environ["TEXINPUTS"] += os.pathsep + os.pathsep.join([str(value) for value in kwargs.values()])
3114
+ pass
3115
+
3116
+ @classmethod
3117
+ def session(cls, *args, **kwargs):
3118
+ """
3119
+ Create all required session tokens for an active Overleaf instance.
3120
+ """
3121
+ status_code = 500; result = {}
3122
+ # Use cached credentials. Defaults to True.
3123
+ if kwargs.get("use_cache",True): return [200,copy.deepcopy(getattr(cls, "auth",{}))]
3124
+ # Procedure
3125
+ try:
3126
+ from bs4 import BeautifulSoup
3127
+ # Create a new session
3128
+ session = requests.Session()
3129
+ email, password = args
3130
+ # Obtain login URL. Fail safe in case of empty input string.
3131
+ login_url = "{}/login".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3132
+ # Get secret token
3133
+ r = session.get(login_url); csrf = BeautifulSoup(r.text, 'html.parser').find('input', { 'name' : '_csrf' })['value']
3134
+ r = session.post(login_url, { '_csrf' : csrf , 'email' : email , 'password' : password })
3135
+ try: # pragma: no cover
3136
+ r.json(); status_code = 403;
3137
+ result = "The given credentials could not be verified."
3138
+ # Interrupt the process
3139
+ return [status_code, result]
3140
+ # Everything worked fine
3141
+ except: pass
3142
+ # Get valid CSRF Token for further commands
3143
+ r = session.get(login_url); csrf = BeautifulSoup(r.text, 'html.parser').find('input', { 'name' : '_csrf' })['value']
3144
+ # Fetch valid session token from the response header
3145
+ if r.status_code == 200:
3146
+ status_code = r.status_code; result = {"Cookie":r.headers.pop("Set-Cookie").split(ntpath.pathsep)[0],"_csrf":csrf}
3147
+ # Check if an error has occurred
3148
+ r.raise_for_status()
3149
+ # Error handler in top-level function
3150
+ except: pass
3151
+ # In all cases. Return something
3152
+ return [status_code, result]
3153
+
3154
+ @classmethod
3155
+ def show(cls,ProjectID, *args, **kwargs):
3156
+ """
3157
+ Show all build files from an Overleaf project remotely given its ID.
3158
+
3159
+ @note: Rebuilds the project in the process.
3160
+ """
3161
+ # Verify credentials
3162
+ code, result = cls.session(*args, **kwargs)
3163
+ if code != 200: return [code, result]
3164
+ # Update header
3165
+ header = result; header.update({'Content-Type':'application/json;charset=utf-8' })
3166
+ # Obtain project URL. Fail safe in case of empty input string.
3167
+ project_url = "{}/project".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3168
+ # Always build from scratch
3169
+ requests.delete(posixpath.join(project_url,ProjectID,"output"),headers={"X-Csrf-Token" if k == "_csrf" else k:v for k,v in result.items()})
3170
+ # Rebuild the project. Compiles everything.
3171
+ r = requests.post(posixpath.join(project_url,ProjectID,"compile"),params={"auto_compile":True}, headers=header,
3172
+ data=json.dumps({"check":"silent","incrementalCompilesEnabled":True,"_csrf":header.pop("_csrf")}))
3173
+ # Return the output file dictionary. Defaults to an empty dictionary in case something went wrong.
3174
+ if r.status_code == 200: return r.json()["outputFiles"]
3175
+ else: return {} # pragma: no cover
3176
+
3177
+ @classmethod
3178
+ def rename(cls, ProjectID, ProjectName, *args, **kwargs):
3179
+ """
3180
+ Rename an Overleaf project remotely given its ID.
3181
+ """
3182
+ # Verify credentials
3183
+ code, result = cls.session(*args, **kwargs)
3184
+ if code != 200: return [code, result]
3185
+ # Update header
3186
+ header = result; header.update({'Content-Type':'application/json;charset=utf-8' })
3187
+ # Fetch CSRF token from session result
3188
+ data = {"_csrf":result.pop("_csrf")}
3189
+ data.update({"newProjectName":str(ProjectName)})
3190
+ # Obtain project URL. Fail safe in case of empty input string.
3191
+ project_url = "{}/project".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3192
+ # Rebuild the project. Compiles everything.
3193
+ r = requests.post(posixpath.join(project_url,ProjectID,"rename"), headers=result, data=json.dumps(data))
3194
+ # Return the output file dictionary. Defaults to an empty dictionary in case something went wrong.
3195
+ if r.status_code == 200: return {"success":True,"message":r.text}
3196
+ else: return {} # pragma: no cover
3197
+
3198
+ @classmethod
3199
+ def upload(cls,archive, *args, **kwargs):
3200
+ """
3201
+ Upload a given archive to an active Overleaf instance creating a new project.
3202
+ """
3203
+ # Default value
3204
+ response = {}
3205
+ # Verify credentials
3206
+ code, result = cls.session(*args, **kwargs)
3207
+ if code != 200: return [code, result]
3208
+ # Obtain project upload URL. Fail safe in case of empty input string.
3209
+ upload_url = "{}/project/new/upload".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3210
+ filename = os.path.basename(archive)
3211
+ mime = "application/zip"
3212
+ files = {"qqfile": (filename, open(archive, "rb"), mime), "name": (None, filename)}
3213
+ params = {
3214
+ "_csrf":result.pop("_csrf"),
3215
+ "qquid": str(uuid.uuid4()),
3216
+ "qqfilename": filename,
3217
+ "qqtotalfilesize": os.path.getsize(archive),
3218
+ }
3219
+ # Execute the request
3220
+ r = requests.post(upload_url,params=params, files=files, headers=result)
3221
+ try:
3222
+ r.raise_for_status(); response = r.json()
3223
+ except Exception: pass
3224
+ # Check if JSON is valid.
3225
+ if not "success" in response: raise Exception("Uploading of %s failed." % archive )
3226
+ # Rename the newly created project
3227
+ cls.rename(r.json()["project_id"], str(filename.split(".")[0]).title(), *args, **kwargs)
3228
+ # Return response
3229
+ return response
3230
+
3231
+ @classmethod
3232
+ def download(cls, ProjectID, *args, **kwargs):
3233
+ """
3234
+ Download the complete archive or final pdf from an Overleaf project given its ID.
3235
+ """
3236
+ # Verify credentials
3237
+ code, result = cls.session(*args, **kwargs)
3238
+ if code != 200: return [code, result]
3239
+ # Default download path
3240
+ endpoint = posixpath.join("download","zip")
3241
+ # Download compiled PDF file instead
3242
+ if kwargs.get("output_format","zip") in ["pdf"]:
3243
+ # Verify that the source compiles with a new request
3244
+ endpoint = [x["url"] for x in cls.show(ProjectID, *args, **kwargs)
3245
+ if str(x["url"]).endswith("output.pdf")][0]
3246
+ ## Download in Overleaf has changed.
3247
+ # Refer to new download path if available but still support the deprecated alternative
3248
+ endpoint = next(iter([ posixpath.join("user",endpoint.split("user")[-1].replace(posixpath.sep,"",1))
3249
+ if "user" in endpoint.split(posixpath.sep)[:4]
3250
+ else posixpath.join("build",endpoint.split("build")[-1].replace(posixpath.sep,"",1)) ] ))
3251
+ # Obtain project URL. Fail safe in case of empty input string.
3252
+ project_url = "{}/project".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3253
+ # Rebuild the project. Compiles everything.
3254
+ r = requests.get(posixpath.join(project_url,ProjectID,endpoint), headers=result , stream=True)
3255
+ # Create the output file name
3256
+ output = os.path.abspath(".".join([os.path.join(os.getcwd(),ProjectID),endpoint[-3:]]))
3257
+ # Download the binary blob
3258
+ with open(output, "wb") as f:
3259
+ for chunk in r.iter_content(chunk_size=1024):
3260
+ if chunk: f.write(chunk)
3261
+ # Return the output path
3262
+ return output
3263
+
3264
+ @classmethod
3265
+ def delete(cls,ProjectID, *args, **kwargs):
3266
+ """
3267
+ Delete an Overleaf project given its ID.
3268
+ """
3269
+ # Check user settings. Defaults to False
3270
+ forever = kwargs.get("forever",False)
3271
+ # Verify credentials
3272
+ code, result = cls.session(*args, **kwargs)
3273
+ if code != 200: return [code, result]
3274
+ # Update header
3275
+ header = result; header.update({'Content-Type':'application/json;charset=utf-8' })
3276
+ # Obtain project URL. Fail safe in case of empty input string.
3277
+ project_url = "{}/project".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3278
+ # Execute the request
3279
+ r = requests.delete(posixpath.join(project_url,ProjectID), data=json.dumps({"forever": forever, "_csrf":header.pop("_csrf")}), headers=header)
3280
+ r.raise_for_status()
3281
+ # Return response
3282
+ return {"success":True,"message":r.text}
3283
+
3284
+ @classmethod
3285
+ def parse(cls, **kwargs): # pragma: no cover
3286
+ """
3287
+ Execute the current class as a CLI command.
3288
+ """
3289
+ # Import its main from VTL
3290
+ from PyXMake.VTL import latex
3291
+ # Evaluate current command line
3292
+ command = kwargs.pop("command",sys.argv)
3293
+ # Process all known arguments
3294
+ parser = argparse.ArgumentParser(description="Compile a TeXfile using MikTeX or Overleaf with custom templates.")
3295
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
3296
+ parser.add_argument('source', type=str, nargs=1, metavar="main.tex", help="Absolute path to the main source file. Must be either a Texfile or an archive.")
3297
+ parser.add_argument('-a', '--api', type=str, nargs=1, default="texworks", help="Additional files required for the process.")
3298
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional files required for the compilation process.")
3299
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
3300
+ # Check all options or run unit tests in default mode
3301
+ try:
3302
+ # Check CLI options
3303
+ _ = command[1]
3304
+ args, _ = parser.parse_known_args(command[1:])
3305
+ # Project name is mandatory
3306
+ project = args.name[0];
3307
+ # Specification of source directory is mandatory
3308
+ source = args.source[0] ;
3309
+ # Optional non-default compilation API
3310
+ try: config = args.api[0]
3311
+ except: config = "texworks"
3312
+ # Optional non-default scratch directory
3313
+ try: scratch = args.scratch[0]
3314
+ except: scratch = os.path.abspath(os.getcwd())
3315
+ # Optional non-default definition of additional files
3316
+ try:
3317
+ _ = args.include[0]
3318
+ # Collect all given paths. Get system independent format
3319
+ include = Utility.GetSanitizedDataFromCommand(args.include)
3320
+ # No extra files have been given
3321
+ except: include = []
3322
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
3323
+ try: verbosity = int(args.verbosity[0])
3324
+ except: verbosity = 2
3325
+ # Create a dictionary combining all settings
3326
+ settings = {"file":source, "API":config, "include":include, "scratch":scratch, "verbosity": verbosity}
3327
+
3328
+ # Use an exception to allow help message to be printed.
3329
+ except Exception as _:
3330
+ # Build all supported features
3331
+ if AllowDefaultMakeOption:
3332
+ # Run default option in a temporary directory
3333
+ with Utility.TemporaryDirectory():
3334
+ # Local import to make test work in a conda environment
3335
+ import PyXMake as _ #@UnusedImport
3336
+ # Use elsevier template for unit test
3337
+ from urllib.request import urlretrieve
3338
+ # Local variables
3339
+ BuildID = "elsevier"
3340
+ filename = "elsarticle.zip"
3341
+ url = "https://mirrors.ctan.org/macros/latex/contrib/elsarticle.zip"
3342
+ # Get a copy of keyword arguments
3343
+ settings = copy.deepcopy(kwargs)
3344
+ # Update test settings when CI specific environment variables can be found
3345
+ if os.getenv("CI_USER","") and os.getenv("CI_PASSWORD",""):
3346
+ settings.update({"secret": Utility.GetDockerEncoding(os.getenv("CI_USER"),os.getenv("CI_PASSWORD"))})
3347
+ # Down the template locally and run remote build process
3348
+ urlretrieve(url, filename); latex(BuildID, filename, API="Overleaf", **settings)
3349
+ else:
3350
+ # Execute CLI command
3351
+ latex(project, **settings)
3352
+ pass
3353
+
3354
+ def create(self, API="TeXworks", GUI=True, **kwargs):
3355
+ """
3356
+ Compile an existing project and/or start the graphical user interface.
3357
+ """
3358
+ command = []
3359
+ # Add all include directories to Latex environment variable
3360
+ os.environ["TEXINPUTS"] += os.pathsep + os.pathsep.join([str(include) for include in self.incdirs])
3361
+ ## Support both TeXWorks and Overleaf implementations
3362
+ # Use remote Overleaf implementation
3363
+ if API.lower() in ["overleaf"]:
3364
+ # Get Latex archive
3365
+ archive = os.path.abspath(self.srcs[0])
3366
+ # Operate fully on a temporary directory
3367
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
3368
+ # Copy file into local scratch folder
3369
+ shutil.copy(archive, os.getcwd())
3370
+ # Upload the archive
3371
+ result = self.upload(archive)
3372
+ try:
3373
+ # If successful, retrieve the project id
3374
+ ProjectID = result["project_id"]
3375
+ # Download the resulting PDF file
3376
+ self.download(ProjectID, output_format="pdf")
3377
+ # Rename the project to build id
3378
+ self.rename(ProjectID, self.buildid)
3379
+ # Delete the project if not explicitly requested otherwise.
3380
+ if not kwargs.get("keep",False): self.delete(ProjectID)
3381
+ # Get name of the result file
3382
+ result_file = [x for x in os.listdir() if x.endswith(".pdf")][0]
3383
+ shutil.copy(result_file,self.outdir)
3384
+ # Explicitly request all errors for logging
3385
+ except Exception as e: print(e.args())
3386
+ pass
3387
+ # Use local TeXworks distribution
3388
+ elif API.lower() in ["texworks"]: # pragma: no cover
3389
+ if os.path.exists(self.srcs[0]):
3390
+ command.append(self.srcs[0])
3391
+ # Open GUI or run command directly from command line
3392
+ if GUI or not os.path.exists(self.srcs[0]):
3393
+ # Run GUI
3394
+ command.insert(0,"texworks.exe")
3395
+ subprocess.Popen(command, close_fds = True, **Make.Detach())
3396
+ else:
3397
+ with tempfile.TemporaryDirectory(dir=self.scrtdir) as temp:
3398
+ # Set current directory to be the current directory
3399
+ os.chdir(temp)
3400
+ # Get name of output file
3401
+ output = os.path.splitext(Utility.PathLeaf(command[-1]))[0] + ".pdf"
3402
+ # Run command line
3403
+ command[0:0] = ["texify.exe","--pdf","--tex-option=-shell-escape","--synctex=1","--clean"]
3404
+ # Print output on-the-fly
3405
+ Utility.Popen(command, self.verbose, collect=False)
3406
+ # Copy output file into the output directory
3407
+ shutil.copyfile(output, os.path.join(self.outdir,output))
3408
+ # Wait until copying has been completed
3409
+ os.chdir(self.scrtdir)
3410
+ # Raise unknown API error
3411
+ else: raise NotImplementedError
3412
+ pass
3413
+
3414
+ @classmethod
3415
+ def __new(cls, ProjectName=None, *args, **kwargs): # pragma: no cover
3416
+ """
3417
+ Create a new project within the current session
3418
+ """
3419
+ # Check user settings. Defaults to False
3420
+ forever = kwargs.get("forever",False)
3421
+ # Verify credentials
3422
+ code, result = cls.session(*args, **kwargs)
3423
+ if code != 200: return [code, result]
3424
+ # Inherit BuildID from BuildID if given
3425
+ if not ProjectName: ProjectID = str(getattr(cls,"buildid",ProjectName))
3426
+ if not ProjectID: raise ValueError
3427
+ # Update header
3428
+ header = result; header.update({'Content-Type':'application/json;charset=utf-8' })
3429
+ # Obtain project URL. Fail safe in case of empty input string.
3430
+ project_url = "{}/project/new".format(kwargs.get("base_url", cls.base_url) or cls.base_url)
3431
+ # Create data segment
3432
+ data = {
3433
+ "_csrf": header.pop("_csrf"),
3434
+ "template": kwargs.get("template","template"),
3435
+ "projectName": ProjectID,
3436
+ "forever": forever,
3437
+ }
3438
+ # Execute the request
3439
+ r = requests.post(project_url, data=json.dumps(data), headers=header)
3440
+ r.raise_for_status()
3441
+ # Return response
3442
+ return {"success":True,"message":r.text,"response":r.json()}
3443
+
3444
+ # Abstract method. Can be invoked as an instance or class method.
3445
+ new = Utility.AbstractMethod(__new.__func__)
3446
+
3447
+ ## @class PyXMake.Build.Make.Coverage
3448
+ # Base class for all Coverage build events. Inherited from Make.
3449
+ class Coverage(Make): # pragma: no cover
3450
+ """
3451
+ Inherited class to automatically build a coverage report using Coverage.
3452
+ """
3453
+ def __init__(self, *args,**kwargs):
3454
+ """
3455
+ Initialization of coverage class object.
3456
+ """
3457
+ # Check if this class is used as a decorator.
3458
+ try: self.isDecorator = kwargs.pop("__as_decorator",any(line.startswith('@') for line in inspect.stack(context=2)[1].code_context))
3459
+ # Fail gracefully if context cannot be resolved.
3460
+ except TypeError: self.isDecorator = False
3461
+ # The current class is instantiated as a runner.
3462
+ if not self.isDecorator:
3463
+ super(Coverage, self).__init__(*args,**kwargs)
3464
+ ## String identifier of current instance.
3465
+ self.MakeObjectKind = 'Coverage'
3466
+ # Reset all pats to avoid collision
3467
+ self.incdirs = []; self.libdirs = []
3468
+ else:
3469
+ # Safe all arguments for later use.
3470
+ self.args = args
3471
+ self.kwargs = kwargs
3472
+
3473
+ def __call__(self, fn):
3474
+ """
3475
+ Implement an executable variant if class is used as a decorator.
3476
+ """
3477
+ import functools
3478
+ # Only run part of the code if environment is a test setup
3479
+ isPytest = self.kwargs.pop("autorun", False) or self.show()
3480
+ # Create a function wrapper
3481
+ @functools.wraps(fn)
3482
+ def decorated(*args, **kwargs):
3483
+ """
3484
+ Execute this part with given *args and **kwargs and the underlying function
3485
+ """
3486
+ # Execute test if running as a test
3487
+ if isPytest and not getattr(decorated,"has_run",False):
3488
+ compare = self.kwargs.pop("result",None);
3489
+ # Where running in a test environment
3490
+ try: result = fn(*self.args, **self.kwargs)
3491
+ except Exception as e: raise e
3492
+ ## Check if result matches the given value
3493
+ # Only meaningful when a result has been provided
3494
+ if compare: assert result == compare
3495
+ # Only run each test only once.
3496
+ decorated.has_run = True
3497
+ # Always return its true statement
3498
+ result = fn(*args, **kwargs)
3499
+ # Return the result of the function
3500
+ return result
3501
+ # Execute part directly when a function is loaded. Only in a test setup
3502
+ if isPytest:
3503
+ try: decorated()
3504
+ except TypeError: pass
3505
+ # Only execute the test setup once
3506
+ if not hasattr(decorated, "has_run"): decorated.has_run = False
3507
+ return decorated
3508
+
3509
+ @classmethod
3510
+ def show(cls):
3511
+ """
3512
+ Get a boolean value if this function is called within an active test environment
3513
+ """
3514
+ # Return the current root
3515
+ return str("pytest") in sys.modules or str("PYTEST_CURRENT_TEST") in os.environ
3516
+
3517
+ @classmethod
3518
+ def add(cls,*args, **kwargs):
3519
+ """
3520
+ Provide a test case for the given method as a decorator.
3521
+ """
3522
+ kwargs.update({"__as_decorator":True})
3523
+ # Do not return decorated function as a class in bundled mode
3524
+ if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
3525
+ def decorator(func): return func
3526
+ return decorator
3527
+ # Return current class instance
3528
+ else: return cls(*args,**kwargs)
3529
+
3530
+ @classmethod
3531
+ def parse(cls, **kwargs): # pragma: no cover
3532
+ """
3533
+ Execute the current class as a CLI command.
3534
+ """
3535
+ # Import its main from VTL
3536
+ from PyXMake.VTL import coverage
3537
+ # Evaluate current command line
3538
+ command = kwargs.get("command",sys.argv)
3539
+ # Process all known arguments
3540
+ parser = argparse.ArgumentParser(description='CLI wrapper options for Coverage with more sensible default settings.')
3541
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
3542
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
3543
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional files defining test cases required for test coverage.")
3544
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
3545
+ parser.add_argument("-e","--exclude", nargs='+', default=[], help="Full paths to be ignored from the coverage.")
3546
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
3547
+ # Check all options or run unit tests in default mode
3548
+ try:
3549
+ # Check CLI options
3550
+ _ = command[1]
3551
+ args, _ = parser.parse_known_args(command[1:])
3552
+ # Project name is mandatory
3553
+ project = args.name[0];
3554
+ # Specification of source directory is mandatory
3555
+ source = os.path.abspath(args.source[0]) ;
3556
+ # Optional non-default definition of additional tests cases
3557
+ try:
3558
+ _ = args.include[0]
3559
+ # Collect all given paths. Get system independent format
3560
+ include = Utility.GetSanitizedDataFromCommand(args.include)
3561
+ # No extra test cases have been given
3562
+ except: include = []
3563
+ # Optional non-default output directory
3564
+ try: output = args.output[0]
3565
+ except: output = os.path.abspath(os.getcwd())
3566
+ # Optional non-default exclude pattern
3567
+ try:
3568
+ _ = args.exclude[0]
3569
+ # Collect all given paths. Get system independent format
3570
+ exclude = Utility.GetSanitizedDataFromCommand(args.exclude)
3571
+ except: exclude = []
3572
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
3573
+ try: verbosity = int(args.verbosity[0])
3574
+ except: verbosity = 0
3575
+ # Verify that the given path is actually a package. Get the first entry
3576
+ source = [r for r, _, f in Utility.PathWalk(source) if any(x in ["__init__.py"] for x in f)][0]
3577
+ # Create a dictionary combining all settings
3578
+ settings = {"source":source, "output":output, "include":include,"exclude_paths":exclude,"verbosity":verbosity}
3579
+
3580
+ # Use an exception to allow help message to be printed.
3581
+ except Exception as _:
3582
+ # Run default test coverage of all integrated projects.
3583
+ if AllowDefaultMakeOption:
3584
+ # Run test coverage for PyXMake
3585
+ BuildID = "PyXMake"
3586
+ coverage(BuildID)
3587
+ else:
3588
+ # Execute CLI command
3589
+ coverage(project, **settings)
3590
+ pass
3591
+
3592
+ def Build(self, command=["--cov-fail-under=10","--cov-report=term-missing","--cov-report=html","--cov-report=xml","--junit-xml=junit.xml", "-W ignore::pytest.PytestAssertRewriteWarning"], **kwargs):
3593
+ """
3594
+ Assemble command strings for the main build event.
3595
+ """
3596
+ delimn = " "
3597
+ # Fetch additional make
3598
+ self.makecmd = command
3599
+ if isinstance(self.makecmd,str): self.makecmd = self.makecmd.split(delimn)
3600
+ pass
3601
+
3602
+ def create(self, **kwargs):
3603
+ """
3604
+ Execute make command
3605
+ """
3606
+ delimn = "_"
3607
+ # Copy the current command line
3608
+ sys_arg_recovery = copy.deepcopy(sys.argv);
3609
+ # Remove all trailing entries to avoid parsing them down.
3610
+ try: sys.argv[1:] = []
3611
+ except IndexError: pass
3612
+ # Copy the current system path
3613
+ sys_path_recovery = copy.deepcopy(sys.path)
3614
+ # Remote VTL directory from the overall system path to remove all shims.
3615
+ _ = [ sys.path.pop(i) for i, x in enumerate(sys.path) if x.endswith("VTL") ]
3616
+ # Check if dependencies can be resolved
3617
+ try: import pytest_cov as _ #@UnusedImport
3618
+ except: raise ImportError
3619
+ # Local imports
3620
+ import pytest
3621
+
3622
+ # Modify local VTL scratch directory
3623
+ from .. import PyXMakePath #@UnusedImport @Reimport
3624
+
3625
+ # Create make command if not already exists
3626
+ if not hasattr(self, "makecmd"): self.Build(**kwargs)
3627
+
3628
+ # Set default level of verbosity. Defaults to 1
3629
+ self.verbose = kwargs.get("verbosity",1)
3630
+
3631
+ # Assemble make command
3632
+ command =self.makecmd; command.extend(["--cov=%s" % path for path in self.srcs])
3633
+
3634
+ # A ordered list of all supported configuration file formats
3635
+ config = kwargs.get("config",None)
3636
+ configfiles = ["pytest.ini","pyproject.toml","tox.ini","setup.cfg"]
3637
+
3638
+ # Check if a configuration file exists within the current directory
3639
+ if any(x in os.listdir(os.getcwd()) for x in configfiles) and not config:
3640
+ # Get the file
3641
+ for i, x in enumerate(configfiles):
3642
+ if os.path.exists(x): break
3643
+ # Overwrite configuration variable
3644
+ config = os.path.abspath(os.path.join(os.getcwd(),configfiles[i]))
3645
+
3646
+ # Default test directory
3647
+ test_directory = "tests"
3648
+
3649
+ # Operate fully in a temporary directory and deactivate user scratch space
3650
+ with Utility.TemporaryDirectory(), Utility.TemporaryEnvironment():
3651
+ os.mkdir(test_directory);
3652
+ # Fetch additional files
3653
+ [shutil.copy(x,os.path.join(os.getcwd(),test_directory,Utility.PathLeaf(x))) for x in self.incdirs if os.path.isfile(x)]
3654
+ # Create a test for each file honoring __main__
3655
+ for file in os.listdir(os.path.join(os.getcwd(),test_directory)):
3656
+ with open(os.path.join(test_directory,delimn.join(["test",Utility.PathLeaf(file)])),"w") as f:
3657
+ f.write("import runpy" +"\n")
3658
+ f.write('def test_script():'+"\n")
3659
+ f.write(" try: runpy.run_path('%s', run_name='__main__')" % str(os.path.join(test_directory,file)).replace(ntpath.sep,ntpath.sep*2) +"\n")
3660
+ f.write(" except SystemExit as exception: exitcode = exception.code" +"\n")
3661
+ f.write(" else: exitcode = 0" +"\n")
3662
+ # Copy all external tests
3663
+ [shutil.copy(os.path.join(self.srcdir,x),os.path.join(os.getcwd(),test_directory)) for x in os.listdir(self.srcdir)
3664
+ if os.path.isfile(os.path.join(self.srcdir,x)) and x.startswith("test_")]
3665
+ # Create a default import check
3666
+ for path in self.srcs:
3667
+ with open(os.path.join(test_directory,delimn.join(["test",Utility.PathLeaf(os.path.dirname(path)),".py"])),"w") as f:
3668
+ f.write("import os; import pkgutil;"+"\n")
3669
+ f.write('os.environ.update(%s)' % str(os.environ.copy()) +"\n")
3670
+ f.write('__all__ = [] ; __path__ = ["%s"];' % os.path.dirname(path).replace(ntpath.sep,ntpath.sep*2) +"\n")
3671
+ f.write('def test_import():'+"\n")
3672
+ f.write(' for loader, module_name, is_pkg in pkgutil.walk_packages(__path__):'+"\n")
3673
+ f.write(' __all__.append(module_name); '+"\n")
3674
+ f.write(' _module = loader.find_module(module_name).load_module(module_name);'+"\n")
3675
+ f.write(' globals()[module_name] = _module'+"\n")
3676
+
3677
+ ## Always create a coverage setup file to prevent error during coverage collection
3678
+ with open(".coveragerc","w") as f:
3679
+ f.write("[run]"+"\n");
3680
+ # Remove given paths. Defaults to an empty list
3681
+ if kwargs.get("exclude_paths",[]): f.write("omit = "+"\n");
3682
+ for x in kwargs.get("exclude_paths",[]):
3683
+ # Update omit settings in dependence of the given path
3684
+ if os.path.isfile(x): path = str(x)
3685
+ else: path = str(x) + os.path.sep + "*"
3686
+ # Exclude all paths explicitly
3687
+ f.write(" *%s" % path +"\n");
3688
+ # Add default code exclude pattern
3689
+ f.write("[report]"+"\n");
3690
+ f.write("exclude_also ="+"\n");
3691
+ f.write(" def __repr__"+"\n");
3692
+ f.write(" if self.debug:"+"\n");
3693
+ f.write(" if settings.DEBUG"+"\n");
3694
+ f.write(" raise AssertionError"+"\n");
3695
+ f.write(" raise NotImplementedError"+"\n");
3696
+ f.write(" if 0:"+"\n");
3697
+ f.write(" except"+"\n");
3698
+ f.write(" if __name__ == .__main__.:"+"\n");
3699
+ f.write(" if TYPE_CHECKING:"+"\n");
3700
+ f.write(" class .*\bProtocol\):"+"\n");
3701
+ f.write(" @(abc\.)?abstractmethod"+"\n");
3702
+ ## Check whether a configuration file has been given
3703
+ # If that is the case, copy it into the current temporary working directory, bypassing root
3704
+ # as both statements are mutually exclusive
3705
+ if config: shutil.copy(config, os.path.abspath(os.getcwd()))
3706
+ # Explicit definition of the root directory
3707
+ else: command.extend(["--rootdir=%s" % os.path.join(os.getcwd())])
3708
+ # Modify default settings path
3709
+ command.extend(["--cov-config=.coveragerc"])
3710
+ # Change default import mode
3711
+ command.extend(["--import-mode=importlib"])
3712
+ # Explicit definition of the test directory. Everything else is excluded.
3713
+ command.extend(['--override-ini="testpaths=%s"' % str(test_directory)])
3714
+ # Print all test cases and passes in a detailed manner
3715
+ if self.verbose >=1: command.extend(['--verbose'])
3716
+ if self.verbose >=2: command.extend(['-rA'])
3717
+ # Add the created test folder to the final command
3718
+ command.extend([os.path.join(os.getcwd(),test_directory)]); pytest.main(command)
3719
+ # Move all results to the output directory
3720
+ result = [x for x in os.listdir(os.getcwd()) if x.startswith(("coverage", "htmlcov", "junit",))]
3721
+ # Copy all files and folders to the output directory
3722
+ for x in result:
3723
+ # Remove any preexisting folders. Folders are recreated in the process.
3724
+ if os.path.isdir(x):
3725
+ try: shutil.rmtree(os.path.join(self.outdir,x));
3726
+ except: pass
3727
+ finally: shutil.copytree(x,os.path.join(self.outdir,x));
3728
+ # Copy data
3729
+ else: shutil.copy(x, os.path.join(self.outdir,x))
3730
+ # Recover the initial command
3731
+ sys.argv = sys_arg_recovery
3732
+ # Recover the old system path.
3733
+ sys.path = sys_path_recovery
3734
+ pass
3735
+
3736
+ ## @class PyXMake.Build.Make.Sphinx
3737
+ # Base class for all Sphinx build events. Inherited from Make.
3738
+ class Sphinx(Make):
3739
+ """
3740
+ Inherited class to automatically build a documentation using Sphinx.
3741
+ """
3742
+ def __init__(self, *args,**kwargs):
3743
+ """
3744
+ Initialization of sphinx class object.
3745
+ """
3746
+ super(Sphinx, self).__init__(*args,**kwargs)
3747
+ ## String identifier of current instance.
3748
+ self.MakeObjectKind = 'Sphinx'
3749
+
3750
+ # Validate third party dependencies
3751
+ from six import exec_
3752
+ from PyXMake.Build import __install__ #@UnresolvedImport
3753
+ exec_(open(__install__.__file__).read(),{"__name__": "__main__", "__file__":__install__.__file__})
3754
+
3755
+ # Build script for Sphinx documentation
3756
+ os.environ["PATH"] += os.pathsep + os.pathsep.join([os.path.join(os.path.dirname(sys.executable),"Scripts")])
3757
+
3758
+ # Immutable settings for Sphinx object
3759
+ ## Path to Sphinx executable.
3760
+ self.path2exe = os.path.join(os.path.dirname(sys.executable),"Scripts")
3761
+ ## Executable of Sphinx.
3762
+ self.exe = 'sphinx-build.exe'
3763
+ ## Ordered dictionary containing all available options
3764
+ self.BuildOption = OrderedDict()
3765
+
3766
+ # Project name from BuildID
3767
+ os.environ["sphinx_project"] = str(self.buildid)
3768
+
3769
+ # Name of index file (master document).
3770
+ os.environ["sphinx_master"] = str(self.srcs[0])
3771
+
3772
+ # Default color scheme
3773
+ from PyXMake.Build.config.stm_color import DLRBlue as sphinx_color #@UnresolvedImport
3774
+ os.environ["sphinx_color"] = sphinx_color
3775
+
3776
+ # Remove MKL from default command line
3777
+ ## Blank version of list containing library directories without initially specifying MKL.
3778
+ self.incdirs = []
3779
+ self.libdirs = []
3780
+ pass
3781
+
3782
+ def Settings(self, **kwargs): # pragma: no cover
3783
+ """
3784
+ Define environment variables for the default configuration file.
3785
+ """
3786
+ delimn = "_"
3787
+ # Set environment variables for configuration script
3788
+ for key, value in kwargs.items():
3789
+ os.environ[delimn.join(["sphinx",str(key)])] = str(value)
3790
+ pass
3791
+
3792
+ @classmethod
3793
+ def parse(cls, **kwargs): # pragma: no cover
3794
+ """
3795
+ Execute the current class as a CLI command.
3796
+ """
3797
+ # Import its main from VTL
3798
+ from PyXMake.VTL import sphinx
3799
+ # Evaluate current command line
3800
+ command = kwargs.get("command",sys.argv)
3801
+ # Process all known arguments
3802
+ parser = argparse.ArgumentParser(description='CLI wrapper options for Sphinx with more sensible default settings.')
3803
+ parser.add_argument('name', type=str, nargs=1, help="Name of the project")
3804
+ parser.add_argument('source', type=str, nargs=1, help="Absolute path to the source file directory.")
3805
+ parser.add_argument('-f', '--file', nargs='+', default=[], help="Top level input file used in the creation of the documentation.")
3806
+ parser.add_argument('-l', '--logo', nargs='+', default=[], help="Top level logo in SVG format.")
3807
+ parser.add_argument('-i', '--include', nargs='+', default=[], help="Additional files defining test cases required for test coverage.")
3808
+ parser.add_argument('-s', '--scratch', nargs='+', default=[], help="Default scratch folder for Sphinx. Defaults to current workspace.")
3809
+ parser.add_argument("-o","--output", type=str, nargs=1, help="Absolute path to output directory. Defaults to current project folder.")
3810
+ parser.add_argument('-t', '--theme', type=str, nargs=1, help="An installed Sphinx theme. Defaults to 'Read the docs' theme.")
3811
+ parser.add_argument("-v","--verbosity", type=str, nargs=1, help="Level of verbosity. Defaults to 0 - meaning no output. Max value is 2.")
3812
+ parser.add_argument('--icon', type=str, nargs=1, help=argparse.SUPPRESS)
3813
+ # Check all options or run unit tests in default mode
3814
+ try:
3815
+ # Check CLI options
3816
+ _ = command[1]
3817
+ args, _ = parser.parse_known_args(command[1:])
3818
+ # Project name is mandatory
3819
+ project = Utility.GetSanitizedDataFromCommand(args.name, is_path=False)[0]
3820
+ ## Specification of main input file.
3821
+ mainfile = args.file[0];
3822
+ # Specification of source directory is mandatory
3823
+ source = os.path.abspath(args.source[0]) ;
3824
+ # Optional non-default definition of additional tests cases
3825
+ try:
3826
+ _ = args.include[0]
3827
+ # Collect all given paths. Get system independent format
3828
+ include = Utility.GetSanitizedDataFromCommand(args.include)
3829
+ # No extra test cases have been given
3830
+ except: include = []
3831
+ # Optional non-default output directory
3832
+ try: output = args.output[0]
3833
+ except: output = os.path.abspath(os.getcwd())
3834
+ # Optional non-default scratch directory
3835
+ try: scratch = args.scratch[0]
3836
+ except: scratch = os.path.abspath(os.getcwd())
3837
+ # Optional non-default icon
3838
+ try: icon = args.logo[0]
3839
+ except: icon = getattr(args, "icon", [None] )
3840
+ if icon: icon = os.path.abspath(next(iter(icon)))
3841
+ # Optional non-default theme
3842
+ try: theme = args.theme[0]
3843
+ except: theme = None
3844
+ # Verbose output level. Defaults to ZERO - meaning no output. Can be increased to 2.
3845
+ try: verbosity = int(args.verbosity[0])
3846
+ except: verbosity = 2
3847
+ # Create a dictionary combining all settings
3848
+ settings = {"source":source, "output":output, "include":include, "scratch": scratch, "verbosity":verbosity, "logo":icon}
3849
+ # Parse additional settings to modify the build
3850
+ if theme: settings.update({"html_theme":theme})
3851
+
3852
+ # Use an exception to allow help message to be printed.
3853
+ except Exception as _:
3854
+ # Execute default.
3855
+ sphinx("Composite Damage Analysis Code", "codac")
3856
+ else:
3857
+ # Execute CLI command
3858
+ sphinx(project, mainfile, **settings)
3859
+ pass
3860
+
3861
+ def create(self, **kwargs): # pragma: no cover
3862
+ """
3863
+ Execute make command
3864
+ """
3865
+ from distutils.dir_util import copy_tree
3866
+
3867
+ # You can set these variables from the command line.
3868
+ self.SPHINXOPTS = os.getenv('sphinx_opts', '')
3869
+ self.SPHINXBUILD = os.getenv('sphinx_build', 'sphinx-build')
3870
+ self.PAPER = os.getenv('sphinx_paper', None)
3871
+
3872
+ self.SOURCEDIR = os.getenv('sphinx_sourcedir', self.srcdir)
3873
+ self.BUILDDIR = os.getenv('sphinx_builddir', Utility.PathLeaf(tempfile.NamedTemporaryFile().name))
3874
+ self.TEMPDIR = os.getenv('sphinx_templates', "_templates")
3875
+ self.STATICEDIR = os.getenv('sphinx_static', "_static")
3876
+
3877
+ # Create static and template folder relative to source directory, if required.
3878
+ os.environ['sphinx_static'] = self.STATICEDIR; os.environ['sphinx_templates'] = self.TEMPDIR
3879
+
3880
+ # Add path dependencies
3881
+ os.environ["sphinx_include"] = os.getenv("sphinx_include","")
3882
+ os.environ["sphinx_include"] += os.pathsep + os.pathsep.join(self.incdirs)
3883
+
3884
+ def validate():
3885
+ """
3886
+ User-friendly check for sphinx-build
3887
+ """
3888
+ with open(os.devnull, 'w') as devnull:
3889
+ try:
3890
+ if subprocess.call([self.SPHINXBUILD, '--version'],stdout=devnull, stderr=devnull) == 0:
3891
+ return
3892
+ except FileNotFoundError:
3893
+ pass
3894
+ print(
3895
+ "The '{0}' command was not found. Make sure you have Sphinx "
3896
+ "installed, then set the SPHINXBUILD environment variable "
3897
+ "to point to the full path of the '{0}' executable. "
3898
+ "Alternatively you can add the directory with the "
3899
+ "executable to your PATH. If you don't have Sphinx "
3900
+ "installed, grab it from http://sphinx-doc.org/)"
3901
+ .format(self.SPHINXBUILD))
3902
+ sys.exit(1)
3903
+ return
3904
+
3905
+ def build(builder, success_msg=None, extra_opts=None, outdir=None,doctrees=True):
3906
+ """
3907
+ The default target
3908
+ """
3909
+ builddir = os.path.join(self.BUILDDIR or outdir)
3910
+ command = [self.SPHINXBUILD, '-M', builder, self.SOURCEDIR, builddir]
3911
+ command.extend(['-c', os.getenv('sphinx_config',self.SOURCEDIR)])
3912
+ if command[-1] == self.SOURCEDIR:
3913
+ command = command[:len(command)-2]
3914
+ if doctrees:
3915
+ command.extend(['-d', os.path.join(self.BUILDDIR, 'doctrees')])
3916
+ if extra_opts:
3917
+ command.extend(extra_opts)
3918
+ command.extend(shlex.split(self.SPHINXOPTS))
3919
+ # Execute build command
3920
+ if Utility.Popen(command,self.verbose).returncode == 0:
3921
+ print('Build finished. ' + success_msg.format(self.outdir or builddir))
3922
+
3923
+ def buildmethod(function):
3924
+ """
3925
+ Decorator function for each build option
3926
+ """
3927
+ self.BuildOption[function.__name__] = function
3928
+ return function
3929
+
3930
+ @buildmethod
3931
+ def default():
3932
+ """
3933
+ The default target
3934
+ """
3935
+ return html()
3936
+
3937
+ @buildmethod
3938
+ def clean():
3939
+ """
3940
+ Remove the build directory
3941
+ """
3942
+ shutil.rmtree(self.BUILDDIR, ignore_errors=True)
3943
+
3944
+ @buildmethod
3945
+ def html():
3946
+ """
3947
+ Make standalone HTML files
3948
+ """
3949
+ return build('html', 'The HTML pages are in {}.')
3950
+
3951
+ @buildmethod
3952
+ def dirhtml():
3953
+ """
3954
+ Make HTML files named index.html in directories
3955
+ """
3956
+ return build('dirhtml', 'The HTML pages are in {}')
3957
+
3958
+ @buildmethod
3959
+ def singlehtml():
3960
+ """
3961
+ Make a single large HTML file
3962
+ """
3963
+ return build('singlehtml', 'The HTML page is in {}.')
3964
+
3965
+ @buildmethod
3966
+ def pickle():
3967
+ """
3968
+ Make pickle files
3969
+ """
3970
+ return build('pickle', 'Now you can process the pickle files.')
3971
+
3972
+ @buildmethod
3973
+ def json():
3974
+ """
3975
+ Make JSON files
3976
+ """
3977
+ return build('json', 'Now you can process the JSON files.')
3978
+
3979
+ @buildmethod
3980
+ def htmlhelp():
3981
+ """
3982
+ Make HTML files and a HTML help project
3983
+ """
3984
+ return build('htmlhelp', 'Now you can run HTML Help Workshop with the .hhp project file in {}.')
3985
+
3986
+ @buildmethod
3987
+ def qthelp():
3988
+ """
3989
+ Make HTML files and a qthelp project
3990
+ """
3991
+ return build('qthelp', 'Now you can run "qcollectiongenerator" with the '
3992
+ '.qhcp project file in {0}, like this: \n'
3993
+ '# qcollectiongenerator {0}/RinohType.qhcp\n'
3994
+ 'To view the help file:\n'
3995
+ '# assistant -collectionFile {0}/RinohType.qhc')
3996
+
3997
+ @buildmethod
3998
+ def devhelp():
3999
+ """
4000
+ Make HTML files and a Devhelp project
4001
+ """
4002
+ return build('devhelp', 'To view the help file:\n'
4003
+ '# mkdir -p $HOME/.local/share/devhelp/RinohType\n'
4004
+ '# ln -s {} $HOME/.local/share/devhelp/RinohType\n'
4005
+ '# devhelp')
4006
+
4007
+ @buildmethod
4008
+ def epub(self):
4009
+ """
4010
+ Make an epub
4011
+ """
4012
+ return self.build('epub', 'The epub file is in {}.')
4013
+
4014
+ @buildmethod
4015
+ def rinoh(self):
4016
+ """
4017
+ Make a PDF using rinohtype
4018
+ """
4019
+ return self.build('rinoh', 'The PDF file is in {}.')
4020
+
4021
+ @buildmethod
4022
+ def latex():
4023
+ """
4024
+ Make LaTeX files, you can set PAPER=a4 or PAPER=letter
4025
+ """
4026
+ extra_opts = ['-D', 'latex_paper_size={}'.format(self.PAPER)] if self.PAPER else None
4027
+ return build('latex', 'The LaTeX files are in {}.\n'
4028
+ "Run 'make' in that directory to run these through "
4029
+ "(pdf)latex (use the 'latexpdf' target to do that "
4030
+ "automatically).", extra_opts)
4031
+
4032
+ @buildmethod
4033
+ def latexpdf():
4034
+ """
4035
+ Make LaTeX files and run them through pdflatex
4036
+ """
4037
+ _ = latex()
4038
+ print('Running LaTeX files through pdflatex...')
4039
+ builddir = os.path.join(self.BUILDDIR, 'latex')
4040
+ subprocess.call(['make', '-C', builddir, 'all-pdf'])
4041
+ print('pdflatex finished; the PDF files are in {}.'.format(builddir))
4042
+
4043
+ @buildmethod
4044
+ def latexpdfja():
4045
+ """
4046
+ Make LaTeX files and run them through platex/dvipdfmx
4047
+ """
4048
+ _ = latex()
4049
+ print('Running LaTeX files through platex and dvipdfmx...')
4050
+ builddir = os.path.join(self.BUILDDIR, 'latex')
4051
+ subprocess.call(['make', '-C', builddir, 'all-pdf-ja'])
4052
+ print('pdflatex finished; the PDF files are in {}.'.format(builddir))
4053
+
4054
+ @buildmethod
4055
+ def text():
4056
+ """
4057
+ Make text files
4058
+ """
4059
+ return build('text', 'The text files are in {}.')
4060
+
4061
+ @buildmethod
4062
+ def man():
4063
+ """
4064
+ Make manual pages
4065
+ """
4066
+ return build('man', 'The manual pages are in {}.')
4067
+
4068
+ @buildmethod
4069
+ def texinfo():
4070
+ """
4071
+ Make TexInfo files
4072
+ """
4073
+ return build('texinfo', 'The Texinfo files are in {}.\n'
4074
+ "Run 'make' in that directory to run these "
4075
+ "through makeinfo (use the 'info' target to do "
4076
+ "that automatically).")
4077
+
4078
+ @buildmethod
4079
+ def info():
4080
+ """
4081
+ Make Texinfo files and run them through makeinfo
4082
+ """
4083
+ _ = texinfo()
4084
+ print('Running Texinfo files through makeinfo...')
4085
+ builddir = os.path.join(self.BUILDDIR, 'texinfo')
4086
+ subprocess.call(['make', '-C', builddir, 'info'])
4087
+ print('makeinfo finished; the Info files are in {}.'.format(builddir))
4088
+
4089
+ @buildmethod
4090
+ def gettext():
4091
+ """
4092
+ Make PO message catalogs
4093
+ """
4094
+ return build('gettext', 'The message catalogs are in {}.', outdir='locale',doctrees=False)
4095
+
4096
+ @buildmethod
4097
+ def changes():
4098
+ """
4099
+ Make an overview of all changed/added/deprecated items
4100
+ """
4101
+ return build('changes', 'The overview file is in {}.')
4102
+
4103
+ @buildmethod
4104
+ def xml():
4105
+ """
4106
+ Make Docutils-native XML files
4107
+ """
4108
+ return build('xml', 'The XML files are in {}.')
4109
+
4110
+ @buildmethod
4111
+ def pseudoxml():
4112
+ """
4113
+ Make pseudoxml-XML files for display purposes
4114
+ """
4115
+ return self.build('pseudoxml', 'The pseudo-XML files are in {}.')
4116
+
4117
+ @buildmethod
4118
+ def linkcheck():
4119
+ """
4120
+ Check all external links for integrity
4121
+ """
4122
+ return build('linkcheck', 'Look for any errors in the above output or in {}/output.txt.')
4123
+
4124
+ @buildmethod
4125
+ def doctest():
4126
+ """
4127
+ Run all doctests embedded in the documentation (if enabled)
4128
+ """
4129
+ return build('doctest', 'Look at the results in {}/output.txt.')
4130
+
4131
+ @buildmethod
4132
+ def assist():
4133
+ """
4134
+ List all targets
4135
+ """
4136
+ print("Please use '{} <target>' where <target> is one of" .format(sys.argv[0]))
4137
+ width = max(len(name) for name in self.BuildOption)
4138
+ for name, target in self.BuildOption.items():
4139
+ print(' {name:{width}} {descr}'.format(name=name, width=width, descr=target.__doc__))
4140
+
4141
+ # Validate installation status
4142
+ validate()
4143
+ # Get additional command line arguments
4144
+ args = ['default'] or sys.argv[1:]
4145
+ for arg in args:
4146
+ # Create a temporary build directory. We do not need unsuccessful build
4147
+ with Utility.TemporaryDirectory(self.scrtdir):
4148
+ # Create a new local directory
4149
+ temp = Utility.PathLeaf(tempfile.NamedTemporaryFile().name)
4150
+ # Copy all source files into the temporary directory
4151
+ shutil.copytree(os.path.abspath(self.SOURCEDIR), os.path.abspath(temp), ignore=shutil.ignore_patterns(".git",".svn")); self.SOURCEDIR = temp
4152
+ # Add auto documentation feature
4153
+ for x in os.getenv("sphinx_include","").split(os.pathsep):
4154
+ try: subprocess.call(["sphinx-apidoc","-o",os.path.join(temp,"_advanced",Utility.PathLeaf(os.path.dirname(x)).lower()),x])
4155
+ except: pass
4156
+ # If no configuration file is found in the source folder, use the default template
4157
+ if not os.path.exists(os.path.join(temp,"conf.py")):
4158
+ # Rename default configuration file to match naming convention.
4159
+ copyfile(os.path.join(PyXMakePath,"Build","config","stm_conf.py"), os.path.join(temp,"conf.py"))
4160
+ # Create an additional static folder if not already existing
4161
+ if not os.path.exists(os.path.join(temp,"_static")):
4162
+ os.mkdir(os.path.join(temp,'_static')); copyfile(os.path.join(PyXMakePath,"Build","config","stm_style.css"), os.path.join(temp,"_static","style.css"))
4163
+ # Create an additional templates folder
4164
+ if not os.path.exists(os.path.join(temp,"_templates")):
4165
+ os.mkdir(os.path.join(temp,'_templates')); copyfile(os.path.join(PyXMakePath,"Build","config","stm_layout.html"), os.path.join(temp,"_templates","layout.html"))
4166
+ # Create all documentations
4167
+ self.BuildOption[arg]()
4168
+ # Do not keep temporary tree by default
4169
+ if not kwargs.get("keep_doctree",False):
4170
+ try:
4171
+ shutil.rmtree(os.path.join(self.BUILDDIR,"doctrees"))
4172
+ except OSError:
4173
+ pass
4174
+ # Copy results to output directory
4175
+ copy_tree(self.BUILDDIR, self.outdir)
4176
+
4177
+ # Return success
4178
+ return 0
4179
+
4180
+ ## @class PyXMake.Build.Make.SSH
4181
+ # Base class for all build events requiring a SSH connection. Inherited from Make.
4182
+ class SSH(Make): # pragma: no cover
4183
+ """
4184
+ Inherited class for all builds using SSH connection.
4185
+ """
4186
+ def __init__(self, *args, **kwargs):
4187
+ """
4188
+ Initialization of SSH class object.
4189
+ """
4190
+ super(SSH, self).__init__(*args, **kwargs)
4191
+
4192
+ # Add Fortran wrapper function to SSH class.
4193
+ setattr(self, str(Fortran.Wrapper.__name__), MethodType(Fortran.Wrapper, self))
4194
+
4195
+ # Defined here to be checked later.
4196
+ ## Wrapper interface file for 3rd party FORTRAN code. Automatically creates a module of the underlying source material.
4197
+ self.intermediate_wrapper = ""
4198
+ self.wrapper_source = ""
4199
+ self.wrapper_module = "pyx_module.f90"
4200
+
4201
+ ## String identifier of current instance.
4202
+ self.MakeObjectKind = 'SSH'
4203
+
4204
+ # Immutable settings for SSH object
4205
+ ## Name of library, assembled using BuildID.
4206
+ self.libname = "lib"+self.buildid + self.architecture
4207
+ ## Temporary build name.
4208
+ self.buildname = self.buildid+'_ssh'
4209
+ ## Environment variables to be set prior to the execution of the build command. Intel Fortran 12+
4210
+ self.export = "export CPATH=$CPATH"
4211
+ ## Environment variables to be set prior to the execution of the build command. Intel Fortran 11 and lower.
4212
+ self.__old_export = "export FPATH=$FPATH"
4213
+ ## Environment variable to be set prior to the execution of the build command.
4214
+ self.__library_path = "export LIBRARY_PATH=$LIBRARY_PATH"
4215
+ ## Custom intel path
4216
+ self.__intel_path = "export pyx_ifort=$(which ifort)"
4217
+
4218
+ ## Define if the input should be compiled exactly as provided.
4219
+ # Defaults to False, meaning that merging & pre-processing utilities will be carried out.
4220
+ self.incremental = kwargs.get('incremental', False)
4221
+
4222
+ # Initialization of lists containing additional sources, modules or libraries
4223
+ ## List of libraries which should be statically linked in.
4224
+ self.linkedIn = []
4225
+
4226
+ # Initialization of tuple containing temporary files
4227
+ ## Blank version of tuple to store temporary file names scheduled for removal.
4228
+ self.temps = ()
4229
+
4230
+ ## Load an additional library prior to execution of all commands. Defaults to an empty string.
4231
+ self.environment = ""
4232
+
4233
+ # Remove MKL from default command line
4234
+ ## Blank version of list containing library directories. MKL library has been removed since location
4235
+ # on the SSH remote computer is not known a priori.
4236
+ self.libdirs = []
4237
+ pass
4238
+
4239
+ def OutputPath(self, libpath, modulepath=None):
4240
+ """
4241
+ Define output directories for modules and libraries. Output written to the workspace is DELETED.
4242
+ """
4243
+ # Output module files to scratch directory by default.
4244
+ if modulepath is None:
4245
+ modulepath = libpath
4246
+ ## Output path for module or header files.
4247
+ self.outmodule = modulepath + posixpath.sep
4248
+ ## Output path for library files.
4249
+ self.outlibs = libpath + posixpath.sep
4250
+ pass
4251
+
4252
+ def Settings(self, user, key="", host='129.247.54.37', port=22, use_cache=True, **kwargs):
4253
+ """
4254
+ Define settings to establish a SSH connection.
4255
+ """
4256
+ # Establish SSH connection to institute cluster // mg 07.08.17
4257
+ # Parse an external client directly to the underlying connection. Use this connection for all following operations.
4258
+ if kwargs.get("client",None):
4259
+ self.ssh_client = kwargs.get("client");
4260
+ sftp = self.ssh_client.open_sftp(); sftp.chdir(".")
4261
+ self.workspace = kwargs.get("workspace",posixpath.join(sftp.getcwd(),"")); sftp.close()
4262
+ return
4263
+ ## Remote workspace. This is the scratch directory for the build event.
4264
+ # Defaults to /home/user/.
4265
+ self.workspace = kwargs.get("workspace",posixpath.join(Utility.AsDrive('home',posixpath.sep),user)+posixpath.sep)
4266
+ ## Instance of SSHClient to establish a SSH connection.
4267
+ self.ssh_client = paramiko.SSHClient()
4268
+ self.ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
4269
+ # Read password (if given)
4270
+ password = kwargs.pop("password","");
4271
+ try:
4272
+ # A password has been given. Use it directly
4273
+ if password: raise ValueError
4274
+ # Try to connect using key file
4275
+ try: self.ssh_client.connect(hostname=host, port=port, username=user, key_filename=key, timeout=kwargs.get("timeout",None))
4276
+ except paramiko.ssh_exception.SSHException:
4277
+ self.ssh_client.connect(hostname=host, port=port, username=user, key_filename=key, timeout=kwargs.get("timeout",None),
4278
+ disabled_algorithms=kwargs.get("disabled",{"pubkeys":["rsa-sha2-512", "rsa-sha2-256"]}))
4279
+ except socket.timeout: raise TimeoutError
4280
+ except:
4281
+ if use_cache:
4282
+ # Check if the password should be cached. Active by default.
4283
+ try: import keyring
4284
+ except: raise ModuleNotFoundError
4285
+ # Check if the stored/cached password can be used. Prompt to reenter the password if that is not the case.
4286
+ try: self.ssh_client.connect(hostname=host, port=port, username=user, password=keyring.get_password(user, user))
4287
+ except:
4288
+ # Get password from user
4289
+ if not password: password = getpass.getpass()
4290
+ keyring.set_password(user, user, password)
4291
+ finally: self.ssh_client.connect(hostname=host, port=port, username=user, password=keyring.get_password(user, user))
4292
+ else:
4293
+ if not password: password = getpass.getpass()
4294
+ self.ssh_client.connect(hostname=host, port=port, username=user, password=password)
4295
+ pass
4296
+
4297
+ def Environment(self, path="", bash="", args="", method="source"):
4298
+ """
4299
+ Load an additional environment file prior to execution of all commands.
4300
+ """
4301
+ source = method
4302
+ ## Execute an additional bash script prior to all build commands.
4303
+ if any([str(posixpath.join(path,bash)).startswith("module"),args.startswith("module")]): source = ""
4304
+ self.environment += " ".join([source,posixpath.join(path,bash),args])+"; "
4305
+ pass
4306
+
4307
+ def Postprocessing(self, cmdstring=""):
4308
+ """
4309
+ Assemble command string for the post-build event.
4310
+ """
4311
+ ## Command executed during post-build event.
4312
+ self.postcmd = cmdstring
4313
+ pass
4314
+
4315
+ def Build(self, cmdstring, run= "ifort", path="", lib= "", linkedIn="", **kwargs):
4316
+ """
4317
+ Assemble command strings for the main build event.
4318
+ """
4319
+ cmd = ""; self.postcmd = ""
4320
+
4321
+ # Which libraries are used for linking (relative path).
4322
+ self.libs.append(lib)
4323
+ self.libs = list(Utility.ArbitraryFlattening(self.libs))
4324
+
4325
+ # Which libraries have to be additionally linked in (absolute path).
4326
+ self.linkedIn.append(linkedIn)
4327
+ self.linkedIn = list(Utility.ArbitraryFlattening(self.linkedIn))
4328
+
4329
+ # Build commands using Intel Fortran (mutable)
4330
+ ## (Intel Fortran) Compiler Path
4331
+ self.path2exe = path; self.exe = run
4332
+
4333
+ # Check whether an interface module wrapper was added to the current folder
4334
+ if os.path.isfile(self.intermediate_wrapper):
4335
+ if (Utility.IsNotEmpty(self.wrapper_source)):
4336
+ self.buildname = self.wrapper_source
4337
+
4338
+ # Immutable settings for SSH object
4339
+ if self.incremental:
4340
+ c_files = [x for x in self.srcs if os.path.splitext(x)[1].lower() in (".for", ".f95", ".f", ".f90")]
4341
+ cmd += ' %s ' % (' '.join(c_files))
4342
+
4343
+ # Always add MKL when used with f2py.
4344
+ if self.exe in ("f2py") and self.exe:
4345
+ cmd += "-m "+str(self.buildid+self.architecture)
4346
+ # Assist f2py by providing shared MKL libraries directly
4347
+ self.libs.extend(["mkl_rt","iomp5", "pthread", "m", "dl"])
4348
+
4349
+ # Add libraries for referencing to the command string (they are only used as resources)
4350
+ for x in [' -l'+x+' ' for x in self.libs if x]:
4351
+ cmd += x
4352
+
4353
+ ## Remote (Intel) Compiler command.
4354
+ if self.exe and self.exe not in ("custom"):
4355
+ self.makecmd = posixpath.join(self.path2exe,self.exe)+" -c "+ cmd + cmdstring
4356
+ else:
4357
+ self.makecmd = cmdstring
4358
+
4359
+ ## Remote Linker command.
4360
+ if self.exe not in ("ifort", "gcc", "g++"):
4361
+ ## Do no use any archiver
4362
+ self.linkcmd = ""
4363
+ elif self.exe == "ifort":
4364
+ ## Remote Intel Linker command.
4365
+ self.linkcmd = posixpath.join(self.path2exe,"xiar")+" -rc "
4366
+ else:
4367
+ ## Simply execute UNIX archiver
4368
+ self.linkcmd = posixpath.join("","ar")+" -rc "
4369
+ pass
4370
+
4371
+ def create(self, **kwargs):
4372
+ """
4373
+ Define settings to establish SSH connection.
4374
+ """
4375
+ cmd = ""
4376
+
4377
+ # Add all include paths to CPATH & FPATH (deprecated!) environment variable
4378
+ includes = [':"'+x+'"' for x in self.incdirs]
4379
+ for x in includes:
4380
+ self.export += x
4381
+ self.__old_export += x
4382
+ self.export += " && " + self.__old_export
4383
+
4384
+ # Add all library paths to LIBRARY_PATH environment variable
4385
+ library = [':"'+x+'" ' for x in self.libdirs]
4386
+ for x in library:
4387
+ self.__library_path += x
4388
+ self.export += " && " + self.__library_path
4389
+
4390
+ # Add libraries for linking to the command string
4391
+ try:
4392
+ if self.linkedIn[0] != "":
4393
+ linklist = ['ar -x "'+x+'" && ' for x in self.linkedIn]
4394
+ for x in linklist:
4395
+ cmd += x
4396
+ # Link list is empty or does not exist
4397
+ except IndexError:
4398
+ pass
4399
+
4400
+ # Get the target and the base name of library (created in the process).
4401
+ target = posixpath.join(self.workspace,self.buildname)
4402
+ # base = os.path.splitext(target)[0]
4403
+
4404
+ # Go into scratch directory (if defined)
4405
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
4406
+
4407
+ # Pre-build event (if required)
4408
+ try:
4409
+ if self.precmd != '':
4410
+ Utility.Popen(self.precmd, self.verbose)
4411
+ except:
4412
+ pass
4413
+
4414
+ # Establish ssh connection and execute make commands on the linux cluster.
4415
+ sftp = self.ssh_client.open_sftp()
4416
+
4417
+ try:
4418
+ sftp.put(self.buildname,target)
4419
+ if os.path.isfile(self.intermediate_wrapper):
4420
+ # Use general-purpose wrapper file
4421
+ Utility.ReplaceTextinFile(self.intermediate_wrapper, self.wrapper_module, {'%pyx_source%':'"'+self.buildname+'"',"#":" "}, source=self.scrtdir)
4422
+ sftp.put(os.path.join(self.scrtdir,self.wrapper_module),posixpath.join(self.workspace, self.wrapper_module))
4423
+ target = posixpath.join(self.workspace,self.wrapper_module)
4424
+ # Preserve the unique name of each object file
4425
+ self.makecmd += " -o "+ os.path.splitext(self.buildname)[0]+".o"
4426
+ except:
4427
+ target = ""
4428
+ for cs in self.srcs:
4429
+ sftp.put(os.path.join(self.scrtdir,cs),posixpath.join(self.workspace,cs))
4430
+
4431
+ # Put f2py mapping file into current workspace
4432
+ if self.exe not in ("ifort", "gcc", "g++","custom"):
4433
+ sftp.put(os.path.join(Path2Config,".f2py_f2cmap"), posixpath.join(self.workspace,".f2py_f2cmap"))
4434
+ sftp.close()
4435
+
4436
+ # Create output folder if not existing
4437
+ Utility.SSHPopen(self.ssh_client,"mkdir -p "+self.outmodule+"; mkdir -p "+self.workspace,self.verbose, **kwargs)
4438
+
4439
+ # Delete old content in output folders
4440
+ if not kwargs.get("combine", False):
4441
+ Utility.SSHPopen(self.ssh_client,'rm -f '+self.outmodule+'*.mod; rm -f '+self.workspace+'*.mod', self.verbose, **kwargs)
4442
+ else:
4443
+ Utility.SSHPopen(self.ssh_client,'rm -f '+self.workspace+'*.mod',self.verbose, **kwargs)
4444
+
4445
+ self.command = self.environment + self.export + " && cd "+self.workspace+ " && " + self.makecmd
4446
+ if Utility.IsNotEmpty(target):
4447
+ self.command+= ' "'+target+'"'
4448
+ sbuild = Utility.SSHPopen(self.ssh_client, self.command, self.verbose, **kwargs)
4449
+
4450
+ if sbuild==0:
4451
+ # There is a valid link command. Use it
4452
+ if Utility.IsNotEmpty(self.linkcmd):
4453
+ self.command = self.environment + self.export + " && cd "+self.workspace+ " && " + cmd + self.linkcmd + self.libname+'.a '+'*.o '
4454
+ sarch = Utility.SSHPopen(self.ssh_client, self.command, self.verbose, **kwargs)
4455
+ elif self.exe in ["custom"]:
4456
+ sarch = 1; scopy = 1
4457
+ # Copy all created shared libraries.
4458
+ else:
4459
+ sarch = 1; scopy = 0
4460
+ self.postcmd += ' && cp -rf '+self.workspace+'*.so '+self.outlibs
4461
+
4462
+ if sarch==0:
4463
+ self.command = 'cp -rf '+self.workspace+'*.mod '+self.outmodule+'; cp -rf '+self.workspace+'*.a '+self.outlibs
4464
+ scopy = Utility.SSHPopen(self.ssh_client, self.command, self.verbose, **kwargs)
4465
+
4466
+ if scopy == 0:
4467
+ Utility.SSHPopen(self.ssh_client,'rm '+self.workspace+'*.o; rm '+self.workspace+'*.mod; rm '+self.workspace+'*.a; rm '+
4468
+ self.workspace+'*.f90; rm -rf '+posixpath.join(self.workspace,"intel"), self.verbose, **kwargs)
4469
+
4470
+ if Utility.IsNotEmpty(self.postcmd):
4471
+ self.export += " && " + self.__intel_path
4472
+ self.command = self.environment + self.export + " && " + self.postcmd
4473
+ spost = Utility.SSHPopen(self.ssh_client, self.command, self.verbose, **kwargs)
4474
+ if spost == 0:
4475
+ Utility.SSHPopen(self.ssh_client,'rm -f '+self.workspace+'*.o; rm '+self.workspace+'*.mod; rm -f '+self.workspace+'*.a; rm -f '+
4476
+ self.workspace+'*.f90; rm -f '+self.workspace+'*.f; rm -rf '+
4477
+ posixpath.join(self.workspace,"intel")+'; rm -f '+
4478
+ posixpath.join(self.workspace,".f2py_f2cmap") +
4479
+ " rm -f %s " % (' '.join([posixpath.join(self.workspace,cs) for cs in self.srcs])),
4480
+ self.verbose, **kwargs)
4481
+ pass
4482
+ pass
4483
+
4484
+ # Combine event (needed for TOMS). Combine multiple libraries into ONE.
4485
+ if kwargs.get("combine", False):
4486
+ librarian = 'ar'; ext = '.a'; decomp = " && "+librarian+" -x "
4487
+ mergedid = "lib"+posixpath.basename(self.outmodule.rstrip("/"))
4488
+ _ , stdout, _ = self.ssh_client.exec_command('ls '+self.outlibs)
4489
+ multi_libs = [x for x in [x.rstrip("\n") for x in stdout.readlines() if x.startswith(mergedid)]]
4490
+
4491
+ try:
4492
+ # Remove old combined library from the list.
4493
+ multi_libs.remove(mergedid+self.architecture+ext)
4494
+ except:
4495
+ pass
4496
+
4497
+ self.postcmd = self.environment + self.export + " && cd "+self.outlibs.rstrip("/")+" && "
4498
+ self.postcmd += librarian +" -x " + decomp.join(multi_libs) +" && "
4499
+ self.postcmd += librarian + " -rc " + mergedid+self.architecture+ext+ " *.o"
4500
+
4501
+ Utility.SSHPopen(self.ssh_client, self.postcmd, self.verbose,**kwargs)
4502
+ for lib in multi_libs:
4503
+ Utility.SSHPopen(self.ssh_client,'rm -f '+posixpath.join(self.outlibs,lib),self.verbose, **kwargs)
4504
+ self.ssh_client.exec_command('rm -f '+self.outlibs+'*.o')
4505
+
4506
+ # Go into scratch directory (if defined)
4507
+ with Utility.ChangedWorkingDirectory(self.scrtdir):
4508
+
4509
+ # Finish and delete redundant files
4510
+ Utility.DeleteFilesbyEnding(self.temps)
4511
+
4512
+ ## Backwards compatibility for deprecated class calls
4513
+ setattr(sys.modules[__name__],"Robot", Coverage)
4514
+ ## Forward compatibility for future class calls
4515
+ setattr(sys.modules[__name__],"CMake", Custom)
4516
+
4517
+ if __name__ == '__main__':
4518
+ pass