├── example_packages ├── version_file │ ├── VERSION │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── with_c │ ├── .gitignore │ ├── fpm.toml │ ├── app │ │ └── main.f90 │ └── src │ │ ├── c_code.c │ │ └── with_c.f90 ├── app_with_c │ ├── .gitignore │ ├── fpm.toml │ └── app │ │ ├── c_code.c │ │ └── main.f90 ├── c_header_only │ ├── .gitignore │ ├── fpm.toml │ └── include │ │ └── c_header.h ├── c_includes │ ├── .gitignore │ ├── fpm.toml │ ├── src │ │ └── lib.c │ └── include │ │ └── lib.h ├── circular_test │ ├── .gitignore │ ├── fpm.toml │ └── src │ │ └── hello_test.f90 ├── hello_complex │ ├── .gitignore │ ├── apps │ │ ├── say_hello │ │ │ └── say_Hello.f90 │ │ └── say_goodbye │ │ │ └── say_goodbye.f90 │ ├── source │ │ ├── subdir │ │ │ └── constants.f90 │ │ ├── greet_m.f90 │ │ └── farewell_m.f90 │ ├── fpm.toml │ └── tests │ │ ├── greet │ │ └── greet_test.f90 │ │ └── farewell │ │ └── farewell_test.f90 ├── hello_fpm │ ├── .gitignore │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── hello_world │ ├── .gitignore │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── link_external │ ├── .gitignore │ ├── fpm.toml │ ├── app │ │ └── main.f90 │ └── src │ │ └── wrapped_gemv.f90 ├── many_targets │ ├── .gitignore │ ├── fpm.toml │ ├── app │ │ ├── run1.f90 │ │ ├── run2.f90 │ │ └── run3.f90 │ ├── test │ │ ├── test1.f90 │ │ ├── test2.f90 │ │ └── test3.f90 │ ├── example │ │ ├── example1.f90 │ │ ├── example2.f90 │ │ └── example3.f90 │ └── src │ │ └── file_mod.f90 ├── nonintrinsic │ ├── .gitignore │ ├── fpm.toml │ ├── src │ │ └── iso_fortran_env.f90 │ └── app │ │ └── main.f90 ├── submodules │ ├── .gitignore │ ├── fpm.toml │ └── src │ │ ├── child2.f90 │ │ ├── grandchild.f90 │ │ ├── child1.f90 │ │ └── parent.f90 ├── tree_shake │ ├── .gitignore │ ├── fpm.toml │ ├── src │ │ ├── subdir │ │ │ └── constants.f90 │ │ ├── extra_m.f90 │ │ ├── greet_m.f90 │ │ └── farewell_m.f90 │ ├── app │ │ └── say_Hello.f90 │ └── test │ │ └── greet_test.f90 ├── with_makefile │ ├── .gitignore │ ├── fpm.toml │ ├── src │ │ └── hello_makefile.f90 │ └── Makefile ├── app_with_submodule │ ├── .gitignore │ ├── fpm.toml │ ├── app │ │ ├── app2 │ │ │ ├── child2.f90 │ │ │ └── main2.f90 │ │ └── app1 │ │ │ ├── grandchild.f90 │ │ │ ├── main1.f90 │ │ │ └── child1.f90 │ └── src │ │ └── parent.f90 ├── auto_discovery_off │ ├── .gitignore │ ├── app │ │ ├── main.f90 │ │ └── unused.f90 │ ├── test │ │ ├── my_test.f90 │ │ └── unused_test.f90 │ └── fpm.toml ├── circular_example │ ├── .gitignore │ ├── fpm.toml │ ├── test │ │ └── main.f90 │ └── src │ │ └── greet_m.f90 ├── cpp_files │ ├── fpm.toml │ ├── README.md │ ├── src │ │ ├── hello_world.cpp │ │ └── cpp_files.f90 │ └── test │ │ └── check.f90 ├── fortran_includes │ ├── .gitignore │ ├── inc │ │ └── parameters.f90 │ ├── fpm.toml │ └── src │ │ └── lib.f90 ├── hello_complex_2 │ ├── .gitignore │ ├── app │ │ ├── app_mod.f90 │ │ ├── say_hello │ │ │ ├── app_extra_mod.f90 │ │ │ ├── app_hello_mod.f90 │ │ │ └── say_Hello.f90 │ │ └── say_goodbye.f90 │ ├── test │ │ ├── test_mod.f90 │ │ ├── greet_test.f90 │ │ └── farewell_test.f90 │ ├── fpm.toml │ └── src │ │ ├── greet_m.f90 │ │ └── farewell_m.f90 ├── link_executable │ ├── .gitignore │ ├── include │ │ └── test.f90 │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── makefile_complex │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ ├── src │ │ └── wrapper_mod.f90 │ └── Makefile ├── many_examples │ ├── .gitignore │ ├── app │ │ └── demo-prog.f90 │ ├── demo1 │ │ └── prog.f90 │ ├── demo2 │ │ └── prog.f90 │ └── fpm.toml ├── preprocess_cpp │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── preprocess_cpp.f90 ├── with_examples │ ├── .gitignore │ ├── app │ │ └── demo-prog.f90 │ ├── demo │ │ └── prog.f90 │ └── fpm.toml ├── preprocess_cpp_suffix │ ├── .gitignore │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── src │ │ └── preprocess_cpp.fpp ├── program_with_module │ ├── .gitignore │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── submodule_tree_shake │ ├── .gitignore │ ├── fpm.toml │ ├── app │ │ └── main.f90 │ └── src │ │ ├── child2.f90 │ │ ├── grandchild.f90 │ │ ├── child_unused.f90 │ │ ├── parent_unused.f90 │ │ ├── child1.f90 │ │ └── parent.f90 ├── c_main_preprocess │ ├── src │ │ ├── stub.c │ │ └── val.h │ ├── app │ │ └── main.c │ └── fpm.toml ├── features_per_compiler │ ├── .gitignore │ └── fpm.toml ├── hello_fpm_path │ ├── crate │ │ ├── utils2 │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ │ └── say_hello.f90 │ │ ├── utils1_1 │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ │ └── say_hello.f90 │ │ └── utils1 │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ └── say_hello.f90 │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── program_with_cpp_guarded_module │ ├── .gitignore │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── auto_with_nondefault_main │ ├── .gitignore │ ├── app │ │ └── non_default_name.f90 │ ├── fpm.toml │ └── README.md ├── c_main │ ├── app │ │ └── main.c │ └── fpm.toml ├── free-form │ ├── app │ │ └── main.f │ ├── fpm.toml │ └── src │ │ └── lib.f ├── preprocess_hello │ ├── README.md │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── fixed-form │ ├── fpm.toml │ ├── app │ │ └── main.f90 │ └── src │ │ └── lib.f90 ├── metapackage_blas │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── metapackage_hdf5 │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── preprocess_hello_dependency │ ├── fpm.toml │ ├── README.md │ └── src │ │ └── preprocess_hello_dependency.f90 ├── implicit-typing │ ├── src │ │ └── impl.f90 │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── metapackage_stdlib │ ├── fpm.toml │ ├── README.md │ ├── src │ │ └── metapackage_stdlib.f90 │ └── app │ │ └── main.f90 ├── metapackage_minpack │ ├── fpm.toml │ ├── app │ │ └── main.f90 │ └── src │ │ └── metapackage_minpack.f90 ├── metapackage_netcdf │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── metapackage_openmp │ ├── fpm.toml │ ├── test │ │ └── check.f90 │ ├── README.md │ ├── src │ │ └── test_openmp.f90 │ └── app │ │ └── main.f90 ├── implicit-external │ ├── fpm.toml │ ├── src │ │ └── impl.f90 │ └── app │ │ └── main.f90 ├── metapackage_mpi_c │ ├── README.md │ ├── fpm.toml │ └── app │ │ └── main.c ├── metapackage_mpi_cpp │ ├── README.md │ ├── fpm.toml │ └── app │ │ └── main.cpp ├── shared_lib │ ├── fpm.toml │ └── src │ │ └── shared_lib.f90 ├── both_lib_types │ ├── fpm.toml │ └── src │ │ └── both_lib_types.f90 ├── shared_app_only │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── test │ │ └── test.f90 ├── static_app_only │ ├── app │ │ └── main.f90 │ ├── fpm.toml │ └── test │ │ └── test.f90 ├── test_duplicates │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── custom_module_dir │ ├── fpm.toml │ ├── src │ │ ├── greeting.f90 │ │ └── math_utils.f90 │ └── README.md ├── preprocess_cpp_c │ ├── app │ │ └── main.c │ ├── include │ │ └── val.h │ └── fpm.toml ├── preprocess_cpp_deps │ ├── crate │ │ └── utils │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ └── say_hello.f90 │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── metapackage_stdlib_extblas │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── preprocess_per_dependency │ ├── crate │ │ └── utils │ │ │ ├── fpm.toml │ │ │ └── src │ │ │ └── say_hello.f90 │ ├── fpm.toml │ └── app │ │ └── main.f90 ├── fpm_test_exe_issues │ ├── src │ │ ├── main.f90 │ │ ├── b_mod.f90 │ │ └── a │ │ │ └── a_mod.f90 │ └── fpm.toml ├── metapackage_mpi │ ├── fpm.toml │ ├── README.md │ └── app │ │ └── main.f90 ├── shared_lib_extra │ ├── fpm.toml │ └── src │ │ └── shared_lib_extra.f90 ├── shared_lib_empty │ └── fpm.toml ├── static_lib_empty │ └── fpm.toml ├── dependency_priority │ ├── README.md │ ├── app │ │ └── main.f90 │ └── fpm.toml ├── features_demo │ ├── app │ │ ├── main.f90 │ │ └── debug_demo.f90 │ ├── fpm.toml │ └── src │ │ └── features_demo.f90 ├── fpm_test_exit_code │ ├── fpm.toml │ ├── README.md │ └── app │ │ └── main.f90 ├── features_with_dependency │ ├── app │ │ └── main.f90 │ ├── src │ │ └── features_with_dependency.f90 │ └── fpm.toml └── README.md ├── ChangeLog.md ├── doc ├── index.md ├── License.md ├── Packaging.md ├── media │ └── favicon.ico ├── Contributing.md └── Manifest.md ├── ci ├── installer-icon.ico ├── single-file-gfortran.sh ├── single-file.patch ├── meta_tests.sh ├── fpm-installer.nsi └── test_custom_build_dir.sh ├── manifest-reference.md ├── .gitignore ├── .github ├── ISSUE_TEMPLATE │ ├── 05_free.md │ ├── config.yml │ ├── 03_feature.yaml │ ├── 02_packaging.yaml │ ├── 01_bug.yaml │ └── 04_specification.yaml ├── dependabot.yml └── workflows │ └── docs.yml ├── src ├── fpm_os.c ├── filesystem_utilities.c ├── ptycheck │ ├── isatty.c │ ├── LICENSE │ ├── iscygpty.h │ └── iscygpty.c ├── fpm_environment.c ├── metapackage │ ├── fpm_meta_minpack.f90 │ ├── fpm_meta_netcdf.f90 │ ├── fpm_meta_stdlib.f90 │ ├── fpm_meta_blas.f90 │ ├── fpm_meta_openmp.f90 │ └── fpm_meta_util.f90 ├── fpm │ ├── fpm_release.F90 │ ├── cmd │ │ ├── update.f90 │ │ ├── export.f90 │ │ └── publish.f90 │ ├── downloader.f90 │ └── error.f90 └── fpm_backend_console.f90 ├── LICENSE ├── fpm.toml ├── install.sh ├── test └── fpm_test │ └── main.f90 ├── app └── main.f90 └── docs.md /example_packages/version_file/VERSION: -------------------------------------------------------------------------------- 1 | 5.42.1 2 | -------------------------------------------------------------------------------- /example_packages/with_c/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/app_with_c/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/c_header_only/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/c_includes/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/circular_test/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_complex/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_fpm/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_world/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/link_external/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/many_targets/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/nonintrinsic/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/submodules/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/tree_shake/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/with_c/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_c" 2 | -------------------------------------------------------------------------------- /example_packages/with_makefile/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/app_with_c/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_c" 2 | -------------------------------------------------------------------------------- /example_packages/app_with_submodule/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/auto_discovery_off/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/circular_example/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/cpp_files/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "cpp_files" 2 | -------------------------------------------------------------------------------- /example_packages/fortran_includes/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_complex_2/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/link_executable/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/makefile_complex/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/many_examples/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/tree_shake/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "tree_shake" -------------------------------------------------------------------------------- /example_packages/with_examples/.gitignore: -------------------------------------------------------------------------------- 1 | /build/* 2 | -------------------------------------------------------------------------------- /example_packages/hello_world/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_world" 2 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_suffix/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/program_with_module/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/submodules/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "submodules" 2 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # Changelog for fpm 2 | 3 | ## Unreleased changes 4 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Packaging and contributing 3 | --- 4 | -------------------------------------------------------------------------------- /example_packages/c_header_only/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c_header_only" 2 | -------------------------------------------------------------------------------- /example_packages/c_main_preprocess/src/stub.c: -------------------------------------------------------------------------------- 1 | #include "val.h" 2 | -------------------------------------------------------------------------------- /example_packages/features_per_compiler/.gitignore: -------------------------------------------------------------------------------- 1 | output.txt 2 | -------------------------------------------------------------------------------- /example_packages/many_targets/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "many_targets" 2 | -------------------------------------------------------------------------------- /example_packages/nonintrinsic/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "non-intrinsic" 2 | -------------------------------------------------------------------------------- /doc/License.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: License 3 | --- 4 | 5 | {!LICENSE!} 6 | -------------------------------------------------------------------------------- /example_packages/app_with_submodule/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "app_with_submodule" -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils2/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils2" 2 | -------------------------------------------------------------------------------- /example_packages/program_with_cpp_guarded_module/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | -------------------------------------------------------------------------------- /example_packages/cpp_files/README.md: -------------------------------------------------------------------------------- 1 | # cpp_files 2 | My cool new project! 3 | -------------------------------------------------------------------------------- /example_packages/hello_fpm_path/crate/utils1_1/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils1_1" 2 | -------------------------------------------------------------------------------- /example_packages/program_with_module/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "Program_with_module" 2 | -------------------------------------------------------------------------------- /example_packages/auto_with_nondefault_main/.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | installed/* 3 | -------------------------------------------------------------------------------- /example_packages/link_executable/include/test.f90: -------------------------------------------------------------------------------- 1 | real, parameter :: a = 2.0 2 | -------------------------------------------------------------------------------- /example_packages/submodule_tree_shake/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "submodule_tree_shake" 2 | -------------------------------------------------------------------------------- /doc/Packaging.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Packaging with fpm 3 | --- 4 | 5 | {!PACKAGING.md!} 6 | -------------------------------------------------------------------------------- /example_packages/c_main/app/main.c: -------------------------------------------------------------------------------- 1 | int 2 | main (void) 3 | { 4 | return 0; 5 | } 6 | -------------------------------------------------------------------------------- /example_packages/version_file/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "version_file" 2 | version = "VERSION" 3 | -------------------------------------------------------------------------------- /example_packages/fortran_includes/inc/parameters.f90: -------------------------------------------------------------------------------- 1 | integer, parameter :: dp = kind(0.d0) -------------------------------------------------------------------------------- /example_packages/free-form/app/main.f: -------------------------------------------------------------------------------- 1 | program test 2 | use lib 3 | call hello 4 | end 5 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello/README.md: -------------------------------------------------------------------------------- 1 | # preprocess_hello 2 | My cool new project! 3 | -------------------------------------------------------------------------------- /ci/installer-icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fortran-lang/fpm/HEAD/ci/installer-icon.ico -------------------------------------------------------------------------------- /doc/media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fortran-lang/fpm/HEAD/doc/media/favicon.ico -------------------------------------------------------------------------------- /example_packages/c_header_only/include/c_header.h: -------------------------------------------------------------------------------- 1 | int printf ( const char * format, ... ); 2 | -------------------------------------------------------------------------------- /example_packages/fixed-form/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "fixed-form" 2 | fortran.source-form = "fixed" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_blas/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_blas" 2 | dependencies.blas="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_hdf5/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_hdf5" 2 | dependencies.hdf5="*" 3 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello_dependency/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_hello_dependency" 2 | -------------------------------------------------------------------------------- /doc/Contributing.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Contributing Guidelines 3 | --- 4 | 5 | {!CONTRIBUTING.md!} 6 | -------------------------------------------------------------------------------- /doc/Manifest.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Manifest reference 3 | --- 4 | 5 | {!manifest-reference.md!} 6 | 7 | -------------------------------------------------------------------------------- /example_packages/implicit-typing/src/impl.f90: -------------------------------------------------------------------------------- 1 | module impl 2 | parameter(ijk = 1) 3 | end module 4 | -------------------------------------------------------------------------------- /example_packages/link_external/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "link_external" 2 | 3 | [build] 4 | link = "blas" 5 | -------------------------------------------------------------------------------- /example_packages/metapackage_stdlib/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_stdlib" 2 | dependencies.stdlib = "*" 3 | -------------------------------------------------------------------------------- /example_packages/implicit-typing/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "implicit-typing" 2 | fortran.implicit-typing = true 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_minpack/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_minpack" 2 | dependencies.minpack="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_netcdf/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "metapackage_netcdf" 2 | dependencies.netcdf="*" 3 | -------------------------------------------------------------------------------- /example_packages/metapackage_openmp/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_openmp" 2 | dependencies.openmp = "*" 3 | 4 | -------------------------------------------------------------------------------- /example_packages/implicit-external/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "implicit-external" 2 | fortran.implicit-external = true 3 | -------------------------------------------------------------------------------- /example_packages/preprocess_hello_dependency/README.md: -------------------------------------------------------------------------------- 1 | # preprocess_hello_dependency 2 | My cool new project! 3 | -------------------------------------------------------------------------------- /example_packages/c_main/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c-main" 2 | 3 | [[executable]] 4 | name = "c-main" 5 | main = "main.c" 6 | -------------------------------------------------------------------------------- /example_packages/fixed-form/app/main.f90: -------------------------------------------------------------------------------- 1 | program test 2 | use lib 3 | call hello 4 | end 5 | -------------------------------------------------------------------------------- /example_packages/fortran_includes/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "fortran_includes" 2 | 3 | [library] 4 | include-dir = "inc" 5 | -------------------------------------------------------------------------------- /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/test/test_mod.f90: -------------------------------------------------------------------------------- 1 | module test_mod 2 | implicit none 3 | 4 | 5 | end module test_mod 6 | -------------------------------------------------------------------------------- /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_cpp/README.md: -------------------------------------------------------------------------------- 1 | # test_mpi 2 | This test program prints the running thread ID using MPI. 3 | -------------------------------------------------------------------------------- /manifest-reference.md: -------------------------------------------------------------------------------- 1 | # 301 - Moved 2 | 3 | This document now lives at https://fpm.fortran-lang.org/spec/manifest.html 4 | -------------------------------------------------------------------------------- /example_packages/hello_world/app/main.f90: -------------------------------------------------------------------------------- 1 | program hello_world 2 | print *, "Hello, World!" 3 | end program hello_world 4 | -------------------------------------------------------------------------------- /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/shared_lib/fpm.toml: -------------------------------------------------------------------------------- 1 | # Shared library with no executables 2 | name = "shared_lib" 3 | library.type="shared" 4 | -------------------------------------------------------------------------------- /example_packages/both_lib_types/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "both_lib_types" 2 | library.type=["shared", "static"] 3 | install.library=true 4 | -------------------------------------------------------------------------------- /example_packages/c_includes/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "c_includes" 2 | 3 | [dependencies] 4 | c_header_only = { path = "../c_header_only"} 5 | -------------------------------------------------------------------------------- /example_packages/hello_fpm/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "hello_fpm" 2 | 3 | [dependencies] 4 | hello_complex = { path = "../hello_complex" } 5 | -------------------------------------------------------------------------------- /example_packages/implicit-external/src/impl.f90: -------------------------------------------------------------------------------- 1 | subroutine impl(ijk) 2 | integer :: ijk 3 | ijk = 1 4 | end subroutine impl 5 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp/app/main.f90: -------------------------------------------------------------------------------- 1 | program cpp 2 | use preprocess_cpp 3 | call say_hello() 4 | end program 5 | -------------------------------------------------------------------------------- /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/hello_fpm_path/crate/utils1/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils1" 2 | 3 | [dependencies] 4 | utils1_1 = { path = "../utils1_1" } 5 | -------------------------------------------------------------------------------- /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/shared_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/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use testdrive 3 | print *, 'Hello, world!' 4 | end program main 5 | -------------------------------------------------------------------------------- /example_packages/test_duplicates/app/main.f90: -------------------------------------------------------------------------------- 1 | program test_program 2 | print *, "Hello from test program" 3 | end program test_program -------------------------------------------------------------------------------- /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_makefile/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "with_makefile" 2 | 3 | [library] 4 | source-dir = "src" 5 | build-script = "Makefile" 6 | -------------------------------------------------------------------------------- /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/custom_module_dir/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "custom-module-dir" 2 | install.library = true 3 | install.module-dir = "custom_modules" 4 | -------------------------------------------------------------------------------- /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_deps/crate/utils/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "utils" 2 | 3 | [preprocess] 4 | [preprocess.cpp] 5 | macros = ["X=1"] 6 | -------------------------------------------------------------------------------- /example_packages/preprocess_cpp_deps/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "preprocess_cpp_deps" 2 | 3 | [dependencies] 4 | utils = { path = "crate/utils" } 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/circular_test/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "circular_test" 2 | 3 | [dependencies] 4 | circular_example = { path = "../circular_example"} 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/metapackage_stdlib_extblas/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_stdlib_ext_blas" 2 | dependencies.blas = "*" 3 | dependencies.stdlib = "*" 4 | -------------------------------------------------------------------------------- /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/auto_with_nondefault_main/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "auto_with_nondefault_main" 2 | [build] 3 | auto-executables = true 4 | [library] 5 | 6 | -------------------------------------------------------------------------------- /example_packages/circular_example/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "circular_example" 2 | 3 | [dev-dependencies] 4 | circular_test = { path = "../circular_test" } 5 | -------------------------------------------------------------------------------- /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/metapackage_openmp/test/check.f90: -------------------------------------------------------------------------------- 1 | program check 2 | implicit none 3 | 4 | print *, "Put some tests in here!" 5 | end program check 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | 3 | # Visual Studio Code 4 | .vscode/ 5 | 6 | # CodeBlocks 7 | project/ 8 | 9 | # Temporary files 10 | *.swp 11 | 12 | -------------------------------------------------------------------------------- /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/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/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/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/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/metapackage_mpi/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_mpi" 2 | dependencies.mpi = "*" 3 | fortran.implicit-external=true 4 | fortran.implicit-typing=true 5 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "monthly" 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/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/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/program_with_cpp_guarded_module/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "Program_with_cpp_guarded_module" 2 | # Enable CPP but do not define macros 3 | [preprocess.cpp] 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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_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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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_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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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_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/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/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/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/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/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/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/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/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/features_demo/app/main.f90: -------------------------------------------------------------------------------- 1 | program main 2 | use features_demo 3 | implicit none 4 | 5 | call show_features() 6 | 7 | write(*,*) '' 8 | write(*,*) get_build_info() 9 | write(*,*) '' 10 | write(*,*) 'Demo completed successfully!' 11 | 12 | end program main -------------------------------------------------------------------------------- /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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/program_with_cpp_guarded_module/app/main.f90: -------------------------------------------------------------------------------- 1 | program program_with_module 2 | #if defined(HAVE_MODULE) 3 | use greet_m, only: greeting 4 | #endif 5 | implicit none 6 | 7 | #ifndef HAVE_MODULE 8 | print *, 'OK without module' 9 | #else 10 | print *, greeting 11 | #endif 12 | end program program_with_module 13 | -------------------------------------------------------------------------------- /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/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/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/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/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/custom_module_dir/src/greeting.f90: -------------------------------------------------------------------------------- 1 | module greeting 2 | implicit none 3 | private 4 | public :: say_hello 5 | 6 | contains 7 | 8 | subroutine say_hello(name) 9 | character(len=*), intent(in) :: name 10 | print *, 'Hello, ' // name // '!' 11 | end subroutine say_hello 12 | 13 | end module greeting -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/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/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/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/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/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/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/features_demo/app/debug_demo.f90: -------------------------------------------------------------------------------- 1 | program debug_demo 2 | use features_demo 3 | implicit none 4 | 5 | write(*,*) 'Debug Demo Program' 6 | write(*,*) '==================' 7 | 8 | #ifdef DEBUG 9 | write(*,*) 'Debug mode: ON' 10 | #else 11 | write(*,*) 'Debug mode: OFF' 12 | #endif 13 | 14 | call show_features() 15 | 16 | end program debug_demo 17 | -------------------------------------------------------------------------------- /example_packages/both_lib_types/src/both_lib_types.f90: -------------------------------------------------------------------------------- 1 | module both_lib_types 2 | implicit none 3 | private 4 | 5 | public :: say_hello 6 | public :: get_number 7 | contains 8 | subroutine say_hello 9 | print *, "Hello from both_lib_types!" 10 | end subroutine say_hello 11 | 12 | integer function get_number() 13 | get_number = 42 14 | end function get_number 15 | end module both_lib_types -------------------------------------------------------------------------------- /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/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/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/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/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/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/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/features_with_dependency/app/main.f90: -------------------------------------------------------------------------------- 1 | program features_with_dependency_demo 2 | use features_with_dependency, only: show_features 3 | implicit none 4 | 5 | print *, "=== Features with Dependency Demo ===" 6 | print *, "" 7 | 8 | call show_features() 9 | 10 | print *, "" 11 | print *, "This demonstrates feature propagation to dependencies." 12 | 13 | end program features_with_dependency_demo 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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/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/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/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/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(c_int) 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/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/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/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/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/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/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/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/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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example_packages/custom_module_dir/src/math_utils.f90: -------------------------------------------------------------------------------- 1 | module math_utils 2 | implicit none 3 | private 4 | public :: add_numbers, multiply_numbers 5 | 6 | contains 7 | 8 | function add_numbers(a, b) result(sum) 9 | integer, intent(in) :: a, b 10 | integer :: sum 11 | sum = a + b 12 | end function add_numbers 13 | 14 | function multiply_numbers(a, b) result(product) 15 | integer, intent(in) :: a, b 16 | integer :: product 17 | product = a * b 18 | end function multiply_numbers 19 | 20 | end module math_utils -------------------------------------------------------------------------------- /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_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/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/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/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/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/test_duplicates/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "test_duplicates" 2 | version = "0.1.0" 3 | 4 | [[executable]] 5 | name = "test_program" 6 | source-dir = "app" 7 | main = "main.f90" 8 | 9 | [features] 10 | # This should cause a duplicate executable error when features are combined 11 | feature1.executable.name = "my_exe" 12 | feature1.executable.source-dir = "app1" 13 | feature1.executable.main = "main1.f90" 14 | 15 | feature2.executable.name = "my_exe" # Same name - should cause duplicate error 16 | feature2.executable.source-dir = "app2" 17 | feature2.executable.main = "main2.f90" 18 | 19 | [profiles] 20 | # This profile combines features with duplicate executables 21 | combined = ["feature1", "feature2"] -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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@v5 10 | - uses: actions/setup-python@v6 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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_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/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 -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /example_packages/custom_module_dir/README.md: -------------------------------------------------------------------------------- 1 | # Custom Module Directory Example 2 | 3 | This example demonstrates the use of a custom module directory in the `[install]` section of `fpm.toml`. 4 | 5 | ## Features 6 | 7 | - Two simple Fortran modules: `greeting` and `math_utils` 8 | - Custom module installation directory specified as `custom_modules` 9 | - Shows how modules can be installed to a different location than headers 10 | 11 | ## Configuration 12 | 13 | In `fpm.toml`: 14 | 15 | ```toml 16 | [install] 17 | library = true 18 | module-dir = "custom_modules" 19 | ``` 20 | 21 | This configuration will install compiled `.mod` files to the `custom_modules` directory instead of the default `include` directory. 22 | 23 | ## Testing 24 | 25 | To test this example: 26 | 27 | ```bash 28 | cd example_packages/custom_module_dir 29 | fpm build 30 | fpm install --prefix /tmp/test_install 31 | # Check that .mod files are in /tmp/test_install/custom_modules/ 32 | ``` -------------------------------------------------------------------------------- /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/features_demo/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "features_demo" 2 | version = "0.1.0" 3 | license = "MIT" 4 | description = "Demo package for FPM features functionality" 5 | 6 | [[executable]] 7 | name = "features_demo" 8 | source-dir = "app" 9 | main = "main.f90" 10 | 11 | [features] 12 | # Base debug feature 13 | debug.flags = "-g" 14 | debug.preprocess.cpp.macros = "DEBUG" 15 | 16 | # Release feature 17 | release.flags = "-O3" 18 | release.preprocess.cpp.macros = "RELEASE" 19 | 20 | # Compiler-specific features 21 | debug.gfortran.flags = "-Wall -fcheck=bounds" 22 | release.gfortran.flags = "-mtune=generic -funroll-loops" 23 | 24 | # Platform-specific features 25 | linux.preprocess.cpp.macros = "LINUX_BUILD" 26 | 27 | # Parallel features 28 | mpi.preprocess.cpp.macros = "USE_MPI" 29 | mpi.dependencies.mpi = "*" 30 | openmp.preprocess.cpp.macros = "USE_OPENMP" 31 | openmp.dependencies.openmp = "*" 32 | 33 | [profiles] 34 | development = ["debug"] 35 | production = ["release", "openmp"] 36 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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.1" 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 | -------------------------------------------------------------------------------- /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 --flag " -Wno-external-argument-mismatch" 32 | "$fpm" run --verbose --flag " -Wno-external-argument-mismatch" 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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example_packages/features_with_dependency/src/features_with_dependency.f90: -------------------------------------------------------------------------------- 1 | module features_with_dependency 2 | #ifdef WITH_DEMO 3 | use features_demo, only: show_demo_features => show_features 4 | #endif 5 | implicit none 6 | private 7 | public :: show_features 8 | 9 | contains 10 | 11 | subroutine show_features() 12 | print *, "Local package features:" 13 | 14 | #ifdef WITH_DEBUG_DEPENDENCY 15 | print *, "✓ WITH_DEBUG_DEPENDENCY - dependency built with debug features" 16 | #endif 17 | 18 | #ifdef WITH_RELEASE_DEPENDENCY 19 | print *, "✓ WITH_RELEASE_DEPENDENCY - dependency built with release features" 20 | #endif 21 | 22 | #ifdef WITH_MULTI_DEPENDENCY 23 | print *, "✓ WITH_MULTI_DEPENDENCY - dependency built with debug+mpi features" 24 | #endif 25 | 26 | #ifdef LINUX_FEATURES 27 | print *, "✓ LINUX_FEATURES - Linux-specific dependency features" 28 | #endif 29 | 30 | ! If no local features are active 31 | #if !defined(WITH_DEBUG_DEPENDENCY) && !defined(WITH_RELEASE_DEPENDENCY) && !defined(WITH_MULTI_DEPENDENCY) && !defined(LINUX_FEATURES) 32 | print *, " NONE - no local features active" 33 | #endif 34 | 35 | print *, "" 36 | #ifdef WITH_DEMO 37 | print *, "Dependency (features_demo) status:" 38 | call show_demo_features() ! Call features_demo's show_features 39 | #else 40 | print *, "Dependency (features_demo) is not attached" 41 | #endif 42 | 43 | end subroutine show_features 44 | 45 | end module features_with_dependency 46 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /example_packages/features_with_dependency/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "features_with_dependency" 2 | version = "0.1.0" 3 | license = "MIT" 4 | description = "Demo package testing dependency features" 5 | 6 | [[executable]] 7 | name = "features_with_dependency" 8 | source-dir = "app" 9 | main = "main.f90" 10 | 11 | [dependencies] 12 | # Base dependencies (none for this demo - features will add them) 13 | 14 | [features] 15 | # Feature that enables debug mode in the dependency 16 | with_feat_debug.dependencies.features_demo = { path = "../features_demo", features = ["debug"] } 17 | with_feat_debug.preprocess.cpp.macros = ["WITH_DEMO","WITH_DEBUG_DEPENDENCY"] 18 | 19 | # Feature that enables release mode in the dependency 20 | with_feat_release.dependencies.features_demo = { path = "../features_demo", features = ["release"] } 21 | with_feat_release.preprocess.cpp.macros = ["WITH_DEMO","WITH_RELEASE_DEPENDENCY"] 22 | 23 | # Feature that enables multiple dependency features 24 | with_feat_multi.dependencies.features_demo = { path = "../features_demo", features = ["debug", "mpi"] } 25 | with_feat_multi.preprocess.cpp.macros = ["WITH_DEMO","WITH_MULTI_DEPENDENCY"] 26 | 27 | # Feature for platform-specific dependency features 28 | linux_specific.linux.dependencies.features_demo = { path = "../features_demo", features = ["linux"] } 29 | linux_specific.preprocess.cpp.macros = ["WITH_DEMO","LINUX_FEATURES"] 30 | 31 | # Feature combining compiler and dependency features 32 | gfortran_optimized.gfortran.dependencies.features_demo = { path = "../features_demo", features = ["release", "gfortran"] } 33 | gfortran_optimized.gfortran.flags = "-O3 -mtune=generic -funroll-loops" 34 | gfortran_optimized.preprocess.cpp.macros = ["WITH_DEMO"] 35 | 36 | 37 | [profiles] 38 | debug_dep = ["with_feat_debug"] 39 | release_dep = ["with_feat_release"] 40 | full_test = ["with_feat_multi", "linux_specific"] 41 | -------------------------------------------------------------------------------- /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/features_demo/src/features_demo.f90: -------------------------------------------------------------------------------- 1 | module features_demo 2 | implicit none 3 | private 4 | public :: show_features, get_build_info 5 | 6 | contains 7 | 8 | !> Display which features are enabled 9 | subroutine show_features() 10 | write(*,*) 'FPM Features Demo' 11 | write(*,*) '=================' 12 | 13 | ! Debug/Release flags 14 | #ifdef DEBUG 15 | write(*,*) '✓ DEBUG mode enabled' 16 | #endif 17 | #ifdef RELEASE 18 | write(*,*) '✓ RELEASE mode enabled' 19 | #endif 20 | 21 | ! Platform detection 22 | #ifdef LINUX_BUILD 23 | write(*,*) '✓ Linux platform detected' 24 | #endif 25 | #ifdef WINDOWS_BUILD 26 | write(*,*) '✓ Windows platform detected' 27 | #endif 28 | 29 | ! Parallel features 30 | #ifdef USE_MPI 31 | write(*,*) '✓ MPI support enabled' 32 | #endif 33 | #ifdef USE_OPENMP 34 | write(*,*) '✓ OpenMP support enabled' 35 | #endif 36 | 37 | ! Compiler info (if available) 38 | write(*,*) 'Build configuration:' 39 | call show_compiler_info() 40 | 41 | end subroutine show_features 42 | 43 | !> Show compiler information 44 | subroutine show_compiler_info() 45 | #ifdef __GFORTRAN__ 46 | write(*,*) ' - Compiler: GNU Fortran' 47 | #endif 48 | #ifdef __INTEL_COMPILER 49 | write(*,*) ' - Compiler: Intel Fortran' 50 | #endif 51 | end subroutine show_compiler_info 52 | 53 | !> Get build information as a string 54 | function get_build_info() result(info) 55 | character(len=200) :: info 56 | 57 | info = 'Features: ' 58 | 59 | #ifdef DEBUG 60 | info = trim(info) // ' DEBUG' 61 | #endif 62 | #ifdef RELEASE 63 | info = trim(info) // ' RELEASE' 64 | #endif 65 | #ifdef USE_MPI 66 | info = trim(info) // ' MPI' 67 | #endif 68 | #ifdef USE_OPENMP 69 | info = trim(info) // ' OPENMP' 70 | #endif 71 | 72 | if (len_trim(info) == 10) then ! Only "Features: " 73 | info = trim(info) // 'NONE' 74 | end if 75 | 76 | end function get_build_info 77 | 78 | end module features_demo 79 | -------------------------------------------------------------------------------- /example_packages/features_per_compiler/fpm.toml: -------------------------------------------------------------------------------- 1 | name = "features_per_compiler" 2 | version = "0.1.0" 3 | license = "MIT" 4 | author = "Federico Perini" 5 | maintainer = "federico.perini@gmail.com" 6 | copyright = "Copyright 2025, Federico Perini" 7 | description = "Demo package showcasing features with per-compiler flags and introspection" 8 | 9 | [build] 10 | auto-executables=false 11 | 12 | [[executable]] 13 | name = "features_per_compiler" 14 | source-dir = "app" 15 | main = "main.f90" 16 | 17 | [profiles] 18 | # Development profile with debugging flags 19 | development = ["debug", "verbose"] 20 | 21 | # Production profile with optimization flags 22 | production = ["release", "fast"] 23 | 24 | # Testing profile with strict checking 25 | testing = ["debug", "strict"] 26 | 27 | [features] 28 | # Debug feature with base flags for all compilers, then per-compiler extensions 29 | debug.flags = "-g" # Base debug flag for ALL compilers (applied first) 30 | debug.gfortran.flags = "-Wall -Wextra -fcheck=bounds,do,mem,pointer -fbacktrace" 31 | debug.ifort.flags = "-warn all -check bounds -traceback" # Unix/Linux/macOS Intel 32 | debug.ifx.flags = "-warn all -check bounds -traceback" # Intel oneAPI 33 | debug.preprocess.cpp.macros = "DEBUG" 34 | 35 | # Release feature with base optimization, then per-compiler extensions 36 | release.flags = "-O3" # Base optimization for ALL compilers (applied first) 37 | release.gfortran.flags = "-mtune=generic -funroll-loops" 38 | release.ifort.flags = "-unroll" # Unix/Linux/macOS Intel 39 | release.ifx.flags = "-unroll" # Intel oneAPI 40 | 41 | # Verbose feature for enhanced diagnostics (applies to all compilers) 42 | verbose.flags = "-v" 43 | 44 | # Fast feature with base optimization, then per-compiler extensions 45 | fast.flags = "-O3" # Base fast optimization for ALL compilers (applied first) 46 | fast.gfortran.flags = "-Ofast -ffast-math" 47 | fast.ifort.flags = "-fp-model fast" 48 | fast.ifx.flags = "-fp-model fast" 49 | 50 | # Strict feature with base standard compliance, then per-compiler extensions 51 | strict.flags = "-std=f2018" # Base standard compliance for ALL compilers (applied first) 52 | strict.gfortran.flags = "-Wpedantic -Werror" 53 | strict.ifort.flags = "-stand f18 -warn errors" 54 | strict.ifx.flags = "-stand f18 -warn errors" 55 | 56 | -------------------------------------------------------------------------------- /src/fpm/cmd/update.f90: -------------------------------------------------------------------------------- 1 | module fpm_cmd_update 2 | use fpm_command_line, only : fpm_update_settings, get_fpm_env 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, build_dir 24 | 25 | call get_package_data(package, "fpm.toml", error, apply_defaults=.true.) 26 | call handle_error(error) 27 | 28 | ! Get build directory from environment variable or use default 29 | build_dir = get_fpm_env("BUILD_DIR", "build") 30 | 31 | if (.not. exists(build_dir)) then 32 | call mkdir(build_dir) 33 | call filewrite(join_path(build_dir, ".gitignore"),["*"]) 34 | end if 35 | 36 | cache = join_path(build_dir, "cache.toml") 37 | if (settings%clean) call delete_file(cache) 38 | 39 | call new_dependency_tree(deps, cache=cache, verbosity=merge(2, 1, settings%verbose), & 40 | & path_to_config=settings%path_to_config, build_dir=build_dir) 41 | 42 | call deps%add(package, error) 43 | call handle_error(error) 44 | 45 | ! Force-update all dependencies if `--clean` 46 | if (settings%clean) then 47 | do ii = 1, deps%ndep 48 | deps%dep(ii)%update = .true. 49 | end do 50 | end if 51 | 52 | if (settings%fetch_only) return 53 | 54 | if (size(settings%name) == 0) then 55 | call deps%update(error) 56 | call handle_error(error) 57 | else 58 | do ii = 1, size(settings%name) 59 | call deps%update(trim(settings%name(ii)), error) 60 | call handle_error(error) 61 | end do 62 | end if 63 | 64 | if (len_trim(settings%dump)>0) then 65 | call deps%dump(trim(settings%dump), error, json=name_is_json(trim(settings%dump))) 66 | call handle_error(error) 67 | end if 68 | 69 | end subroutine cmd_update 70 | 71 | !> Error handling for this command 72 | subroutine handle_error(error) 73 | !> Potential error 74 | type(error_t), intent(in), optional :: error 75 | if (present(error)) then 76 | call fpm_stop(1, '*cmd_update* error: '//error%message) 77 | end if 78 | end subroutine handle_error 79 | 80 | end module fpm_cmd_update 81 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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(settings%build_dir, "cache.toml") 48 | call new_dependency_tree(deps, cache=filename, verbosity=merge(2, 1, settings%verbose), build_dir=settings%build_dir) 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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ci/test_custom_build_dir.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | # Test script for custom build directory functionality 5 | # Usage: ./test_custom_build_dir.sh [fpm_executable] [example_package_dir] 6 | 7 | if [ "$1" ]; then 8 | fpm="$1" 9 | else 10 | fpm=fpm 11 | fi 12 | 13 | if [ "$2" ]; then 14 | test_package="$2" 15 | else 16 | test_package="hello_world" 17 | fi 18 | 19 | echo "Testing custom build directory functionality with package: $test_package" 20 | 21 | # Test 1: Custom build directory with CLI option 22 | pushd "$test_package" 23 | echo "Test 1: CLI option --build-dir" 24 | rm -rf ./build custom_build_test 25 | "$fpm" build --build-dir custom_build_test 26 | test -d custom_build_test 27 | test -f custom_build_test/.gitignore 28 | "$fpm" run --build-dir custom_build_test --target "$test_package" 29 | # Verify standard build directory was not created 30 | test ! -d build 31 | echo "✓ CLI option --build-dir works" 32 | 33 | # Test 2: Environment variable 34 | echo "Test 2: Environment variable FPM_BUILD_DIR" 35 | rm -rf custom_build_test env_build_test 36 | FPM_BUILD_DIR=env_build_test "$fpm" build 37 | test -d env_build_test 38 | test -f env_build_test/.gitignore 39 | FPM_BUILD_DIR=env_build_test "$fpm" run --target "$test_package" 40 | echo "✓ Environment variable FPM_BUILD_DIR works" 41 | 42 | # Test 3: CLI option overrides environment variable 43 | echo "Test 3: CLI option overrides environment variable" 44 | rm -rf env_build_test cli_override_test 45 | FPM_BUILD_DIR=env_build_test "$fpm" build --build-dir cli_override_test 46 | test -d cli_override_test 47 | test ! -d env_build_test 48 | echo "✓ CLI option correctly overrides environment variable" 49 | 50 | # Test 4: Build directory validation - reserved names 51 | echo "Test 4: Build directory validation" 52 | # These should fail with specific error messages 53 | if "$fpm" build --build-dir src 2>&1 | grep -q "conflicts with source directory"; then 54 | echo "✓ Correctly rejected 'src'" 55 | else 56 | echo "ERROR: Should reject 'src'" && exit 1 57 | fi 58 | 59 | if "$fpm" build --build-dir app 2>&1 | grep -q "conflicts with source directory"; then 60 | echo "✓ Correctly rejected 'app'" 61 | else 62 | echo "ERROR: Should reject 'app'" && exit 1 63 | fi 64 | 65 | if "$fpm" build --build-dir test 2>&1 | grep -q "conflicts with source directory"; then 66 | echo "✓ Correctly rejected 'test'" 67 | else 68 | echo "ERROR: Should reject 'test'" && exit 1 69 | fi 70 | 71 | if "$fpm" build --build-dir . 2>&1 | grep -q "would overwrite the current"; then 72 | echo "✓ Correctly rejected '.'" 73 | else 74 | echo "ERROR: Should reject '.'" && exit 1 75 | fi 76 | 77 | # Test 5: Path normalization 78 | echo "Test 5: Path normalization" 79 | if "$fpm" build --build-dir ./src 2>&1 | grep -q "conflicts with source directory"; then 80 | echo "✓ Correctly rejected './src' (path normalization works)" 81 | else 82 | echo "ERROR: Should reject './src'" && exit 1 83 | fi 84 | 85 | # Test 6: Different commands with custom build directory 86 | echo "Test 6: Different commands with custom build directory" 87 | rm -rf test_build_all 88 | "$fpm" build --build-dir test_build_all 89 | "$fpm" run --build-dir test_build_all --target "$test_package" 90 | # Some packages may not have tests, so this might fail but that's expected 91 | "$fpm" test --build-dir test_build_all 2>/dev/null || echo "No tests in $test_package (expected)" 92 | echo "✓ All commands work with custom build directory" 93 | 94 | # Cleanup test directories 95 | rm -rf custom_build_test env_build_test cli_override_test test_build_all 96 | popd 97 | 98 | echo "All custom build directory tests passed!" 99 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | use test_features, only : collect_features 18 | 19 | implicit none 20 | integer :: stat, is 21 | character(len=:), allocatable :: suite_name, test_name 22 | type(testsuite_t), allocatable :: suite(:) 23 | character(len=*), parameter :: fmt = '("#", *(1x, a))' 24 | 25 | stat = 0 26 | 27 | suite = [ & 28 | & new_testsuite("fpm_toml", collect_toml), & 29 | & new_testsuite("fpm_manifest", collect_manifest), & 30 | & new_testsuite("fpm_features", collect_features), & 31 | & new_testsuite("fpm_filesystem", collect_filesystem), & 32 | & new_testsuite("fpm_source_parsing", collect_source_parsing), & 33 | & new_testsuite("fpm_module_dependencies", collect_module_dependencies), & 34 | & new_testsuite("fpm_package_dependencies", collect_package_dependencies), & 35 | & new_testsuite("fpm_test_backend", collect_backend), & 36 | & new_testsuite("fpm_installer", collect_installer), & 37 | & new_testsuite("fpm_versioning", collect_versioning), & 38 | & new_testsuite("fpm_settings", collect_settings), & 39 | & new_testsuite("fpm_os", collect_os), & 40 | & new_testsuite("fpm_compiler", collect_compiler) & 41 | & ] 42 | 43 | call get_argument(1, suite_name) 44 | call get_argument(2, test_name) 45 | 46 | if (allocated(suite_name)) then 47 | is = select_suite(suite, suite_name) 48 | if (is > 0 .and. is <= size(suite)) then 49 | if (allocated(test_name)) then 50 | write(error_unit, fmt) "Suite:", suite(is)%name 51 | call run_selected(suite(is)%collect, test_name, error_unit, stat) 52 | if (stat < 0) then 53 | error stop 1 54 | end if 55 | else 56 | write(error_unit, fmt) "Testing:", suite(is)%name 57 | call run_testsuite(suite(is)%collect, error_unit, stat) 58 | end if 59 | else 60 | write(error_unit, fmt) "Available testsuites" 61 | do is = 1, size(suite) 62 | write(error_unit, fmt) "-", suite(is)%name 63 | end do 64 | error stop 1 65 | end if 66 | else 67 | do is = 1, size(suite) 68 | write(error_unit, fmt) "Testing:", suite(is)%name 69 | call run_testsuite(suite(is)%collect, error_unit, stat) 70 | end do 71 | end if 72 | 73 | if (stat > 0) then 74 | write(error_unit, '(i0, 1x, a)') stat, "test(s) failed!" 75 | error stop 1 76 | end if 77 | 78 | 79 | contains 80 | 81 | 82 | !> Obtain the command line argument at a given index 83 | subroutine get_argument(idx, arg) 84 | 85 | !> Index of command line argument, range [0:command_argument_count()] 86 | integer, intent(in) :: idx 87 | 88 | !> Command line argument 89 | character(len=:), allocatable, intent(out) :: arg 90 | 91 | integer :: length, arg_stat 92 | 93 | call get_command_argument(idx, length=length, status=arg_stat) 94 | if (arg_stat /= 0) then 95 | return 96 | endif 97 | 98 | allocate(character(len=length) :: arg, stat=arg_stat) 99 | if (arg_stat /= 0) then 100 | return 101 | endif 102 | 103 | if (length > 0) then 104 | call get_command_argument(idx, arg, status=arg_stat) 105 | if (arg_stat /= 0) then 106 | deallocate(arg) 107 | return 108 | end if 109 | end if 110 | 111 | end subroutine get_argument 112 | 113 | 114 | end program fpm_testing 115 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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_classic, flag_gnu_openmp, flag_intel_openmp_win, & 6 | flag_intel_openmp, flag_pgi_openmp, flag_nag_openmp, & 7 | flag_lfortran_openmp, flag_flang_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 | !> Local variables for OpenMP testing 29 | character(:), allocatable :: openmp_flag, link_flag 30 | character(len=*), parameter :: openmp_test_fortran = & 31 | "use omp_lib; if (omp_get_max_threads() <= 0) stop 1; end" 32 | character(len=*), parameter :: openmp_test_c = & 33 | "#include " // new_line('a') // & 34 | "int main() { return omp_get_max_threads() > 0 ? 0 : 1; }" 35 | character(len=*), parameter :: openmp_test_cxx = & 36 | "#include " // new_line('a') // & 37 | "int main() { return omp_get_max_threads() > 0 ? 0 : 1; }" 38 | 39 | !> Cleanup 40 | call destroy(this) 41 | 42 | !> Set name 43 | this%name = "openmp" 44 | 45 | !> Get OpenMP flags based on compiler 46 | which_compiler: select case (compiler%id) 47 | case (id_gcc,id_f95) 48 | openmp_flag = flag_gnu_openmp 49 | link_flag = flag_gnu_openmp 50 | 51 | case (id_intel_classic_windows,id_intel_llvm_windows) 52 | openmp_flag = flag_intel_openmp_win 53 | link_flag = flag_intel_openmp_win 54 | 55 | case (id_intel_classic_nix,id_intel_classic_mac,& 56 | id_intel_llvm_nix) 57 | openmp_flag = flag_intel_openmp 58 | link_flag = flag_intel_openmp 59 | 60 | case (id_pgi,id_nvhpc) 61 | openmp_flag = flag_pgi_openmp 62 | link_flag = flag_pgi_openmp 63 | 64 | case (id_ibmxl) 65 | openmp_flag = " -qsmp=omp" 66 | link_flag = " -qsmp=omp" 67 | 68 | case (id_nag) 69 | openmp_flag = flag_nag_openmp 70 | link_flag = flag_nag_openmp 71 | 72 | case (id_lfortran) 73 | openmp_flag = flag_lfortran_openmp 74 | link_flag = flag_lfortran_openmp 75 | 76 | case (id_flang, id_flang_classic) 77 | openmp_flag = flag_flang_openmp 78 | link_flag = flag_flang_openmp 79 | 80 | case default 81 | call fatal_error(error,'openmp not supported on compiler '//compiler%name()//' yet') 82 | return 83 | 84 | end select which_compiler 85 | 86 | !> Test Fortran OpenMP support 87 | if (compiler%check_fortran_source_runs(openmp_test_fortran, openmp_flag, link_flag)) then 88 | this%has_fortran_flags = .true. 89 | this%fflags = string_t(openmp_flag) 90 | endif 91 | 92 | !> Test C OpenMP support 93 | if (compiler%check_c_source_runs(openmp_test_c, openmp_flag, link_flag)) then 94 | this%has_c_flags = .true. 95 | this%cflags = string_t(openmp_flag) 96 | endif 97 | 98 | !> Test C++ OpenMP support 99 | if (compiler%check_cxx_source_runs(openmp_test_cxx, openmp_flag, link_flag)) then 100 | this%has_cxx_flags = .true. 101 | this%cxxflags = string_t(openmp_flag) 102 | endif 103 | 104 | !> Always set link flags when OpenMP is requested 105 | !> The linker needs OpenMP flags regardless of individual compiler support 106 | this%has_link_flags = .true. 107 | this%link_flags = string_t(link_flag) 108 | 109 | end subroutine init_openmp 110 | end module fpm_meta_openmp 111 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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, add_strings 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, add_strings 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 | call add_strings(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 | call add_strings(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 | 123 | end module fpm_meta_util 124 | -------------------------------------------------------------------------------- /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/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/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 | --------------------------------------------------------------------------------