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.
- PyXMake/Build/Make.py +4518 -0
- PyXMake/Build/__init__.py +34 -0
- PyXMake/Build/__install__.py +176 -0
- PyXMake/Build/cmd/windows/iniCompiler.bat +35 -0
- PyXMake/Build/config/.f2py_f2cmap +6 -0
- PyXMake/Build/config/stm_choco.ps1 +61 -0
- PyXMake/Build/config/stm_color.py +57 -0
- PyXMake/Build/config/stm_conf.py +243 -0
- PyXMake/Build/config/stm_doc_config +2492 -0
- PyXMake/Build/config/stm_doc_footer.html +0 -0
- PyXMake/Build/config/stm_docker.ps1 +98 -0
- PyXMake/Build/config/stm_docker_ports.ps1 +88 -0
- PyXMake/Build/config/stm_docu.yml +29 -0
- PyXMake/Build/config/stm_layout.html +4 -0
- PyXMake/Build/config/stm_logo.ico +0 -0
- PyXMake/Build/config/stm_logo.png +0 -0
- PyXMake/Build/config/stm_makefile +123 -0
- PyXMake/Build/config/stm_pyfilter.py +61 -0
- PyXMake/Build/config/stm_sphinx.sty +1858 -0
- PyXMake/Build/config/stm_style.css +9 -0
- PyXMake/Tools/ErrorHandling.py +112 -0
- PyXMake/Tools/Utility.py +2065 -0
- PyXMake/Tools/__init__.py +33 -0
- PyXMake/VTL/__init__.py +796 -0
- PyXMake/VTL/__install__.py +109 -0
- PyXMake/VTL/abaqus.py +154 -0
- PyXMake/VTL/api.py +86 -0
- PyXMake/VTL/app.py +87 -0
- PyXMake/VTL/archive.py +90 -0
- PyXMake/VTL/bundle.py +76 -0
- PyXMake/VTL/chocolatey.py +126 -0
- PyXMake/VTL/cmake.py +78 -0
- PyXMake/VTL/cmd/linux/stm_cara_software.sh +533 -0
- PyXMake/VTL/cmd/linux/stm_conda_env.sh +113 -0
- PyXMake/VTL/cmd/linux/stm_jenkins_job.sh +50 -0
- PyXMake/VTL/cmd/linux/stm_lab_image.sh +66 -0
- PyXMake/VTL/cmd/linux/stm_lab_pages.sh +28 -0
- PyXMake/VTL/cmd/linux/stm_routines_docs.sh +37 -0
- PyXMake/VTL/cmd/windows/stm_conda.bat +18 -0
- PyXMake/VTL/cmd/windows/stm_lab.bat +117 -0
- PyXMake/VTL/cmd/windows/stm_latex.bat +32 -0
- PyXMake/VTL/cmd/windows/stm_make.bat +315 -0
- PyXMake/VTL/cmd/windows/stm_post.bat +63 -0
- PyXMake/VTL/cmd/windows/stm_server.bat +86 -0
- PyXMake/VTL/coverage.py +72 -0
- PyXMake/VTL/cxx.py +100 -0
- PyXMake/VTL/doc/documentation.rst +35 -0
- PyXMake/VTL/doc/pyx_core/core.rst +42 -0
- PyXMake/VTL/doc/pyx_core/html/____init_____8py_source.html +90 -0
- PyXMake/VTL/doc/pyx_core/html/____install_____8py_source.html +91 -0
- PyXMake/VTL/doc/pyx_core/html/_build_2____init_____8py_source.html +90 -0
- PyXMake/VTL/doc/pyx_core/html/_error_handling_8py_source.html +101 -0
- PyXMake/VTL/doc/pyx_core/html/_make_8py_source.html +235 -0
- PyXMake/VTL/doc/pyx_core/html/_tools_2____init_____8py_source.html +90 -0
- PyXMake/VTL/doc/pyx_core/html/_utility_8py_source.html +129 -0
- PyXMake/VTL/doc/pyx_core/html/_v_t_l_2____init_____8py_source.html +95 -0
- PyXMake/VTL/doc/pyx_core/html/abaqus_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/annotated.html +149 -0
- PyXMake/VTL/doc/pyx_core/html/annotated_dup.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/api_8py_source.html +95 -0
- PyXMake/VTL/doc/pyx_core/html/app_8py_source.html +92 -0
- PyXMake/VTL/doc/pyx_core/html/bc_s.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/bdwn.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/bundle_8py_source.html +92 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx-members.html +138 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx.html +625 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx.js +20 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_c_cxx.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom-members.html +132 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom.html +428 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom.js +13 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_custom.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen-members.html +133 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen.html +490 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen.js +15 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_doxygen.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran-members.html +143 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran.html +702 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran.js +28 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_fortran.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make-members.html +128 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make.html +951 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make.js +38 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_make.png +0 -0
- 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
- 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
- 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
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x-members.html +135 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x.html +442 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x.js +14 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py2_x.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer-members.html +136 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer.html +478 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer.js +18 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_py_installer.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h.html +906 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h.js +33 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_s_s_h.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx-members.html +142 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx.html +441 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx.js +19 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_build_1_1_make_1_1_sphinx.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error-members.html +95 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error.html +175 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_error.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error.html +187 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_input_error.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error.html +242 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_error_handling_1_1_transition_error.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory.html +138 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory.js +8 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_tools_1_1_utility_1_1_changed_working_directory.png +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean.html +115 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_clean.png +0 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test.html +150 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1_test.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac.html +175 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1abq__mcodac.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac.html +160 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1app__pycodac.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac.html +151 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1bundle__pycodac.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam.html +154 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__boxbeam.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore.html +154 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdcore.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper.html +154 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdmapper.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac.html +154 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdpycodac.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck.html +154 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__mcdsubbuck.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake.html +154 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1doxy__pyxmake.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos.html +166 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__beos.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__boxbeam.html +159 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__boxbeam.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac.html +172 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1f2py__mcodac.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam.html +172 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__boxbeam.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac.html +172 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1java__mcodac.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint-members.html +102 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint.html +173 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pylint.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app.html +224 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app.js +12 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__app.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle.html +218 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle.js +10 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__bundle.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom.html +233 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom.js +15 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__custom.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen.html +223 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen.js +10 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__doxygen.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py.html +235 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py.js +15 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__f2py.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran.html +236 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran.js +15 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__fortran.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx.html +221 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx.js +11 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1pyx__sphinx.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab.html +157 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1sphinx__stmlab.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam.html +175 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__boxbeam.png +0 -0
- 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
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac.html +175 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/class_py_x_make_1_1_v_t_l_1_1stm__make_1_1win__mcodac.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/classes.html +131 -0
- PyXMake/VTL/doc/pyx_core/html/closed.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/cxx_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/dir_4335ce4a64d0d4f9f4c8f0a782dfdfeb.html +90 -0
- PyXMake/VTL/doc/pyx_core/html/dir_4335ce4a64d0d4f9f4c8f0a782dfdfeb.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/dir_7e461070e7b716e896e0d97cd6a82321.html +90 -0
- PyXMake/VTL/doc/pyx_core/html/dir_7e461070e7b716e896e0d97cd6a82321.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/doc.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/doxygen.css +1596 -0
- PyXMake/VTL/doc/pyx_core/html/doxygen.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/doxygen_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/dynsections.js +104 -0
- PyXMake/VTL/doc/pyx_core/html/files.html +118 -0
- PyXMake/VTL/doc/pyx_core/html/files.js +21 -0
- PyXMake/VTL/doc/pyx_core/html/folderclosed.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/folderopen.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/functions.html +463 -0
- PyXMake/VTL/doc/pyx_core/html/functions_func.html +242 -0
- PyXMake/VTL/doc/pyx_core/html/functions_vars.html +345 -0
- PyXMake/VTL/doc/pyx_core/html/hierarchy.html +145 -0
- PyXMake/VTL/doc/pyx_core/html/hierarchy.js +69 -0
- PyXMake/VTL/doc/pyx_core/html/ifort_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/index.html +90 -0
- PyXMake/VTL/doc/pyx_core/html/java_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/jquery.js +87 -0
- PyXMake/VTL/doc/pyx_core/html/menu.js +26 -0
- PyXMake/VTL/doc/pyx_core/html/menudata.js +90 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make.html +164 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build.html +128 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build_1_1____install____.html +131 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build_1_1_make.html +201 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_build_1_1_make.js +13 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools.html +128 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_error_handling.html +131 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_error_handling.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_utility.html +907 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_tools_1_1_utility.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l.html +350 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1abaqus.html +222 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1api.html +268 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1app.html +251 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1bundle.html +199 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1cxx.html +219 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1doxygen.html +230 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ifort.html +414 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1java.html +242 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1py2x.html +264 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1sphinx.html +307 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ssh__f2py.html +296 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ssh__ifort.html +489 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1ssh__make.html +275 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1stm__make.html +185 -0
- PyXMake/VTL/doc/pyx_core/html/namespace_py_x_make_1_1_v_t_l_1_1stm__make.js +34 -0
- PyXMake/VTL/doc/pyx_core/html/namespacemembers.html +252 -0
- PyXMake/VTL/doc/pyx_core/html/namespacemembers_func.html +245 -0
- PyXMake/VTL/doc/pyx_core/html/namespacemembers_vars.html +95 -0
- PyXMake/VTL/doc/pyx_core/html/namespaces.html +116 -0
- PyXMake/VTL/doc/pyx_core/html/namespaces.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/nav_f.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/nav_g.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/nav_h.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/navtree.css +146 -0
- PyXMake/VTL/doc/pyx_core/html/navtree.js +517 -0
- PyXMake/VTL/doc/pyx_core/html/navtreedata.js +35 -0
- PyXMake/VTL/doc/pyx_core/html/navtreeindex0.js +253 -0
- PyXMake/VTL/doc/pyx_core/html/navtreeindex1.js +197 -0
- PyXMake/VTL/doc/pyx_core/html/open.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/py2x_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/resize.js +114 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_0.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_0.js +8 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_1.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_1.js +14 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_10.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_10.js +15 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_11.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_11.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_12.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_12.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_13.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_13.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_14.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_14.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_2.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_2.js +8 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_3.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_3.js +12 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_4.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_4.js +12 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_5.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_5.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_6.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_6.js +13 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_7.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_7.js +15 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_8.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_8.js +13 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_9.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_9.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_a.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_a.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_b.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_b.js +10 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_c.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_c.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_d.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_d.js +8 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_e.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_e.js +46 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_f.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/all_f.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_0.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_0.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_1.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_1.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_10.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_10.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_11.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_11.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_2.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_2.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_3.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_3.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_4.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_4.js +10 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_5.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_5.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_6.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_6.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_7.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_7.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_8.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_8.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_9.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_9.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_a.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_a.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_b.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_b.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_c.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_c.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_d.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_d.js +13 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_e.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_e.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_f.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/classes_f.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/close.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_0.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_0.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_1.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_1.js +11 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_10.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_10.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_2.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_2.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_3.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_3.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_4.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_4.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_5.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_5.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_6.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_6.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_7.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_7.js +14 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_8.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_8.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_9.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_9.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_a.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_a.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_b.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_b.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_c.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_c.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_d.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_d.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_e.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_e.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_f.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/functions_f.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/mag_sel.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/search/namespaces_0.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/namespaces_0.js +25 -0
- PyXMake/VTL/doc/pyx_core/html/search/nomatches.html +12 -0
- PyXMake/VTL/doc/pyx_core/html/search/search.css +271 -0
- PyXMake/VTL/doc/pyx_core/html/search/search.js +791 -0
- PyXMake/VTL/doc/pyx_core/html/search/search_l.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/search/search_m.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/search/search_r.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/search/searchdata.js +27 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_0.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_0.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_1.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_1.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_2.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_2.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_3.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_3.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_4.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_4.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_5.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_5.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_6.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_6.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_7.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_7.js +8 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_8.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_8.js +7 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_9.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_9.js +5 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_a.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_a.js +6 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_b.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_b.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_c.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_c.js +9 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_d.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_d.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_e.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_e.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_f.html +26 -0
- PyXMake/VTL/doc/pyx_core/html/search/variables_f.js +4 -0
- PyXMake/VTL/doc/pyx_core/html/sphinx_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/splitbar.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/ssh__f2py_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/ssh__ifort_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/ssh__make_8py_source.html +93 -0
- PyXMake/VTL/doc/pyx_core/html/stm__make_8py_source.html +241 -0
- PyXMake/VTL/doc/pyx_core/html/stm_logo.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/sync_off.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/sync_on.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/tab_a.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/tab_b.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/tab_h.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/tab_s.png +0 -0
- PyXMake/VTL/doc/pyx_core/html/tabs.css +1 -0
- PyXMake/VTL/doc/pyx_qmh/DLR_QMH_Guidelines_PyXMake.docx +0 -0
- PyXMake/VTL/doc/pyx_qmh/DLR_QMH_Guidelines_PyXMake.pdf +0 -0
- PyXMake/VTL/docker.py +86 -0
- PyXMake/VTL/doxygen.py +87 -0
- PyXMake/VTL/examples/pyx_abaqus.py +154 -0
- PyXMake/VTL/examples/pyx_api.py +86 -0
- PyXMake/VTL/examples/pyx_app.py +87 -0
- PyXMake/VTL/examples/pyx_archive.py +90 -0
- PyXMake/VTL/examples/pyx_bundle.py +76 -0
- PyXMake/VTL/examples/pyx_chocolatey.py +126 -0
- PyXMake/VTL/examples/pyx_cmake.py +78 -0
- PyXMake/VTL/examples/pyx_coverage.py +72 -0
- PyXMake/VTL/examples/pyx_cxx.py +100 -0
- PyXMake/VTL/examples/pyx_docker.py +86 -0
- PyXMake/VTL/examples/pyx_doxygen.py +87 -0
- PyXMake/VTL/examples/pyx_gfortran.py +207 -0
- PyXMake/VTL/examples/pyx_gitlab.py +177 -0
- PyXMake/VTL/examples/pyx_ifort.py +204 -0
- PyXMake/VTL/examples/pyx_java.py +117 -0
- PyXMake/VTL/examples/pyx_latex.py +64 -0
- PyXMake/VTL/examples/pyx_openapi.py +168 -0
- PyXMake/VTL/examples/pyx_portainer.py +58 -0
- PyXMake/VTL/examples/pyx_py2x.py +103 -0
- PyXMake/VTL/examples/pyx_pyreq.py +80 -0
- PyXMake/VTL/examples/pyx_sphinx.py +82 -0
- PyXMake/VTL/examples/pyx_ssh_f2py.py +161 -0
- PyXMake/VTL/examples/pyx_ssh_ifort.py +187 -0
- PyXMake/VTL/examples/pyx_ssh_make.py +227 -0
- PyXMake/VTL/examples/pyx_stm_cara.py +125 -0
- PyXMake/VTL/examples/pyx_stm_svn2git.py +123 -0
- PyXMake/VTL/gfortran.py +207 -0
- PyXMake/VTL/gitlab.py +177 -0
- PyXMake/VTL/hooks/windows/stm_pre_commit.bat +35 -0
- PyXMake/VTL/ifort.py +204 -0
- PyXMake/VTL/java.py +117 -0
- PyXMake/VTL/latex.py +64 -0
- PyXMake/VTL/make/pyx_module.f90 +28 -0
- PyXMake/VTL/make/pyx_muesli.mk +217 -0
- PyXMake/VTL/make/pyx_muesli_def.h +265 -0
- PyXMake/VTL/make/pyx_muesli_undef.h +27 -0
- PyXMake/VTL/openapi.py +168 -0
- PyXMake/VTL/portainer.py +58 -0
- PyXMake/VTL/py2x.py +103 -0
- PyXMake/VTL/pyreq.py +80 -0
- PyXMake/VTL/sphinx.py +82 -0
- PyXMake/VTL/ssh_f2py.py +161 -0
- PyXMake/VTL/ssh_ifort.py +187 -0
- PyXMake/VTL/ssh_make.py +227 -0
- PyXMake/VTL/stm_cara.py +125 -0
- PyXMake/VTL/stm_make.py +1154 -0
- PyXMake/VTL/stm_post.py +60 -0
- PyXMake/VTL/stm_svn2git.py +123 -0
- PyXMake/__init__.py +130 -0
- PyXMake/__install__.py +109 -0
- PyXMake/__setenv__.py +67 -0
- pyx_core-1.19.2.dist-info/LICENSE +11 -0
- pyx_core-1.19.2.dist-info/METADATA +155 -0
- pyx_core-1.19.2.dist-info/RECORD +553 -0
- pyx_core-1.19.2.dist-info/WHEEL +4 -0
- 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
|