├── .github ├── ISSUE_TEMPLATE │ ├── 01_bug.yaml │ ├── 02_packaging.yaml │ ├── 03_feature.yaml │ ├── 04_specification.yaml │ ├── 05_free.md │ └── config.yml ├── actions │ └── setup-intel │ │ └── action.yml ├── dependabot.yml └── workflows │ ├── CI.yml │ ├── docs.yml │ ├── meta.yml │ └── release.yml ├── .gitignore ├── CONTRIBUTING.md ├── ChangeLog.md ├── LICENSE ├── PACKAGING.md ├── README.md ├── app └── main.f90 ├── ci ├── fpm-installer.nsi ├── installer-icon.ico ├── meta_tests.sh ├── run_tests.sh ├── single-file-gfortran.sh └── single-file.patch ├── doc ├── Contributing.md ├── License.md ├── Manifest.md ├── Packaging.md ├── index.md └── media │ └── favicon.ico ├── docs.md ├── example_packages ├── README.md ├── app_with_c │ ├── .gitignore │ ├── app │ │ ├── c_code.c │ │ └── main.f90 │ └── fpm.toml ├── app_with_submodule │ ├── .gitignore │ ├── app │ │ ├── app1 │ │ │ ├── child1.f90 │ │ │ ├── grandchild.f90 │ │ │ └── main1.f90 │ │ └── app2 │ │ │ ├── child2.f90 │ │ │ └── main2.f90 │ ├── fpm.toml │ └── src │ │ └── parent.f90 ├── auto_discovery_off │ ├── .gitignore │ ├── app │ │ ├── main.f90 │ │ └── unused.f90 │ ├── fpm.toml │ └── test │ │ ├── my_test.f90 │ │ └── unused_test.f90 ├── auto_with_nondefault_main │ ├── .gitignore │ ├── README.md │ ├── app │ │ └── non_default_name.f90 │ └── fpm.toml ├── c_header_only │ ├── .gitignore │ ├── fpm.toml │ └── include │ │ └── c_header.h ├── c_includes │ ├── .gitignore │ ├── fpm.toml │ ├── include │ │ └── lib.h │ └── src │ │ └── lib.c ├── c_main │ ├── app │ │ └── main.c │ └── fpm.toml ├── c_main_preprocess │ ├── app │ │ └── main.c │ ├── fpm.toml │ └── src │ │ ├── stub.c │ │ └── val.h ├── circular_example │ ├── .gitignore │ ├── fpm.toml │ ├── src │ │ └── greet_m.f90 │ └── test │ │ └── main.f90 ├── circular_test │ ├── .gitignore │ ├── fpm.toml │ └── src │ │ └── hello_test.f90 ├── cpp_files │ ├── README.md │ ├── fpm.toml │ ├── src │ │ ├── cpp_files.f90 │ │ └── hello_world.cpp │ └── test │ │ └── check.f90 ├── dependency_priority │ ├── README.md │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── fixed-form │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── lib.f90 ├── fortran_includes │ ├── .gitignore │ ├── fpm.toml │ ├── inc │ │ └── parameters.f90 │ └── src │ │ └── lib.f90 ├── fpm_test_exe_issues │ ├── fpm.toml │ └── src │ │ ├── a │ │ └── a_mod.f90 │ │ ├── b_mod.f90 │ │ └── main.f90 ├── fpm_test_exit_code │ ├── README.md │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── free-form │ ├── app │ │ └── main.f │ ├── fpm.toml │ └── src │ │ └── lib.f ├── hello_complex │ ├── .gitignore │ ├── apps │ │ ├── say_goodbye │ │ │ └── say_goodbye.f90 │ │ └── say_hello │ │ │ └── say_Hello.f90 │ ├── fpm.toml │ ├── source │ │ ├── farewell_m.f90 │ │ ├── greet_m.f90 │ │ └── subdir │ │ │ └── constants.f90 │ └── tests │ │ ├── farewell │ │ └── farewell_test.f90 │ │ └── greet │ │ └── greet_test.f90 ├── hello_complex_2 │ ├── .gitignore │ ├── app │ │ ├── app_mod.f90 │ │ ├── say_goodbye.f90 │ │ └── say_hello │ │ │ ├── app_extra_mod.f90 │ │ │ ├── app_hello_mod.f90 │ │ │ └── say_Hello.f90 │ ├── fpm.toml │ ├── src │ │ ├── farewell_m.f90 │ │ └── greet_m.f90 │ └── test │ │ ├── farewell_test.f90 │ │ ├── greet_test.f90 │ │ └── test_mod.f90 ├── hello_fpm │ ├── .gitignore │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── hello_fpm_path │ ├── app │ │ └── main.f90 │ ├── crate │ │ ├── utils1 │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ │ └── say_hello.f90 │ │ ├── utils1_1 │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ │ └── say_hello.f90 │ │ └── utils2 │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ └── say_hello.f90 │ └── fpm.toml ├── hello_world │ ├── .gitignore │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── implicit-external │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── impl.f90 ├── implicit-typing │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── impl.f90 ├── link_executable │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── include │ │ └── test.f90 ├── link_external │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── wrapped_gemv.f90 ├── makefile_complex │ ├── .gitignore │ ├── Makefile │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── wrapper_mod.f90 ├── many_examples │ ├── .gitignore │ ├── app │ │ └── demo-prog.f90 │ ├── demo1 │ │ └── prog.f90 │ ├── demo2 │ │ └── prog.f90 │ └── fpm.toml ├── many_targets │ ├── .gitignore │ ├── app │ │ ├── run1.f90 │ │ ├── run2.f90 │ │ └── run3.f90 │ ├── example │ │ ├── example1.f90 │ │ ├── example2.f90 │ │ └── example3.f90 │ ├── fpm.toml │ ├── src │ │ └── file_mod.f90 │ └── test │ │ ├── test1.f90 │ │ ├── test2.f90 │ │ └── test3.f90 ├── metapackage_blas │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── metapackage_hdf5 │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── metapackage_minpack │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── metapackage_minpack.f90 ├── metapackage_mpi │ ├── README.md │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── metapackage_mpi_c │ ├── README.md │ ├── app │ │ └── main.c │ └── fpm.toml ├── metapackage_mpi_cpp │ ├── README.md │ ├── app │ │ └── main.cpp │ └── fpm.toml ├── metapackage_netcdf │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── metapackage_openmp │ ├── README.md │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ ├── src │ │ └── test_openmp.f90 │ └── test │ │ └── check.f90 ├── metapackage_stdlib │ ├── README.md │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── metapackage_stdlib.f90 ├── metapackage_stdlib_extblas │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── nonintrinsic │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── iso_fortran_env.f90 ├── preprocess_cpp │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── preprocess_cpp.f90 ├── preprocess_cpp_c │ ├── app │ │ └── main.c │ ├── fpm.toml │ └── include │ │ └── val.h ├── preprocess_cpp_deps │ ├── app │ │ └── main.f90 │ ├── crate │ │ └── utils │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ └── say_hello.f90 │ └── fpm.toml ├── preprocess_cpp_suffix │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── preprocess_cpp.fpp ├── preprocess_hello │ ├── README.md │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── preprocess_hello_dependency │ ├── README.md │ ├── fpm.toml │ └── src │ │ └── preprocess_hello_dependency.f90 ├── preprocess_per_dependency │ ├── app │ │ └── main.f90 │ ├── crate │ │ └── utils │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ └── say_hello.f90 │ └── fpm.toml ├── program_with_module │ ├── .gitignore │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── shared_app_only │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── test │ │ └── test.f90 ├── shared_lib │ ├── fpm.toml │ └── src │ │ └── shared_lib.f90 ├── shared_lib_empty │ └── fpm.toml ├── shared_lib_extra │ ├── fpm.toml │ └── src │ │ └── shared_lib_extra.f90 ├── static_app_only │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── test │ │ └── test.f90 ├── static_lib_empty │ └── fpm.toml ├── submodule_tree_shake │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ ├── child1.f90 │ │ ├── child2.f90 │ │ ├── child_unused.f90 │ │ ├── grandchild.f90 │ │ ├── parent.f90 │ │ └── parent_unused.f90 ├── submodules │ ├── .gitignore │ ├── fpm.toml │ └── src │ │ ├── child1.f90 │ │ ├── child2.f90 │ │ ├── grandchild.f90 │ │ └── parent.f90 ├── tree_shake │ ├── .gitignore │ ├── app │ │ └── say_Hello.f90 │ ├── fpm.toml │ ├── src │ │ ├── extra_m.f90 │ │ ├── farewell_m.f90 │ │ ├── greet_m.f90 │ │ └── subdir │ │ │ └── constants.f90 │ └── test │ │ └── greet_test.f90 ├── version_file │ ├── VERSION │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── with_c │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ ├── c_code.c │ │ └── with_c.f90 ├── with_examples │ ├── .gitignore │ ├── app │ │ └── demo-prog.f90 │ ├── demo │ │ └── prog.f90 │ └── fpm.toml └── with_makefile │ ├── .gitignore │ ├── Makefile │ ├── fpm.toml │ └── src │ └── hello_makefile.f90 ├── fpm.toml ├── install.sh ├── manifest-reference.md ├── src ├── filesystem_utilities.c ├── fpm.f90 ├── fpm │ ├── cmd │ │ ├── export.f90 │ │ ├── install.f90 │ │ ├── new.f90 │ │ ├── publish.f90 │ │ └── update.f90 │ ├── dependency.f90 │ ├── downloader.f90 │ ├── error.f90 │ ├── fpm_release.F90 │ ├── git.f90 │ ├── installer.f90 │ ├── manifest.f90 │ ├── manifest │ │ ├── build.f90 │ │ ├── dependency.f90 │ │ ├── example.f90 │ │ ├── executable.f90 │ │ ├── fortran.f90 │ │ ├── install.f90 │ │ ├── library.f90 │ │ ├── meta.f90 │ │ ├── package.f90 │ │ ├── preprocess.f90 │ │ ├── profiles.f90 │ │ └── test.f90 │ ├── toml.f90 │ └── versioning.f90 ├── fpm_backend.F90 ├── fpm_backend_console.f90 ├── fpm_backend_output.f90 ├── fpm_command_line.f90 ├── fpm_compile_commands.F90 ├── fpm_compiler.F90 ├── fpm_environment.c ├── fpm_environment.f90 ├── fpm_filesystem.F90 ├── fpm_meta.f90 ├── fpm_model.f90 ├── fpm_os.F90 ├── fpm_os.c ├── fpm_pkg_config.f90 ├── fpm_settings.f90 ├── fpm_source_parsing.f90 ├── fpm_sources.f90 ├── fpm_strings.f90 ├── fpm_targets.f90 ├── metapackage │ ├── fpm_meta_base.f90 │ ├── fpm_meta_blas.f90 │ ├── fpm_meta_hdf5.f90 │ ├── fpm_meta_minpack.f90 │ ├── fpm_meta_mpi.f90 │ ├── fpm_meta_netcdf.f90 │ ├── fpm_meta_openmp.f90 │ ├── fpm_meta_stdlib.f90 │ └── fpm_meta_util.f90 └── ptycheck │ ├── LICENSE │ ├── isatty.c │ ├── iscygpty.c │ └── iscygpty.h └── test ├── cli_test └── cli_test.f90 ├── fpm_test ├── main.f90 ├── test_backend.f90 ├── test_compiler.f90 ├── test_filesystem.f90 ├── test_installer.f90 ├── test_manifest.f90 ├── test_module_dependencies.f90 ├── test_os.f90 ├── test_package_dependencies.f90 ├── test_settings.f90 ├── test_source_parsing.f90 ├── test_toml.f90 ├── test_versioning.f90 └── testsuite.f90 ├── help_test └── help_test.f90 └── new_test └── new_test.f90 /.github/ISSUE_TEMPLATE/01_bug.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Something is not working 3 | labels: [bug] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Description 9 | placeholder: Please include steps to reproduce your issue, provide example code snippets if possible 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: expected 14 | attributes: 15 | label: Expected Behaviour 16 | placeholder: What did you expect to happen instead 17 | validations: 18 | required: true 19 | - type: input 20 | id: fpm-version 21 | attributes: 22 | label: Version of fpm 23 | placeholder: 0.4.0, 04da9a1ce99e8fce1abdb7eb9a2073f3188038ea, ... 24 | validations: 25 | required: true 26 | - type: input 27 | id: platform 28 | attributes: 29 | label: Platform and Architecture 30 | placeholder: MacOS/ARM, Windows, OpenBSD, ... 31 | validations: 32 | required: true 33 | - type: textarea 34 | id: additional 35 | attributes: 36 | label: Additional Information 37 | placeholder: Further relevant context, i.e. links to other issues 38 | validations: 39 | required: false 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/02_packaging.yaml: -------------------------------------------------------------------------------- 1 | name: Packaging Issue 2 | description: Porting or packaging a project to fpm 3 | labels: [packaging] 4 | body: 5 | - type: input 6 | id: project 7 | attributes: 8 | label: Upstream Project 9 | placeholder: URL for the upstream project 10 | validations: 11 | required: true 12 | - type: textarea 13 | id: description 14 | attributes: 15 | label: Description 16 | placeholder: Please describe the issue with porting or packaging a project with fpm 17 | validations: 18 | required: true 19 | - type: input 20 | id: fpm-version 21 | attributes: 22 | label: Version of fpm 23 | placeholder: 0.4.0, 04da9a1ce99e8fce1abdb7eb9a2073f3188038ea, ... 24 | validations: 25 | required: true 26 | - type: textarea 27 | id: additional 28 | attributes: 29 | label: Additional Information 30 | placeholder: Further relevant context, i.e. links to other issues 31 | validations: 32 | required: false 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/03_feature.yaml: -------------------------------------------------------------------------------- 1 | name: Feature request 2 | description: An idea for a new feature 3 | labels: [enhancement] 4 | body: 5 | - type: textarea 6 | id: description 7 | attributes: 8 | label: Description 9 | placeholder: | 10 | Please describe the feature, please provide examples 11 | Use codefences (```) to add literal codeblocks for examples 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: solution 16 | attributes: 17 | label: Possible Solution 18 | placeholder: | 19 | Please describe possible solutions or currently available workarounds if available. 20 | validations: 21 | required: false 22 | - type: textarea 23 | id: additional 24 | attributes: 25 | label: Additional Information 26 | placeholder: Further relevant context, i.e. links to other issues 27 | validations: 28 | required: false 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/04_specification.yaml: -------------------------------------------------------------------------------- 1 | name: Specification Proposal 2 | description: Suggestion for extending the package manifest, command line interface, ... 3 | labels: [specification] 4 | body: 5 | - type: textarea 6 | id: motivation 7 | attributes: 8 | label: Motivation 9 | placeholder: | 10 | What is the purpose of this proposal. Please provide usage examples for the new functionality as well. 11 | validations: 12 | required: true 13 | - type: textarea 14 | id: description 15 | attributes: 16 | label: Specification 17 | placeholder: | 18 | Please provide possible realisations of this proposal, i.e. an example how this would work in the manifest. 19 | 20 | Use code fences to provide the example. 21 | 22 | ```toml 23 | [extra] 24 | fpm = "feature" 25 | ``` 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: prior-art 30 | attributes: 31 | label: Prior Art 32 | placeholder: | 33 | Include links and references to other package manager or build systems if available. 34 | validations: 35 | required: false 36 | - type: textarea 37 | id: additional 38 | attributes: 39 | label: Additional Information 40 | placeholder: Further relevant context, i.e. links to other issues 41 | validations: 42 | required: false 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/05_free.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Free Form 3 | about: If the topic doesn't fit anything above and is not suitable for the lists below 4 | --- 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | contact_links: 2 | - name: Fpm discussion board 3 | url: https://github.com/fortran-lang/fpm/discussions 4 | about: Discussion about fpm related topics 5 | - name: Fortran-lang discourse 6 | url: https://fortran-lang.discourse.group/ 7 | about: Discussion about all things Fortran 8 | - name: Fortran-lang mailing list 9 | url: https://groups.io/g/fortran-lang 10 | about: Mailinglist for the Fortran language 11 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 7 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | name: docs 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build-and-deploy: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: actions/setup-python@v5 11 | with: 12 | python-version: '3.x' 13 | - name: Install dependencies 14 | run: pip install ford 15 | - name: Build Documentation 16 | run: ford docs.md 17 | - uses: JamesIves/github-pages-deploy-action@v4.7.3 18 | if: github.event_name == 'push' && github.repository == 'fortran-lang/fpm' && ( startsWith( github.ref, 'refs/tags/' ) || github.ref == 'refs/heads/main' ) 19 | with: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | BRANCH: gh-pages 22 | FOLDER: fpm-doc 23 | CLEAN: true 24 | 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | 3 | # Visual Studio Code 4 | .vscode/ 5 | 6 | # CodeBlocks 7 | project/ 8 | 9 | # Temporary files 10 | *.swp 11 | 12 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the Fortran Package Manager 2 | 3 | Thank you for considering contributing to the Fortran Package Manager (*fpm*). 4 | Please review and follow these guidelines to make the contribution process 5 | simple and effective for all involved. It will help communicate that you 6 | respect the time of the community developers. In return, the community will 7 | help address your problem, evaluate changes, and guide you through your pull 8 | requests. 9 | 10 | By contributing to *fpm*, you certify that you own or are allowed to share the 11 | content of your contribution under the [fpm license](LICENSE). 12 | 13 | * [Style](#style) 14 | * [Reporting a bug](#reporting-a-bug) 15 | * [Suggesting a feature](#suggesting-a-feature) 16 | * [Workflow](#workflow) 17 | * [General guidelines](#general-guidelines) 18 | * [For new contributors](#for-new-contributors) 19 | 20 | ## Style 21 | 22 | Please follow the 23 | [Fortran stdlib style guide](https://github.com/fortran-lang/stdlib/blob/master/STYLE_GUIDE.md) 24 | for any Fortran code that you contribute. 25 | This allows us to focus on substance rather than style. 26 | 27 | ## Reporting a bug 28 | 29 | A bug is a *demonstrable problem* caused by the code in this repository. 30 | Good bug reports are extremely valuable to us—thank you! 31 | 32 | Before opening a bug report: 33 | 34 | 1. Check if the issue has already been reported 35 | ([issues](https://github.com/fortran-lang/fpm/issues)). 36 | 2. Check if it is still an issue or it has been fixed? 37 | Try to reproduce it with the latest version from the default branch. 38 | 3. Isolate the problem and create a minimal test case. 39 | 40 | A good bug report should include all information needed to reproduce the bug. 41 | Please be as detailed as possible: 42 | 43 | 1. Which version of *fpm* are you using? Please be specific. 44 | 2. What are the steps to reproduce the issue? 45 | 3. What is the expected outcome? 46 | 4. What happens instead? 47 | 48 | This information will help the community diagnose the issue quickly and with 49 | minimal back-and-forth. 50 | 51 | ## Suggesting a feature 52 | 53 | Before suggesting a new feature, take a moment to find out if it fits the scope 54 | of the project, or if it has already been discussed. It is up to you to provide 55 | a strong argument to convince the community of the benefits of this feature. 56 | Please provide as much detail and context as possible. If applicable, include a 57 | mocked-up snippet of what the output or behavior would look like with this 58 | feature implemented. “Crazy”, out-of-the-box ideas are especially welcome. 59 | It’s quite possible that we are not considering an unusually creative solution. 60 | 61 | ## Workflow 62 | 63 | *fpm* is a community project. There is no one single person making final 64 | decisions. This is the workflow that we follow: 65 | 66 | 1. Open a [new issue](https://github.com/fortran-lang/fpm/issues/new) to 67 | describe a bug or propose a new feature. 68 | Refer to the earlier sections on how to write a good bug report or feature 69 | request. 70 | 2. Discuss with the community and reach majority consensus about what should be 71 | done about the bug or feature request. 72 | We define “majority” loosely as 80%. 73 | This means that at least 4 of 5 people engaged in the discussion should be 74 | able to agree on the next step. 75 | This allows us to have the community mostly agree while not getting stuck if 76 | one person disagrees. 77 | At this stage, the scope of the fix/feature, its behavior, and API if 78 | applicable should be defined. 79 | Only when you have community consensus on these items you should proceed to 80 | writing code and opening a PR. 81 | **When actively working on code towards a PR, please assign yourself to the 82 | issue on GitHub.** 83 | This is good collaborative practice to avoid duplicated effort and also 84 | inform others what you are currently working on. 85 | 3. Open a new Pull Request (PR) with your contribution. 86 | The body of the PR should at least include a bullet-point summary of the 87 | changes, and a detailed description is encouraged. 88 | If the PR completely addresses the issue you opened in step 1, include in 89 | the PR description the following line: `Fixes #`. 90 | 4. Request reviewers to your PR. 91 | For small bug fixes or documentation improvements, 1 to 2 reviewers is 92 | sufficient. 93 | For implementation of bigger features, request 3 to 4 or more reviewers. 94 | Ideally, request reviewers that participated in step 2. 95 | 5. If your PR implements a feature that adds or changes the behavior of *fpm*, 96 | your PR must also include appropriate changes to the documentation. 97 | 98 | This workflow can evolve and change over time as we learn how best to work 99 | together. If you have an idea on how to improve the workflow itself, please 100 | open an issue and we’ll discuss it. 101 | 102 | ## General guidelines 103 | 104 | * A PR should implement *only one* feature or bug fix. 105 | * Do not commit changes to files that are irrelevant to your feature or bug fix. 106 | * Smaller PRs are better than large PRs, and will lead to a shorter review and 107 | merge cycle 108 | * Add tests for your feature or bug fix to be sure that it stays functional and useful 109 | * Be open to constructive criticism and requests for improving your code. 110 | * Again, please follow the 111 | [Fortran stdlib style guide](https://github.com/fortran-lang/stdlib/blob/master/STYLE_GUIDE.md). 112 | 113 | ## For new contributors 114 | 115 | If you have never created a pull request before, welcome :tada:. 116 | You can learn how from 117 | [this great tutorial](https://app.egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github). 118 | 119 | Don’t know where to start? 120 | You can start by looking through the list of 121 | [open issues](https://github.com/fortran-lang/fpm/issues). 122 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for fpm 2 | 3 | ## Unreleased changes 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 fpm contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use, intrinsic :: iso_fortran_env, only : error_unit, output_unit 3 | use fpm_command_line, only: & 4 | fpm_cmd_settings, & 5 | fpm_new_settings, & 6 | fpm_build_settings, & 7 | fpm_export_settings, & 8 | fpm_run_settings, & 9 | fpm_test_settings, & 10 | fpm_install_settings, & 11 | fpm_update_settings, & 12 | fpm_clean_settings, & 13 | fpm_publish_settings, & 14 | get_command_line_settings 15 | use fpm_error, only: error_t 16 | use fpm_filesystem, only: exists, parent_dir, join_path 17 | use fpm, only: cmd_build, cmd_run, cmd_clean 18 | use fpm_cmd_install, only: cmd_install 19 | use fpm_cmd_export, only: cmd_export 20 | use fpm_cmd_new, only: cmd_new 21 | use fpm_cmd_update, only : cmd_update 22 | use fpm_cmd_publish, only: cmd_publish 23 | use fpm_os, only: change_directory, get_current_directory 24 | 25 | implicit none 26 | 27 | class(fpm_cmd_settings), allocatable :: cmd_settings 28 | type(error_t), allocatable :: error 29 | character(len=:), allocatable :: pwd_start, pwd_working, working_dir, project_root 30 | 31 | call get_command_line_settings(cmd_settings) 32 | 33 | call get_current_directory(pwd_start, error) 34 | call handle_error(error) 35 | 36 | call get_working_dir(cmd_settings, working_dir) 37 | if (allocated(working_dir)) then 38 | ! Change working directory if requested 39 | if (len_trim(working_dir) > 0) then 40 | call change_directory(working_dir, error) 41 | call handle_error(error) 42 | 43 | call get_current_directory(pwd_working, error) 44 | call handle_error(error) 45 | write(output_unit, '(*(a))') "fpm: Entering directory '"//pwd_working//"'" 46 | else 47 | pwd_working = pwd_start 48 | end if 49 | else 50 | pwd_working = pwd_start 51 | end if 52 | 53 | select type (settings => cmd_settings) 54 | type is (fpm_new_settings) 55 | class default 56 | if (.not.has_manifest(pwd_working)) then 57 | project_root = pwd_working 58 | do while(.not.has_manifest(project_root)) 59 | working_dir = parent_dir(project_root) 60 | if (len(working_dir) == 0) exit 61 | project_root = working_dir 62 | end do 63 | 64 | if (has_manifest(project_root)) then 65 | call change_directory(project_root, error) 66 | call handle_error(error) 67 | write(output_unit, '(*(a))') "fpm: Entering directory '"//project_root//"'" 68 | end if 69 | end if 70 | end select 71 | 72 | select type(settings=>cmd_settings) 73 | type is (fpm_new_settings) 74 | call cmd_new(settings) 75 | type is (fpm_build_settings) 76 | call cmd_build(settings) 77 | type is (fpm_run_settings) 78 | call cmd_run(settings,test=.false.) 79 | type is (fpm_test_settings) 80 | call cmd_run(settings,test=.true.) 81 | type is (fpm_export_settings) 82 | call cmd_export(settings) 83 | type is (fpm_install_settings) 84 | call cmd_install(settings) 85 | type is (fpm_update_settings) 86 | call cmd_update(settings) 87 | type is (fpm_clean_settings) 88 | call cmd_clean(settings) 89 | type is (fpm_publish_settings) 90 | call cmd_publish(settings) 91 | end select 92 | 93 | if (allocated(project_root)) then 94 | write(output_unit, '(*(a))') "fpm: Leaving directory '"//project_root//"'" 95 | end if 96 | 97 | if (pwd_start /= pwd_working) then 98 | write(output_unit, '(*(a))') "fpm: Leaving directory '"//pwd_working//"'" 99 | end if 100 | 101 | contains 102 | 103 | function has_manifest(dir) 104 | character(len=*), intent(in) :: dir 105 | logical :: has_manifest 106 | 107 | has_manifest = exists(join_path(dir, "fpm.toml")) 108 | end function has_manifest 109 | 110 | subroutine handle_error(error_) 111 | type(error_t), optional, intent(in) :: error_ 112 | if (present(error_)) then 113 | write (error_unit, '("[Error]", 1x, a)') error_%message 114 | stop 1 115 | end if 116 | end subroutine handle_error 117 | 118 | !> Save access to working directory in settings, in case setting have not been allocated 119 | subroutine get_working_dir(settings, working_dir_) 120 | class(fpm_cmd_settings), optional, intent(in) :: settings 121 | character(len=:), allocatable, intent(out) :: working_dir_ 122 | if (present(settings)) then 123 | working_dir_ = settings%working_dir 124 | end if 125 | end subroutine get_working_dir 126 | 127 | end program main 128 | -------------------------------------------------------------------------------- /ci/fpm-installer.nsi: -------------------------------------------------------------------------------- 1 | ; NSIS Installer script for the Fortran Package Manager 2 | 3 | ; ---------------- Properties ---------------- 4 | ; Name used in installer GUI 5 | Name "Fortran Package Manager" 6 | 7 | ; Name for folder location and reg key 8 | !define INSTALL_NAME "fortran-lang" 9 | 10 | ; Installer icon 11 | !define MUI_ICON "installer-icon.ico" 12 | 13 | ; Compress installer 14 | SetCompress auto 15 | 16 | ; Always produce unicode installer 17 | Unicode true 18 | 19 | ; ---------------- Setup ---------------- 20 | ; Use EnVar plugin (https://nsis.sourceforge.io/EnVar_plug-in) 21 | !addplugindir ".\EnVar_plugin\Plugins\x86-unicode" 22 | 23 | ; Use the 'Modern' Installer UI macros 24 | !include "MUI2.nsh" 25 | 26 | ; Default installation folder (local) 27 | InstallDir "$LOCALAPPDATA\${INSTALL_NAME}" 28 | 29 | ; Get installation folder from registry if available 30 | InstallDirRegKey HKCU "Software\${INSTALL_NAME}" "" 31 | 32 | ; Request application privileges 33 | RequestExecutionLevel user 34 | 35 | 36 | ; ---------------- Installer Pages ---------------- 37 | !insertmacro MUI_PAGE_COMPONENTS 38 | !insertmacro MUI_PAGE_DIRECTORY 39 | !insertmacro MUI_PAGE_INSTFILES 40 | 41 | 42 | ; ---------------- Uninstaller Pages ---------------- 43 | !insertmacro MUI_UNPAGE_CONFIRM 44 | !insertmacro MUI_UNPAGE_INSTFILES 45 | 46 | 47 | ; MUI Language 48 | !insertmacro MUI_LANGUAGE "English" 49 | 50 | 51 | ; ---------------- Component: Core Installation ---------------- 52 | Section "-Core" SecCore 53 | 54 | SetOutPath "$INSTDIR" 55 | 56 | ; Store installation folder 57 | WriteRegStr HKCU "Software\${INSTALL_NAME}" "" $INSTDIR 58 | 59 | ; Create uninstaller 60 | WriteUninstaller "$INSTDIR\Uninstall.exe" 61 | 62 | ; Add to path 63 | EnVar::SetHKCU 64 | EnVar::AddValue "PATH" "$INSTDIR\fpm" 65 | EnVar::AddValue "PATH" "$INSTDIR\MinGit\mingw64\bin" 66 | 67 | SectionEnd 68 | 69 | 70 | ; ---------------- Component: fpm ---------------- 71 | Section "FPM" SecFPM 72 | 73 | SetOutPath "$INSTDIR\fpm" 74 | 75 | File "fpm.exe" 76 | 77 | SectionEnd 78 | 79 | 80 | ; ---------------- Component: Git ---------------- 81 | Section "Git for Windows" SecGit 82 | 83 | SetOutPath "$INSTDIR" 84 | 85 | File /r "MinGit" 86 | 87 | SectionEnd 88 | 89 | 90 | ; ---------------- Uninstaller ---------------- 91 | Section "Uninstall" 92 | 93 | RMDir /r "$INSTDIR" 94 | 95 | DeleteRegKey /ifempty HKCU "Software\${INSTALL_NAME}" 96 | 97 | EnVar::SetHKCU 98 | EnVar::DeleteValue "PATH" "$INSTDIR\fpm" 99 | EnVar::DeleteValue "PATH" "$INSTDIR\MinGit\mingw64\bin" 100 | 101 | SectionEnd 102 | 103 | 104 | ; ---------------- Component description Strings (EN) ---------------- 105 | LangString DESC_SecFPM ${LANG_ENGLISH} "The Fortran Package Manager" 106 | LangString DESC_SecGit ${LANG_ENGLISH} "Git version control (required for FPM)" 107 | 108 | 109 | !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN 110 | !insertmacro MUI_DESCRIPTION_TEXT ${SecFPM} $(DESC_SecFPM) 111 | !insertmacro MUI_DESCRIPTION_TEXT ${SecGit} $(DESC_SecGit) 112 | !insertmacro MUI_FUNCTION_DESCRIPTION_END 113 | -------------------------------------------------------------------------------- /ci/installer-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fortran-lang/fpm/6908108f5f364ca50e36a01d935967954cdee23e/ci/installer-icon.ico -------------------------------------------------------------------------------- /ci/meta_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # *********************** 5 | # This script tests all example packages using any metapackage/system dependencies 6 | # *********************** 7 | 8 | cd "$(dirname $0)/.." 9 | 10 | if [ "$1" ]; then 11 | fpm="$1" 12 | else 13 | fpm=fpm 14 | fi 15 | 16 | # Build example packages 17 | pushd example_packages/ 18 | rm -rf ./*/build 19 | 20 | pushd metapackage_openmp 21 | "$fpm" build --verbose 22 | "$fpm" run --verbose 23 | popd 24 | 25 | pushd metapackage_stdlib 26 | "$fpm" build --verbose 27 | "$fpm" run --verbose 28 | popd 29 | 30 | pushd metapackage_minpack 31 | "$fpm" build --verbose 32 | "$fpm" run --verbose 33 | popd 34 | 35 | pushd metapackage_mpi 36 | "$fpm" build --verbose 37 | "$fpm" run --verbose 38 | popd 39 | 40 | pushd metapackage_mpi_c 41 | "$fpm" build --verbose 42 | "$fpm" run --verbose 43 | popd 44 | 45 | pushd metapackage_hdf5 46 | "$fpm" build --verbose 47 | "$fpm" run --verbose 48 | popd 49 | 50 | pushd metapackage_netcdf 51 | "$fpm" build --verbose 52 | "$fpm" run --verbose 53 | popd 54 | 55 | pushd metapackage_blas 56 | "$fpm" build --verbose 57 | "$fpm" run --verbose 58 | popd 59 | 60 | pushd metapackage_stdlib_extblas 61 | "$fpm" build --verbose 62 | "$fpm" run --verbose 63 | popd 64 | 65 | # Cleanup 66 | rm -rf ./*/build 67 | -------------------------------------------------------------------------------- /ci/single-file-gfortran.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | output="${OUTPUT:-fpm-single-file.F90}" 4 | 5 | args=("$@") 6 | file=$(printf "%s\n" "${args[@]}" | grep -P '^.+\.[fF]90$') 7 | if [ $? = 0 ]; then 8 | echo " + Appending source file '$file' to '${output}'" 9 | cat $file >> "${output}" 10 | fi 11 | exec gfortran "${args[@]}" 12 | -------------------------------------------------------------------------------- /ci/single-file.patch: -------------------------------------------------------------------------------- 1 | diff --git a/fpm.toml b/fpm.toml 2 | index 51faa0e9..273e8e0e 100644 3 | --- a/fpm.toml 4 | +++ b/fpm.toml 5 | @@ -17,23 +17,5 @@ M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" 6 | jonquil.git = "https://github.com/toml-f/jonquil" 7 | jonquil.rev = "05d30818bb12fb877226ce284b9a3a41b971a889" 8 | 9 | -[[test]] 10 | -name = "cli-test" 11 | -source-dir = "test/cli_test" 12 | -main = "cli_test.f90" 13 | - 14 | -[[test]] 15 | -name = "new-test" 16 | -source-dir = "test/new_test" 17 | -main = "new_test.f90" 18 | - 19 | -[[test]] 20 | -name = "fpm-test" 21 | -source-dir = "test/fpm_test" 22 | -main = "main.f90" 23 | - 24 | -[[test]] 25 | -name = "help-test" 26 | -source-dir = "test/help_test" 27 | -main = "help_test.f90" 28 | - 29 | +[build] 30 | +auto-tests = false 31 | -------------------------------------------------------------------------------- /doc/Contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing Guidelines 3 | --- 4 | 5 | {!CONTRIBUTING.md!} 6 | -------------------------------------------------------------------------------- /doc/License.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: License 3 | --- 4 | 5 | {!LICENSE!} 6 | -------------------------------------------------------------------------------- /doc/Manifest.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Manifest reference 3 | --- 4 | 5 | {!manifest-reference.md!} 6 | 7 | -------------------------------------------------------------------------------- /doc/Packaging.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Packaging with fpm 3 | --- 4 | 5 | {!PACKAGING.md!} 6 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Packaging and contributing 3 | --- 4 | -------------------------------------------------------------------------------- /doc/media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fortran-lang/fpm/6908108f5f364ca50e36a01d935967954cdee23e/doc/media/favicon.ico -------------------------------------------------------------------------------- /docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | project: Fortran-lang/fpm 3 | summary: Fortran Package Manager 4 | project_github: https://github.com/fortran-lang/fpm 5 | project_download: https://github.com/fortran-lang/fpm/archive/main.zip 6 | author: fortran-lang/fpm contributors 7 | author_pic: https://fortran-lang.org/assets/img/fortran_logo_512x512.png 8 | author_email: fortran-lang@groups.io 9 | github: https://github.com/fortran-lang 10 | twitter: https://twitter.com/fortranlang 11 | website: https://fortran-lang.org 12 | src_dir: ./src 13 | ./app 14 | output_dir: ./fpm-doc 15 | page_dir: ./doc 16 | media_dir: ./doc/media 17 | exclude_dir: ./example_packages 18 | ./test 19 | display: public 20 | protected 21 | source: true 22 | proc_internals: true 23 | sort: permission-alpha 24 | favicon: doc/media/favicon.ico 25 | print_creation_date: true 26 | extra_mods: iso_fortran_env:https://gcc.gnu.org/onlinedocs/gfortran/ISO_005fFORTRAN_005fENV.html 27 | tomlf:https://toml-f.github.io/toml-f 28 | M_CLI2:https://github.com/urbanjost/M_CLI2 29 | creation_date: %Y-%m-%d %H:%M %z 30 | md_extensions: markdown.extensions.toc 31 | markdown.extensions.smarty 32 | --- 33 | 34 | [TOC] 35 | 36 | # Fortran package manager developer documentation 37 | 38 | This is the main documentation of the Fortran package manager (*fpm*). 39 | This document serves as developer documentation of *fpm* itself and contains general advice for developing in the *fpm* code base. 40 | 41 | 42 | ## The package manifest 43 | 44 | The central object describing an *fpm* project is the package manifest ``fpm.toml``. 45 | The manifest is written in TOML, you can find the TOML specification at the official [TOML homepage](https://toml.io). 46 | 47 | The ``fpm.toml`` file targets project developers and maintainers to relieve them from writing build files for their packages. 48 | With the package manifest a central place to collect information about the project is provided. 49 | It contains the versioning and licensing meta data, as well as the information on external dependencies and the required build-tools or compiler settings. 50 | 51 | The manifest format specific to *fpm* projects is documented in the [manifest reference](page/Manifest.html). 52 | 53 | @Note For a more practical but less complete guide on creating *fpm* projects see the [packaging guide](page/Packaging.html). 54 | 55 | The details of the TOML parsing are implemented with using the [tomlf](https://toml-f.github.io/toml-f) module. 56 | Generally, the interface to all TOML related functions for *fpm* is found in the proxy module [[fpm_toml]]. 57 | 58 | All the manifest types are bundled in [[fpm_manifest]]. 59 | While the specific subtables for the package configuration are found in the ``src/fpm/manifest`` directory, they should be reexported in the [[fpm_manifest]] module if they should be elsewhere in *fpm*. 60 | 61 | 62 | ## Command line interface 63 | 64 | *fpm* is mainly used as a command line tool. 65 | To work with an *fpm* project as a user you can completely rely on the command line. 66 | 67 | The command line interface is build with the [M_CLI2](https://github.com/urbanjost/M_CLI2) module and can be found in [[fpm_command_line]]. 68 | 69 | 70 | ## The package model 71 | 72 | Once front-end inputs have been received from the package manifest and command line interface, *fpm* will construct an 73 | internal representation of the package and its dependencies. This internal representation is known as the package *model*. 74 | The model and its associated data types should encapsulate all the information required to correctly build a package and 75 | should be independent of the intended backend build system. Information stored in the model includes: build targets and 76 | their inter-dependencies; compiler and compiler flags; library linking information. 77 | 78 | For more information on the contents of the package model and the process for constructing it, please see [[fpm_model]]. 79 | 80 | ## The build backend 81 | 82 | Once a complete package model has been constructed, it can be passed to a *backend* for either performing the compilation 83 | and linking of targets, or for generating configuration files for a third-party build system. 84 | Currently, only a native backend is implemented in *fpm*. See [[fpm_backend]] for more information. 85 | 86 | ## Generating this documentation 87 | 88 | This documentation is generated by [FORD](https://github.com/Fortran-FOSS-Programmers/FORD). 89 | For more details on the [project file](https://github.com/fortran-lang/fpm/docs.md) and the comment markup in the source code visit the [FORD documentation](https://github.com/Fortran-FOSS-Programmers/ford/wiki). 90 | 91 | To regenerate this documentation run: 92 | 93 | ```shell 94 | ford docs.md 95 | ``` 96 | -------------------------------------------------------------------------------- /example_packages/README.md: -------------------------------------------------------------------------------- 1 | # Example packages 2 | 3 | See the table below for a list of the example packages provided in this directory including 4 | the features demonstrated in each package and which versions of fpm are supported. 5 | 6 | | Name | Features | Bootstrap (Haskell) fpm | fpm | 7 | | --------------------------- | ------------------------------------------------------------- | :---------------------: | :-: | 8 | | app_with_c | C files located in app directory (not src) | N | Y | 9 | | app_with_submodule | Submodules located in app directory (not src) | N | Y | 10 | | auto_discovery_off | Default layout with auto-discovery disabled | N | Y | 11 | | c_header_only | C header-only library | N | Y | 12 | | c_includes | C library with c include directory and dependency includes | N | Y | 13 | | circular_example | Local path dependency; circular dependency | Y | Y | 14 | | circular_test | Local path dependency; circular dependency | Y | Y | 15 | | c_main | C App | N | Y | 16 | | c_main_preprocess | C App; propagate command line preprocessor macros to the app | N | Y | 17 | | cpp_files | C++ files get compiled using fpm | N | Y | 18 | | fortran_includes | Fortran library with explicit include directory | Y | N | 19 | | fpm_test_exe_issues | Test parse order of module files and apps | N | Y | 20 | | hello_complex | Non-standard directory layout; multiple tests and executables | Y | Y | 21 | | hello_complex_2 | Auto-discovery of tests and executables with modules | N | Y | 22 | | hello_fpm | App-only; local path dependency | Y | Y | 23 | | hello_fpm_path | Define local path dependencies | N | Y | 24 | | hello_world | App-only | Y | Y | 25 | | link_executable | Link external library to a single executable | N | Y | 26 | | link_external | Link external library | N | Y | 27 | | makefile_complex | External build command (makefile); local path dependency | Y | N | 28 | | preprocess_cpp | Lib only; C preprocessing; Macro parsing | N | Y | 29 | | preprocess_cpp_c | C App; progate macros from fpm.toml to app | N | Y | 30 | | preprocess_cpp_deps | App; cpp preprocessor settings in local path dependency only | N | Y | 31 | | preprocess_hello | App only; Macros remain local to the package | N | Y | 32 | | preprocess_hello_dependency | Lib only; Macros not getting passed here from root | N | Y | 33 | | program_with_module | App-only; module+program in single source file | Y | Y | 34 | | submodules | Lib-only; submodules (3 levels) | N | Y | 35 | | submodule_tree_shake | Test tree-shaking/pruning with submodules dependencies | N | Y | 36 | | tree_shake | Test tree-shaking/pruning of unused module dependencies | N | Y | 37 | | version_file | Read version number from a file in the project root | N | Y | 38 | | with_c | Compile with `c` source files | N | Y | 39 | | with_examples | Example-only | Y | Y | 40 | | with_makefile | External build command (makefile) | Y | N | 41 | -------------------------------------------------------------------------------- /example_packages/app_with_c/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/app_with_c/app/c_code.c: -------------------------------------------------------------------------------- 1 | #include 2 | /* 3 | * Decides whether a given file name is a directory. 4 | * return 1 if file exists and is a directory 5 | * Source (Public domain): https://github.com/urbanjost/M_system 6 | */ 7 | int my_isdir(const char *path) 8 | { 9 | struct stat sb; 10 | return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode); 11 | } -------------------------------------------------------------------------------- /example_packages/app_with_c/app/main.f90: -------------------------------------------------------------------------------- 1 | module with_c 2 | use iso_c_binding, only: c_char, c_int, c_null_char 3 | implicit none 4 | 5 | contains 6 | 7 | function system_isdir(dirname) 8 | ! Source (Public domain): https://github.com/urbanjost/M_system 9 | ! 10 | implicit none 11 | character(len=*), intent(in) :: dirname 12 | logical :: system_isdir 13 | 14 | interface 15 | function c_isdir(dirname) bind(C, name="my_isdir") result(c_ierr) 16 | import c_char, c_int 17 | character(kind=c_char, len=1), intent(in) :: dirname(*) 18 | integer(kind=c_int) :: c_ierr 19 | end function c_isdir 20 | end interface 21 | 22 | system_isdir = c_isdir(trim(dirname)//c_null_char) == 1 23 | 24 | end function system_isdir 25 | 26 | end module with_c 27 | 28 | program with_c_app 29 | use with_c 30 | implicit none 31 | 32 | write (*, *) "isdir('app') = ", system_isdir('app') 33 | write (*, *) "isdir('src') = ", system_isdir('src') 34 | write (*, *) "isdir('test') = ", system_isdir('test') 35 | write (*, *) "isdir('bench') = ", system_isdir('bench') 36 | 37 | end program with_c_app 38 | -------------------------------------------------------------------------------- /example_packages/app_with_c/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_c" 2 | -------------------------------------------------------------------------------- /example_packages/app_with_submodule/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/app_with_submodule/app/app1/child1.f90: -------------------------------------------------------------------------------- 1 | submodule(parent) child1 2 | implicit none 3 | 4 | interface 5 | module function my_fun() result (b) 6 | integer :: b 7 | end function my_fun 8 | end interface 9 | 10 | contains 11 | 12 | module procedure my_sub1 13 | a = my_fun() 14 | end procedure my_sub1 15 | 16 | end submodule child1 -------------------------------------------------------------------------------- /example_packages/app_with_submodule/app/app1/grandchild.f90: -------------------------------------------------------------------------------- 1 | submodule(parent:child1) grandchild 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure my_fun 7 | b = 1 8 | end procedure my_fun 9 | 10 | end submodule grandchild -------------------------------------------------------------------------------- /example_packages/app_with_submodule/app/app1/main1.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use parent 3 | implicit none 4 | 5 | integer :: a 6 | 7 | call my_sub1(a) 8 | 9 | if (a /= 1) then 10 | write(*,*) 'FAILED: Unexpected value of a' 11 | stop 1 12 | end if 13 | 14 | end program test -------------------------------------------------------------------------------- /example_packages/app_with_submodule/app/app2/child2.f90: -------------------------------------------------------------------------------- 1 | submodule(parent) child2 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure my_sub1 7 | a = 2 8 | end procedure my_sub1 9 | 10 | end submodule child2 -------------------------------------------------------------------------------- /example_packages/app_with_submodule/app/app2/main2.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use parent 3 | implicit none 4 | 5 | integer :: a 6 | 7 | call my_sub1(a) 8 | 9 | if (a /= 2) then 10 | write(*,*) 'FAILED: Unexpected value of a' 11 | stop 1 12 | end if 13 | 14 | end program test -------------------------------------------------------------------------------- /example_packages/app_with_submodule/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "app_with_submodule" -------------------------------------------------------------------------------- /example_packages/app_with_submodule/src/parent.f90: -------------------------------------------------------------------------------- 1 | module parent 2 | implicit none 3 | 4 | interface 5 | 6 | module subroutine my_sub1(a) 7 | integer, intent(out) :: a 8 | end subroutine my_sub1 9 | end interface 10 | 11 | end module parent -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | implicit none 3 | 4 | print *, "This program should run." 5 | 6 | end program main 7 | -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/app/unused.f90: -------------------------------------------------------------------------------- 1 | program unused 2 | implicit none 3 | 4 | print *, "This program should NOT run." 5 | 6 | end program unused 7 | -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "auto_discovery_off" 2 | 3 | [build] 4 | auto-executables = false 5 | auto-tests = false 6 | 7 | 8 | [[test]] 9 | name = "my_test" 10 | source-dir="test" 11 | main="my_test.f90" 12 | 13 | -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/test/my_test.f90: -------------------------------------------------------------------------------- 1 | program my_test 2 | implicit none 3 | 4 | print *, "Test passed! That was easy!" 5 | 6 | end program my_test 7 | -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/test/unused_test.f90: -------------------------------------------------------------------------------- 1 | program unused_test 2 | implicit none 3 | 4 | print *, "This program should NOT run." 5 | 6 | end program unused_test 7 | 8 | -------------------------------------------------------------------------------- /example_packages/auto_with_nondefault_main/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | installed/* 3 | -------------------------------------------------------------------------------- /example_packages/auto_with_nondefault_main/README.md: -------------------------------------------------------------------------------- 1 | # auto_with_nondefault_main 2 | Install auto-executable with non-default source name 3 | fpm install --prefix=/path/to/install 4 | -------------------------------------------------------------------------------- /example_packages/auto_with_nondefault_main/app/non_default_name.f90: -------------------------------------------------------------------------------- 1 | program main 2 | print *, 'hello, world!' 3 | end program main 4 | -------------------------------------------------------------------------------- /example_packages/auto_with_nondefault_main/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "auto_with_nondefault_main" 2 | [build] 3 | auto-executables = true 4 | [library] 5 | 6 | -------------------------------------------------------------------------------- /example_packages/c_header_only/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/c_header_only/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c_header_only" 2 | -------------------------------------------------------------------------------- /example_packages/c_header_only/include/c_header.h: -------------------------------------------------------------------------------- 1 | int printf ( const char * format, ... ); 2 | -------------------------------------------------------------------------------- /example_packages/c_includes/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/c_includes/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c_includes" 2 | 3 | [dependencies] 4 | c_header_only = { path = "../c_header_only"} 5 | -------------------------------------------------------------------------------- /example_packages/c_includes/include/lib.h: -------------------------------------------------------------------------------- 1 | // Include from "c_header_only" dependency 2 | #include "c_header.h" 3 | 4 | int test(const int a); 5 | -------------------------------------------------------------------------------- /example_packages/c_includes/src/lib.c: -------------------------------------------------------------------------------- 1 | #include "lib.h" 2 | 3 | int test(const int a){ 4 | 5 | return printf("input: %d\n", a); 6 | 7 | } -------------------------------------------------------------------------------- /example_packages/c_main/app/main.c: -------------------------------------------------------------------------------- 1 | int 2 | main (void) 3 | { 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /example_packages/c_main/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c-main" 2 | 3 | [[executable]] 4 | name = "c-main" 5 | main = "main.c" 6 | -------------------------------------------------------------------------------- /example_packages/c_main_preprocess/app/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "val.h" 4 | 5 | int main() { 6 | printf("%d\n", variable); 7 | } 8 | -------------------------------------------------------------------------------- /example_packages/c_main_preprocess/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c_main_preprocess" 2 | [library] 3 | include-dir = ["src"] 4 | 5 | [[executable]] 6 | name="main-c" 7 | main="main.c" 8 | -------------------------------------------------------------------------------- /example_packages/c_main_preprocess/src/stub.c: -------------------------------------------------------------------------------- 1 | #include "val.h" 2 | -------------------------------------------------------------------------------- /example_packages/c_main_preprocess/src/val.h: -------------------------------------------------------------------------------- 1 | #ifndef _VAL_H_ 2 | #define _VAL_H_ 3 | 4 | #ifdef VAL 5 | const int variable = 1; 6 | #endif 7 | #endif 8 | -------------------------------------------------------------------------------- /example_packages/circular_example/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/circular_example/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "circular_example" 2 | 3 | [dev-dependencies] 4 | circular_test = { path = "../circular_test" } 5 | -------------------------------------------------------------------------------- /example_packages/circular_example/src/greet_m.f90: -------------------------------------------------------------------------------- 1 | module greet_m 2 | implicit none 3 | private 4 | 5 | public :: make_greeting 6 | contains 7 | function make_greeting(name) result(greeting) 8 | character(len=*), intent(in) :: name 9 | character(len=:), allocatable :: greeting 10 | 11 | greeting = "Hello, " // name // "!" 12 | end function make_greeting 13 | end module greet_m 14 | -------------------------------------------------------------------------------- /example_packages/circular_example/test/main.f90: -------------------------------------------------------------------------------- 1 | program run_tests 2 | use hello_test, only: run_test 3 | 4 | implicit none 5 | 6 | call run_test 7 | end program run_tests 8 | -------------------------------------------------------------------------------- /example_packages/circular_test/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/circular_test/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "circular_test" 2 | 3 | [dependencies] 4 | circular_example = { path = "../circular_example"} 5 | -------------------------------------------------------------------------------- /example_packages/circular_test/src/hello_test.f90: -------------------------------------------------------------------------------- 1 | module hello_test 2 | use greet_m, only: make_greeting 3 | 4 | implicit none 5 | private 6 | 7 | public :: run_test 8 | contains 9 | subroutine run_test 10 | print *, make_greeting("from test") 11 | end subroutine run_test 12 | end module hello_test 13 | -------------------------------------------------------------------------------- /example_packages/cpp_files/README.md: -------------------------------------------------------------------------------- 1 | # cpp_files 2 | My cool new project! 3 | -------------------------------------------------------------------------------- /example_packages/cpp_files/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "cpp_files" 2 | -------------------------------------------------------------------------------- /example_packages/cpp_files/src/cpp_files.f90: -------------------------------------------------------------------------------- 1 | module cpp_files 2 | use, intrinsic :: ISO_C_Binding 3 | implicit none 4 | private 5 | 6 | public :: intvec_maxval 7 | 8 | interface 9 | integer function intvec_maxval(array, n) bind(C, name = "intvec_maxval") 10 | import :: c_int, c_size_t 11 | integer(c_int), intent(in) :: array(*) 12 | integer(c_size_t), intent(in), value :: n 13 | end function intvec_maxval 14 | end interface 15 | end module cpp_files 16 | -------------------------------------------------------------------------------- /example_packages/cpp_files/src/hello_world.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" { 5 | 6 | int intvec_maxval(int* array, size_t n){ 7 | 8 | std::vector vec(array, array + n); 9 | 10 | return *(std::max_element(vec.begin(), vec.end())); 11 | 12 | } 13 | 14 | } -------------------------------------------------------------------------------- /example_packages/cpp_files/test/check.f90: -------------------------------------------------------------------------------- 1 | program check 2 | use iso_c_binding, only: c_size_t 3 | use cpp_files 4 | implicit none 5 | 6 | integer :: i, max_element 7 | integer, parameter :: array(*) = [(i,i=-50,10)] 8 | 9 | max_element = intvec_maxval(array,size(array,1,c_size_t)) 10 | 11 | if (max_element == maxval(array)) then 12 | write(*,*) ' PASSED: Max element is ',max_element 13 | else 14 | write(*,*) ' (!) FAILED: Incorrect max element returned' 15 | stop 1 16 | end if 17 | 18 | end program check 19 | -------------------------------------------------------------------------------- /example_packages/dependency_priority/README.md: -------------------------------------------------------------------------------- 1 | # dependency_tree 2 | Check dependency tree cascade. "Standard" fpm dependencies feature that the highest-priority one wins 3 | (i.e., top-level dependencies in the manifest, or the first time it's found down the dependency tree) 4 | Check this behavior is confirmed. 5 | -------------------------------------------------------------------------------- /example_packages/dependency_priority/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use tomlf_version, only: tomlf_version_string 3 | implicit none 4 | 5 | print *, 'using version =',tomlf_version_string 6 | print *, 'should be =0.3.1' 7 | 8 | if (tomlf_version_string=="0.3.1") then 9 | stop 0 10 | else 11 | stop 1 12 | endif 13 | 14 | end program main 15 | -------------------------------------------------------------------------------- /example_packages/dependency_priority/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "dependency_tree" 2 | version = "0.1.0" 3 | [build] 4 | auto-executables=true 5 | [dependencies] 6 | # Request toml-f v0.3.1. 7 | toml-f.git = "https://github.com/toml-f/toml-f" 8 | toml-f.tag = "v0.3.1" 9 | # jonquil 0.2.0 requires toml-f v0.4.0. 10 | # Because 0.4.0 is a derived dependency, it should not be used 11 | jonquil.git = "https://github.com/toml-f/jonquil" 12 | jonquil.tag = "v0.2.0" 13 | -------------------------------------------------------------------------------- /example_packages/fixed-form/app/main.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use lib 3 | call hello 4 | end 5 | -------------------------------------------------------------------------------- /example_packages/fixed-form/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "fixed-form" 2 | fortran.source-form = "fixed" 3 | -------------------------------------------------------------------------------- /example_packages/fixed-form/src/lib.f90: -------------------------------------------------------------------------------- 1 | module lib 2 | contains 3 | subroutine h e l l o 4 | print '(a)', 5 | +"Hello, fixed world!" 6 | end subroutine 7 | end module 8 | -------------------------------------------------------------------------------- /example_packages/fortran_includes/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/fortran_includes/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "fortran_includes" 2 | 3 | [library] 4 | include-dir = "inc" 5 | -------------------------------------------------------------------------------- /example_packages/fortran_includes/inc/parameters.f90: -------------------------------------------------------------------------------- 1 | integer, parameter :: dp = kind(0.d0) -------------------------------------------------------------------------------- /example_packages/fortran_includes/src/lib.f90: -------------------------------------------------------------------------------- 1 | module test_mod 2 | implicit none 3 | 4 | include "parameters.f90" 5 | 6 | contains 7 | 8 | subroutine test_sub(a) 9 | real(dp), intent(in) :: a 10 | 11 | write(*,*) 'a: ', a 12 | end subroutine test_sub 13 | 14 | end module test_mod -------------------------------------------------------------------------------- /example_packages/fpm_test_exe_issues/fpm.toml: -------------------------------------------------------------------------------- 1 | # See https://github.com/fortran-lang/fpm/issues/734 2 | name = "fpm-test" 3 | 4 | [build] 5 | auto-executables = true 6 | auto-tests = true 7 | auto-examples = true 8 | 9 | [install] 10 | library = false 11 | 12 | [[executable]] 13 | name = "main" 14 | source-dir = "src" 15 | main = "main.f90" -------------------------------------------------------------------------------- /example_packages/fpm_test_exe_issues/src/a/a_mod.f90: -------------------------------------------------------------------------------- 1 | module a_mod 2 | use b_mod, only: hello_world 3 | 4 | contains 5 | 6 | subroutine a_mod_sub() 7 | call hello_world() 8 | end subroutine 9 | 10 | end module 11 | -------------------------------------------------------------------------------- /example_packages/fpm_test_exe_issues/src/b_mod.f90: -------------------------------------------------------------------------------- 1 | module b_mod 2 | implicit none 3 | 4 | contains 5 | 6 | subroutine hello_world() 7 | print *, "Hello world!" 8 | end subroutine 9 | 10 | end module 11 | -------------------------------------------------------------------------------- /example_packages/fpm_test_exe_issues/src/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use a_mod 3 | implicit none 4 | 5 | call a_mod_sub() 6 | end program 7 | -------------------------------------------------------------------------------- /example_packages/fpm_test_exit_code/README.md: -------------------------------------------------------------------------------- 1 | # fpm_test_exit_code 2 | Test program for application exit codes 3 | see https://github.com/fortran-lang/fpm/issues/848 4 | 5 | This app expects to receive an integer command line argument, to check whether it is odd or even. 6 | It returns 0 on success (odd input), or among a few error codes otherwise. 7 | -------------------------------------------------------------------------------- /example_packages/fpm_test_exit_code/app/main.f90: -------------------------------------------------------------------------------- 1 | ! Test program for application exit codes 2 | ! see https://github.com/fortran-lang/fpm/issues/848 3 | 4 | ! This app expects to receive an integer command line argument, to check whether it is odd or even. 5 | ! It returns 0 on success (odd input), or among a few error codes otherwise. 6 | 7 | program check_odd_number 8 | implicit none 9 | 10 | integer, parameter :: SUCCESS = 0 11 | integer, parameter :: INVALID_ARGUMENT = 1 12 | integer, parameter :: NOT_AN_INTEGER = 2 13 | integer, parameter :: NOT_ODD = 3 14 | 15 | character(len=1024) :: buffer 16 | integer :: ierr,ln,the_number 17 | 18 | ! If the argument is missing or not an integer, return an error flag 19 | if (command_argument_count()/=1) stop INVALID_ARGUMENT 20 | 21 | ! Get command argument 22 | call get_command_argument(1,value=buffer,length=ln,status=ierr) 23 | 24 | ! On invalid string 25 | if (ln<1 .or. ierr/=0) stop INVALID_ARGUMENT 26 | 27 | ! Read to int 28 | read(buffer(:ln),*,iostat=ierr) the_number 29 | 30 | ! On invalid integer 31 | if (ierr/=0) stop NOT_AN_INTEGER 32 | 33 | ! Check if it is odd or even 34 | if (mod(the_number,2)==0) then 35 | ! Is even 36 | stop NOT_ODD 37 | else 38 | ! Is odd 39 | stop SUCCESS 40 | end if 41 | 42 | end program check_odd_number 43 | -------------------------------------------------------------------------------- /example_packages/fpm_test_exit_code/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "fpm_test_exit_code" 2 | version = "0.1.0" 3 | license = "license" 4 | author = "Federico Perini" 5 | maintainer = "jane.doe@example.com" 6 | copyright = "Copyright 2023, Jane Doe" 7 | [build] 8 | auto-executables = true 9 | [install] 10 | library = false 11 | -------------------------------------------------------------------------------- /example_packages/free-form/app/main.f: -------------------------------------------------------------------------------- 1 | program test 2 | use lib 3 | call hello 4 | end 5 | -------------------------------------------------------------------------------- /example_packages/free-form/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "free-form" 2 | fortran.source-form = "free" 3 | executable = [{main="main.f", name="free-form"}] 4 | -------------------------------------------------------------------------------- /example_packages/free-form/src/lib.f: -------------------------------------------------------------------------------- 1 | module lib 2 | contains 3 | subroutine hello 4 | print '(a)', "Hello, free world!" 5 | end subroutine 6 | end module 7 | -------------------------------------------------------------------------------- /example_packages/hello_complex/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_complex/apps/say_goodbye/say_goodbye.f90: -------------------------------------------------------------------------------- 1 | program say_goodbye 2 | use farewell_m, only: make_farewell 3 | 4 | implicit none 5 | 6 | print *, make_farewell("World") 7 | end program say_goodbye 8 | -------------------------------------------------------------------------------- /example_packages/hello_complex/apps/say_hello/say_Hello.f90: -------------------------------------------------------------------------------- 1 | program say_Hello 2 | use greet_m, only: make_greeting 3 | 4 | implicit none 5 | 6 | print *, make_greeting("World") 7 | end program say_Hello 8 | -------------------------------------------------------------------------------- /example_packages/hello_complex/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_complex" 2 | 3 | [library] 4 | source-dir="source" 5 | 6 | [[executable]] 7 | name="say_Hello" 8 | source-dir="apps/say_hello" 9 | main="say_Hello.f90" 10 | 11 | [[executable]] 12 | name="say_goodbye" 13 | source-dir="apps/say_goodbye" 14 | main="say_goodbye.f90" 15 | 16 | [[test]] 17 | name="greet_test" 18 | source-dir="tests/greet" 19 | main="greet_test.f90" 20 | 21 | [[test]] 22 | name="farewell_test" 23 | source-dir="tests/farewell" 24 | main="farewell_test.f90" 25 | -------------------------------------------------------------------------------- /example_packages/hello_complex/source/farewell_m.f90: -------------------------------------------------------------------------------- 1 | module farewell_m 2 | use subdir_constants, only: FAREWELL_STR 3 | implicit none 4 | private 5 | 6 | public :: make_farewell 7 | contains 8 | function make_farewell(name) result(greeting) 9 | character(len=*), intent(in) :: name 10 | character(len=:), allocatable :: greeting 11 | 12 | greeting = FAREWELL_STR // name // "!" 13 | end function make_farewell 14 | end module farewell_m 15 | -------------------------------------------------------------------------------- /example_packages/hello_complex/source/greet_m.f90: -------------------------------------------------------------------------------- 1 | module greet_m 2 | use subdir_constants, only: GREET_STR 3 | implicit none 4 | private 5 | 6 | public :: make_greeting 7 | contains 8 | function make_greeting(name) result(greeting) 9 | character(len=*), intent(in) :: name 10 | character(len=:), allocatable :: greeting 11 | 12 | greeting = GREET_STR // name // "!" 13 | end function make_greeting 14 | end module greet_m 15 | -------------------------------------------------------------------------------- /example_packages/hello_complex/source/subdir/constants.f90: -------------------------------------------------------------------------------- 1 | module subdir_constants 2 | implicit none 3 | 4 | character(*), parameter :: GREET_STR = 'Hello, ' 5 | character(*), parameter :: FAREWELL_STR = 'Goodbye, ' 6 | 7 | end module subdir_constants 8 | -------------------------------------------------------------------------------- /example_packages/hello_complex/tests/farewell/farewell_test.f90: -------------------------------------------------------------------------------- 1 | program farewell_test 2 | use farewell_m, only: make_farewell 3 | use iso_fortran_env, only: error_unit, output_unit 4 | 5 | implicit none 6 | 7 | character(len=:), allocatable :: farewell 8 | 9 | allocate(character(len=0) :: farewell) 10 | farewell = make_farewell("World") 11 | 12 | if (farewell == "Goodbye, World!") then 13 | write(output_unit, *) "Passed" 14 | else 15 | write(error_unit, *) "Failed" 16 | call exit(1) 17 | end if 18 | end program farewell_test 19 | -------------------------------------------------------------------------------- /example_packages/hello_complex/tests/greet/greet_test.f90: -------------------------------------------------------------------------------- 1 | program greet_test 2 | use greet_m, only: make_greeting 3 | use iso_fortran_env, only: error_unit, output_unit 4 | 5 | implicit none 6 | 7 | character(len=:), allocatable :: greeting 8 | 9 | allocate(character(len=0) :: greeting) 10 | greeting = make_greeting("World") 11 | 12 | if (greeting == "Hello, World!") then 13 | write(output_unit, *) "Passed" 14 | else 15 | write(error_unit, *) "Failed" 16 | call exit(1) 17 | end if 18 | end program greet_test 19 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/app/app_mod.f90: -------------------------------------------------------------------------------- 1 | module app_mod 2 | implicit none 3 | 4 | 5 | end module app_mod 6 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/app/say_goodbye.f90: -------------------------------------------------------------------------------- 1 | program say_goodbye 2 | use farewell_m, only: make_farewell 3 | use app_mod 4 | 5 | implicit none 6 | 7 | print *, make_farewell("World") 8 | end program say_goodbye 9 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/app/say_hello/app_extra_mod.f90: -------------------------------------------------------------------------------- 1 | module app_extra_mod 2 | implicit none 3 | 4 | character(len=5) :: greet_object = "World" 5 | 6 | end module app_extra_mod 7 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/app/say_hello/app_hello_mod.f90: -------------------------------------------------------------------------------- 1 | module app_hello_mod 2 | use app_extra_mod, only: greet_object 3 | implicit none 4 | 5 | integer :: hello_int = 42 6 | 7 | end module app_hello_mod 8 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/app/say_hello/say_Hello.f90: -------------------------------------------------------------------------------- 1 | program say_Hello 2 | use greet_m, only: make_greeting 3 | use app_hello_mod, only: greet_object 4 | 5 | implicit none 6 | 7 | print *, make_greeting(greet_object) 8 | end program say_Hello 9 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_complex" 2 | 3 | [[executable]] 4 | name="say_hello_world" 5 | source-dir="app/say_hello" 6 | main="say_Hello.f90" 7 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/src/farewell_m.f90: -------------------------------------------------------------------------------- 1 | module farewell_m 2 | implicit none 3 | private 4 | 5 | public :: make_farewell 6 | contains 7 | function make_farewell(name) result(greeting) 8 | character(len=*), intent(in) :: name 9 | character(len=:), allocatable :: greeting 10 | 11 | greeting = "Goodbye, " // name // "!" 12 | end function make_farewell 13 | end module farewell_m 14 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/src/greet_m.f90: -------------------------------------------------------------------------------- 1 | module greet_m 2 | implicit none 3 | private 4 | 5 | public :: make_greeting 6 | contains 7 | function make_greeting(name) result(greeting) 8 | character(len=*), intent(in) :: name 9 | character(len=:), allocatable :: greeting 10 | 11 | greeting = "Hello, " // name // "!" 12 | end function make_greeting 13 | end module greet_m 14 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/test/farewell_test.f90: -------------------------------------------------------------------------------- 1 | program farewell_test 2 | use farewell_m, only: make_farewell 3 | use test_mod 4 | use iso_fortran_env, only: error_unit, output_unit 5 | 6 | implicit none 7 | 8 | character(len=:), allocatable :: farewell 9 | 10 | allocate(character(len=0) :: farewell) 11 | farewell = make_farewell("World") 12 | 13 | if (farewell == "Goodbye, World!") then 14 | write(output_unit, *) "Passed" 15 | else 16 | write(error_unit, *) "Failed" 17 | call exit(1) 18 | end if 19 | end program farewell_test 20 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/test/greet_test.f90: -------------------------------------------------------------------------------- 1 | program greet_test 2 | use greet_m, only: make_greeting 3 | use test_mod 4 | use iso_fortran_env, only: error_unit, output_unit 5 | 6 | implicit none 7 | 8 | character(len=:), allocatable :: greeting 9 | 10 | allocate(character(len=0) :: greeting) 11 | greeting = make_greeting("World") 12 | 13 | if (greeting == "Hello, World!") then 14 | write(output_unit, *) "Passed" 15 | else 16 | write(error_unit, *) "Failed" 17 | call exit(1) 18 | end if 19 | end program greet_test 20 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/test/test_mod.f90: -------------------------------------------------------------------------------- 1 | module test_mod 2 | implicit none 3 | 4 | 5 | end module test_mod 6 | -------------------------------------------------------------------------------- /example_packages/hello_fpm/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_fpm/app/main.f90: -------------------------------------------------------------------------------- 1 | program hello_fpm 2 | use farewell_m, only: make_farewell 3 | use greet_m, only: make_greeting 4 | 5 | implicit none 6 | 7 | print *, make_greeting("fpm") 8 | print *, make_farewell("fpm") 9 | end program hello_fpm 10 | -------------------------------------------------------------------------------- /example_packages/hello_fpm/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_fpm" 2 | 3 | [dependencies] 4 | hello_complex = { path = "../hello_complex" } 5 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/app/main.f90: -------------------------------------------------------------------------------- 1 | program hello_fpm 2 | use utils1_m, only: say_hello1 3 | use utils1_1_m, only: say_hello1_1 4 | use utils2_m, only: say_hello2 5 | 6 | call say_hello1() 7 | call say_hello1_1() 8 | call say_hello2() 9 | 10 | end program hello_fpm 11 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils1/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils1" 2 | 3 | [dependencies] 4 | utils1_1 = { path = "../utils1_1" } 5 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils1/src/say_hello.f90: -------------------------------------------------------------------------------- 1 | module utils1_m 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | subroutine say_hello1() 8 | print '(a)', "Hello, utils1." 9 | end subroutine say_hello1 10 | 11 | end module utils1_m 12 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils1_1/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils1_1" 2 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils1_1/src/say_hello.f90: -------------------------------------------------------------------------------- 1 | module utils1_1_m 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | subroutine say_hello1_1() 8 | print '(a)', "Hello, utils1_1." 9 | end subroutine say_hello1_1 10 | 11 | end module utils1_1_m 12 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils2/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils2" 2 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils2/src/say_hello.f90: -------------------------------------------------------------------------------- 1 | module utils2_m 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | subroutine say_hello2() 8 | print '(a)', "Hello, utils2." 9 | end subroutine say_hello2 10 | 11 | end module utils2_m 12 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_fpm_path" 2 | 3 | [dependencies] 4 | utils1 = { path = "crate/utils1" } 5 | utils2 = { path = "crate/utils2" } 6 | -------------------------------------------------------------------------------- /example_packages/hello_world/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_world/app/main.f90: -------------------------------------------------------------------------------- 1 | program hello_world 2 | print *, "Hello, World!" 3 | end program hello_world 4 | -------------------------------------------------------------------------------- /example_packages/hello_world/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_world" 2 | -------------------------------------------------------------------------------- /example_packages/implicit-external/app/main.f90: -------------------------------------------------------------------------------- 1 | program test 2 | integer :: ijk 3 | call impl(ijk) 4 | if (ijk /= 1) error stop 5 | end program test 6 | -------------------------------------------------------------------------------- /example_packages/implicit-external/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "implicit-external" 2 | fortran.implicit-external = true 3 | -------------------------------------------------------------------------------- /example_packages/implicit-external/src/impl.f90: -------------------------------------------------------------------------------- 1 | subroutine impl(ijk) 2 | integer :: ijk 3 | ijk = 1 4 | end subroutine impl 5 | -------------------------------------------------------------------------------- /example_packages/implicit-typing/app/main.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use impl 3 | if (ijk /= 1) error stop 4 | end program 5 | -------------------------------------------------------------------------------- /example_packages/implicit-typing/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "implicit-typing" 2 | fortran.implicit-typing = true 3 | -------------------------------------------------------------------------------- /example_packages/implicit-typing/src/impl.f90: -------------------------------------------------------------------------------- 1 | module impl 2 | parameter(ijk = 1) 3 | end module 4 | -------------------------------------------------------------------------------- /example_packages/link_executable/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/link_executable/app/main.f90: -------------------------------------------------------------------------------- 1 | program gomp_example 2 | implicit none 3 | 4 | interface 5 | integer function omp_get_num_procs() 6 | end function 7 | end interface 8 | 9 | print *, omp_get_num_procs() 10 | 11 | end program gomp_example 12 | -------------------------------------------------------------------------------- /example_packages/link_executable/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "link_executable" 2 | 3 | [[executable]] 4 | name = "gomp_test" 5 | source-dir = "app" 6 | main = "main.f90" 7 | link = ["gomp"] 8 | -------------------------------------------------------------------------------- /example_packages/link_executable/include/test.f90: -------------------------------------------------------------------------------- 1 | real, parameter :: a = 2.0 2 | -------------------------------------------------------------------------------- /example_packages/link_external/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/link_external/app/main.f90: -------------------------------------------------------------------------------- 1 | program test_blas 2 | use wrapped_gemv, only : sp, gemv 3 | implicit none 4 | 5 | integer :: i, j 6 | real(sp) :: mat(4, 4), vec(4), res(4) 7 | 8 | do i = 1, size(vec) 9 | vec(i) = sqrt(real(i, sp)) 10 | end do 11 | 12 | do i = 1, size(mat, 2) 13 | do j = 1, size(mat, 1) 14 | mat(j, i) = sqrt(real(j * i, sp)) 15 | end do 16 | end do 17 | 18 | call gemv(mat, vec, res, alpha=-1.0_sp, trans='t') 19 | 20 | end program test_blas 21 | 22 | -------------------------------------------------------------------------------- /example_packages/link_external/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "link_external" 2 | 3 | [build] 4 | link = "blas" 5 | -------------------------------------------------------------------------------- /example_packages/link_external/src/wrapped_gemv.f90: -------------------------------------------------------------------------------- 1 | !> Performs one of the matrix-vector operations 2 | !> 3 | !> y := alpha*A*x + beta*y, or y := alpha*A**T*x + beta*y, 4 | !> 5 | !> where alpha and beta are scalars, x and y are vectors and A is an 6 | !> m by n matrix. 7 | module wrapped_gemv 8 | implicit none 9 | private 10 | 11 | public :: sp, dp, gemv 12 | 13 | integer, parameter :: sp = selected_real_kind(6) 14 | integer, parameter :: dp = selected_real_kind(15) 15 | 16 | 17 | interface gemv 18 | module procedure :: wrap_sgemv 19 | module procedure :: wrap_dgemv 20 | end interface gemv 21 | 22 | 23 | interface blas_gemv 24 | subroutine sgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) 25 | import :: sp 26 | real(sp), intent(in) :: a(lda, *) 27 | real(sp), intent(in) :: x(*) 28 | real(sp), intent(inout) :: y(*) 29 | real(sp), intent(in) :: alpha 30 | real(sp), intent(in) :: beta 31 | character(len=1), intent(in) :: trans 32 | integer, intent(in) :: incx 33 | integer, intent(in) :: incy 34 | integer, intent(in) :: m 35 | integer, intent(in) :: n 36 | integer, intent(in) :: lda 37 | end subroutine sgemv 38 | subroutine dgemv(trans, m, n, alpha, a, lda, x, incx, beta, y, incy) 39 | import :: dp 40 | real(dp), intent(in) :: a(lda, *) 41 | real(dp), intent(in) :: x(*) 42 | real(dp), intent(inout) :: y(*) 43 | real(dp), intent(in) :: alpha 44 | real(dp), intent(in) :: beta 45 | character(len=1), intent(in) :: trans 46 | integer, intent(in) :: incx 47 | integer, intent(in) :: incy 48 | integer, intent(in) :: m 49 | integer, intent(in) :: n 50 | integer, intent(in) :: lda 51 | end subroutine dgemv 52 | end interface blas_gemv 53 | 54 | 55 | contains 56 | 57 | 58 | subroutine wrap_sgemv(amat, xvec, yvec, alpha, beta, trans) 59 | real(sp), intent(in) :: amat(:, :) 60 | real(sp), intent(in) :: xvec(:) 61 | real(sp), intent(inout) :: yvec(:) 62 | real(sp), intent(in), optional :: alpha 63 | real(sp), intent(in), optional :: beta 64 | character(len=1), intent(in), optional :: trans 65 | real(sp) :: a, b 66 | character(len=1) :: tra 67 | integer :: incx, incy, m, n, lda 68 | if (present(alpha)) then 69 | a = alpha 70 | else 71 | a = 1.0_sp 72 | end if 73 | if (present(beta)) then 74 | b = beta 75 | else 76 | b = 0 77 | end if 78 | if (present(trans)) then 79 | tra = trans 80 | else 81 | tra = 'n' 82 | end if 83 | incx = 1 84 | incy = 1 85 | lda = max(1, size(amat, 1)) 86 | m = size(amat, 1) 87 | n = size(amat, 2) 88 | call blas_gemv(tra, m, n, a, amat, lda, xvec, incx, b, yvec, incy) 89 | end subroutine wrap_sgemv 90 | 91 | 92 | subroutine wrap_dgemv(amat, xvec, yvec, alpha, beta, trans) 93 | real(dp), intent(in) :: amat(:, :) 94 | real(dp), intent(in) :: xvec(:) 95 | real(dp), intent(inout) :: yvec(:) 96 | real(dp), intent(in), optional :: alpha 97 | real(dp), intent(in), optional :: beta 98 | character(len=1), intent(in), optional :: trans 99 | real(dp) :: a, b 100 | character(len=1) :: tra 101 | integer :: incx, incy, m, n, lda 102 | if (present(alpha)) then 103 | a = alpha 104 | else 105 | a = 1.0_dp 106 | end if 107 | if (present(beta)) then 108 | b = beta 109 | else 110 | b = 0 111 | end if 112 | if (present(trans)) then 113 | tra = trans 114 | else 115 | tra = 'n' 116 | end if 117 | incx = 1 118 | incy = 1 119 | lda = max(1, size(amat, 1)) 120 | m = size(amat, 1) 121 | n = size(amat, 2) 122 | call blas_gemv(tra, m, n, a, amat, lda, xvec, incx, b, yvec, incy) 123 | end subroutine wrap_dgemv 124 | 125 | 126 | end module wrapped_gemv 127 | -------------------------------------------------------------------------------- /example_packages/makefile_complex/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/makefile_complex/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE_FLAGS = $(addprefix -I,$(INCLUDE_DIRS)) 2 | 3 | $(BUILD_DIR)/libmakefile_complex.a: $(BUILD_DIR)/wrapper_mod.o 4 | ar rs $(@) $(^) 5 | 6 | $(BUILD_DIR)/wrapper_mod.mod: src/wrapper_mod.f90 7 | 8 | $(BUILD_DIR)/wrapper_mod.o: src/wrapper_mod.f90 9 | $(FC) -c -J$(BUILD_DIR) $(INCLUDE_FLAGS) $(FFLAGS) -o $(@) $(<) 10 | -------------------------------------------------------------------------------- /example_packages/makefile_complex/app/main.f90: -------------------------------------------------------------------------------- 1 | program makefile_complex 2 | use wrapper_mod, only: do_stuff 3 | 4 | implicit none 5 | 6 | call do_stuff 7 | end program makefile_complex 8 | -------------------------------------------------------------------------------- /example_packages/makefile_complex/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "makefile_complex" 2 | 3 | [dependencies] 4 | with_makefile = { path = "../with_makefile" } 5 | 6 | [library] 7 | source-dir = "src" 8 | build-script = "Makefile" 9 | -------------------------------------------------------------------------------- /example_packages/makefile_complex/src/wrapper_mod.f90: -------------------------------------------------------------------------------- 1 | module wrapper_mod 2 | use hello_makefile, only: say_hello_from_makefile 3 | 4 | implicit none 5 | private 6 | 7 | public :: do_stuff 8 | contains 9 | subroutine do_stuff 10 | call say_hello_from_makefile 11 | end subroutine do_stuff 12 | end module wrapper_mod 13 | -------------------------------------------------------------------------------- /example_packages/many_examples/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | -------------------------------------------------------------------------------- /example_packages/many_examples/app/demo-prog.f90: -------------------------------------------------------------------------------- 1 | program demo 2 | write(*, '(a)') "This is a simple program" 3 | end program demo 4 | -------------------------------------------------------------------------------- /example_packages/many_examples/demo1/prog.f90: -------------------------------------------------------------------------------- 1 | program demo 2 | integer :: i 3 | open(newunit=i,file="demo1.txt",form="formatted",action="write") 4 | write(i, '(a)') "DEMO1" 5 | close(i) 6 | stop 0 7 | end program demo 8 | -------------------------------------------------------------------------------- /example_packages/many_examples/demo2/prog.f90: -------------------------------------------------------------------------------- 1 | program demo 2 | integer :: i 3 | open(newunit=i,file="demo2.txt",form="formatted",action="write") 4 | write(i, '(a)') "DEMO2" 5 | close(i) 6 | stop 0 7 | end program demo 8 | -------------------------------------------------------------------------------- /example_packages/many_examples/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "many_examples" 2 | build.auto-examples = false 3 | 4 | [[example]] 5 | name = "demo-1" 6 | source-dir = "demo1" 7 | main = "prog.f90" 8 | 9 | [[example]] 10 | name = "demo-2" 11 | source-dir = "demo2" 12 | main = "prog.f90" 13 | -------------------------------------------------------------------------------- /example_packages/many_targets/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/many_targets/app/run1.f90: -------------------------------------------------------------------------------- 1 | program run1 2 | use file_mod 3 | implicit none 4 | call print_file("run",1) 5 | stop 0 6 | end program run1 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/app/run2.f90: -------------------------------------------------------------------------------- 1 | program run2 2 | use file_mod 3 | implicit none 4 | call print_file("run",2) 5 | stop 0 6 | end program run2 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/app/run3.f90: -------------------------------------------------------------------------------- 1 | program run3 2 | use file_mod 3 | implicit none 4 | call print_file("run",3) 5 | stop 0 6 | end program run3 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/example/example1.f90: -------------------------------------------------------------------------------- 1 | program example1 2 | use file_mod 3 | implicit none 4 | call print_file("example",1) 5 | stop 0 6 | end program example1 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/example/example2.f90: -------------------------------------------------------------------------------- 1 | program example2 2 | use file_mod 3 | implicit none 4 | call print_file("example",2) 5 | stop 0 6 | end program example2 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/example/example3.f90: -------------------------------------------------------------------------------- 1 | program example3 2 | use file_mod 3 | implicit none 4 | call print_file("example",3) 5 | stop 0 6 | end program example3 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "many_targets" 2 | -------------------------------------------------------------------------------- /example_packages/many_targets/src/file_mod.f90: -------------------------------------------------------------------------------- 1 | module file_mod 2 | implicit none 3 | public 4 | contains 5 | subroutine print_file(name,id) 6 | character(*), intent(in) :: name 7 | integer, intent(in) :: id 8 | integer :: i 9 | character(len(name)+1) :: nm 10 | write(nm,1)name,id 11 | open(newunit=i,file=nm//'.txt',form="formatted",action="write") 12 | write(i, '(a)') nm 13 | close(i) 14 | 1 format(a,i1) 15 | end subroutine print_file 16 | end module file_mod 17 | 18 | -------------------------------------------------------------------------------- /example_packages/many_targets/test/test1.f90: -------------------------------------------------------------------------------- 1 | program test1 2 | use file_mod 3 | implicit none 4 | call print_file("test",1) 5 | stop 0 6 | end program test1 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/test/test2.f90: -------------------------------------------------------------------------------- 1 | program test2 2 | use file_mod 3 | implicit none 4 | call print_file("test",2) 5 | stop 0 6 | end program test2 7 | -------------------------------------------------------------------------------- /example_packages/many_targets/test/test3.f90: -------------------------------------------------------------------------------- 1 | program test3 2 | use file_mod 3 | implicit none 4 | call print_file("test",3) 5 | stop 0 6 | end program test3 7 | -------------------------------------------------------------------------------- /example_packages/metapackage_blas/app/main.f90: -------------------------------------------------------------------------------- 1 | program metapackage_blas 2 | implicit none 3 | 4 | interface 5 | subroutine dgesv(n, nrhs, a, lda, ipiv, b, ldb, info) 6 | integer, intent(in) :: n, nrhs, lda, ldb 7 | double precision, intent(in out) :: a(lda,*), b(ldb,*) 8 | integer, intent(out) :: ipiv(*), info 9 | end subroutine dgesv 10 | end interface 11 | 12 | integer, parameter :: dp = kind(1.0d0) 13 | real(dp), dimension(:,:), allocatable :: a 14 | real(dp), dimension(:), allocatable :: b 15 | integer :: info 16 | 17 | allocate(a(3,3), b(3)) 18 | a = reshape([1.0_dp, 2.0_dp, 3.0_dp, & 19 | 4.0_dp, 5.0_dp, 6.0_dp, & 20 | 7.0_dp, 8.0_dp, 9.0_dp], [3,3]) 21 | b = [1.0_dp, 2.0_dp, 3.0_dp] 22 | 23 | call solve_eqsys(a, b, info) 24 | if (info /= 0) error stop 25 | 26 | stop 0 27 | 28 | contains 29 | 30 | !> simple wrapper for solvers for real system of linear 31 | !> equations A * X = B 32 | subroutine solve_eqsys(a, b, info) 33 | 34 | real(dp), dimension(:,:), intent(inout) :: a 35 | real(dp), dimension(:), intent(inout) :: b 36 | integer, intent(out) :: info 37 | integer :: i_alloc 38 | integer :: n, nrhs, lda, ldb 39 | integer, dimension(:), allocatable :: ipiv 40 | ! ------------------------------------------------------------------ 41 | 42 | lda = size(a,1) 43 | n = size(a,2) 44 | ldb = size(b,1) 45 | nrhs = 1 46 | 47 | allocate(ipiv(n), stat = i_alloc) 48 | if (i_alloc /= 0) stop 'solve_eqsys: Allocation for array failed!' 49 | 50 | call dgesv(n, nrhs, a, lda, ipiv, b, ldb, info) 51 | 52 | info = 0 53 | 54 | deallocate(ipiv, stat = i_alloc) 55 | if (i_alloc /= 0) stop 'solve_eqsys: Deallocation for array failed!' 56 | 57 | end subroutine solve_eqsys 58 | end program metapackage_blas 59 | -------------------------------------------------------------------------------- /example_packages/metapackage_blas/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_blas" 2 | dependencies.blas="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_hdf5/app/main.f90: -------------------------------------------------------------------------------- 1 | program metapackage_hdf5 2 | use hdf5 3 | implicit none 4 | 5 | integer :: error 6 | 7 | call h5open_f(error) 8 | if (error/=0) stop -1 9 | 10 | call h5close_f(error) 11 | if (error/=0) stop -2 12 | 13 | stop 0 14 | 15 | end program metapackage_hdf5 16 | -------------------------------------------------------------------------------- /example_packages/metapackage_hdf5/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_hdf5" 2 | dependencies.hdf5="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_minpack/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use metapackage_minpack, only: simple_test 3 | implicit none 4 | logical :: success 5 | call simple_test(success) 6 | stop merge(0,1,success) 7 | end program main 8 | -------------------------------------------------------------------------------- /example_packages/metapackage_minpack/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_minpack" 2 | dependencies.minpack="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_minpack/src/metapackage_minpack.f90: -------------------------------------------------------------------------------- 1 | module metapackage_minpack 2 | use minpack_module, only: wp 3 | use iso_fortran_env, only: real64 4 | implicit none 5 | private 6 | 7 | public :: simple_test 8 | contains 9 | subroutine simple_test(success) 10 | logical, intent(out) :: success 11 | ! Success! can read minpack module 12 | success = wp == real64 13 | end subroutine simple_test 14 | end module metapackage_minpack 15 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi/README.md: -------------------------------------------------------------------------------- 1 | # test_mpi 2 | This test program prints the running thread ID using MPI. 3 | PLEASE NOTE: 4 | - Test app uses 'mpif.h' and not 'use mpi' or 'use mpi_f08' because the latter are compiler-dependent, 5 | and the MPI implementation on the local machine may not offer an implementation for them with the same 6 | compiler that fpm is using. 7 | - Using mpif.h will be the most backward compatible and platform agnostic 8 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi/app/main.f90: -------------------------------------------------------------------------------- 1 | program with_mpi 2 | implicit none 3 | 4 | include 'mpif.h' 5 | 6 | integer, parameter :: INIT_ERROR = 1 7 | integer, parameter :: RANK_ERROR = 2 8 | 9 | integer :: ierror,ncpus,cpuid 10 | 11 | 12 | ! Initialize MPI argument 13 | call MPI_INIT(ierror); 14 | if (ierror/=0) stop INIT_ERROR 15 | 16 | ! Get number of processes and current rank 17 | call MPI_Comm_size(MPI_COMM_WORLD, ncpus, ierror) 18 | if (ierror/=0) stop RANK_ERROR 19 | 20 | call MPI_Comm_rank(MPI_COMM_WORLD, cpuid, ierror) 21 | if (ierror/=0) stop RANK_ERROR 22 | 23 | print "('Hello, mpi world from rank ',i0,' of ',i0,'!')", cpuid+1,ncpus 24 | 25 | ! Finalize MPI environment. 26 | call MPI_FINALIZE(ierror) 27 | if (ierror/=0) stop INIT_ERROR 28 | 29 | stop 0 30 | 31 | end program with_mpi 32 | 33 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_mpi" 2 | dependencies.mpi = "*" 3 | fortran.implicit-external=true 4 | fortran.implicit-typing=true 5 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi_c/README.md: -------------------------------------------------------------------------------- 1 | # test_mpi 2 | This test program prints the running thread ID using MPI. 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi_c/app/main.c: -------------------------------------------------------------------------------- 1 | // Test MPI linking from a C main program 2 | #include 3 | #include 4 | 5 | int main(int argc, char** argv) 6 | { 7 | 8 | int ierror,ncpus,cpuid; 9 | 10 | // Initialize MPI argument 11 | ierror = MPI_Init(&argc, &argv); 12 | if (ierror) { 13 | printf("MPI_Init failed with error %d \n",ierror); 14 | return 1; 15 | } 16 | 17 | // Get number of processes and current rank 18 | MPI_Comm_size(MPI_COMM_WORLD, &ncpus); 19 | 20 | // Get Rank of the current process 21 | MPI_Comm_rank(MPI_COMM_WORLD, &cpuid); 22 | 23 | printf("Hello, MPI C World from rank %d of %d! \n",cpuid+1,ncpus); 24 | 25 | // Finalize MPI environment. 26 | ierror = MPI_Finalize(); 27 | if (ierror) { 28 | printf("MPI_Finalize failed with error %d \n",ierror); 29 | return 1; 30 | } else { 31 | return 0; 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi_c/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_mpi_c" 2 | dependencies.mpi = "*" 3 | fortran.implicit-typing=true 4 | fortran.implicit-external=true 5 | 6 | [[executable]] 7 | name = "test-mpi-c-main" 8 | main = "main.c" 9 | 10 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi_cpp/README.md: -------------------------------------------------------------------------------- 1 | # test_mpi 2 | This test program prints the running thread ID using MPI. 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi_cpp/app/main.cpp: -------------------------------------------------------------------------------- 1 | // Test MPI linking from a C main program 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | int main(int argc, char** argv) 8 | { 9 | 10 | int ierror,ncpus,cpuid; 11 | 12 | // Initialize MPI argument 13 | ierror = MPI_Init(&argc, &argv); 14 | if (ierror) { 15 | cout << "MPI_Init failed with error " << ierror << endl; 16 | return 1; 17 | } 18 | 19 | // Get number of processes and current rank 20 | MPI_Comm_size(MPI_COMM_WORLD, &ncpus); 21 | 22 | // Get Rank of the current process 23 | MPI_Comm_rank(MPI_COMM_WORLD, &cpuid); 24 | 25 | cout << "Hello, MPI C++ World from rank " << cpuid << " of " << ncpus << "!" << endl; 26 | 27 | // Finalize MPI environment. 28 | ierror = MPI_Finalize(); 29 | if (ierror) { 30 | cout << "MPI_Finalize failed with error " << ierror << endl; 31 | return 1; 32 | } else { 33 | return 0; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /example_packages/metapackage_mpi_cpp/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_mpi_cpp" 2 | dependencies.mpi="*" 3 | fortran.implicit-typing=true 4 | fortran.implicit-external=true 5 | 6 | [[executable]] 7 | name="test-mpi-cpp" 8 | main="main.cpp" 9 | -------------------------------------------------------------------------------- /example_packages/metapackage_netcdf/app/main.f90: -------------------------------------------------------------------------------- 1 | program metapackage_netcdf 2 | use netcdf4_f03 3 | implicit none 4 | 5 | integer(c_int) :: ncid, retval 6 | 7 | retval = nf_create("dummy.nc", NF_INMEMORY, ncid) 8 | if (retval /= nf_noerr) error stop nf_strerror(retval) 9 | stop 0 10 | end program metapackage_netcdf 11 | -------------------------------------------------------------------------------- /example_packages/metapackage_netcdf/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_netcdf" 2 | dependencies.netcdf="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_openmp/README.md: -------------------------------------------------------------------------------- 1 | # test_openmp 2 | This test program prints the running thread ID using OpenMP. 3 | Module omp_lib is invoked, so, this code cannot build if the OpenMP library 4 | is not properly enabled by the compiler flags. 5 | -------------------------------------------------------------------------------- /example_packages/metapackage_openmp/app/main.f90: -------------------------------------------------------------------------------- 1 | ! OpenMP test case 2 | ! This test program will only run if openmp is properly enabled in the compiler flags. 3 | ! Otherwise, the omp_lib module won't be found and the code cannot be built. 4 | program openmp_test 5 | use test_openmp, only: say_hello 6 | use omp_lib 7 | implicit none 8 | 9 | !$omp parallel 10 | call say_hello(thread_ID=OMP_GET_THREAD_NUM()) 11 | !$omp end parallel 12 | 13 | ! Successful return 14 | stop 0 15 | 16 | end program openmp_test 17 | -------------------------------------------------------------------------------- /example_packages/metapackage_openmp/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_openmp" 2 | dependencies.openmp = "*" 3 | 4 | -------------------------------------------------------------------------------- /example_packages/metapackage_openmp/src/test_openmp.f90: -------------------------------------------------------------------------------- 1 | module test_openmp 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | contains 7 | subroutine say_hello(thread_ID) 8 | integer, intent(in) :: thread_ID 9 | print "(a,i0,a)", "Hello, test_openmp is called from thread ",thread_ID,"!" 10 | end subroutine say_hello 11 | end module test_openmp 12 | -------------------------------------------------------------------------------- /example_packages/metapackage_openmp/test/check.f90: -------------------------------------------------------------------------------- 1 | program check 2 | implicit none 3 | 4 | print *, "Put some tests in here!" 5 | end program check 6 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib/README.md: -------------------------------------------------------------------------------- 1 | # test_stdlib 2 | This test program generates a real [1,2,3,4,5] array using stdlib. 3 | stdlib math and kinds modules are invoked; so this program cannot be built if stdlib is not 4 | properly built and linked. stdlib tests are not run in this program. 5 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib/app/main.f90: -------------------------------------------------------------------------------- 1 | ! fortran-lang stdlib test case 2 | ! This test program will only run if stdlib is properly built and linked to this project. 3 | program test_stdlib_metapackage 4 | 5 | ! These USEs would not be possible if stdlib is not found 6 | use stdlib_kinds, only: int32, int64, dp, sp 7 | use stdlib_math 8 | implicit none 9 | 10 | real(dp), allocatable :: indices(:) 11 | 12 | indices = linspace(1.0_dp,5.0_dp,5) 13 | 14 | if (.not.allocated(indices)) then 15 | stop 1 16 | elseif (size(indices)/=5) then 17 | stop 2 18 | elseif (any(nint(indices)/=[1,2,3,4,5])) then 19 | stop 3 20 | else 21 | stop 0 22 | endif 23 | 24 | end program test_stdlib_metapackage 25 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_stdlib" 2 | dependencies.stdlib = "*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib/src/metapackage_stdlib.f90: -------------------------------------------------------------------------------- 1 | module metapackage_stdlib 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | contains 7 | subroutine say_hello 8 | print *, "Hello, metapackage_stdlib!" 9 | end subroutine say_hello 10 | end module metapackage_stdlib 11 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib_extblas/app/main.f90: -------------------------------------------------------------------------------- 1 | ! fortran-lang stdlib + external BLAS test case 2 | ! Program will fail if an external BLAS has not been linked against. 3 | program test_stdlib_metapackage 4 | use stdlib_linalg_constants, only: external_blas_ilp32,external_lapack_ilp32, & 5 | external_blas_ilp64,external_lapack_ilp64 6 | implicit none 7 | 8 | if (.not.(external_blas_ilp32 .or. external_blas_ilp64)) then 9 | stop 1 10 | elseif (.not.(external_lapack_ilp32 .or. external_lapack_ilp64)) then 11 | stop 2 12 | else 13 | stop 0 14 | end if 15 | 16 | end program test_stdlib_metapackage 17 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib_extblas/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_stdlib_ext_blas" 2 | dependencies.blas = "*" 3 | dependencies.stdlib = "*" 4 | -------------------------------------------------------------------------------- /example_packages/nonintrinsic/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/nonintrinsic/app/main.f90: -------------------------------------------------------------------------------- 1 | program test_nonintr 2 | use, non_intrinsic :: iso_fortran_env 3 | 4 | ! ijk=0 can be read 5 | stop ijk 6 | end program test_nonintr 7 | -------------------------------------------------------------------------------- /example_packages/nonintrinsic/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "non-intrinsic" 2 | -------------------------------------------------------------------------------- /example_packages/nonintrinsic/src/iso_fortran_env.f90: -------------------------------------------------------------------------------- 1 | module iso_fortran_env 2 | implicit none 3 | integer, parameter :: ijk = 0 4 | end module iso_fortran_env 5 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp/app/main.f90: -------------------------------------------------------------------------------- 1 | program cpp 2 | use preprocess_cpp 3 | call say_hello() 4 | end program 5 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_cpp" 2 | 3 | version = "1" 4 | 5 | [preprocess] 6 | [preprocess.cpp] 7 | macros = ["TESTMACRO", "TESTMACRO2=3", "TESTMACRO3={version}"] 8 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp/src/preprocess_cpp.f90: -------------------------------------------------------------------------------- 1 | module preprocess_cpp 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | contains 7 | subroutine say_hello 8 | print *, "Hello, preprocess_cpp!" 9 | #ifndef TESTMACRO 10 | This breaks the build. 11 | #endif 12 | 13 | #if TESTMACRO2 != 3 14 | This breaks the build. 15 | #endif 16 | 17 | #if TESTMACRO3 != 1 18 | This breaks the build. 19 | #endif 20 | 21 | end subroutine say_hello 22 | end module preprocess_cpp 23 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_c/app/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "val.h" 4 | 5 | int main() { printf("%d\n", variable); } 6 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_c/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_cpp_c" 2 | 3 | [[executable]] 4 | name = "main" 5 | main = "main.c" 6 | 7 | [preprocess] 8 | [preprocess.cpp] 9 | macros = ["VAL"] 10 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_c/include/val.h: -------------------------------------------------------------------------------- 1 | #ifndef _VAL_H_ 2 | #define _VAL_H_ 3 | 4 | #ifdef VAL 5 | const int variable = 1; 6 | #endif 7 | #endif 8 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_deps/app/main.f90: -------------------------------------------------------------------------------- 1 | program hello_fpm 2 | use utils, only: say_hello 3 | 4 | call say_hello() 5 | 6 | end program hello_fpm 7 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_deps/crate/utils/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils" 2 | 3 | [preprocess] 4 | [preprocess.cpp] 5 | macros = ["X=1"] 6 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_deps/crate/utils/src/say_hello.f90: -------------------------------------------------------------------------------- 1 | module utils 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | subroutine say_hello() 8 | print '(a,1x,i0)', "Hello, X =", X 9 | end subroutine say_hello 10 | 11 | end module utils 12 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_deps/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_cpp_deps" 2 | 3 | [dependencies] 4 | utils = { path = "crate/utils" } 5 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_suffix/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_suffix/app/main.f90: -------------------------------------------------------------------------------- 1 | program test_preprocess_suffix 2 | use preprocess_cpp 3 | #ifndef TESTMACRO 4 | stop -1 5 | #else 6 | stop 0 7 | #endif 8 | end program test_preprocess_suffix 9 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_suffix/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_cpp_suffix" 2 | version = "1" 3 | 4 | [preprocess] 5 | [preprocess.cpp] 6 | macros = ["TESTMACRO", "TESTMACRO2=3", "TESTMACRO3={version}"] 7 | suffixes = ["fpp"] 8 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_suffix/src/preprocess_cpp.fpp: -------------------------------------------------------------------------------- 1 | module preprocess_cpp 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | contains 7 | subroutine say_hello 8 | print *, "Hello, preprocess_cpp!" 9 | #ifndef TESTMACRO 10 | This breaks the build. 11 | #endif 12 | 13 | #if TESTMACRO2 != 3 14 | This breaks the build. 15 | #endif 16 | 17 | #if TESTMACRO3 != 1 18 | This breaks the build. 19 | #endif 20 | 21 | end subroutine say_hello 22 | end module preprocess_cpp 23 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello/README.md: -------------------------------------------------------------------------------- 1 | # preprocess_hello 2 | My cool new project! 3 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello/app/main.f90: -------------------------------------------------------------------------------- 1 | program preprocess_hello 2 | use preprocess_hello_dependency, only: say_hello 3 | 4 | implicit none 5 | call say_hello() 6 | end program preprocess_hello 7 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_hello" 2 | 3 | [preprocess] 4 | [preprocess.cpp] 5 | macros = ["FOO"] 6 | 7 | [dependencies] 8 | preprocess_hello_dependency = { path = "../preprocess_hello_dependency" } -------------------------------------------------------------------------------- /example_packages/preprocess_hello_dependency/README.md: -------------------------------------------------------------------------------- 1 | # preprocess_hello_dependency 2 | My cool new project! 3 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello_dependency/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_hello_dependency" 2 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello_dependency/src/preprocess_hello_dependency.f90: -------------------------------------------------------------------------------- 1 | module preprocess_hello_dependency 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | contains 7 | subroutine say_hello 8 | 9 | !> If this build fails, then it implies that macros are getting passed to the dependency. 10 | #ifdef FOO 11 | This breaks the build inside dependency. This implies that macros are getting passed to the dependeny. 12 | #endif 13 | print *, "Hello, preprocess_hello_dependency!" 14 | end subroutine say_hello 15 | end module preprocess_hello_dependency 16 | -------------------------------------------------------------------------------- /example_packages/preprocess_per_dependency/app/main.f90: -------------------------------------------------------------------------------- 1 | program hello_fpm 2 | use utils, only: say_hello 3 | integer :: ierr 4 | 5 | call say_hello(ierr) 6 | stop ierr ! ierr==0 if DEPENDENCY_MACRO is defined 7 | 8 | end program hello_fpm 9 | -------------------------------------------------------------------------------- /example_packages/preprocess_per_dependency/crate/utils/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils" 2 | 3 | [preprocess] 4 | [preprocess.cpp] 5 | macros = ["X=1"] 6 | -------------------------------------------------------------------------------- /example_packages/preprocess_per_dependency/crate/utils/src/say_hello.f90: -------------------------------------------------------------------------------- 1 | module utils 2 | 3 | implicit none 4 | 5 | contains 6 | 7 | subroutine say_hello(ierr) 8 | integer, intent(out) :: ierr 9 | 10 | ierr = -1 11 | #ifdef DEPENDENCY_MACRO 12 | ierr = 0 13 | #endif 14 | 15 | print *, "Dependency macro ", merge(" IS","NOT",ierr==0)," defined" 16 | 17 | end subroutine say_hello 18 | 19 | end module utils 20 | -------------------------------------------------------------------------------- /example_packages/preprocess_per_dependency/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_cpp_deps" 2 | 3 | [dependencies] 4 | utils = { path = "crate/utils" , preprocess.cpp.macros=["DEPENDENCY_MACRO"] } 5 | -------------------------------------------------------------------------------- /example_packages/program_with_module/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/program_with_module/app/main.f90: -------------------------------------------------------------------------------- 1 | module greet_m 2 | implicit none 3 | character(*), parameter :: greeting = 'Hello, fpm!' 4 | end module greet_m 5 | 6 | program program_with_module 7 | use greet_m, only: greeting 8 | implicit none 9 | print *, greeting 10 | end program program_with_module 11 | -------------------------------------------------------------------------------- /example_packages/program_with_module/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "Program_with_module" 2 | -------------------------------------------------------------------------------- /example_packages/shared_app_only/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use testdrive 3 | print *, 'Hello, world!' 4 | end program main 5 | -------------------------------------------------------------------------------- /example_packages/shared_app_only/fpm.toml: -------------------------------------------------------------------------------- 1 | # App only, use shared lib from other folder 2 | name = "shared_app_only" 3 | library.type="shared" 4 | install.library=true 5 | [dependencies] 6 | shared_lib_extra = { path = "../shared_lib_extra" } 7 | [dev-dependencies] 8 | test-drive = { git = "https://github.com/fortran-lang/test-drive", tag="v0.5.0" } 9 | -------------------------------------------------------------------------------- /example_packages/shared_app_only/test/test.f90: -------------------------------------------------------------------------------- 1 | module test_shared_lib 2 | use testdrive, only : new_unittest, unittest_type, error_type, check 3 | use shared_lib, only: test_something 4 | 5 | implicit none 6 | 7 | public :: collect 8 | 9 | 10 | contains 11 | 12 | !> Collect all exported unit tests 13 | subroutine collect(testsuite) 14 | !> Collection of tests 15 | type(unittest_type), allocatable, intent(out) :: testsuite(:) 16 | 17 | testsuite = [ new_unittest("shared_lib", test_shared) ] 18 | 19 | end subroutine collect 20 | 21 | subroutine test_shared(error) 22 | type(error_type), allocatable, intent(out) :: error 23 | 24 | call check(error, test_something(), 123, "Should be test_something==123") 25 | 26 | end subroutine test_shared 27 | 28 | end module test_shared_lib 29 | 30 | program tester 31 | use, intrinsic :: iso_fortran_env, only : error_unit 32 | use testdrive, only : run_testsuite, new_testsuite, testsuite_type 33 | use test_shared_lib, only : collect 34 | implicit none 35 | integer :: stat 36 | type(testsuite_type), allocatable :: testsuite 37 | character(len=*), parameter :: fmt = '("#", *(1x, a))' 38 | 39 | stat = 0 40 | 41 | testsuite = new_testsuite("shared_lib", collect) 42 | 43 | write(error_unit, fmt) "Testing:", testsuite%name 44 | call run_testsuite(testsuite%collect, error_unit, stat) 45 | 46 | if (stat > 0) then 47 | write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" 48 | error stop 49 | end if 50 | end program tester 51 | -------------------------------------------------------------------------------- /example_packages/shared_lib/fpm.toml: -------------------------------------------------------------------------------- 1 | # Shared library with no executables 2 | name = "shared_lib" 3 | library.type="shared" 4 | -------------------------------------------------------------------------------- /example_packages/shared_lib/src/shared_lib.f90: -------------------------------------------------------------------------------- 1 | module shared_lib 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | public :: test_something 7 | contains 8 | subroutine say_hello 9 | print *, "Hello, shared_lib!" 10 | end subroutine say_hello 11 | integer function test_something() 12 | test_something = 123 13 | end function test_something 14 | end module shared_lib 15 | -------------------------------------------------------------------------------- /example_packages/shared_lib_empty/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "shared_lib_empty" 2 | library.type="shared" 3 | [dependencies] 4 | shared_lib = { path = "../shared_lib" } 5 | shared_app_only = { path = "../shared_app_only" } 6 | -------------------------------------------------------------------------------- /example_packages/shared_lib_extra/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "shared_lib_extra" 2 | library.type="shared" 3 | [dependencies] 4 | shared_lib = { path = "../shared_lib" } 5 | -------------------------------------------------------------------------------- /example_packages/shared_lib_extra/src/shared_lib_extra.f90: -------------------------------------------------------------------------------- 1 | module shared_lib_extra 2 | implicit none 3 | private 4 | 5 | public :: say_extra_hello 6 | contains 7 | subroutine say_extra_hello 8 | print *, "Hello, shared_lib_extra!" 9 | end subroutine say_extra_hello 10 | end module shared_lib_extra 11 | -------------------------------------------------------------------------------- /example_packages/static_app_only/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use testdrive 3 | print *, 'Hello, world!' 4 | end program main 5 | -------------------------------------------------------------------------------- /example_packages/static_app_only/fpm.toml: -------------------------------------------------------------------------------- 1 | # App only, use shared libs from other folder, no provided sources 2 | name = "static_app_only" 3 | library.type="static" 4 | install.library=true 5 | [dependencies] 6 | shared_lib_extra = { path = "../shared_lib_extra" } 7 | [dev-dependencies] 8 | test-drive = { git = "https://github.com/fortran-lang/test-drive", tag="v0.5.0" } 9 | -------------------------------------------------------------------------------- /example_packages/static_app_only/test/test.f90: -------------------------------------------------------------------------------- 1 | module test_shared_lib 2 | use testdrive, only : new_unittest, unittest_type, error_type, check 3 | use shared_lib, only: test_something 4 | 5 | implicit none 6 | 7 | public :: collect 8 | 9 | 10 | contains 11 | 12 | !> Collect all exported unit tests 13 | subroutine collect(testsuite) 14 | !> Collection of tests 15 | type(unittest_type), allocatable, intent(out) :: testsuite(:) 16 | 17 | testsuite = [ new_unittest("shared_lib", test_shared) ] 18 | 19 | end subroutine collect 20 | 21 | subroutine test_shared(error) 22 | type(error_type), allocatable, intent(out) :: error 23 | 24 | call check(error, test_something(), 123, "Should be test_something==123") 25 | 26 | end subroutine test_shared 27 | 28 | end module test_shared_lib 29 | 30 | program tester 31 | use, intrinsic :: iso_fortran_env, only : error_unit 32 | use testdrive, only : run_testsuite, new_testsuite, testsuite_type 33 | use test_shared_lib, only : collect 34 | implicit none 35 | integer :: stat 36 | type(testsuite_type), allocatable :: testsuite 37 | character(len=*), parameter :: fmt = '("#", *(1x, a))' 38 | 39 | stat = 0 40 | 41 | testsuite = new_testsuite("shared_lib", collect) 42 | 43 | write(error_unit, fmt) "Testing:", testsuite%name 44 | call run_testsuite(testsuite%collect, error_unit, stat) 45 | 46 | if (stat > 0) then 47 | write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" 48 | error stop 49 | end if 50 | end program tester 51 | -------------------------------------------------------------------------------- /example_packages/static_lib_empty/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "static_lib_empty" 2 | library.type="static" 3 | [dependencies] 4 | shared_lib = { path = "../shared_lib" } 5 | shared_app_only = { path = "../shared_app_only" } 6 | -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/app/main.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use parent 3 | 4 | integer :: a, b 5 | 6 | call my_sub1(a) 7 | call my_sub2(b) 8 | 9 | end program test -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "submodule_tree_shake" 2 | -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/src/child1.f90: -------------------------------------------------------------------------------- 1 | submodule(parent) child1 2 | implicit none 3 | 4 | interface 5 | module function my_fun() result (b) 6 | integer :: b 7 | end function my_fun 8 | end interface 9 | 10 | contains 11 | 12 | module procedure my_sub1 13 | a = my_fun() 14 | end procedure my_sub1 15 | 16 | end submodule child1 -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/src/child2.f90: -------------------------------------------------------------------------------- 1 | submodule(parent) child2 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure my_sub2 7 | a = 2 8 | end procedure my_sub2 9 | 10 | end submodule child2 -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/src/child_unused.f90: -------------------------------------------------------------------------------- 1 | submodule(parent_unused) child_unused 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure unused_sub 7 | a = 1 8 | end procedure unused_sub 9 | 10 | end submodule child_unused -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/src/grandchild.f90: -------------------------------------------------------------------------------- 1 | submodule(parent:child1) grandchild 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure my_fun 7 | b = 2 8 | end procedure my_fun 9 | 10 | end submodule grandchild -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/src/parent.f90: -------------------------------------------------------------------------------- 1 | module parent 2 | implicit none 3 | 4 | interface 5 | 6 | module subroutine my_sub1(a) 7 | integer, intent(out) :: a 8 | end subroutine my_sub1 9 | 10 | module subroutine my_sub2(a) 11 | integer, intent(out) :: a 12 | end subroutine my_sub2 13 | end interface 14 | 15 | end module parent -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/src/parent_unused.f90: -------------------------------------------------------------------------------- 1 | module parent_unused 2 | implicit none 3 | 4 | interface 5 | 6 | module subroutine unused_sub(a) 7 | integer, intent(out) :: a 8 | end subroutine unused_sub 9 | 10 | end interface 11 | 12 | end module parent_unused -------------------------------------------------------------------------------- /example_packages/submodules/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/submodules/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "submodules" 2 | -------------------------------------------------------------------------------- /example_packages/submodules/src/child1.f90: -------------------------------------------------------------------------------- 1 | submodule(parent) child1 2 | implicit none 3 | 4 | interface 5 | module function my_fun() result (b) 6 | integer :: b 7 | end function my_fun 8 | end interface 9 | 10 | contains 11 | 12 | module procedure my_sub1 13 | a = 1 14 | end procedure my_sub1 15 | 16 | end submodule child1 -------------------------------------------------------------------------------- /example_packages/submodules/src/child2.f90: -------------------------------------------------------------------------------- 1 | submodule(parent) child2 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure my_sub2 7 | a = 2 8 | end procedure my_sub2 9 | 10 | end submodule child2 -------------------------------------------------------------------------------- /example_packages/submodules/src/grandchild.f90: -------------------------------------------------------------------------------- 1 | submodule(parent:child1) grandchild 2 | implicit none 3 | 4 | contains 5 | 6 | module procedure my_fun 7 | b = 2 8 | end procedure my_fun 9 | 10 | end submodule grandchild -------------------------------------------------------------------------------- /example_packages/submodules/src/parent.f90: -------------------------------------------------------------------------------- 1 | module parent 2 | implicit none 3 | 4 | interface 5 | 6 | module subroutine my_sub1(a) 7 | integer, intent(out) :: a 8 | end subroutine my_sub1 9 | 10 | module subroutine my_sub2(a) 11 | integer, intent(out) :: a 12 | end subroutine my_sub2 13 | end interface 14 | 15 | end module parent -------------------------------------------------------------------------------- /example_packages/tree_shake/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/tree_shake/app/say_Hello.f90: -------------------------------------------------------------------------------- 1 | program say_Hello 2 | use greet_m, only: make_greeting 3 | 4 | implicit none 5 | 6 | interface 7 | function external_function() result(i) 8 | integer :: i 9 | end function external_function 10 | end interface 11 | 12 | print *, make_greeting("World") 13 | print *, external_function() 14 | 15 | end program say_Hello 16 | -------------------------------------------------------------------------------- /example_packages/tree_shake/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "tree_shake" -------------------------------------------------------------------------------- /example_packages/tree_shake/src/extra_m.f90: -------------------------------------------------------------------------------- 1 | ! This module is not used by any other sources, 2 | ! however because it also contains an external function 3 | ! it cannot be dropped during tree-shaking/pruning 4 | module extra_m 5 | use subdir_constants, only: FAREWELL_STR 6 | implicit none 7 | private 8 | 9 | integer, parameter :: m = 0 10 | end 11 | 12 | function external_function() result(i) 13 | integer :: i 14 | i = 1 15 | end function external_function -------------------------------------------------------------------------------- /example_packages/tree_shake/src/farewell_m.f90: -------------------------------------------------------------------------------- 1 | ! This module is not used by any other sources 2 | ! and only contains a module (no non-module subprograms), 3 | ! therefore it should be dropped during tree-shaking/pruning 4 | module farewell_m 5 | use subdir_constants, only: FAREWELL_STR 6 | implicit none 7 | private 8 | 9 | public :: make_farewell 10 | contains 11 | function make_farewell(name) result(greeting) 12 | character(len=*), intent(in) :: name 13 | character(len=:), allocatable :: greeting 14 | 15 | greeting = FAREWELL_STR // name // "!" 16 | end function make_farewell 17 | end module farewell_m 18 | -------------------------------------------------------------------------------- /example_packages/tree_shake/src/greet_m.f90: -------------------------------------------------------------------------------- 1 | ! This module is directly by the executables and 2 | ! hence should not be dropped during tree-shaking/pruning 3 | module greet_m 4 | use subdir_constants, only: GREET_STR 5 | implicit none 6 | private 7 | 8 | public :: make_greeting 9 | contains 10 | function make_greeting(name) result(greeting) 11 | character(len=*), intent(in) :: name 12 | character(len=:), allocatable :: greeting 13 | 14 | greeting = GREET_STR // name // "!" 15 | end function make_greeting 16 | end module greet_m 17 | -------------------------------------------------------------------------------- /example_packages/tree_shake/src/subdir/constants.f90: -------------------------------------------------------------------------------- 1 | ! This module is used indirectly by the executables 2 | ! and hence should not be dropped during tree-shaking/pruning 3 | module subdir_constants 4 | implicit none 5 | 6 | character(*), parameter :: GREET_STR = 'Hello, ' 7 | character(*), parameter :: FAREWELL_STR = 'Goodbye, ' 8 | 9 | end module subdir_constants 10 | -------------------------------------------------------------------------------- /example_packages/tree_shake/test/greet_test.f90: -------------------------------------------------------------------------------- 1 | program greet_test 2 | use greet_m, only: make_greeting 3 | use iso_fortran_env, only: error_unit, output_unit 4 | 5 | implicit none 6 | 7 | character(len=:), allocatable :: greeting 8 | 9 | allocate(character(len=0) :: greeting) 10 | greeting = make_greeting("World") 11 | 12 | if (greeting == "Hello, World!") then 13 | write(output_unit, *) "Passed" 14 | else 15 | write(error_unit, *) "Failed" 16 | call exit(1) 17 | end if 18 | end program greet_test 19 | -------------------------------------------------------------------------------- /example_packages/version_file/VERSION: -------------------------------------------------------------------------------- 1 | 5.42.1 2 | -------------------------------------------------------------------------------- /example_packages/version_file/app/main.f90: -------------------------------------------------------------------------------- 1 | program stub 2 | implicit none 3 | logical :: exists 4 | integer :: unit 5 | character(len=100) :: line 6 | inquire(file="VERSION", exist=exists) 7 | if (.not.exists) error stop "File VERSION does not exist." 8 | open(file="VERSION", newunit=unit) 9 | read(unit, '(a)') line 10 | close(unit) 11 | 12 | print '(*(a))', "File VERSION contains '", trim(line), "'" 13 | end program stub 14 | -------------------------------------------------------------------------------- /example_packages/version_file/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "version_file" 2 | version = "VERSION" 3 | -------------------------------------------------------------------------------- /example_packages/with_c/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/with_c/app/main.f90: -------------------------------------------------------------------------------- 1 | program with_c_app 2 | use with_c 3 | implicit none 4 | 5 | write(*,*) "isdir('app') = ", system_isdir('app') 6 | write(*,*) "isdir('src') = ", system_isdir('src') 7 | write(*,*) "isdir('test') = ", system_isdir('test') 8 | write(*,*) "isdir('bench') = ", system_isdir('bench') 9 | 10 | end program with_c_app -------------------------------------------------------------------------------- /example_packages/with_c/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_c" 2 | -------------------------------------------------------------------------------- /example_packages/with_c/src/c_code.c: -------------------------------------------------------------------------------- 1 | #include 2 | /* 3 | * Decides whether a given file name is a directory. 4 | * return 1 if file exists and is a directory 5 | * Source (Public domain): https://github.com/urbanjost/M_system 6 | */ 7 | int my_isdir (const char *path) { 8 | struct stat sb; 9 | return stat(path, &sb) == 0 && S_ISDIR (sb.st_mode); 10 | } -------------------------------------------------------------------------------- /example_packages/with_c/src/with_c.f90: -------------------------------------------------------------------------------- 1 | module with_c 2 | use iso_c_binding, only: c_char, c_int, c_null_char 3 | implicit none 4 | 5 | contains 6 | 7 | function system_isdir(dirname) 8 | ! Source (Public domain): https://github.com/urbanjost/M_system 9 | ! 10 | implicit none 11 | character(len=*),intent(in) :: dirname 12 | logical :: system_isdir 13 | 14 | interface 15 | function c_isdir(dirname) bind (C,name="my_isdir") result (c_ierr) 16 | import c_char,c_int 17 | character(kind=c_char,len=1),intent(in) :: dirname(*) 18 | integer(kind=c_int) :: c_ierr 19 | end function c_isdir 20 | end interface 21 | 22 | system_isdir= c_isdir(trim(dirname)//c_null_char) == 1 23 | 24 | end function system_isdir 25 | 26 | end module with_c -------------------------------------------------------------------------------- /example_packages/with_examples/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | -------------------------------------------------------------------------------- /example_packages/with_examples/app/demo-prog.f90: -------------------------------------------------------------------------------- 1 | program demo 2 | write(*, '(a)') "This is a simple program" 3 | end program demo 4 | -------------------------------------------------------------------------------- /example_packages/with_examples/demo/prog.f90: -------------------------------------------------------------------------------- 1 | program demo 2 | write(*, '(a)') "This is a simple demo program, but not a real application" 3 | end program demo 4 | -------------------------------------------------------------------------------- /example_packages/with_examples/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_examples" 2 | build.auto-examples = false 3 | 4 | [[example]] 5 | name = "demo-prog" 6 | source-dir = "demo" 7 | main = "prog.f90" 8 | -------------------------------------------------------------------------------- /example_packages/with_makefile/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/with_makefile/Makefile: -------------------------------------------------------------------------------- 1 | INCLUDE_FLAGS = $(addprefix -I,$(INCLUDE_DIRS)) 2 | 3 | $(BUILD_DIR)/libwith_makefile.a: $(BUILD_DIR)/hello_makefile.o 4 | ar rs $(@) $(^) 5 | 6 | $(BUILD_DIR)/hello_makefile.mod: src/hello_makefile.f90 7 | 8 | $(BUILD_DIR)/hello_makefile.o: src/hello_makefile.f90 9 | $(FC) -c -J$(BUILD_DIR) $(INCLUDE_FLAGS) $(FFLAGS) -o $(@) $(<) 10 | -------------------------------------------------------------------------------- /example_packages/with_makefile/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_makefile" 2 | 3 | [library] 4 | source-dir = "src" 5 | build-script = "Makefile" 6 | -------------------------------------------------------------------------------- /example_packages/with_makefile/src/hello_makefile.f90: -------------------------------------------------------------------------------- 1 | module hello_makefile 2 | implicit none 3 | private 4 | 5 | public :: say_hello_from_makefile 6 | contains 7 | subroutine say_hello_from_makefile() 8 | print *, "Hello from Makefile library!" 9 | end subroutine say_hello_from_makefile 10 | end module hello_makefile 11 | -------------------------------------------------------------------------------- /fpm.toml: -------------------------------------------------------------------------------- 1 | name = "fpm" 2 | version = "0.12.0" 3 | license = "MIT" 4 | author = "fpm maintainers" 5 | maintainer = "@fortran-lang/fpm" 6 | copyright = "2020-2025 fpm contributors" 7 | homepage = "https://fpm.fortran-lang.org/" 8 | description = "Fortran Package Manager" 9 | 10 | [preprocess] 11 | [preprocess.cpp] 12 | macros=["FPM_RELEASE_VERSION={version}"] 13 | 14 | [dependencies] 15 | toml-f.git = "https://github.com/toml-f/toml-f" 16 | toml-f.rev = "d7b892b1d074b7cfc5d75c3e0eb36ebc1f7958c1" 17 | M_CLI2.git = "https://github.com/urbanjost/M_CLI2.git" 18 | M_CLI2.rev = "7264878cdb1baff7323cc48596d829ccfe7751b8" 19 | fortran-regex.git = "https://github.com/perazz/fortran-regex" 20 | fortran-regex.tag = "1.1.2" 21 | jonquil.git = "https://github.com/toml-f/jonquil" 22 | jonquil.rev = "4fbd4cf34d577c0fd25e32667ee9e41bf231ece8" 23 | fortran-shlex.git = "https://github.com/perazz/fortran-shlex" 24 | fortran-shlex.tag = "2.0.0" 25 | 26 | [[test]] 27 | name = "cli-test" 28 | source-dir = "test/cli_test" 29 | main = "cli_test.f90" 30 | 31 | [[test]] 32 | name = "new-test" 33 | source-dir = "test/new_test" 34 | main = "new_test.f90" 35 | 36 | [[test]] 37 | name = "fpm-test" 38 | source-dir = "test/fpm_test" 39 | main = "main.f90" 40 | 41 | [[test]] 42 | name = "help-test" 43 | source-dir = "test/help_test" 44 | main = "help_test.f90" 45 | 46 | -------------------------------------------------------------------------------- /install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e # exit on error 4 | 5 | usage() 6 | { 7 | echo "Fortran Package Manager Bootstrap Script" 8 | echo "" 9 | echo "USAGE:" 10 | echo "./install.sh [--help | [--prefix=PREFIX]" 11 | echo "" 12 | echo " --help Display this help text" 13 | echo " --prefix=PREFIX Install binary in 'PREFIX/bin'" 14 | echo " Default prefix='\$HOME/.local/bin'" 15 | echo "" 16 | echo "FC and FFLAGS environment variables can be used to select the" 17 | echo "Fortran compiler and the build flags." 18 | echo "" 19 | } 20 | 21 | # Return a download command 22 | get_fetch_command() 23 | { 24 | if command -v curl > /dev/null 2>&1; then 25 | echo "curl -L" 26 | elif command -v wget > /dev/null 2>&1; then 27 | echo "wget -O -" 28 | else 29 | echo "No download mechanism found. Install curl or wget first." 30 | return 1 31 | fi 32 | } 33 | 34 | # Return value of the latest published release on GitHub, with no heading "v" (e.g., "0.7.0") 35 | get_latest_release() 36 | { 37 | $2 "https://api.github.com/repos/$1/releases/latest" | # Get latest release from GitHub api 38 | grep '"tag_name":' | # Get tag line 39 | sed -E 's/.*"([^"]+)".*/\1/' | # Pluck JSON value 40 | sed -E 's/^v//' # Remove heading "v" if present 41 | } 42 | 43 | PREFIX="$HOME/.local" 44 | 45 | while [ "$1" != "" ]; do 46 | PARAM=$(echo "$1" | awk -F= '{print $1}') 47 | VALUE=$(echo "$1" | awk -F= '{print $2}') 48 | case $PARAM in 49 | -h | --help) 50 | usage 51 | exit 52 | ;; 53 | --prefix) 54 | PREFIX=$VALUE 55 | ;; 56 | *) 57 | echo "ERROR: unknown parameter \"$PARAM\"" 58 | usage 59 | exit 1 60 | ;; 61 | esac 62 | shift 63 | done 64 | 65 | set -u # error on use of undefined variable 66 | 67 | # Get download command 68 | FETCH=$(get_fetch_command) 69 | if [ $? -ne 0 ]; then 70 | echo "No download mechanism found. Install curl or wget first." 71 | exit 2 72 | fi 73 | 74 | # Use 0.8.0 too bootstrap 75 | BOOTSTRAP_RELEASE="0.8.0" 76 | SOURCE_URL="https://github.com/fortran-lang/fpm/releases/download/v${BOOTSTRAP_RELEASE}/fpm-${BOOTSTRAP_RELEASE}.F90" 77 | BOOTSTRAP_DIR="build/bootstrap" 78 | 79 | if [ -z ${FC+x} ]; then 80 | FC="gfortran" 81 | fi 82 | if [ -z ${FFLAGS+x} ]; then 83 | FFLAGS="-g -fbacktrace -O3" 84 | fi 85 | 86 | mkdir -p $BOOTSTRAP_DIR 87 | 88 | $FETCH $SOURCE_URL > $BOOTSTRAP_DIR/fpm.F90 89 | 90 | SAVEDIR="$(pwd)" 91 | cd $BOOTSTRAP_DIR 92 | $FC $FFLAGS fpm.F90 -o fpm 93 | cd "$SAVEDIR" 94 | 95 | $BOOTSTRAP_DIR/fpm update 96 | $BOOTSTRAP_DIR/fpm install --compiler "$FC" --flag "$FFLAGS" --prefix "$PREFIX" 97 | rm -r $BOOTSTRAP_DIR 98 | -------------------------------------------------------------------------------- /manifest-reference.md: -------------------------------------------------------------------------------- 1 | # 301 - Moved 2 | 3 | This document now lives at https://fpm.fortran-lang.org/spec/manifest.html 4 | -------------------------------------------------------------------------------- /src/filesystem_utilities.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if defined(__APPLE__) && !defined(__aarch64__) && !defined(__ppc__) && !defined(__i386__) 5 | DIR * opendir$INODE64( const char * dirName ); 6 | struct dirent * readdir$INODE64( DIR * dir ); 7 | #define opendir opendir$INODE64 8 | #define readdir readdir$INODE64 9 | #endif 10 | 11 | int c_is_dir(const char *path) 12 | { 13 | struct stat m; 14 | int r = stat(path, &m); 15 | return r == 0 && S_ISDIR(m.st_mode); 16 | } 17 | 18 | const char *get_d_name(struct dirent *d) 19 | { 20 | return (const char *) d->d_name; 21 | } 22 | 23 | DIR *c_opendir(const char *dirname) 24 | { 25 | return opendir(dirname); 26 | } 27 | 28 | struct dirent *c_readdir(DIR *dirp) 29 | { 30 | return readdir(dirp); 31 | } 32 | -------------------------------------------------------------------------------- /src/fpm/cmd/export.f90: -------------------------------------------------------------------------------- 1 | module fpm_cmd_export 2 | use fpm_command_line, only : fpm_export_settings 3 | use fpm_dependency, only : dependency_tree_t, new_dependency_tree 4 | use fpm_error, only : error_t, fpm_stop 5 | use fpm_filesystem, only : join_path 6 | use fpm_manifest, only : package_config_t, get_package_data 7 | use fpm_toml, only: name_is_json 8 | use fpm_model, only: fpm_model_t 9 | use fpm, only: build_model 10 | implicit none 11 | private 12 | public :: cmd_export 13 | 14 | contains 15 | 16 | !> Entry point for the export subcommand 17 | subroutine cmd_export(settings) 18 | !> Representation of the command line arguments 19 | type(fpm_export_settings), intent(inout) :: settings 20 | type(package_config_t) :: package 21 | type(dependency_tree_t) :: deps 22 | type(fpm_model_t) :: model 23 | type(error_t), allocatable :: error 24 | 25 | character(len=:), allocatable :: filename 26 | 27 | if (len_trim(settings%dump_manifest)<=0 .and. & 28 | len_trim(settings%dump_model)<=0 .and. & 29 | len_trim(settings%dump_dependencies)<=0) then 30 | call fpm_stop(0,'*cmd_export* exiting: no manifest/model/dependencies keyword provided') 31 | end if 32 | 33 | !> Read in manifest 34 | call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) 35 | call handle_error(error) 36 | 37 | !> Export manifest 38 | if (len_trim(settings%dump_manifest)>0) then 39 | filename = trim(settings%dump_manifest) 40 | call package%dump(filename, error, json=name_is_json(filename)) 41 | end if 42 | 43 | !> Export dependency tree 44 | if (len_trim(settings%dump_dependencies)>0) then 45 | 46 | !> Generate dependency tree 47 | filename = join_path("build", "cache.toml") 48 | call new_dependency_tree(deps, cache=filename, verbosity=merge(2, 1, settings%verbose)) 49 | call deps%add(package, error) 50 | call handle_error(error) 51 | 52 | !> Export dependency tree 53 | filename = settings%dump_dependencies 54 | call deps%dump(filename, error, json=name_is_json(filename)) 55 | call handle_error(error) 56 | end if 57 | 58 | !> Export full model 59 | if (len_trim(settings%dump_model)>0) then 60 | 61 | call build_model(model, settings%fpm_build_settings, package, error) 62 | if (allocated(error)) then 63 | call fpm_stop(1,'*cmd_export* Model error: '//error%message) 64 | end if 65 | 66 | filename = settings%dump_model 67 | call model%dump(filename, error, json=name_is_json(filename)) 68 | call handle_error(error) 69 | end if 70 | 71 | end subroutine cmd_export 72 | 73 | !> Error handling for this command 74 | subroutine handle_error(error) 75 | !> Potential error 76 | type(error_t), intent(in), optional :: error 77 | if (present(error)) then 78 | call fpm_stop(1, '*cmd_export* error: '//error%message) 79 | end if 80 | end subroutine handle_error 81 | 82 | end module fpm_cmd_export 83 | -------------------------------------------------------------------------------- /src/fpm/cmd/publish.f90: -------------------------------------------------------------------------------- 1 | !> Upload a package to the registry using the `publish` command. 2 | !> 3 | !> To upload a package you need to provide a token that will be linked to your username and created for a namespace. 4 | !> The token can be obtained from the registry website. It can be used as `fpm publish --token `. 5 | module fpm_cmd_publish 6 | use fpm_command_line, only: fpm_publish_settings 7 | use fpm_manifest, only: package_config_t, get_package_data 8 | use fpm_model, only: fpm_model_t 9 | use fpm_error, only: error_t, fpm_stop 10 | use fpm_versioning, only: version_t 11 | use fpm_filesystem, only: exists, join_path, get_temp_filename, delete_file 12 | use fpm_git, only: git_archive 13 | use fpm_downloader, only: downloader_t 14 | use fpm_strings, only: string_t 15 | use fpm_settings, only: official_registry_base_url 16 | use fpm, only: build_model 17 | 18 | implicit none 19 | private 20 | public :: cmd_publish 21 | 22 | contains 23 | 24 | !> The `publish` command first builds the root package to obtain all the relevant information such as the 25 | !> package version. It then creates a tarball of the package and uploads it to the registry. 26 | subroutine cmd_publish(settings) 27 | type(fpm_publish_settings), intent(inout) :: settings 28 | 29 | type(package_config_t) :: package 30 | type(fpm_model_t) :: model 31 | type(error_t), allocatable :: error 32 | type(version_t), allocatable :: version 33 | type(string_t), allocatable :: upload_data(:) 34 | character(len=:), allocatable :: tmp_file 35 | type(downloader_t) :: downloader 36 | integer :: i 37 | 38 | ! Get package data to determine package version. 39 | call get_package_data(package, 'fpm.toml', error, apply_defaults=.true.) 40 | if (allocated(error)) call fpm_stop(1, '*cmd_build* Package error: '//error%message) 41 | version = package%version 42 | 43 | if (settings%show_package_version) then 44 | print *, version%s(); return 45 | end if 46 | 47 | !> Checks before uploading the package. 48 | if (.not. allocated(package%license)) call fpm_stop(1, 'No license specified in fpm.toml.') 49 | if (.not. package%build%module_naming) call fpm_stop(1, 'The package does not meet the module naming requirements. '// & 50 | & 'Please set "module-naming = true" in fpm.toml [build] or specify a custom module prefix.') 51 | if (.not. allocated(version)) call fpm_stop(1, 'No version specified in fpm.toml.') 52 | if (version%s() == '0') call fpm_stop(1, 'Invalid version: "'//version%s()//'".') 53 | if (.not. exists('fpm.toml')) call fpm_stop(1, "Cannot find 'fpm.toml' file. Are you in the project root?") 54 | 55 | ! Build model to obtain dependency tree. 56 | call build_model(model, settings%fpm_build_settings, package, error) 57 | if (allocated(error)) call fpm_stop(1, '*cmd_build* Model error: '//error%message) 58 | 59 | ! Check if package contains git dependencies. Only publish packages without git dependencies. 60 | do i = 1, model%deps%ndep 61 | if (allocated(model%deps%dep(i)%git)) then 62 | call fpm_stop(1, 'Do not publish packages containing git dependencies. '// & 63 | & "Please upload '"//model%deps%dep(i)%name//"' to the registry first.") 64 | end if 65 | end do 66 | 67 | tmp_file = get_temp_filename() 68 | call git_archive('.', tmp_file, 'HEAD', additional_files=['fpm_model.json'], verbose=settings%verbose, error=error) 69 | if (allocated(error)) call fpm_stop(1, '*cmd_publish* Archive error: '//error%message) 70 | call model%dump('fpm_model.json', error, json=.true.) 71 | if (allocated(error)) call fpm_stop(1, '*cmd_publish* Model dump error: '//error%message) 72 | 73 | upload_data = [ & 74 | & string_t('package_name="'//package%name//'"'), & 75 | & string_t('package_license="'//package%license//'"'), & 76 | & string_t('package_version="'//version%s()//'"'), & 77 | & string_t('tarball=@"'//tmp_file//'"') & 78 | & ] 79 | 80 | if (allocated(settings%token)) upload_data = [upload_data, string_t('upload_token="'//settings%token//'"')] 81 | 82 | if (settings%show_upload_data) then 83 | call print_upload_data(upload_data); return 84 | end if 85 | 86 | ! Make sure a token is provided for publishing. 87 | if (allocated(settings%token)) then 88 | if (settings%token == '') then 89 | call delete_file(tmp_file); call fpm_stop(1, 'No token provided.') 90 | end if 91 | else 92 | call delete_file(tmp_file); call fpm_stop(1, 'No token provided.') 93 | end if 94 | 95 | if (settings%verbose) then 96 | call print_upload_data(upload_data) 97 | print *, '' 98 | end if 99 | 100 | ! Perform network request and validate package, token etc. on the backend once 101 | ! https://github.com/fortran-lang/registry/issues/41 is resolved. 102 | if (settings%is_dry_run) then 103 | print *, 'Dry run successful. Generated tarball: ', tmp_file; return 104 | end if 105 | 106 | call downloader%upload_form(official_registry_base_url//'/packages', upload_data, settings%verbose, error) 107 | call delete_file(tmp_file) 108 | if (allocated(error)) call fpm_stop(1, '*cmd_publish* Upload error: '//error%message) 109 | end 110 | 111 | subroutine print_upload_data(upload_data) 112 | type(string_t), intent(in) :: upload_data(:) 113 | integer :: i 114 | 115 | print *, 'Upload data:' 116 | do i = 1, size(upload_data) 117 | print *, upload_data(i)%s 118 | end do 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /src/fpm/cmd/update.f90: -------------------------------------------------------------------------------- 1 | module fpm_cmd_update 2 | use fpm_command_line, only : fpm_update_settings 3 | use fpm_dependency, only : dependency_tree_t, new_dependency_tree 4 | use fpm_error, only : error_t, fpm_stop 5 | use fpm_filesystem, only : exists, mkdir, join_path, delete_file, filewrite 6 | use fpm_manifest, only : package_config_t, get_package_data 7 | use fpm_toml, only: name_is_json 8 | implicit none 9 | private 10 | public :: cmd_update 11 | 12 | contains 13 | 14 | !> Entry point for the update subcommand 15 | subroutine cmd_update(settings) 16 | !> Representation of the command line arguments 17 | type(fpm_update_settings), intent(in) :: settings 18 | 19 | type(package_config_t) :: package 20 | type(dependency_tree_t) :: deps 21 | type(error_t), allocatable :: error 22 | integer :: ii 23 | character(len=:), allocatable :: cache 24 | 25 | call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) 26 | call handle_error(error) 27 | 28 | if (.not. exists("build")) then 29 | call mkdir("build") 30 | call filewrite(join_path("build", ".gitignore"),["*"]) 31 | end if 32 | 33 | cache = join_path("build", "cache.toml") 34 | if (settings%clean) call delete_file(cache) 35 | 36 | call new_dependency_tree(deps, cache=cache, verbosity=merge(2, 1, settings%verbose), & 37 | & path_to_config=settings%path_to_config) 38 | 39 | call deps%add(package, error) 40 | call handle_error(error) 41 | 42 | ! Force-update all dependencies if `--clean` 43 | if (settings%clean) then 44 | do ii = 1, deps%ndep 45 | deps%dep(ii)%update = .true. 46 | end do 47 | end if 48 | 49 | if (settings%fetch_only) return 50 | 51 | if (size(settings%name) == 0) then 52 | call deps%update(error) 53 | call handle_error(error) 54 | else 55 | do ii = 1, size(settings%name) 56 | call deps%update(trim(settings%name(ii)), error) 57 | call handle_error(error) 58 | end do 59 | end if 60 | 61 | if (len_trim(settings%dump)>0) then 62 | call deps%dump(trim(settings%dump), error, json=name_is_json(trim(settings%dump))) 63 | call handle_error(error) 64 | end if 65 | 66 | end subroutine cmd_update 67 | 68 | !> Error handling for this command 69 | subroutine handle_error(error) 70 | !> Potential error 71 | type(error_t), intent(in), optional :: error 72 | if (present(error)) then 73 | call fpm_stop(1, '*cmd_update* error: '//error%message) 74 | end if 75 | end subroutine handle_error 76 | 77 | end module fpm_cmd_update 78 | -------------------------------------------------------------------------------- /src/fpm/downloader.f90: -------------------------------------------------------------------------------- 1 | module fpm_downloader 2 | use fpm_error, only: error_t, fatal_error 3 | use fpm_filesystem, only: which, run 4 | use fpm_versioning, only: version_t 5 | use jonquil, only: json_object, json_value, json_error, json_load, cast_to_object 6 | use fpm_strings, only: string_t 7 | 8 | implicit none 9 | private 10 | 11 | public :: downloader_t 12 | 13 | !> This type could be entirely avoided but it is quite practical because it can be mocked for testing. 14 | type downloader_t 15 | contains 16 | procedure, nopass :: get_pkg_data, get_file, upload_form, unpack 17 | end type 18 | 19 | contains 20 | 21 | !> Perform an http get request, save output to file, and parse json. 22 | subroutine get_pkg_data(url, version, tmp_pkg_file, json, error) 23 | character(*), intent(in) :: url 24 | type(version_t), allocatable, intent(in) :: version 25 | character(*), intent(in) :: tmp_pkg_file 26 | type(json_object), intent(out) :: json 27 | type(error_t), allocatable, intent(out) :: error 28 | 29 | class(json_value), allocatable :: j_value 30 | type(json_object), pointer :: ptr 31 | type(json_error), allocatable :: j_error 32 | 33 | if (allocated(version)) then 34 | ! Request specific version. 35 | call get_file(url//'/'//version%s(), tmp_pkg_file, error) 36 | else 37 | ! Request latest version. 38 | call get_file(url, tmp_pkg_file, error) 39 | end if 40 | if (allocated(error)) return 41 | 42 | call json_load(j_value, tmp_pkg_file, error=j_error) 43 | if (allocated(j_error)) then 44 | allocate (error); call move_alloc(j_error%message, error%message); call json%destroy(); return 45 | end if 46 | 47 | ptr => cast_to_object(j_value) 48 | if (.not. associated(ptr)) then 49 | call fatal_error(error, "Error parsing JSON from '"//url//"'."); return 50 | end if 51 | 52 | json = ptr 53 | end 54 | 55 | !> Download a file from a url using either curl or wget. 56 | subroutine get_file(url, tmp_pkg_file, error) 57 | character(*), intent(in) :: url 58 | character(*), intent(in) :: tmp_pkg_file 59 | type(error_t), allocatable, intent(out) :: error 60 | 61 | integer :: stat 62 | 63 | if (which('curl') /= '') then 64 | print *, "Downloading '"//url//"' -> '"//tmp_pkg_file//"'" 65 | call execute_command_line('curl '//url//' -s -o '//tmp_pkg_file, exitstat=stat) 66 | else if (which('wget') /= '') then 67 | print *, "Downloading '"//url//"' -> '"//tmp_pkg_file//"'" 68 | call execute_command_line('wget '//url//' -q -O '//tmp_pkg_file, exitstat=stat) 69 | else 70 | call fatal_error(error, "Neither 'curl' nor 'wget' installed."); return 71 | end if 72 | 73 | if (stat /= 0) then 74 | call fatal_error(error, "Error downloading package from '"//url//"'."); return 75 | end if 76 | end 77 | 78 | !> Perform an http post request with form data. 79 | subroutine upload_form(endpoint, form_data, verbose, error) 80 | !> Endpoint to upload to. 81 | character(len=*), intent(in) :: endpoint 82 | !> Form data to upload. 83 | type(string_t), intent(in) :: form_data(:) 84 | !> Print additional information if true. 85 | logical, intent(in) :: verbose 86 | !> Error handling. 87 | type(error_t), allocatable, intent(out) :: error 88 | 89 | integer :: stat, i 90 | character(len=:), allocatable :: form_data_str 91 | 92 | form_data_str = '' 93 | do i = 1, size(form_data) 94 | form_data_str = form_data_str//"-F '"//form_data(i)%s//"' " 95 | end do 96 | 97 | if (which('curl') /= '') then 98 | print *, 'Uploading package ...' 99 | call run('curl -X POST -H "Content-Type: multipart/form-data" '// & 100 | & form_data_str//endpoint, exitstat=stat, echo=verbose) 101 | else 102 | call fatal_error(error, "'curl' not installed."); return 103 | end if 104 | 105 | if (stat /= 0) then 106 | call fatal_error(error, "Error uploading package to registry."); return 107 | end if 108 | end 109 | 110 | !> Unpack a tarball to a destination. 111 | subroutine unpack(tmp_pkg_file, destination, error) 112 | !> Path to tarball. 113 | character(*), intent(in) :: tmp_pkg_file 114 | !> Destination to unpack to. 115 | character(*), intent(in) :: destination 116 | !> Error handling. 117 | type(error_t), allocatable, intent(out) :: error 118 | 119 | integer :: stat 120 | 121 | if (which('tar') == '') then 122 | call fatal_error(error, "'tar' not installed."); return 123 | end if 124 | 125 | print *, "Unpacking '"//tmp_pkg_file//"' to '"//destination//"' ..." 126 | call execute_command_line('tar -zxf '//tmp_pkg_file//' -C '//destination, exitstat=stat) 127 | 128 | if (stat /= 0) then 129 | call fatal_error(error, "Error unpacking '"//tmp_pkg_file//"'."); return 130 | end if 131 | end 132 | end 133 | -------------------------------------------------------------------------------- /src/fpm/error.f90: -------------------------------------------------------------------------------- 1 | !> Implementation of basic error handling. 2 | module fpm_error 3 | use,intrinsic :: iso_fortran_env, only : stdin=>input_unit, stdout=>output_unit, stderr=>error_unit 4 | use fpm_strings, only : is_fortran_name, to_fortran_name 5 | implicit none 6 | private 7 | 8 | public :: error_t 9 | public :: fatal_error, syntax_error, file_not_found_error 10 | public :: file_parse_error 11 | public :: bad_name_error 12 | public :: fpm_stop 13 | 14 | 15 | !> Data type defining an error 16 | type :: error_t 17 | 18 | !> Error message 19 | character(len=:), allocatable :: message 20 | 21 | end type error_t 22 | 23 | contains 24 | 25 | !> Generic fatal runtime error 26 | subroutine fatal_error(error, message) 27 | 28 | !> Instance of the error data 29 | type(error_t), allocatable, intent(out) :: error 30 | 31 | !> Error message 32 | character(len=*), intent(in) :: message 33 | 34 | allocate(error) 35 | error%message = message 36 | 37 | end subroutine fatal_error 38 | 39 | subroutine syntax_error(error, message) 40 | 41 | !> Instance of the error data 42 | type(error_t), allocatable, intent(out) :: error 43 | 44 | !> Error message 45 | character(len=*), intent(in) :: message 46 | 47 | allocate(error) 48 | error%message = message 49 | 50 | end subroutine syntax_error 51 | 52 | function bad_name_error(error, label,name) 53 | 54 | !> Instance of the error data 55 | type(error_t), allocatable, intent(out) :: error 56 | 57 | !> Error message label to add to message 58 | character(len=*), intent(in) :: label 59 | 60 | !> name value to check 61 | character(len=*), intent(in) :: name 62 | 63 | logical :: bad_name_error 64 | 65 | if(.not.is_fortran_name(to_fortran_name(name)))then 66 | bad_name_error=.true. 67 | allocate(error) 68 | error%message = 'manifest file syntax error: '//label//' name must be composed only of & 69 | &alphanumerics, "-" and "_" and start with a letter ::'//name 70 | else 71 | bad_name_error=.false. 72 | endif 73 | 74 | end function bad_name_error 75 | 76 | 77 | !> Error created when a file is missing or not found 78 | subroutine file_not_found_error(error, file_name) 79 | 80 | !> Instance of the error data 81 | type(error_t), allocatable, intent(out) :: error 82 | 83 | !> Name of the missing file 84 | character(len=*), intent(in) :: file_name 85 | 86 | allocate(error) 87 | error%message = "'"//file_name//"' could not be found, check if the file exists" 88 | 89 | end subroutine file_not_found_error 90 | 91 | 92 | !> Error created when file parsing fails 93 | subroutine file_parse_error(error, file_name, message, line_num, & 94 | line_string, line_col) 95 | 96 | !> Instance of the error data 97 | type(error_t), allocatable, intent(out) :: error 98 | 99 | !> Name of file 100 | character(len=*), intent(in) :: file_name 101 | 102 | !> Parse error message 103 | character(len=*), intent(in) :: message 104 | 105 | !> Line number of parse error 106 | integer, intent(in), optional :: line_num 107 | 108 | !> Line context string 109 | character(len=*), intent(in), optional :: line_string 110 | 111 | !> Line context column 112 | integer, intent(in), optional :: line_col 113 | 114 | character(50) :: temp_string 115 | 116 | allocate(error) 117 | error%message = 'Parse error: '//message//new_line('a') 118 | 119 | error%message = error%message//file_name 120 | 121 | if (present(line_num)) then 122 | 123 | write(temp_string,'(I0)') line_num 124 | 125 | error%message = error%message//':'//trim(temp_string) 126 | 127 | end if 128 | 129 | if (present(line_col)) then 130 | 131 | if (line_col > 0) then 132 | 133 | write(temp_string,'(I0)') line_col 134 | error%message = error%message//':'//trim(temp_string) 135 | 136 | end if 137 | 138 | end if 139 | 140 | if (present(line_string)) then 141 | 142 | error%message = error%message//new_line('a') 143 | error%message = error%message//' | '//line_string 144 | 145 | if (present(line_col)) then 146 | 147 | if (line_col > 0) then 148 | 149 | error%message = error%message//new_line('a') 150 | error%message = error%message//' | '//repeat(' ',line_col-1)//'^' 151 | 152 | end if 153 | 154 | end if 155 | 156 | end if 157 | 158 | end subroutine file_parse_error 159 | 160 | subroutine fpm_stop(value,message) 161 | ! TODO: if verbose mode, call ERROR STOP instead of STOP 162 | ! TODO: if M_escape is used, add color 163 | ! to work with older compilers might need a case statement for values 164 | 165 | !> value to use on STOP 166 | integer, intent(in) :: value 167 | !> Error message 168 | character(len=*), intent(in) :: message 169 | integer :: iostat 170 | if(message/='')then 171 | flush(unit=stderr,iostat=iostat) 172 | flush(unit=stdout,iostat=iostat) 173 | if(value>0)then 174 | write(stderr,'(" ",a)')trim(message) 175 | else 176 | write(stderr,'(" ",a)')trim(message) 177 | endif 178 | flush(unit=stderr,iostat=iostat) 179 | endif 180 | stop value 181 | end subroutine fpm_stop 182 | 183 | end module fpm_error 184 | -------------------------------------------------------------------------------- /src/fpm/fpm_release.F90: -------------------------------------------------------------------------------- 1 | !># Release parameters 2 | !> Module fpm_release contains public constants storing this build's unique version IDs 3 | module fpm_release 4 | use fpm_versioning, only: version_t,new_version 5 | use fpm_error, only: error_t, fpm_stop 6 | implicit none 7 | private 8 | 9 | public :: fpm_version 10 | public :: version_t 11 | 12 | contains 13 | 14 | !> Return the current fpm version from fpm_version_ID as a version type 15 | type(version_t) function fpm_version() 16 | 17 | type(error_t), allocatable :: error 18 | 19 | ! Fallback to last known version in case of undefined macro 20 | #ifndef FPM_RELEASE_VERSION 21 | # define FPM_RELEASE_VERSION 0.12.0 22 | #endif 23 | 24 | ! Accept solution from https://stackoverflow.com/questions/31649691/stringify-macro-with-gnu-gfortran 25 | ! which provides the "easiest" way to pass a macro to a string in Fortran complying with both 26 | ! gfortran's "traditional" cpp and the standard cpp syntaxes 27 | #ifdef __GFORTRAN__ /* traditional-cpp stringification */ 28 | # define STRINGIFY_START(X) "& 29 | # define STRINGIFY_END(X) &X" 30 | #else /* default stringification */ 31 | # define STRINGIFY_(X) #X 32 | # define STRINGIFY_START(X) & 33 | # define STRINGIFY_END(X) STRINGIFY_(X) 34 | #endif 35 | 36 | character (len=:), allocatable :: ver_string 37 | ver_string = STRINGIFY_START(FPM_RELEASE_VERSION) 38 | STRINGIFY_END(FPM_RELEASE_VERSION) 39 | 40 | call new_version(fpm_version,ver_string,error) 41 | 42 | if (allocated(error)) call fpm_stop(1,'*fpm*:internal error: cannot get version - '//error%message) 43 | 44 | end function fpm_version 45 | 46 | end module fpm_release 47 | -------------------------------------------------------------------------------- /src/fpm/manifest/install.f90: -------------------------------------------------------------------------------- 1 | !> Implementation of the installation configuration. 2 | !> 3 | !> An install table can currently have the following fields 4 | !> 5 | !>```toml 6 | !>library = bool 7 | !>``` 8 | module fpm_manifest_install 9 | use fpm_error, only : error_t, fatal_error, syntax_error 10 | use tomlf, only : toml_table, toml_key, toml_stat 11 | use fpm_toml, only : get_value, set_value, serializable_t 12 | implicit none 13 | private 14 | 15 | public :: install_config_t, new_install_config 16 | 17 | !> Configuration data for installation 18 | type, extends(serializable_t) :: install_config_t 19 | 20 | !> Install library with this project 21 | logical :: library = .false. 22 | 23 | !> Install tests with this project 24 | logical :: test = .false. 25 | 26 | contains 27 | 28 | !> Print information on this instance 29 | procedure :: info 30 | 31 | !> Serialization interface 32 | procedure :: serializable_is_same => install_conf_same 33 | procedure :: dump_to_toml 34 | procedure :: load_from_toml 35 | 36 | end type install_config_t 37 | 38 | character(*), parameter, private :: class_name = 'install_config_t' 39 | 40 | contains 41 | 42 | !> Create a new installation configuration from a TOML data structure 43 | subroutine new_install_config(self, table, error) 44 | 45 | !> Instance of the install configuration 46 | type(install_config_t), intent(out) :: self 47 | 48 | !> Instance of the TOML data structure 49 | type(toml_table), intent(inout) :: table 50 | 51 | !> Error handling 52 | type(error_t), allocatable, intent(out) :: error 53 | 54 | call check(table, error) 55 | if (allocated(error)) return 56 | 57 | call get_value(table, "library", self%library, .false.) 58 | call get_value(table, "test", self%test, .false.) 59 | 60 | end subroutine new_install_config 61 | 62 | 63 | !> Check local schema for allowed entries 64 | subroutine check(table, error) 65 | 66 | !> Instance of the TOML data structure 67 | type(toml_table), intent(inout) :: table 68 | 69 | !> Error handling 70 | type(error_t), allocatable, intent(out) :: error 71 | 72 | type(toml_key), allocatable :: list(:) 73 | integer :: ikey 74 | 75 | call table%get_keys(list) 76 | if (size(list) < 1) return 77 | 78 | do ikey = 1, size(list) 79 | select case(list(ikey)%key) 80 | case default 81 | call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in install table") 82 | exit 83 | case("library","test") 84 | continue 85 | end select 86 | end do 87 | if (allocated(error)) return 88 | 89 | end subroutine check 90 | 91 | !> Write information on install configuration instance 92 | subroutine info(self, unit, verbosity) 93 | 94 | !> Instance of the build configuration 95 | class(install_config_t), intent(in) :: self 96 | 97 | !> Unit for IO 98 | integer, intent(in) :: unit 99 | 100 | !> Verbosity of the printout 101 | integer, intent(in), optional :: verbosity 102 | 103 | integer :: pr 104 | character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)' 105 | 106 | if (present(verbosity)) then 107 | pr = verbosity 108 | else 109 | pr = 1 110 | end if 111 | 112 | if (pr < 1) return 113 | 114 | write(unit, fmt) "Install configuration" 115 | write(unit, fmt) " - library install", trim(merge("enabled ", "disabled", self%library)) 116 | write(unit, fmt) " - test install", trim(merge("enabled ", "disabled", self%test)) 117 | 118 | end subroutine info 119 | 120 | logical function install_conf_same(this,that) 121 | class(install_config_t), intent(in) :: this 122 | class(serializable_t), intent(in) :: that 123 | 124 | install_conf_same = .false. 125 | 126 | select type (other=>that) 127 | type is (install_config_t) 128 | if (this%library.neqv.other%library) return 129 | if (this%test.neqv.other%test) return 130 | class default 131 | ! Not the same type 132 | return 133 | end select 134 | 135 | !> All checks passed! 136 | install_conf_same = .true. 137 | 138 | end function install_conf_same 139 | 140 | !> Dump install config to toml table 141 | subroutine dump_to_toml(self, table, error) 142 | 143 | !> Instance of the serializable object 144 | class(install_config_t), intent(inout) :: self 145 | 146 | !> Data structure 147 | type(toml_table), intent(inout) :: table 148 | 149 | !> Error handling 150 | type(error_t), allocatable, intent(out) :: error 151 | 152 | call set_value(table, "library", self%library, error, class_name) 153 | if (allocated(error)) return 154 | 155 | call set_value(table, "test", self%test, error, class_name) 156 | if (allocated(error)) return 157 | 158 | end subroutine dump_to_toml 159 | 160 | !> Read install config from toml table (no checks made at this stage) 161 | subroutine load_from_toml(self, table, error) 162 | 163 | !> Instance of the serializable object 164 | class(install_config_t), intent(inout) :: self 165 | 166 | !> Data structure 167 | type(toml_table), intent(inout) :: table 168 | 169 | !> Error handling 170 | type(error_t), allocatable, intent(out) :: error 171 | 172 | integer :: stat 173 | 174 | call get_value(table, "library", self%library, error, class_name) 175 | if (allocated(error)) return 176 | call get_value(table, "test", self%test, error, class_name) 177 | if (allocated(error)) return 178 | 179 | end subroutine load_from_toml 180 | 181 | end module fpm_manifest_install 182 | -------------------------------------------------------------------------------- /src/fpm/manifest/test.f90: -------------------------------------------------------------------------------- 1 | !> Implementation of the meta data for a test. 2 | !> 3 | !> The test data structure is effectively a decorated version of an executable 4 | !> and shares most of its properties, except for the defaults and can be 5 | !> handled under most circumstances just like any other executable. 6 | !> 7 | !> A test table can currently have the following fields 8 | !> 9 | !>```toml 10 | !>[[ test ]] 11 | !>name = "string" 12 | !>source-dir = "path" 13 | !>main = "file" 14 | !>link = ["lib"] 15 | !>[test.dependencies] 16 | !>``` 17 | module fpm_manifest_test 18 | use fpm_manifest_dependency, only : new_dependencies 19 | use fpm_manifest_executable, only : executable_config_t 20 | use fpm_error, only : error_t, syntax_error, bad_name_error 21 | use tomlf, only : toml_table, toml_key, toml_stat 22 | use fpm_toml, only : get_value, get_list 23 | implicit none 24 | private 25 | 26 | public :: test_config_t, new_test 27 | 28 | 29 | !> Configuation meta data for an test 30 | type, extends(executable_config_t) :: test_config_t 31 | 32 | contains 33 | 34 | !> Print information on this instance 35 | procedure :: info 36 | 37 | end type test_config_t 38 | 39 | 40 | contains 41 | 42 | 43 | !> Construct a new test configuration from a TOML data structure 44 | subroutine new_test(self, table, error) 45 | 46 | !> Instance of the test configuration 47 | type(test_config_t), intent(out) :: self 48 | 49 | !> Instance of the TOML data structure 50 | type(toml_table), intent(inout) :: table 51 | 52 | !> Error handling 53 | type(error_t), allocatable, intent(out) :: error 54 | 55 | type(toml_table), pointer :: child 56 | 57 | call check(table, error) 58 | if (allocated(error)) return 59 | 60 | call get_value(table, "name", self%name) 61 | if (.not.allocated(self%name)) then 62 | call syntax_error(error, "Could not retrieve test name") 63 | return 64 | end if 65 | if (bad_name_error(error,'test',self%name))then 66 | return 67 | endif 68 | call get_value(table, "source-dir", self%source_dir, "test") 69 | call get_value(table, "main", self%main, "main.f90") 70 | 71 | call get_value(table, "dependencies", child, requested=.false.) 72 | if (associated(child)) then 73 | call new_dependencies(self%dependency, child, error=error) 74 | if (allocated(error)) return 75 | end if 76 | 77 | call get_list(table, "link", self%link, error) 78 | if (allocated(error)) return 79 | 80 | end subroutine new_test 81 | 82 | 83 | !> Check local schema for allowed entries 84 | subroutine check(table, error) 85 | 86 | !> Instance of the TOML data structure 87 | type(toml_table), intent(inout) :: table 88 | 89 | !> Error handling 90 | type(error_t), allocatable, intent(out) :: error 91 | 92 | type(toml_key), allocatable :: list(:) 93 | logical :: name_present 94 | integer :: ikey 95 | 96 | name_present = .false. 97 | 98 | call table%get_keys(list) 99 | 100 | if (size(list) < 1) then 101 | call syntax_error(error, "Test section does not provide sufficient entries") 102 | return 103 | end if 104 | 105 | do ikey = 1, size(list) 106 | select case(list(ikey)%key) 107 | case default 108 | call syntax_error(error, "Key "//list(ikey)%key//" is not allowed in test entry") 109 | exit 110 | 111 | case("name") 112 | name_present = .true. 113 | 114 | case("source-dir", "main", "dependencies", "link") 115 | continue 116 | 117 | end select 118 | end do 119 | if (allocated(error)) return 120 | 121 | if (.not.name_present) then 122 | call syntax_error(error, "Test name is not provided, please add a name entry") 123 | end if 124 | 125 | end subroutine check 126 | 127 | 128 | !> Write information on instance 129 | subroutine info(self, unit, verbosity) 130 | 131 | !> Instance of the test configuration 132 | class(test_config_t), intent(in) :: self 133 | 134 | !> Unit for IO 135 | integer, intent(in) :: unit 136 | 137 | !> Verbosity of the printout 138 | integer, intent(in), optional :: verbosity 139 | 140 | integer :: pr, ii 141 | character(len=*), parameter :: fmt = '("#", 1x, a, t30, a)', & 142 | & fmti = '("#", 1x, a, t30, i0)' 143 | 144 | if (present(verbosity)) then 145 | pr = verbosity 146 | else 147 | pr = 1 148 | end if 149 | 150 | if (pr < 1) return 151 | 152 | write(unit, fmt) "Test target" 153 | if (allocated(self%name)) then 154 | write(unit, fmt) "- name", self%name 155 | end if 156 | if (allocated(self%source_dir)) then 157 | if (self%source_dir /= "test" .or. pr > 2) then 158 | write(unit, fmt) "- source directory", self%source_dir 159 | end if 160 | end if 161 | if (allocated(self%main)) then 162 | if (self%main /= "main.f90" .or. pr > 2) then 163 | write(unit, fmt) "- test source", self%main 164 | end if 165 | end if 166 | 167 | if (allocated(self%dependency)) then 168 | if (size(self%dependency) > 1 .or. pr > 2) then 169 | write(unit, fmti) "- dependencies", size(self%dependency) 170 | end if 171 | do ii = 1, size(self%dependency) 172 | call self%dependency(ii)%info(unit, pr - 1) 173 | end do 174 | end if 175 | 176 | end subroutine info 177 | 178 | 179 | end module fpm_manifest_test 180 | -------------------------------------------------------------------------------- /src/fpm_backend_console.f90: -------------------------------------------------------------------------------- 1 | !># Build Backend Console 2 | !> This module provides a lightweight implementation for printing to the console 3 | !> and updating previously-printed console lines. It used by `[[fpm_backend_output]]` 4 | !> for pretty-printing build status and progress. 5 | !> 6 | !> @note The implementation for updating previous lines relies on no other output 7 | !> going to `stdout`/`stderr` except through the `console_t` object provided. 8 | !> 9 | !> @note All write statements to `stdout` are enclosed within OpenMP `critical` regions 10 | !> 11 | module fpm_backend_console 12 | use iso_fortran_env, only: stdout=>output_unit 13 | implicit none 14 | 15 | private 16 | public :: console_t 17 | public :: LINE_RESET 18 | public :: COLOR_RED, COLOR_GREEN, COLOR_YELLOW, COLOR_RESET 19 | 20 | character(len=*), parameter :: ESC = char(27) 21 | !> Escape code for erasing current line 22 | character(len=*), parameter :: LINE_RESET = ESC//"[2K"//ESC//"[1G" 23 | !> Escape code for moving up one line 24 | character(len=*), parameter :: LINE_UP = ESC//"[1A" 25 | !> Escape code for moving down one line 26 | character(len=*), parameter :: LINE_DOWN = ESC//"[1B" 27 | !> Escape code for red foreground color 28 | character(len=*), parameter :: COLOR_RED = ESC//"[31m" 29 | !> Escape code for green foreground color 30 | character(len=*), parameter :: COLOR_GREEN = ESC//"[32m" 31 | !> Escape code for yellow foreground color 32 | character(len=*), parameter :: COLOR_YELLOW = ESC//"[93m" 33 | !> Escape code to reset foreground color 34 | character(len=*), parameter :: COLOR_RESET = ESC//"[0m" 35 | 36 | !> Console object 37 | type console_t 38 | !> Number of lines printed 39 | integer :: n_line = 1 40 | 41 | contains 42 | !> Write a single line to the console 43 | procedure :: write_line => console_write_line 44 | !> Update a previously-written console line 45 | procedure :: update_line => console_update_line 46 | end type console_t 47 | 48 | contains 49 | 50 | !> Write a single line to the standard output 51 | subroutine console_write_line(console,str,line,advance) 52 | !> Console object 53 | class(console_t), intent(inout) :: console 54 | !> String to write 55 | character(*), intent(in) :: str 56 | !> Integer needed to later update console line 57 | integer, intent(out), optional :: line 58 | !> Advancing output (print newline?) 59 | logical, intent(in), optional :: advance 60 | 61 | character(3) :: adv 62 | 63 | adv = "yes" 64 | if (present(advance)) then 65 | if (.not.advance) then 66 | adv = "no" 67 | end if 68 | end if 69 | 70 | !$omp critical 71 | 72 | if (present(line)) then 73 | line = console%n_line 74 | end if 75 | 76 | write(stdout,'(A)',advance=trim(adv)) LINE_RESET//str 77 | 78 | if (adv=="yes") then 79 | console%n_line = console%n_line + 1 80 | end if 81 | 82 | !$omp end critical 83 | 84 | end subroutine console_write_line 85 | 86 | !> Overwrite a previously-written line in standard output 87 | subroutine console_update_line(console,line_no,str) 88 | !> Console object 89 | class(console_t), intent(in) :: console 90 | !> Integer output from `[[console_write_line]]` 91 | integer, intent(in) :: line_no 92 | !> New string to overwrite line 93 | character(*), intent(in) :: str 94 | 95 | integer :: n 96 | 97 | !$omp critical 98 | 99 | n = console%n_line - line_no 100 | 101 | ! Step back to line 102 | write(stdout,'(A)',advance="no") repeat(LINE_UP,n)//LINE_RESET 103 | 104 | write(stdout,'(A)',advance="no") str 105 | 106 | ! Step forward to end 107 | write(stdout,'(A)',advance="no") repeat(LINE_DOWN,n)//LINE_RESET 108 | 109 | !$omp end critical 110 | 111 | end subroutine console_update_line 112 | 113 | end module fpm_backend_console -------------------------------------------------------------------------------- /src/fpm_environment.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /// @brief Set environment variable using the C standard library 5 | /// @param envname: points to a string containing the name of an environment variable to be added or altered. 6 | /// @param envval: points to the value the environment variable is set to 7 | /// @param overwrite: flag to determine whether an old value should be overwritten 8 | /// @return success flag, 0 on successful execution 9 | int c_setenv(const char *envname, const char *envval, int overwrite) { 10 | #ifndef _WIN32 11 | return setenv(envname, envval, overwrite); 12 | #else 13 | int errcode = 0; 14 | if(!overwrite) { 15 | size_t envsize = 0; 16 | errcode = getenv_s(&envsize, NULL, 0, envname); 17 | if (errcode || envsize) return errcode; 18 | } 19 | return _putenv_s(envname, envval); 20 | #endif 21 | } 22 | 23 | /// @brief Delete environment variable using the C standard library 24 | /// @param envname: points to a string containing the name of an environment variable. 25 | /// @return success flag, 0 on successful execution 26 | int c_unsetenv(const char *envname) { 27 | #ifndef _WIN32 28 | return unsetenv(envname); 29 | #else 30 | char* str = malloc(64*sizeof(char)); 31 | *str = '\0'; 32 | int errcode = _putenv_s(envname,str); 33 | // Windows returns a non-0 code when setting empty variable 34 | if (errcode==-1) errcode=0; 35 | free(str); 36 | return errcode; 37 | #endif 38 | } 39 | -------------------------------------------------------------------------------- /src/fpm_meta.f90: -------------------------------------------------------------------------------- 1 | !># The fpm meta-package model 2 | !> 3 | !> This is a wrapper data type that encapsulate all pre-processing information 4 | !> (compiler flags, linker libraries, etc.) required to correctly enable a package 5 | !> to use a core library. 6 | !> 7 | !> 8 | !>### Available core libraries 9 | !> 10 | !> - OpenMP 11 | !> - MPI 12 | !> - HDF5 13 | !> - fortran-lang stdlib 14 | !> - fortran-lang minpack 15 | !> 16 | !> 17 | !> @note Core libraries are enabled in the [build] section of the fpm.toml manifest 18 | !> 19 | !> 20 | module fpm_meta 21 | use fpm_compiler, only: compiler_t 22 | use fpm_manifest, only: package_config_t 23 | use fpm_model, only: fpm_model_t 24 | use fpm_command_line, only: fpm_cmd_settings, fpm_build_settings, fpm_run_settings 25 | use fpm_error, only: error_t, syntax_error, fatal_error 26 | 27 | use fpm_meta_base, only: metapackage_t, destroy 28 | use fpm_meta_openmp, only: init_openmp 29 | use fpm_meta_stdlib, only: init_stdlib 30 | use fpm_meta_minpack, only: init_minpack 31 | use fpm_meta_mpi, only: init_mpi 32 | use fpm_meta_hdf5, only: init_hdf5 33 | use fpm_meta_netcdf, only: init_netcdf 34 | use fpm_meta_blas, only: init_blas 35 | use fpm_manifest_metapackages, only: metapackage_request_t 36 | 37 | use shlex_module, only: shlex_split => split 38 | use regex_module, only: regex 39 | use iso_fortran_env, only: stdout => output_unit 40 | 41 | implicit none 42 | 43 | private 44 | 45 | public :: resolve_metapackages 46 | 47 | interface resolve_metapackages 48 | module procedure resolve_metapackage_model 49 | end interface resolve_metapackages 50 | 51 | contains 52 | 53 | !> Initialize a metapackage from the given name 54 | subroutine init_from_request(this,request,compiler,all_meta,error) 55 | class(metapackage_t), intent(inout) :: this 56 | type(metapackage_request_t), intent(in) :: request 57 | type(compiler_t), intent(in) :: compiler 58 | !> Pass a list of all metapackage requests so dependencies can be sorted out 59 | type(metapackage_request_t), intent(in) :: all_meta(:) 60 | type(error_t), allocatable, intent(out) :: error 61 | 62 | !> Initialize metapackage by name 63 | select case(request%name) 64 | case("openmp"); call init_openmp (this,compiler,all_meta,error) 65 | case("stdlib"); call init_stdlib (this,compiler,all_meta,error) 66 | case("minpack"); call init_minpack(this,compiler,all_meta,error) 67 | case("mpi"); call init_mpi (this,compiler,all_meta,error) 68 | case("hdf5"); call init_hdf5 (this,compiler,all_meta,error) 69 | case("netcdf"); call init_netcdf (this,compiler,all_meta,error) 70 | case("blas"); call init_blas (this,compiler,all_meta,error) 71 | case default 72 | call syntax_error(error, "Package "//request%name//" is not supported in [metapackages]") 73 | return 74 | end select 75 | 76 | end subroutine init_from_request 77 | 78 | !> Add named metapackage dependency to the model 79 | subroutine add_metapackage_model(model,package,settings,meta,error) 80 | type(fpm_model_t), intent(inout) :: model 81 | type(package_config_t), intent(inout) :: package 82 | class(fpm_cmd_settings), intent(inout) :: settings 83 | type(metapackage_t), intent(inout) :: meta 84 | type(error_t), allocatable, intent(out) :: error 85 | 86 | !> Add it into the model 87 | call meta%resolve(model,error) 88 | if (allocated(error)) return 89 | 90 | !> Add it into the package 91 | call meta%resolve(package,error) 92 | if (allocated(error)) return 93 | 94 | !> Add it into the settings 95 | call meta%resolve(settings,error) 96 | if (allocated(error)) return 97 | 98 | ! If we need to run executables, there should be an MPI runner 99 | if (meta%name=="mpi") then 100 | select type (settings) 101 | class is (fpm_run_settings) ! run, test 102 | if (.not.meta%has_run_command) & 103 | call fatal_error(error,"cannot find a valid mpi runner on the local host") 104 | end select 105 | endif 106 | 107 | end subroutine add_metapackage_model 108 | 109 | !> Resolve all metapackages into the package config 110 | subroutine resolve_metapackage_model(model,package,settings,error) 111 | type(fpm_model_t), intent(inout) :: model 112 | type(package_config_t), intent(inout) :: package 113 | class(fpm_build_settings), intent(inout) :: settings 114 | type(error_t), allocatable, intent(out) :: error 115 | 116 | integer :: m 117 | type(metapackage_t) :: meta 118 | type(metapackage_request_t), allocatable :: requested(:) 119 | 120 | ! Dependencies are added to the package config, so they're properly resolved 121 | ! into the dependency tree later. 122 | ! Flags are added to the model (whose compiler needs to be already initialized) 123 | if (model%compiler%is_unknown()) & 124 | write(stdout,'(a)') ' compiler not initialized: metapackages may not be available' 125 | 126 | ! Get all requested metapackages 127 | requested = package%meta%get_requests() 128 | if (size(requested)<1) return 129 | 130 | do m=1,size(requested) 131 | 132 | call init_from_request(meta,requested(m),model%compiler,requested,error) 133 | if (allocated(error)) return 134 | 135 | call add_metapackage_model(model,package,settings,meta,error) 136 | if (allocated(error)) return 137 | 138 | end do 139 | 140 | end subroutine resolve_metapackage_model 141 | 142 | end module fpm_meta 143 | -------------------------------------------------------------------------------- /src/fpm_os.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /// @brief Determine the absolute, canonicalized path for a given path. 5 | /// @param path 6 | /// @param resolved_path 7 | /// @param maxLength 8 | /// @return 9 | char* c_realpath(char* path, char* resolved_path, int maxLength) { 10 | // Checking macro in C because it doesn't work with gfortran on Windows, even 11 | // when exported manually. 12 | #ifndef _WIN32 13 | return realpath(path, resolved_path); 14 | #else 15 | return _fullpath(resolved_path, path, maxLength); 16 | #endif 17 | } 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/metapackage/fpm_meta_blas.f90: -------------------------------------------------------------------------------- 1 | module fpm_meta_blas 2 | use fpm_compiler, only: compiler_t, get_include_flag 3 | use fpm_environment, only: get_os_type, OS_MACOS, OS_WINDOWS 4 | use fpm_meta_base, only: metapackage_t, destroy 5 | use fpm_meta_util, only: add_pkg_config_compile_options 6 | use fpm_pkg_config, only: assert_pkg_config, pkgcfg_has_package 7 | use fpm_manifest_metapackages, only: metapackage_request_t 8 | use fpm_strings, only: string_t 9 | use fpm_error, only: error_t, fatal_error 10 | 11 | implicit none 12 | 13 | private 14 | 15 | public :: init_blas 16 | 17 | contains 18 | 19 | !> Initialize blas metapackage for the current system 20 | subroutine init_blas(this, compiler, all_meta, error) 21 | class(metapackage_t), intent(inout) :: this 22 | type(compiler_t), intent(in) :: compiler 23 | type(metapackage_request_t), intent(in) :: all_meta(:) 24 | type(error_t), allocatable, intent(out) :: error 25 | 26 | integer :: i 27 | character(len=:), allocatable :: include_flag, libdir 28 | character(*), parameter :: candidates(*) = & 29 | [character(20) :: 'mkl-dynamic-lp64-tbb', 'openblas', 'blas'] 30 | 31 | include_flag = get_include_flag(compiler, "") 32 | 33 | !> Cleanup 34 | call destroy(this) 35 | allocate (this%link_libs(0), this%incl_dirs(0), this%external_modules(0)) 36 | this%link_flags = string_t("") 37 | this%flags = string_t("") 38 | this%has_external_modules = .false. 39 | 40 | !> Set name 41 | this%name = "" 42 | 43 | if (get_os_type() == OS_MACOS) then 44 | if (compile_and_link_flags_supported(compiler, "-framework Accelerate")) then 45 | call set_compile_and_link_flags(this, compiler, "-framework Accelerate") 46 | return 47 | end if 48 | end if 49 | 50 | if (compiler%is_intel()) then 51 | if (get_os_type() == OS_WINDOWS) then 52 | if (compile_and_link_flags_supported(compiler, "/Qmkl")) then 53 | call set_compile_and_link_flags(this, compiler, "/Qmkl") 54 | return 55 | end if 56 | else if (compile_and_link_flags_supported(compiler, "-qmkl")) then 57 | call set_compile_and_link_flags(this, compiler, "-qmkl") 58 | return 59 | endif 60 | end if 61 | 62 | !> Assert pkg-config is installed 63 | if (.not. assert_pkg_config()) then 64 | call fatal_error(error, 'blas metapackage requires pkg-config to continue lookup') 65 | return 66 | end if 67 | 68 | do i = 1, size(candidates) 69 | if (pkgcfg_has_package(trim(candidates(i)))) then 70 | call add_pkg_config_compile_options( & 71 | this, trim(candidates(i)), include_flag, libdir, error) 72 | print *, 'found blas package: ', trim(candidates(i)) 73 | return 74 | end if 75 | end do 76 | 77 | call fatal_error(error, 'pkg-config could not find a suitable blas package.') 78 | end subroutine init_blas 79 | 80 | function compile_and_link_flags_supported(compiler, flags) result(is_supported) 81 | type(compiler_t), intent(in) :: compiler 82 | character(len=*), intent(in) :: flags 83 | logical :: is_supported 84 | 85 | is_supported = compiler%check_flags_supported(compile_flags=flags, link_flags=flags) 86 | end function compile_and_link_flags_supported 87 | 88 | subroutine set_compile_and_link_flags(this, compiler, flags) 89 | class(metapackage_t), intent(inout) :: this 90 | type(compiler_t), intent(in) :: compiler 91 | character(len=*), intent(in) :: flags 92 | 93 | this%flags = string_t(flags) 94 | this%link_flags = string_t(flags) 95 | this%has_build_flags = .true. 96 | this%has_link_flags = .true. 97 | end subroutine set_compile_and_link_flags 98 | end module fpm_meta_blas 99 | -------------------------------------------------------------------------------- /src/metapackage/fpm_meta_minpack.f90: -------------------------------------------------------------------------------- 1 | module fpm_meta_minpack 2 | use fpm_compiler, only: compiler_t 3 | use fpm_meta_base, only: metapackage_t, destroy 4 | use fpm_error, only: error_t, fatal_error 5 | use fpm_git, only: git_target_tag 6 | use fpm_manifest_metapackages, only: metapackage_request_t 7 | 8 | implicit none 9 | 10 | private 11 | 12 | public :: init_minpack 13 | 14 | contains 15 | 16 | !> Initialize minpack metapackage for the current system 17 | subroutine init_minpack(this,compiler,all_meta,error) 18 | class(metapackage_t), intent(inout) :: this 19 | type(compiler_t), intent(in) :: compiler 20 | type(metapackage_request_t), intent(in) :: all_meta(:) 21 | type(error_t), allocatable, intent(out) :: error 22 | 23 | !> Cleanup 24 | call destroy(this) 25 | 26 | !> Set name 27 | this%name = "minpack" 28 | 29 | !> minpack is queried as a dependency from the official repository 30 | this%has_dependencies = .true. 31 | 32 | allocate(this%dependency(1)) 33 | 34 | !> 1) minpack. There are no true releases currently. Fetch HEAD 35 | this%dependency(1)%name = "minpack" 36 | this%dependency(1)%git = git_target_tag("https://github.com/fortran-lang/minpack", "v2.0.0-rc.1") 37 | if (.not.allocated(this%dependency(1)%git)) then 38 | call fatal_error(error,'cannot initialize git repo dependency for minpack metapackage') 39 | return 40 | end if 41 | 42 | end subroutine init_minpack 43 | end module fpm_meta_minpack 44 | -------------------------------------------------------------------------------- /src/metapackage/fpm_meta_netcdf.f90: -------------------------------------------------------------------------------- 1 | module fpm_meta_netcdf 2 | use fpm_compiler, only: compiler_t, get_include_flag 3 | use fpm_meta_base, only: metapackage_t, destroy 4 | use fpm_meta_util, only: add_pkg_config_compile_options 5 | use fpm_pkg_config, only: assert_pkg_config, pkgcfg_has_package 6 | use fpm_strings, only: string_t 7 | use fpm_error, only: error_t, fatal_error 8 | use fpm_manifest_metapackages, only: metapackage_request_t 9 | 10 | implicit none 11 | 12 | private 13 | 14 | public :: init_netcdf 15 | 16 | contains 17 | 18 | !> Initialize NetCDF metapackage for the current system 19 | subroutine init_netcdf(this, compiler, all_meta, error) 20 | class(metapackage_t), intent(inout) :: this 21 | type(compiler_t), intent(in) :: compiler 22 | type(metapackage_request_t), intent(in) :: all_meta(:) 23 | type(error_t), allocatable, intent(out) :: error 24 | 25 | logical :: s 26 | character(len=:), allocatable :: include_flag, libdir 27 | 28 | include_flag = get_include_flag(compiler, "") 29 | 30 | !> Cleanup 31 | call destroy(this) 32 | allocate (this % link_libs(0), this % incl_dirs(0), this % external_modules(0)) 33 | this % link_flags = string_t("") 34 | this % flags = string_t("") 35 | 36 | !> Set name 37 | this%name = "netcdf" 38 | 39 | !> Assert pkg-config is installed 40 | if (.not. assert_pkg_config()) then 41 | call fatal_error(error, 'netcdf metapackage requires pkg-config') 42 | return 43 | end if 44 | 45 | if (.not. pkgcfg_has_package('netcdf')) then 46 | call fatal_error(error, 'pkg-config could not find a suitable netcdf package.') 47 | return 48 | end if 49 | call add_pkg_config_compile_options(this, 'netcdf', include_flag, libdir, error) 50 | if (allocated(error)) return 51 | 52 | if (.not. pkgcfg_has_package('netcdf-fortran')) then 53 | call fatal_error(error, & 54 | 'pkg-config could not find a suitable netcdf-fortran package.') 55 | return 56 | end if 57 | call add_pkg_config_compile_options(this, 'netcdf-fortran', include_flag, libdir, error) 58 | if (allocated(error)) return 59 | 60 | !> Add NetCDF modules as external 61 | this % has_external_modules = .true. 62 | this % external_modules = [string_t('netcdf'), & 63 | string_t('netcdf4_f03'), & 64 | string_t('netcdf4_nc_interfaces'), & 65 | string_t('netcdf4_nf_interfaces'), & 66 | string_t('netcdf_f03'), & 67 | string_t('netcdf_fortv2_c_interfaces'), & 68 | string_t('netcdf_nc_data'), & 69 | string_t('netcdf_nc_interfaces'), & 70 | string_t('netcdf_nf_data'), & 71 | string_t('netcdf_nf_interfaces')] 72 | end subroutine init_netcdf 73 | end module fpm_meta_netcdf 74 | -------------------------------------------------------------------------------- /src/metapackage/fpm_meta_openmp.f90: -------------------------------------------------------------------------------- 1 | module fpm_meta_openmp 2 | use fpm_compiler, only: compiler_t, id_gcc, id_f95, id_intel_classic_windows, & 3 | id_intel_llvm_windows, id_intel_classic_nix, id_intel_llvm_nix, & 4 | id_intel_classic_mac, id_pgi, id_nvhpc, id_ibmxl, id_nag, id_lfortran, & 5 | id_flang, id_flang_new, flag_gnu_openmp, flag_intel_openmp_win, & 6 | flag_intel_openmp, flag_pgi_openmp, flag_nag_openmp, & 7 | flag_lfortran_openmp, flag_flang_new_openmp 8 | use fpm_strings, only: string_t 9 | use fpm_meta_base, only: metapackage_t, destroy 10 | use fpm_error, only: error_t, fatal_error 11 | use fpm_manifest_metapackages, only: metapackage_request_t 12 | 13 | implicit none 14 | 15 | private 16 | 17 | public :: init_openmp 18 | 19 | contains 20 | 21 | !> Initialize OpenMP metapackage for the current system 22 | subroutine init_openmp(this,compiler,all_meta,error) 23 | class(metapackage_t), intent(inout) :: this 24 | type(compiler_t), intent(in) :: compiler 25 | type(metapackage_request_t), intent(in) :: all_meta(:) 26 | type(error_t), allocatable, intent(out) :: error 27 | 28 | !> Cleanup 29 | call destroy(this) 30 | 31 | !> Set name 32 | this%name = "openmp" 33 | 34 | !> OpenMP has compiler flags 35 | this%has_build_flags = .true. 36 | this%has_link_flags = .true. 37 | 38 | !> OpenMP flags should be added to 39 | which_compiler: select case (compiler%id) 40 | case (id_gcc,id_f95) 41 | this%flags = string_t(flag_gnu_openmp) 42 | this%link_flags = string_t(flag_gnu_openmp) 43 | 44 | case (id_intel_classic_windows,id_intel_llvm_windows) 45 | this%flags = string_t(flag_intel_openmp_win) 46 | this%link_flags = string_t(flag_intel_openmp_win) 47 | 48 | case (id_intel_classic_nix,id_intel_classic_mac,& 49 | id_intel_llvm_nix) 50 | this%flags = string_t(flag_intel_openmp) 51 | this%link_flags = string_t(flag_intel_openmp) 52 | 53 | case (id_pgi,id_nvhpc) 54 | this%flags = string_t(flag_pgi_openmp) 55 | this%link_flags = string_t(flag_pgi_openmp) 56 | 57 | case (id_ibmxl) 58 | this%flags = string_t(" -qsmp=omp") 59 | this%link_flags = string_t(" -qsmp=omp") 60 | 61 | case (id_nag) 62 | this%flags = string_t(flag_nag_openmp) 63 | this%link_flags = string_t(flag_nag_openmp) 64 | 65 | case (id_lfortran) 66 | this%flags = string_t(flag_lfortran_openmp) 67 | this%link_flags = string_t(flag_lfortran_openmp) 68 | 69 | case (id_flang, id_flang_new) 70 | this%flags = string_t(flag_flang_new_openmp) 71 | this%link_flags = string_t(flag_flang_new_openmp) 72 | 73 | case default 74 | 75 | call fatal_error(error,'openmp not supported on compiler '//compiler%name()//' yet') 76 | 77 | end select which_compiler 78 | 79 | 80 | end subroutine init_openmp 81 | end module fpm_meta_openmp 82 | -------------------------------------------------------------------------------- /src/metapackage/fpm_meta_stdlib.f90: -------------------------------------------------------------------------------- 1 | module fpm_meta_stdlib 2 | use fpm_compiler, only: compiler_t 3 | use fpm_error, only: error_t, fatal_error 4 | use fpm_meta_base, only: metapackage_t, destroy 5 | use fpm_git, only: git_target_branch 6 | use fpm_manifest_metapackages, only: metapackage_request_t 7 | use fpm_strings, only: string_t 8 | use iso_fortran_env, only: stdout => output_unit 9 | 10 | implicit none 11 | 12 | private 13 | 14 | public :: init_stdlib 15 | 16 | contains 17 | 18 | !> Initialize stdlib metapackage for the current system 19 | subroutine init_stdlib(this,compiler,all_meta,error) 20 | class(metapackage_t), intent(inout) :: this 21 | type(compiler_t), intent(in) :: compiler 22 | type(metapackage_request_t), intent(in) :: all_meta(:) 23 | type(error_t), allocatable, intent(out) :: error 24 | 25 | integer :: i 26 | logical :: with_blas 27 | 28 | !> Cleanup 29 | call destroy(this) 30 | 31 | !> Set name 32 | this%name = "stdlib" 33 | 34 | !> Stdlib is queried as a dependency from the official repository 35 | this%has_dependencies = .true. 36 | 37 | allocate(this%dependency(2)) 38 | 39 | !> 1) Test-drive 40 | this%dependency(1)%name = "test-drive" 41 | this%dependency(1)%git = git_target_branch("https://github.com/fortran-lang/test-drive","v0.4.0") 42 | if (.not.allocated(this%dependency(1)%git)) then 43 | call fatal_error(error,'cannot initialize test-drive git dependency for stdlib metapackage') 44 | return 45 | end if 46 | 47 | !> 2) stdlib 48 | this%dependency(2)%name = "stdlib" 49 | this%dependency(2)%git = git_target_branch("https://github.com/fortran-lang/stdlib","stdlib-fpm") 50 | if (.not.allocated(this%dependency(2)%git)) then 51 | call fatal_error(error,'cannot initialize git repo dependency for stdlib metapackage') 52 | return 53 | end if 54 | 55 | !> If an external BLAS is available, deploy appropriate macros 56 | with_blas = external_blas(all_meta) 57 | if (with_blas) then 58 | allocate(this%preprocess) 59 | call this%preprocess%new([string_t('STDLIB_EXTERNAL_BLAS'),string_t('STDLIB_EXTERNAL_LAPACK')]) 60 | call this%dependency(2)%add_preprocess(this%preprocess) 61 | end if 62 | 63 | ! Stdlib is not 100% thread safe. print a warning to the user 64 | do i=1,size(all_meta) 65 | if (all_meta(i)%name=="openmp") then 66 | write(stdout,'(a)')' both openmp and stdlib requested: some functions may not be thread-safe!' 67 | end if 68 | end do 69 | 70 | end subroutine init_stdlib 71 | 72 | logical function external_blas(all_meta) 73 | type(metapackage_request_t), intent(in) :: all_meta(:) 74 | integer :: i 75 | external_blas = .false. 76 | do i=1,size(all_meta) 77 | if (all_meta(i)%name=="blas") then 78 | external_blas = .true. 79 | exit 80 | end if 81 | end do 82 | end function external_blas 83 | 84 | end module fpm_meta_stdlib 85 | -------------------------------------------------------------------------------- /src/metapackage/fpm_meta_util.f90: -------------------------------------------------------------------------------- 1 | module fpm_meta_util 2 | use fpm_meta_base, only: metapackage_t, destroy 3 | use fpm_filesystem, only: join_path 4 | use fpm_strings, only: split, string_t, str_begins_with_str 5 | use fpm_error, only: error_t 6 | use fpm_versioning, only: new_version 7 | use fpm_pkg_config, only: pkgcfg_get_libs, pkgcfg_get_build_flags, pkgcfg_get_version 8 | 9 | implicit none 10 | 11 | private 12 | 13 | public :: add_pkg_config_compile_options, lib_get_trailing 14 | 15 | contains 16 | 17 | !> Add pkgconfig compile options to a metapackage 18 | subroutine add_pkg_config_compile_options(this, name, include_flag, libdir, error) 19 | class(metapackage_t), intent(inout) :: this 20 | character(len=*), intent(in) :: name 21 | character(len=*), intent(in) :: include_flag 22 | type(error_t), allocatable, intent(out) :: error 23 | 24 | character(len=:), allocatable :: libdir 25 | type(string_t) :: log, current_include_dir, current_lib 26 | type(string_t), allocatable :: libs(:), flags(:) 27 | integer :: i 28 | 29 | !> Get version 30 | if (.not. allocated(this%version)) then 31 | log = pkgcfg_get_version(name, error) 32 | if (allocated(error)) return 33 | allocate(this%version) 34 | call new_version(this%version, log%s, error) 35 | if (allocated(error)) return 36 | end if 37 | 38 | !> Get libraries 39 | libs = pkgcfg_get_libs(name, error) 40 | if (allocated(error)) return 41 | 42 | libdir = "" 43 | do i = 1, size(libs) 44 | if (str_begins_with_str(libs(i)%s, '-l')) then 45 | current_lib = string_t(libs(i)%s(3:)) 46 | if (len_trim(current_lib%s) == 0) cycle 47 | this%has_link_libraries = .true. 48 | this%link_libs = [this%link_libs, current_lib] 49 | else ! -L and others: concatenate 50 | this%has_link_flags = .true. 51 | this%link_flags = string_t(trim(this%link_flags%s)//' '//libs(i)%s) 52 | 53 | ! Also save library dir 54 | if (str_begins_with_str(libs(i)%s, '-L')) then 55 | libdir = libs(i)%s(3:) 56 | elseif (str_begins_with_str(libs(i)%s, '/LIBPATH')) then 57 | libdir = libs(i)%s(9:) 58 | end if 59 | end if 60 | end do 61 | 62 | !> Get compiler flags 63 | flags = pkgcfg_get_build_flags(name, .true., error) 64 | if (allocated(error)) return 65 | 66 | do i = 1, size(flags) 67 | if (str_begins_with_str(flags(i)%s, include_flag)) then 68 | current_include_dir = string_t(flags(i)%s(len(include_flag)+1:)) 69 | if (len_trim(current_include_dir%s) == 0) cycle 70 | this%has_include_dirs = .true. 71 | this%incl_dirs = [this%incl_dirs, current_include_dir] 72 | else 73 | this%has_build_flags = .true. 74 | this%flags = string_t(trim(this%flags%s)//' '//flags(i)%s) 75 | end if 76 | end do 77 | end subroutine add_pkg_config_compile_options 78 | 79 | !> Given a library name and folder, find extension and prefix 80 | subroutine lib_get_trailing(lib_name,lib_dir,prefix,suffix,found) 81 | character(*), intent(in) :: lib_name,lib_dir 82 | character(:), allocatable, intent(out) :: prefix,suffix 83 | logical, intent(out) :: found 84 | 85 | character(*), parameter :: extensions(*) = [character(11) :: '.dll.a','.a','.dylib','.dll'] 86 | logical :: is_file 87 | character(:), allocatable :: noext,tokens(:),path 88 | integer :: l,k 89 | 90 | ! Extract name with no extension 91 | call split(lib_name,tokens,'.') 92 | noext = trim(tokens(1)) 93 | 94 | ! Get library extension: find file name: NAME.a, NAME.dll.a, NAME.dylib, libNAME.a, etc. 95 | found = .false. 96 | suffix = "" 97 | prefix = "" 98 | with_pref: do l=1,2 99 | if (l==2) then 100 | prefix = "lib" 101 | else 102 | prefix = "" 103 | end if 104 | find_ext: do k=1,size(extensions) 105 | path = join_path(lib_dir,prefix//noext//trim(extensions(k))) 106 | inquire(file=path,exist=is_file) 107 | 108 | if (is_file) then 109 | suffix = trim(extensions(k)) 110 | found = .true. 111 | exit with_pref 112 | end if 113 | end do find_ext 114 | end do with_pref 115 | 116 | if (.not.found) then 117 | prefix = "" 118 | suffix = "" 119 | end if 120 | 121 | end subroutine lib_get_trailing 122 | end module fpm_meta_util 123 | -------------------------------------------------------------------------------- /src/ptycheck/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 K.Takata 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/ptycheck/isatty.c: -------------------------------------------------------------------------------- 1 | // This file provides a `c_isatty` wrapper function to check if `stdout` is connected 2 | // to a terminal or not. This wrapper is required for better portability, specifically 3 | // for supporting the MS Windows command prompt and the MinTTY terminal used by MSYS2. 4 | 5 | #include //for isatty() 6 | #include //for fileno() 7 | 8 | #ifdef __MINGW64__ 9 | // ptycheck/iscygpty allows us to check if connected to MinTTY in MSYS2 on Windows 10 | #include "iscygpty.h" 11 | #endif 12 | 13 | // Check if `stdout` is connected to a terminal 14 | // Returns 1 if is a terminal, and 0 otherwise 15 | int c_isatty(void) 16 | { 17 | 18 | if (isatty(fileno(stdout))){ 19 | return 1; 20 | } else { 21 | 22 | #ifdef __MINGW64__ 23 | if (is_cygpty(fileno(stdout))){ 24 | return 1; 25 | } else { 26 | return 0; 27 | } 28 | #endif 29 | 30 | return 0; 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/ptycheck/iscygpty.c: -------------------------------------------------------------------------------- 1 | /* 2 | * iscygpty.c -- part of ptycheck 3 | * https://github.com/k-takata/ptycheck 4 | * 5 | * Copyright (c) 2015-2017 K.Takata 6 | * 7 | * You can redistribute it and/or modify it under the terms of either 8 | * the MIT license (as described below) or the Vim license. 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files (the 12 | * "Software"), to deal in the Software without restriction, including 13 | * without limitation the rights to use, copy, modify, merge, publish, 14 | * distribute, sublicense, and/or sell copies of the Software, and to 15 | * permit persons to whom the Software is furnished to do so, subject to 16 | * the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | #ifdef _WIN32 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #ifdef USE_FILEEXTD 38 | /* VC 7.1 or earlier doesn't support SAL. */ 39 | # if !defined(_MSC_VER) || (_MSC_VER < 1400) 40 | # define __out 41 | # define __in 42 | # define __in_opt 43 | # endif 44 | /* Win32 FileID API Library: 45 | * http://www.microsoft.com/en-us/download/details.aspx?id=22599 46 | * Needed for WinXP. */ 47 | # include 48 | #else /* USE_FILEEXTD */ 49 | /* VC 8 or earlier. */ 50 | # if defined(_MSC_VER) && (_MSC_VER < 1500) 51 | # ifdef ENABLE_STUB_IMPL 52 | # define STUB_IMPL 53 | # else 54 | # error "Win32 FileID API Library is required for VC2005 or earlier." 55 | # endif 56 | # endif 57 | #endif /* USE_FILEEXTD */ 58 | 59 | 60 | #include "iscygpty.h" 61 | 62 | //#define USE_DYNFILEID 63 | #ifdef USE_DYNFILEID 64 | typedef BOOL (WINAPI *pfnGetFileInformationByHandleEx)( 65 | HANDLE hFile, 66 | FILE_INFO_BY_HANDLE_CLASS FileInformationClass, 67 | LPVOID lpFileInformation, 68 | DWORD dwBufferSize 69 | ); 70 | static pfnGetFileInformationByHandleEx pGetFileInformationByHandleEx = NULL; 71 | 72 | # ifndef USE_FILEEXTD 73 | static BOOL WINAPI stub_GetFileInformationByHandleEx( 74 | HANDLE hFile, 75 | FILE_INFO_BY_HANDLE_CLASS FileInformationClass, 76 | LPVOID lpFileInformation, 77 | DWORD dwBufferSize 78 | ) 79 | { 80 | return FALSE; 81 | } 82 | # endif 83 | 84 | static void setup_fileid_api(void) 85 | { 86 | if (pGetFileInformationByHandleEx != NULL) { 87 | return; 88 | } 89 | pGetFileInformationByHandleEx = (pfnGetFileInformationByHandleEx) 90 | GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), 91 | "GetFileInformationByHandleEx"); 92 | if (pGetFileInformationByHandleEx == NULL) { 93 | # ifdef USE_FILEEXTD 94 | pGetFileInformationByHandleEx = GetFileInformationByHandleEx; 95 | # else 96 | pGetFileInformationByHandleEx = stub_GetFileInformationByHandleEx; 97 | # endif 98 | } 99 | } 100 | #else 101 | # define pGetFileInformationByHandleEx GetFileInformationByHandleEx 102 | # define setup_fileid_api() 103 | #endif 104 | 105 | 106 | #define is_wprefix(s, prefix) \ 107 | (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) 108 | 109 | /* Check if the fd is a cygwin/msys's pty. */ 110 | int is_cygpty(int fd) 111 | { 112 | #ifdef STUB_IMPL 113 | return 0; 114 | #else 115 | HANDLE h; 116 | int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * (MAX_PATH - 1); 117 | FILE_NAME_INFO *nameinfo; 118 | WCHAR *p = NULL; 119 | 120 | setup_fileid_api(); 121 | 122 | h = (HANDLE) _get_osfhandle(fd); 123 | if (h == INVALID_HANDLE_VALUE) { 124 | return 0; 125 | } 126 | /* Cygwin/msys's pty is a pipe. */ 127 | if (GetFileType(h) != FILE_TYPE_PIPE) { 128 | return 0; 129 | } 130 | nameinfo = malloc(size + sizeof(WCHAR)); 131 | if (nameinfo == NULL) { 132 | return 0; 133 | } 134 | /* Check the name of the pipe: 135 | * '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' */ 136 | if (pGetFileInformationByHandleEx(h, FileNameInfo, nameinfo, size)) { 137 | nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; 138 | p = nameinfo->FileName; 139 | if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ 140 | p += 8; 141 | } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */ 142 | p += 6; 143 | } else { 144 | p = NULL; 145 | } 146 | if (p != NULL) { 147 | while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */ 148 | ++p; 149 | if (is_wprefix(p, L"-pty")) { 150 | p += 4; 151 | } else { 152 | p = NULL; 153 | } 154 | } 155 | if (p != NULL) { 156 | while (*p && isdigit(*p)) /* Skip pty number. */ 157 | ++p; 158 | if (is_wprefix(p, L"-from-master")) { 159 | //p += 12; 160 | } else if (is_wprefix(p, L"-to-master")) { 161 | //p += 10; 162 | } else { 163 | p = NULL; 164 | } 165 | } 166 | } 167 | free(nameinfo); 168 | return (p != NULL); 169 | #endif /* STUB_IMPL */ 170 | } 171 | 172 | /* Check if at least one cygwin/msys pty is used. */ 173 | int is_cygpty_used(void) 174 | { 175 | int fd, ret = 0; 176 | 177 | for (fd = 0; fd < 3; fd++) { 178 | ret |= is_cygpty(fd); 179 | } 180 | return ret; 181 | } 182 | 183 | #endif /* _WIN32 */ 184 | 185 | /* vim: set ts=4 sw=4: */ 186 | -------------------------------------------------------------------------------- /src/ptycheck/iscygpty.h: -------------------------------------------------------------------------------- 1 | /* 2 | * iscygpty.h -- part of ptycheck 3 | * https://github.com/k-takata/ptycheck 4 | * 5 | * Copyright (c) 2015-2017 K.Takata 6 | * 7 | * You can redistribute it and/or modify it under the terms of either 8 | * the MIT license (as described below) or the Vim license. 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining 11 | * a copy of this software and associated documentation files (the 12 | * "Software"), to deal in the Software without restriction, including 13 | * without limitation the rights to use, copy, modify, merge, publish, 14 | * distribute, sublicense, and/or sell copies of the Software, and to 15 | * permit persons to whom the Software is furnished to do so, subject to 16 | * the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be 19 | * included in all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 25 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 26 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 27 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | #ifndef _ISCYGPTY_H 31 | #define _ISCYGPTY_H 32 | 33 | #ifdef _WIN32 34 | int is_cygpty(int fd); 35 | int is_cygpty_used(void); 36 | #else 37 | #define is_cygpty(fd) 0 38 | #define is_cygpty_used() 0 39 | #endif 40 | 41 | #endif /* _ISCYGPTY_H */ 42 | -------------------------------------------------------------------------------- /test/fpm_test/main.f90: -------------------------------------------------------------------------------- 1 | !> Driver for unit testing 2 | program fpm_testing 3 | use, intrinsic :: iso_fortran_env, only : error_unit 4 | use testsuite, only : run_testsuite, new_testsuite, testsuite_t, select_suite, run_selected 5 | use test_toml, only : collect_toml 6 | use test_compiler, only : collect_compiler 7 | use test_manifest, only : collect_manifest 8 | use test_filesystem, only : collect_filesystem 9 | use test_source_parsing, only : collect_source_parsing 10 | use test_module_dependencies, only : collect_module_dependencies 11 | use test_package_dependencies, only : collect_package_dependencies 12 | use test_backend, only: collect_backend 13 | use test_installer, only : collect_installer 14 | use test_versioning, only : collect_versioning 15 | use test_settings, only : collect_settings 16 | use test_os, only: collect_os 17 | 18 | implicit none 19 | integer :: stat, is 20 | character(len=:), allocatable :: suite_name, test_name 21 | type(testsuite_t), allocatable :: suite(:) 22 | character(len=*), parameter :: fmt = '("#", *(1x, a))' 23 | 24 | stat = 0 25 | 26 | suite = [ & 27 | & new_testsuite("fpm_toml", collect_toml), & 28 | & new_testsuite("fpm_manifest", collect_manifest), & 29 | & new_testsuite("fpm_filesystem", collect_filesystem), & 30 | & new_testsuite("fpm_source_parsing", collect_source_parsing), & 31 | & new_testsuite("fpm_module_dependencies", collect_module_dependencies), & 32 | & new_testsuite("fpm_package_dependencies", collect_package_dependencies), & 33 | & new_testsuite("fpm_test_backend", collect_backend), & 34 | & new_testsuite("fpm_installer", collect_installer), & 35 | & new_testsuite("fpm_versioning", collect_versioning), & 36 | & new_testsuite("fpm_settings", collect_settings), & 37 | & new_testsuite("fpm_os", collect_os), & 38 | & new_testsuite("fpm_compiler", collect_compiler) & 39 | & ] 40 | 41 | call get_argument(1, suite_name) 42 | call get_argument(2, test_name) 43 | 44 | if (allocated(suite_name)) then 45 | is = select_suite(suite, suite_name) 46 | if (is > 0 .and. is <= size(suite)) then 47 | if (allocated(test_name)) then 48 | write(error_unit, fmt) "Suite:", suite(is)%name 49 | call run_selected(suite(is)%collect, test_name, error_unit, stat) 50 | if (stat < 0) then 51 | error stop 1 52 | end if 53 | else 54 | write(error_unit, fmt) "Testing:", suite(is)%name 55 | call run_testsuite(suite(is)%collect, error_unit, stat) 56 | end if 57 | else 58 | write(error_unit, fmt) "Available testsuites" 59 | do is = 1, size(suite) 60 | write(error_unit, fmt) "-", suite(is)%name 61 | end do 62 | error stop 1 63 | end if 64 | else 65 | do is = 1, size(suite) 66 | write(error_unit, fmt) "Testing:", suite(is)%name 67 | call run_testsuite(suite(is)%collect, error_unit, stat) 68 | end do 69 | end if 70 | 71 | if (stat > 0) then 72 | write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" 73 | error stop 1 74 | end if 75 | 76 | 77 | contains 78 | 79 | 80 | !> Obtain the command line argument at a given index 81 | subroutine get_argument(idx, arg) 82 | 83 | !> Index of command line argument, range [0:command_argument_count()] 84 | integer, intent(in) :: idx 85 | 86 | !> Command line argument 87 | character(len=:), allocatable, intent(out) :: arg 88 | 89 | integer :: length, arg_stat 90 | 91 | call get_command_argument(idx, length=length, status=arg_stat) 92 | if (arg_stat /= 0) then 93 | return 94 | endif 95 | 96 | allocate(character(len=length) :: arg, stat=arg_stat) 97 | if (arg_stat /= 0) then 98 | return 99 | endif 100 | 101 | if (length > 0) then 102 | call get_command_argument(idx, arg, status=arg_stat) 103 | if (arg_stat /= 0) then 104 | deallocate(arg) 105 | return 106 | end if 107 | end if 108 | 109 | end subroutine get_argument 110 | 111 | 112 | end program fpm_testing 113 | --------------------------------------------------------------------------------